diff options
2161 files changed, 138025 insertions, 11078 deletions
diff --git a/.dockerignore b/.dockerignore index 8662306f..46051a1f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -15,6 +15,5 @@ bower.json package.json Vagrantfile web.config -Makefile bower_components node_modules diff --git a/.gitattributes b/.gitattributes index d3dac044..5ca66a71 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,16 +1,30 @@ app/constants.php export-subst +.github export-ignore .gitattributes export-ignore .gitignore export-ignore -.docker export-ignore -.scrutinizer.yml export-ignore +.dockerignore export-ignore .travis.yml export-ignore -yarn.lock export-ignore + +docker export-ignore Dockerfile export-ignore docker-compose.yml export-ignore Makefile export-ignore README.md export-ignore Vagrantfile export-ignore -issue_template.md export-ignore data/*.sqlite export-ignore tests export-ignore +CONTRIBUTING export-ignore +app.json export-ignore +bower.json export-ignore +composer.json export-ignore +composer.lock export-ignore +gulpfile.js export-ignore +package.json export-ignore + +assets/sass export-ignore +assets/js/components export-ignore +assets/js/core export-ignore +assets/js/polyfills export-ignore +assets/js/src export-ignore +assets/sass export-ignore diff --git a/.github/issue_template.md b/.github/issue_template.md index 8155eeb1..0518f9f3 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -1,31 +1,20 @@ -**Please, do not create duplicate issues** +This software focus on simplicity, the number of features is voluntary limited. ### Actual behaviour -Tell us what happens - ### Expected behaviour -Tell us what should actually happen - ### Steps to reproduce -Please supply step-by-step instructions so that anyone with the same environment can reproduce the issue. Whenever you offer a description, imagine -yourself in the shoes of someone who has never encountered this problem before and would like to know exactly how to observe it under a given set of conditions. -Here is an example: -1. Log in to the application as the administrative user -2. Click on button X, then on the modal dialog that appears, click Y -3. Wait for 3 seconds, and note the appearance of an unexpected message +### Screenshots ### Configuration -Copy and paste the configuration section from the Kanboard settings page or supply values for these fields: - - Kanboard version: - Database type and version: - PHP version: @@ -4,6 +4,7 @@ .project /.settings/ .idea +/nbproject/ .DS_Store ehthumbs.db Icon? @@ -17,7 +18,6 @@ Thumbs.db config.php data/files data/cache -/vendor *.bak !docker/kanboard/config.php node_modules @@ -1,3 +1,10 @@ +# Pass HTTP Authorization header via environment variable to PHP backend +# to make HTTP Basic Authentication work for Apache/FastCGI/php-fpm +# setups (required to authenticate over the API) +<IfModule mod_setenvif.c> + SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0 +</IfModule> + <IfModule mod_rewrite.c> Options -MultiViews diff --git a/.travis.yml b/.travis.yml index 113e7dcb..a2f8e600 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,11 +10,9 @@ php: - 5.6 - 5.5 - 5.4 - - 5.3 env: - DB=sqlite - - DB=mysql - DB=postgres matrix: @@ -23,7 +21,7 @@ matrix: before_script: - if [[ $TRAVIS_PHP_VERSION != 7.x ]]; then phpenv config-rm xdebug.ini; fi - phpenv config-add tests/php.ini - - composer install + - composer install --dev - npm install script: diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md deleted file mode 100644 index 47d9a6b5..00000000 --- a/CONTRIBUTORS.md +++ /dev/null @@ -1,159 +0,0 @@ -Contributors -============ - -Original author and main developer: Frédéric Guillot (fguillot) - -Contributors: - -- [85pando](https://github.com/85pando) -- Alex Butum -- [Aleix Pol](https://github.com/aleixpol) -- [Ally Raza](https://github.com/alirz23) -- [Angystardust](https://github.com/angystardust) -- [Anjar Febrianto](https://github.com/febrianjar) -- [Anton](https://github.com/tester22) -- [Ashbike](https://github.com/ashbike) -- [Ashish Kulkarni](https://github.com/ashkulz) -- [António Pereira](https://github.com/Shaxine) -- [Bernhard Kau](https://github.com/2ndkauboy) -- [Biniou180](https://github.com/Biniou180) -- [Bitcoin 333](https://github.com/bitcoin333) -- [Busfreak](https://github.com/Busfreak) -- [Carlos Ferreira](https://github.com/acs-ferreira) -- [Christian González](https://github.com/nerdoc) -- [Christopher Geelen](https://github.com/cdgeelen) -- [Chorgroup](https://github.com/chorgroup) -- Claudio Lobo -- [Cluxter](https://github.com/cluxter) -- [Cmer](https://github.com/chncsu) -- [Colin Williams](https://github.com/crwilliams) -- [Connor Bode](https://github.com/connorbode) -- [Cose385](https://github.com/cose385) -- [Crash5](https://github.com/crash5) -- [Creador30](https://github.com/creador30) -- [Cynthia Pereira](https://github.com/cynthiapereira) -- [d4rk5eed](https://github.com/d4rk5eed) -- [Damian](https://github.com/dromek) -- [Daniel Raknes](https://github.com/danielraknes) -- [David-Norris](https://github.com/David-Norris) -- [Diego Betto](https://github.com/diego-betto) -- [Dmitry](https://github.com/dmkcv) -- [Dj Padzensky](https://github.com/djpadz) -- [Draza (bdpsoft)](https://github.com/bdpsoft) -- [erpnedir](https://github.com/erpnedir) -- [Eskiso](https://github.com/eSkiSo) -- [Esteban Monge](https://github.com/EstebanMonge) -- [Eugene (JohnBat26)](https://github.com/JohnBat26) -- [Fábio Hideki](https://github.com/fabiohxcx) -- [Fabiano Castro Pereira](https://github.com/fabiano-pereira) -- [Filip](https://github.com/megapctr) -- [Federico Lazcano](https://github.com/lazki) -- [Fengchao](https://github.com/fengchao) -- [Floaltvater](https://github.com/floaltvater) -- [Gavlepeter](https://github.com/gavlepeter) -- [Gerardo Zamudio](https://github.com/gerardozamudio) -- [Girish Ramakrishnan](https://github.com/gramakri) -- [Goofy](https://github.com/goofy-bz) -- [Hairetdin](https://github.com/hairetdin) -- [Hendrik Stocker](https://github.com/hendrik-stoker) -- [Honda2](https://github.com/honda2) -- [Iterate From 0](https://github.com/freebsd-kanboard) -- [Jan Dittrich](https://github.com/jdittrich) -- [Janne Mäntyharju](https://github.com/JanneMantyharju) -- [Jannik Winkel](https://github.com/kiney) -- [Jean-François Magnier](https://github.com/lefakir) -- [Jeff Guillou](https://github.com/jf-guillou) -- [Jesusaplsoft](https://github.com/jesusaplsoft) -- [Jesús MarÃn](https://github.com/alu0100502114) -- [Jonas Oliveira Francisco](https://github.com/jonasof) -- [Jules Verhaeren](https://github.com/julesverhaeren) -- [JunglaCODE](https://github.com/junglaCODE) -- [Karol J](https://github.com/dzudek) -- [Kiswa](https://github.com/kiswa) -- [Kralo](https://github.com/kralo) -- [Kolesar](https://github.com/Kolesar) -- [Konstantin](https://github.com/kvj) -- [Lars Christian Schou](https://github.com/NegoZiatoR) -- [Lesstat](https://github.com/Lesstat) -- [Levlaz](https://github.com/levlaz) -- [Lim Yuen Hoe](https://github.com/jasonmoofang) -- [Åukasz Klim](https://github.com/lukas346) -- [Maces](https://github.com/maces) -- [Manish Lad](https://github.com/manishlad) -- [Mark Szymanski](https://github.com/markjszy) -- [Mathgl67](https://github.com/mathgl67) -- [Matthieu Keller](https://github.com/maggick) -- [Mauro Mariño](https://github.com/moromarino) -- [Maxime](https://github.com/EpocDotFr) -- [Max Kamashev](https://github.com/ukko) -- [mfoucrier](https://github.com/mfoucrier) -- [Matthew Cillo](https://github.com/peripatetic-sojourner) -- [Maxime Corteel](https://github.com/mcorteel) -- [Mgro](https://github.com/mgro) -- [Michael Lüpkes](https://github.com/mluepkes) -- [Mihailov Vasilievic Filho](https://github.com/mihailov-vf) -- [Moraxy](https://github.com/moraxy) -- [Muhaimin](https://github.com/infacq) -- [Nala Ginrut](https://github.com/NalaGinrut) -- [Napier](https://github.com/napiera) -- [Nekohayo](https://github.com/nekohayo) -- [Ngtech](https://github.com/ngtech) -- [Nicolas LÅ“uillet](https://github.com/nicosomb) -- [Nick Blackledge](https://github.com/nttict-nick-b) -- [Norcnorc](https://github.com/norcnorc) -- [Notrinos](https://github.com/notrinos) -- [Nramel](https://github.com/nramel) -- [Null-Kelvin](https://github.com/Null-Kelvin) -- [Ogün KarakuÅŸ](https://github.com/ogunkarakus) -- [Olaf Lessenich](https://github.com/xai) -- [Oliver Bertuch](https://github.com/poikilotherm) -- [Oliver Jakoubek](https://github.com/jakoubek) -- [Olivier Maridat](https://github.com/oliviermaridat) -- [Oren Ben-Kiki](https://github.com/orenbenkiki) -- [Patrick Keil](https://github.com/Soily) -- [Patrick Van Elk](https://github.com/patrickvanelk) -- Paolo Mainieri -- [Pavel RouÅ¡ar](https://github.com/rousarp) -- [Peller Zoltan](https://github.com/PierP) -- [Perburn](https://github.com/perburn) -- [Peripatetic-sojourner](https://github.com/peripatetic-sojourner) -- [Petja Touru](https://github.com/Petja) -- [PhilLAL](https://github.com/PhilLAL) -- [Pierre-Alexis de Solminihac](https://github.com/pa-de-solminihac) -- [Piotr ZÄ™gota](https://github.com/ZegalPL) -- [Rafaelrossa](https://github.com/rafaelrossa) -- [Raphaël Doursenaud](https://github.com/rdoursenaud) -- [René Stoltenberg](https://github.com/rstoltenberg) -- [Renothing](https://github.com/renothing) -- [Rys Sommefeldt](https://github.com/rys) -- [Rzeka](https://github.com/rzeka) -- [Sébastien Kergreis](https://github.com/kerro) -- [Sebastien Pacilly](https://github.com/spacilly) -- [Sebastian Reese](https://github.com/ReeseSebastian) -- [Semyon Novikov](https://github.com/semka) -- [StavrosKa](https://github.com/StavrosKa) -- [Stefan](https://github.com/spst) -- [Sylvain Veyrié](https://github.com/turb) -- [Thomas Lutz](https://github.com/phoen1x) -- [Thomas Stinner](https://github.com/stinnux) -- [Timo](https://github.com/BlueTeck) -- [Timotheus Pokorra](https://github.com/tpokorra) -- [Tomáš Votruba](https://github.com/TomasVotruba) -- [Toomyem](https://github.com/Toomyem) -- [Tony G. Bolaño](https://github.com/tonybolanyo) -- [Trapulo](https://github.com/Trapulo) -- [Torsten](https://github.com/misterfu) -- [Troloo](https://github.com/troloo) -- [Typz](https://github.com/Typz) -- [Valentino Pistis](https://github.com/vpistis) -- [Vedovator](https://github.com/vedovator) -- [Vitaliy S. Orlov](https://github.com/orlov0562) -- [Vladimir Babin](https://github.com/Chiliec) -- [Volodymyr Kushnir](https://github.com/volodymyr-kushnir) -- [Yannick Ihmels](https://github.com/ihmels) -- [Yakovenkov](https://github.com/yakovenkov) -- [Ybarc](https://github.com/ybarc) -- [Yu Yongwoo](https://github.com/uyu423) -- [Yuichi Murata](https://github.com/yuichi1004) - -There is also many people who have reported bugs or proposed awesome ideas. @@ -1,5 +1,158 @@ -Version 1.0.41 (unreleased) ---------------------------- +Version 1.x (unreleased) +------------------------ + +Breaking changes: + +* Remove feature "Allow everybody to access to this project" (You must define members and groups) +* Composer dependencies are now included in the repository to be able to use git-archive (except development dependencies) + +New features: + +* Add predefined templates for task descriptions +* Romanian translation + +Improvements: + +* Task CSV import is now able to handle the priority, start date, tags and one external link +* Improve iCalendar feed to include tasks with start/end date and due date with a time +* Check if the start date is before due date +* You can get an archive of Kanboard by using the download button in Github or the command git archive + +Version 1.0.48 (October 23, 2017) +--------------------------------- + +Improvements: + +* Add bulk subtasks creation +* Add filter by score/complexity +* Improved display of the header bar +* Displays bullets from lists in tooltips +* Updated translations +* Add tags and priority to task export +* Make the number of events stored in project activities configurable +* Do not use jQuery tooltip for task title in collapsed mode +* Remove dependency on Yarn +* Improve external task integration +* Add support for array parameters in automatic actions +* Add tooltip to subtask icons +* Add attribute title to external links +* Render a link if the reference is a URL +* Add icon to edit a task quickly on the board +* Improve .htaccess when using HTTP Basic Authentication for Apache/FastCGI +* Add note to specify incompatibility with mod_security + +Version 1.0.47 (October 3, 2017) +-------------------------------- + +New features: + +* Vietnamese translation + +Improvements: + +* Updated translations + +Security Issues: + +* Avoid people to alter other project resources by changing form data + +Version 1.0.46 (August 13, 2017) +-------------------------------- + +Security Issues: + +* Fix two privilege escalation issues: a standard user could reset the password +of another user (including admin) by altering form data. +(CVE-2017-12850 and CVE-2017-12851, discovered by "chbi"). + +Improvements: + +* Add "Create another link" checkbox for internal link as in sub-task creation +* Updated translations + +Bug fixes: + +* Fix parsing issue in phpToBytes() method + +Version 1.0.45 (June 23, 2017) +------------------------------ + +New features: + +* Automatic action to assign tasks to its creator +* Add the possibility to create a comment when a task is sent by email +* Add dropdown menu to autocomplete email field from project members +* Add configurable list of predefined subjects when sending a task or a a comment by email +* Add command line argument to filter overdue notification for a given project + +Improvements: + +* Improve SQL migrations when old default swimlanes have the same name as a normal swimlanes + +Bug fixes: + +* Add missing subtask permissions for project viewer role +* Fix Javascript language mapping + +Version 1.0.44 (May 28, 2017) +----------------------------- + +Improvements: + +* Use datetime field for due date +* Update Docker image to Alpine Linux 3.6 +* Add the possibility to pass API token as environment variable for Docker container +* Add wildcard search for task reference field +* Improve automated action TaskAssignColorOnDueDate to update task only when necessary +* Add task and project API formatters +* Update translations + +Bug fixes: + +* Fix broken user mentions in comment form at the bottom of the task view page +* Ensure project tags are removed when the project is removed +* Avoid PHP notice when regenerating API token for a user +* Fix wrong dropdown menu in group members list +* Show only active users in auto-complete forms (project permissions) +* Check owner existence before to create project + +Version 1.0.43 (April 30, 2017) +------------------------------- + +Improvements: + +* Add "[DUPLICATE]" prefix to duplicated tasks title +* Add sorting by position and start date in task list view +* Update translations + +Bug fixes: + +* Add missing plugin parameter for search box (Gantt and calendar plugin) +* Fix broken start date button + +Version 1.0.42 (April 8, 2017) +------------------------------ + +New features: + +* New restrictions for custom project roles + +Improvements: + +* Improved dashboard + +Breaking Changes: + +* Move calendar to external plugin: https://github.com/kanboard/plugin-calendar +* Move Gantt charts to external plugin: https://github.com/kanboard/plugin-gantt +* Move Gravatar to external plugin: https://github.com/kanboard/plugin-gravatar + +Bug fixes: + +* Fix typo in Sqlite schema + +Version 1.0.41 (March 19, 2017) +------------------------------- New features: @@ -7,6 +160,7 @@ New features: Improvements: +* Simplify dashboard to use new tasks list view * Move notifications outside of dashboard * Render QR code for TwoFactor authentication without Google Chart API * Add toggle button to show/hide subtasks in task list view @@ -15,6 +169,8 @@ Improvements: * Make user actions available from contextual menu * Change users and groups list layout * Project priority is always rendered now +* Do not list private projects when adding a new user +* Restore link for task title on board Breaking Changes: @@ -6,7 +6,6 @@ COPY docker/crontab/cronjob.alpine /var/spool/cron/crontabs/nginx COPY docker/services.d/cron /etc/services.d/cron COPY docker/php/env.conf /etc/php7/php-fpm.d/env.conf -RUN cd /var/www/app && composer --prefer-dist --no-dev --optimize-autoloader --quiet install RUN chown -R nginx:nginx /var/www/app/data /var/www/app/plugins VOLUME /var/www/app/data @@ -1,12 +1,10 @@ -BUILD_DIR = /tmp - all: static clean: @ rm -rf ./node_modules ./bower_components static: clean - @ yarn install || npm install + @ npm install @ ./node_modules/.bin/gulp bower @ ./node_modules/.bin/gulp vendor js css @ ./node_modules/.bin/jshint assets/js/{core,components,polyfills} @@ -16,40 +14,7 @@ jshint: archive: @ echo "Build archive: version=${version}, destination=${dst}" - @ rm -rf ${BUILD_DIR}/kanboard ${BUILD_DIR}/kanboard-*.zip - @ cd ${BUILD_DIR} && git clone --depth 1 -q https://github.com/kanboard/kanboard.git - @ cd ${BUILD_DIR}/kanboard && composer --prefer-dist --no-dev --optimize-autoloader --quiet install - @ rm -rf ${BUILD_DIR}/kanboard/data/*.sqlite - @ rm -rf ${BUILD_DIR}/kanboard/data/*.log - @ rm -rf ${BUILD_DIR}/kanboard/data/files - @ rm -rf ${BUILD_DIR}/kanboard/.git* - @ rm -rf ${BUILD_DIR}/kanboard/tests - @ rm -rf ${BUILD_DIR}/kanboard/Makefile - @ rm -rf ${BUILD_DIR}/kanboard/Vagrantfile - @ rm -rf ${BUILD_DIR}/kanboard/Dockerfile - @ rm -rf ${BUILD_DIR}/kanboard/docker-compose.yml - @ rm -rf ${BUILD_DIR}/kanboard/.*.yml - @ rm -rf ${BUILD_DIR}/kanboard/*.md - @ rm -rf ${BUILD_DIR}/kanboard/*.markdown - @ rm -rf ${BUILD_DIR}/kanboard/*.lock - @ rm -rf ${BUILD_DIR}/kanboard/*.json - @ rm -rf ${BUILD_DIR}/kanboard/*.js - @ rm -rf ${BUILD_DIR}/kanboard/.dockerignore - @ rm -rf ${BUILD_DIR}/kanboard/docker - @ cd ${BUILD_DIR}/kanboard && find ./vendor -name doc -type d -exec rm -rf {} +; - @ cd ${BUILD_DIR}/kanboard && find ./vendor -name notes -type d -exec rm -rf {} +; - @ cd ${BUILD_DIR}/kanboard && find ./vendor -name test -type d -exec rm -rf {} +; - @ cd ${BUILD_DIR}/kanboard && find ./vendor -name tests -type d -exec rm -rf {} +; - @ find ${BUILD_DIR}/kanboard/vendor -name composer.json -delete - @ find ${BUILD_DIR}/kanboard/vendor -name phpunit.xml -delete - @ find ${BUILD_DIR}/kanboard/vendor -name .travis.yml -delete - @ find ${BUILD_DIR}/kanboard/vendor -name README.* -delete - @ find ${BUILD_DIR}/kanboard/vendor -name .gitignore -delete - @ cd ${BUILD_DIR}/kanboard && sed -i.bak 11s/.*/"define('APP_VERSION', '${version}');"/g app/constants.php && rm -f app/*.bak - @ cd ${BUILD_DIR} && zip -r kanboard-${version}.zip kanboard > /dev/null - @ cd ${BUILD_DIR} && mv kanboard-${version}.zip ${dst} - @ cd ${dst} && if [ -L kanboard-latest.zip ]; then unlink kanboard-latest.zip; ln -s kanboard-${version}.zip kanboard-latest.zip; fi - @ rm -rf ${BUILD_DIR}/kanboard + @ git archive --format=zip --prefix=kanboard/ ${version} -o ${dst}/kanboard-${version}.zip test-sqlite-coverage: @ ./vendor/bin/phpunit --coverage-html /tmp/coverage --whitelist app/ -c tests/units.sqlite.xml @@ -67,21 +32,21 @@ test-browser: @ ./vendor/bin/phpunit -c tests/acceptance.xml integration-test-mysql: - @ composer install + @ composer install --dev @ docker-compose -f tests/docker/compose.integration.mysql.yaml build @ docker-compose -f tests/docker/compose.integration.mysql.yaml up -d mysql app @ docker-compose -f tests/docker/compose.integration.mysql.yaml up tests @ docker-compose -f tests/docker/compose.integration.mysql.yaml down integration-test-postgres: - @ composer install + @ composer install --dev @ docker-compose -f tests/docker/compose.integration.postgres.yaml build @ docker-compose -f tests/docker/compose.integration.postgres.yaml up -d postgres app @ docker-compose -f tests/docker/compose.integration.postgres.yaml up tests @ docker-compose -f tests/docker/compose.integration.postgres.yaml down integration-test-sqlite: - @ composer install + @ composer install --dev @ docker-compose -f tests/docker/compose.integration.sqlite.yaml build @ docker-compose -f tests/docker/compose.integration.sqlite.yaml up -d app @ docker-compose -f tests/docker/compose.integration.sqlite.yaml up tests @@ -2,7 +2,6 @@ Kanboard ======== [](https://travis-ci.org/kanboard/kanboard) -[](https://scrutinizer-ci.com/g/kanboard/kanboard/?branch=master) Kanboard is project management software that focuses on the Kanban methodology. @@ -23,7 +22,7 @@ Authors ------- - Main developer: [Frédéric Guillot](https://github.com/fguillot) -- [List of contributors](https://github.com/kanboard/kanboard/blob/master/CONTRIBUTORS.md) +- [List of contributors](https://github.com/kanboard/kanboard/graphs/contributors) Installation and Upgrade ------------------------ diff --git a/Vagrantfile b/Vagrantfile index 30fd6d1d..e25d755e 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -1,7 +1,7 @@ $script = <<SCRIPT apt-get update -apt-get install -y apache2 php5 php5-sqlite php5-mysql php5-pgsql php5-gd curl unzip php5-curl && \ +apt-get install -y apache2 php5 php5-sqlite php5-mysql php5-pgsql php5-gd curl unzip php5-curl php5-ldap && \ apt-get clean && \ echo "ServerName localhost" >> /etc/apache2/apache2.conf && \ sed -ri 's/AllowOverride None/AllowOverride All/g' /etc/apache2/apache2.conf && \ diff --git a/app/Action/TaskAssignColorOnDueDate.php b/app/Action/TaskAssignColorOnDueDate.php index 06b70a18..791f0cff 100644 --- a/app/Action/TaskAssignColorOnDueDate.php +++ b/app/Action/TaskAssignColorOnDueDate.php @@ -74,7 +74,7 @@ class TaskAssignColorOnDueDate extends Base $results = array(); foreach ($data['tasks'] as $task) { - if ($task['date_due'] <= time() && $task['date_due'] > 0) { + if ($task['date_due'] <= time() && $task['date_due'] > 0 && $task['color_id'] != $this->getParam('color_id')) { $values = array( 'id' => $task['id'], 'color_id' => $this->getParam('color_id'), diff --git a/app/Action/TaskAssignCreator.php b/app/Action/TaskAssignCreator.php new file mode 100644 index 00000000..ce10b35f --- /dev/null +++ b/app/Action/TaskAssignCreator.php @@ -0,0 +1,98 @@ +<?php + +namespace Kanboard\Action; + +use Kanboard\Model\TaskModel; + +/** + * Assign a task to the creator + * + * @package Kanboard\Action + * @author Frederic Guillot + */ +class TaskAssignCreator extends Base +{ + /** + * Get automatic action description + * + * @access public + * @return string + */ + public function getDescription() + { + return t('Assign the task to its creator'); + } + + /** + * Get the list of compatible events + * + * @access public + * @return array + */ + public function getCompatibleEvents() + { + return array( + TaskModel::EVENT_MOVE_COLUMN, + ); + } + + /** + * Get the required parameter for the action (defined by the user) + * + * @access public + * @return array + */ + public function getActionRequiredParameters() + { + return array( + 'column_id' => t('Column'), + ); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array( + 'task_id', + 'task' => array( + 'project_id', + 'column_id', + 'creator_id', + ), + ); + } + + /** + * Execute the action + * + * @access public + * @param array $data Event data dictionary + * @return bool True if the action was executed or false when not executed + */ + public function doAction(array $data) + { + $values = array( + 'id' => $data['task_id'], + 'owner_id' => $data['task']['creator_id'], + ); + + return $this->taskModificationModel->update($values); + } + + /** + * Check if the event data meet the action condition + * + * @access public + * @param array $data Event data dictionary + * @return bool + */ + public function hasRequiredCondition(array $data) + { + return $data['task']['column_id'] == $this->getParam('column_id'); + } +} diff --git a/app/Api/Middleware/AuthenticationMiddleware.php b/app/Api/Middleware/AuthenticationMiddleware.php index 174dc467..d2910589 100644 --- a/app/Api/Middleware/AuthenticationMiddleware.php +++ b/app/Api/Middleware/AuthenticationMiddleware.php @@ -78,6 +78,10 @@ class AuthenticationMiddleware extends Base implements MiddlewareInterface return API_AUTHENTICATION_TOKEN; } + if (getenv('API_AUTHENTICATION_TOKEN')) { + return getenv('API_AUTHENTICATION_TOKEN'); + } + return $this->configModel->get('api_token'); } } diff --git a/app/Api/Procedure/BaseProcedure.php b/app/Api/Procedure/BaseProcedure.php index e31b3027..16ef5e05 100644 --- a/app/Api/Procedure/BaseProcedure.php +++ b/app/Api/Procedure/BaseProcedure.php @@ -21,51 +21,6 @@ abstract class BaseProcedure extends Base UserAuthorization::getInstance($this->container)->check($this->getClassName(), $procedure); } - protected function formatTask($task) - { - if (! empty($task)) { - $task['url'] = $this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), '', true); - $task['color'] = $this->colorModel->getColorProperties($task['color_id']); - } - - return $task; - } - - protected function formatTasks($tasks) - { - if (! empty($tasks)) { - foreach ($tasks as &$task) { - $task = $this->formatTask($task); - } - } - - return $tasks; - } - - protected function formatProject($project) - { - if (! empty($project)) { - $project['url'] = array( - 'board' => $this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id']), '', true), - 'calendar' => $this->helper->url->to('CalendarController', 'show', array('project_id' => $project['id']), '', true), - 'list' => $this->helper->url->to('TaskListController', 'show', array('project_id' => $project['id']), '', true), - ); - } - - return $project; - } - - protected function formatProjects($projects) - { - if (! empty($projects)) { - foreach ($projects as &$project) { - $project = $this->formatProject($project); - } - } - - return $projects; - } - protected function filterValues(array $values) { foreach ($values as $key => $value) { diff --git a/app/Api/Procedure/MeProcedure.php b/app/Api/Procedure/MeProcedure.php index 71d5555b..5a64cdb3 100644 --- a/app/Api/Procedure/MeProcedure.php +++ b/app/Api/Procedure/MeProcedure.php @@ -2,8 +2,6 @@ namespace Kanboard\Api\Procedure; -use Kanboard\Model\SubtaskModel; - /** * Me API controller * @@ -19,15 +17,12 @@ class MeProcedure extends BaseProcedure public function getMyDashboard() { - $user_id = $this->userSession->getId(); - $projects = $this->projectModel->getQueryColumnStats($this->projectPermissionModel->getActiveProjectIds($user_id))->findAll(); - $tasks = $this->taskFinderModel->getUserQuery($user_id)->findAll(); + $userId = $this->userSession->getId(); - return array( - 'projects' => $this->formatProjects($projects), - 'tasks' => $this->formatTasks($tasks), - 'subtasks' => $this->subtaskModel->getUserQuery($user_id, array(SubtaskModel::STATUS_TODO, SubtaskModel::STATUS_INPROGRESS))->findAll(), - ); + return $this->taskListSubtaskAssigneeFormatter + ->withQuery($this->taskFinderModel->getUserQuery($userId)) + ->withUserId($userId) + ->format(); } public function getMyActivityStream() @@ -67,6 +62,6 @@ class MeProcedure extends BaseProcedure $project_ids = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId()); $projects = $this->projectModel->getAllByIds($project_ids); - return $this->formatProjects($projects); + return $this->projectsApiFormatter->withProjects($projects)->format(); } } diff --git a/app/Api/Procedure/ProjectProcedure.php b/app/Api/Procedure/ProjectProcedure.php index e8a34cd3..c9ac0ae6 100644 --- a/app/Api/Procedure/ProjectProcedure.php +++ b/app/Api/Procedure/ProjectProcedure.php @@ -15,33 +15,34 @@ class ProjectProcedure extends BaseProcedure public function getProjectById($project_id) { ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectById', $project_id); - return $this->formatProject($this->projectModel->getById($project_id)); + $project = $this->projectModel->getById($project_id); + return $this->projectApiFormatter->withProject($project)->format(); } public function getProjectByName($name) { $project = $this->projectModel->getByName($name); ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectByName', $project['id']); - return $this->formatProject($project); + return $this->projectApiFormatter->withProject($project)->format(); } public function getProjectByIdentifier($identifier) { - $project = $this->formatProject($this->projectModel->getByIdentifier($identifier)); + $project = $this->projectModel->getByIdentifier($identifier); ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectByIdentifier', $project['id']); - return $this->formatProject($project); + return $this->projectApiFormatter->withProject($project)->format(); } public function getProjectByEmail($email) { - $project = $this->formatProject($this->projectModel->getByEmail($email)); + $project = $this->projectModel->getByEmail($email); ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectByEmail', $project['id']); - return $this->formatProject($project); + return $this->projectApiFormatter->withProject($project)->format(); } public function getAllProjects() { - return $this->formatProjects($this->projectModel->getAll()); + return $this->projectsApiFormatter->withProjects($this->projectModel->getAll())->format(); } public function removeProject($project_id) diff --git a/app/Api/Procedure/TaskProcedure.php b/app/Api/Procedure/TaskProcedure.php index 847d336f..b1ea0516 100644 --- a/app/Api/Procedure/TaskProcedure.php +++ b/app/Api/Procedure/TaskProcedure.php @@ -24,19 +24,22 @@ class TaskProcedure extends BaseProcedure public function getTask($task_id) { TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTask', $task_id); - return $this->formatTask($this->taskFinderModel->getById($task_id)); + $task = $this->taskFinderModel->getById($task_id); + return $this->taskApiFormatter->withTask($task)->format(); } public function getTaskByReference($project_id, $reference) { ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskByReference', $project_id); - return $this->formatTask($this->taskFinderModel->getByReference($project_id, $reference)); + $task = $this->taskFinderModel->getByReference($project_id, $reference); + return $this->taskApiFormatter->withTask($task)->format(); } public function getAllTasks($project_id, $status_id = TaskModel::STATUS_OPEN) { ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTasks', $project_id); - return $this->formatTasks($this->taskFinderModel->getAll($project_id, $status_id)); + $tasks = $this->taskFinderModel->getAll($project_id, $status_id); + return $this->tasksApiFormatter->withTasks($tasks)->format(); } public function getOverdueTasks() diff --git a/app/Console/TaskOverdueNotificationCommand.php b/app/Console/TaskOverdueNotificationCommand.php index 36276615..1ef88cf5 100644 --- a/app/Console/TaskOverdueNotificationCommand.php +++ b/app/Console/TaskOverdueNotificationCommand.php @@ -2,6 +2,7 @@ namespace Kanboard\Console; +use Kanboard\Model\ProjectModel; use Kanboard\Model\TaskModel; use Kanboard\Core\Security\Role; use Symfony\Component\Console\Helper\Table; @@ -18,17 +19,30 @@ class TaskOverdueNotificationCommand extends BaseCommand ->setDescription('Send notifications for overdue tasks') ->addOption('show', null, InputOption::VALUE_NONE, 'Show sent overdue tasks') ->addOption('group', null, InputOption::VALUE_NONE, 'Group all overdue tasks for one user (from all projects) in one email') - ->addOption('manager', null, InputOption::VALUE_NONE, 'Send all overdue tasks to project manager(s) in one email'); + ->addOption('manager', null, InputOption::VALUE_NONE, 'Send all overdue tasks to project manager(s) in one email') + ->addOption('project', 'p', InputOption::VALUE_REQUIRED, 'Send notifications only the given project') + ; } protected function execute(InputInterface $input, OutputInterface $output) { + if ($input->getOption('project')) { + $tasks = $this->taskFinderModel->getOverdueTasksQuery() + ->beginOr() + ->eq(TaskModel::TABLE.'.project_id', $input->getOption('project')) + ->eq(ProjectModel::TABLE.'.identifier', $input->getOption('project')) + ->closeOr() + ->findAll(); + } else { + $tasks = $this->taskFinderModel->getOverdueTasks(); + } + if ($input->getOption('group')) { - $tasks = $this->sendGroupOverdueTaskNotifications(); + $tasks = $this->sendGroupOverdueTaskNotifications($tasks); } elseif ($input->getOption('manager')) { - $tasks = $this->sendOverdueTaskNotificationsToManagers(); + $tasks = $this->sendOverdueTaskNotificationsToManagers($tasks); } else { - $tasks = $this->sendOverdueTaskNotifications(); + $tasks = $this->sendOverdueTaskNotifications($tasks); } if ($input->getOption('show')) { @@ -44,7 +58,7 @@ class TaskOverdueNotificationCommand extends BaseCommand $rows[] = array( $task['id'], $task['title'], - date('Y-m-d', $task['date_due']), + date('Y-m-d H:i', $task['date_due']), $task['project_id'], $task['project_name'], $task['assignee_name'] ?: $task['assignee_username'], @@ -62,11 +76,11 @@ class TaskOverdueNotificationCommand extends BaseCommand * Send all overdue tasks for one user in one email * * @access public + * @param array $tasks + * @return array */ - public function sendGroupOverdueTaskNotifications() + public function sendGroupOverdueTaskNotifications(array $tasks) { - $tasks = $this->taskFinderModel->getOverdueTasks(); - foreach ($this->groupByColumn($tasks, 'owner_id') as $user_tasks) { $users = $this->userNotificationModel->getUsersWithNotificationEnabled($user_tasks[0]['project_id']); @@ -82,18 +96,18 @@ class TaskOverdueNotificationCommand extends BaseCommand * Send all overdue tasks in one email to project manager(s) * * @access public + * @param array $tasks + * @return array */ - public function sendOverdueTaskNotificationsToManagers() + public function sendOverdueTaskNotificationsToManagers(array $tasks) { - $tasks = $this->taskFinderModel->getOverdueTasks(); - foreach ($this->groupByColumn($tasks, 'project_id') as $project_id => $project_tasks) { $users = $this->userNotificationModel->getUsersWithNotificationEnabled($project_id); $managers = array(); foreach ($users as $user) { $role = $this->projectUserRoleModel->getUserRole($project_id, $user['id']); - if($role == Role::PROJECT_MANAGER) { + if ($role == Role::PROJECT_MANAGER) { $managers[] = $user; } } @@ -110,11 +124,11 @@ class TaskOverdueNotificationCommand extends BaseCommand * Send overdue tasks * * @access public + * @param array $tasks + * @return array */ - public function sendOverdueTaskNotifications() + public function sendOverdueTaskNotifications(array $tasks) { - $tasks = $this->taskFinderModel->getOverdueTasks(); - foreach ($this->groupByColumn($tasks, 'project_id') as $project_id => $project_tasks) { $users = $this->userNotificationModel->getUsersWithNotificationEnabled($project_id); diff --git a/app/Controller/ActionController.php b/app/Controller/ActionController.php index c935125a..43acf590 100644 --- a/app/Controller/ActionController.php +++ b/app/Controller/ActionController.php @@ -46,9 +46,10 @@ class ActionController extends BaseController public function confirm() { $project = $this->getProject(); + $action = $this->getAction($project); $this->response->html($this->helper->layout->project('action/remove', array( - 'action' => $this->actionModel->getById($this->request->getIntegerParam('action_id')), + 'action' => $action, 'available_events' => $this->eventManager->getAll(), 'available_actions' => $this->actionManager->getAvailableActions(), 'project' => $project, @@ -65,7 +66,7 @@ class ActionController extends BaseController { $this->checkCSRFParam(); $project = $this->getProject(); - $action = $this->actionModel->getById($this->request->getIntegerParam('action_id')); + $action = $this->getAction($project); if (! empty($action) && $this->actionModel->remove($action['id'])) { $this->flash->success(t('Action removed successfully.')); diff --git a/app/Controller/ActionCreationController.php b/app/Controller/ActionCreationController.php index 7fee58d1..abb214e6 100644 --- a/app/Controller/ActionCreationController.php +++ b/app/Controller/ActionCreationController.php @@ -35,8 +35,9 @@ class ActionCreationController extends BaseController { $project = $this->getProject(); $values = $this->request->getValues(); + $values['project_id'] = $project['id']; - if (empty($values['action_name']) || empty($values['project_id'])) { + if (empty($values['action_name'])) { return $this->create(); } @@ -57,8 +58,9 @@ class ActionCreationController extends BaseController { $project = $this->getProject(); $values = $this->request->getValues(); + $values['project_id'] = $project['id']; - if (empty($values['action_name']) || empty($values['project_id']) || empty($values['event_name'])) { + if (empty($values['action_name']) || empty($values['event_name'])) { $this->create(); return; } @@ -109,6 +111,7 @@ class ActionCreationController extends BaseController */ private function doCreation(array $project, array $values) { + $values['project_id'] = $project['id']; list($valid, ) = $this->actionValidator->validateCreation($values); if ($valid) { diff --git a/app/Controller/ActivityController.php b/app/Controller/ActivityController.php index a1734af1..7a58e670 100644 --- a/app/Controller/ActivityController.php +++ b/app/Controller/ActivityController.php @@ -19,7 +19,7 @@ class ActivityController extends BaseController { $user = $this->getUser(); - $this->response->html($this->helper->layout->dashboard('activity/user', array( + $this->response->html($this->template->render('activity/user', array( 'title' => t('Activity stream for %s', $this->helper->user->getFullname($user)), 'events' => $this->helper->projectActivity->getProjectsEvents($this->projectPermissionModel->getActiveProjectIds($user['id']), 100), 'user' => $user, diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php index 34b9c8cc..645cae7f 100644 --- a/app/Controller/AppController.php +++ b/app/Controller/AppController.php @@ -27,7 +27,7 @@ class AppController extends Base $this->response->html($this->helper->layout->app('app/forbidden', array( 'title' => t('Access Forbidden'), 'no_layout' => $withoutLayout, - ))); + )), 403); } } diff --git a/app/Controller/BaseController.php b/app/Controller/BaseController.php index 5233e27f..43ecfaab 100644 --- a/app/Controller/BaseController.php +++ b/app/Controller/BaseController.php @@ -74,13 +74,14 @@ abstract class BaseController extends Base { $task_id = $this->request->getIntegerParam('task_id'); $file_id = $this->request->getIntegerParam('file_id'); + $project_id = $this->request->getIntegerParam('project_id'); $model = 'projectFileModel'; if ($task_id > 0) { $model = 'taskFileModel'; - $project_id = $this->taskFinderModel->getProjectId($task_id); + $task_project_id = $this->taskFinderModel->getProjectId($task_id); - if ($project_id !== $this->request->getIntegerParam('project_id')) { + if ($project_id != $task_project_id) { throw new AccessForbiddenException(); } } @@ -91,6 +92,12 @@ abstract class BaseController extends Base throw new PageNotFoundException(); } + if (isset($file['task_id']) && $file['task_id'] != $task_id) { + throw new AccessForbiddenException(); + } else if (isset($file['project_id']) && $file['project_id'] != $project_id) { + throw new AccessForbiddenException(); + } + $file['model'] = $model; return $file; } @@ -138,14 +145,7 @@ abstract class BaseController extends Base return $user; } - /** - * Get the current subtask - * - * @access protected - * @return array - * @throws PageNotFoundException - */ - protected function getSubtask() + protected function getSubtask(array $task) { $subtask = $this->subtaskModel->getById($this->request->getIntegerParam('subtask_id')); @@ -153,6 +153,149 @@ abstract class BaseController extends Base throw new PageNotFoundException(); } + if ($subtask['task_id'] != $task['id']) { + throw new AccessForbiddenException(); + } + return $subtask; } + + protected function getComment(array $task) + { + $comment = $this->commentModel->getById($this->request->getIntegerParam('comment_id')); + + if (empty($comment)) { + throw new PageNotFoundException(); + } + + if (! $this->userSession->isAdmin() && $comment['user_id'] != $this->userSession->getId()) { + throw new AccessForbiddenException(); + } + + if ($comment['task_id'] != $task['id']) { + throw new AccessForbiddenException(); + } + + return $comment; + } + + protected function getExternalTaskLink(array $task) + { + $link = $this->taskExternalLinkModel->getById($this->request->getIntegerParam('link_id')); + + if (empty($link)) { + throw new PageNotFoundException(); + } + + if ($link['task_id'] != $task['id']) { + throw new AccessForbiddenException(); + } + + return $link; + } + + protected function getInternalTaskLink(array $task) + { + $link = $this->taskLinkModel->getById($this->request->getIntegerParam('link_id')); + + if (empty($link)) { + throw new PageNotFoundException(); + } + + if ($link['task_id'] != $task['id']) { + throw new AccessForbiddenException(); + } + + return $link; + } + + protected function getColumn(array $project) + { + $column = $this->columnModel->getById($this->request->getIntegerParam('column_id')); + + if (empty($column)) { + throw new PageNotFoundException(); + } + + if ($column['project_id'] != $project['id']) { + throw new AccessForbiddenException(); + } + + return $column; + } + + protected function getSwimlane(array $project) + { + $swimlane = $this->swimlaneModel->getById($this->request->getIntegerParam('swimlane_id')); + + if (empty($swimlane)) { + throw new PageNotFoundException(); + } + + if ($swimlane['project_id'] != $project['id']) { + throw new AccessForbiddenException(); + } + + return $swimlane; + } + + protected function getCategory(array $project) + { + $category = $this->categoryModel->getById($this->request->getIntegerParam('category_id')); + + if (empty($category)) { + throw new PageNotFoundException(); + } + + if ($category['project_id'] != $project['id']) { + throw new AccessForbiddenException(); + } + + return $category; + } + + protected function getProjectTag(array $project) + { + $tag = $this->tagModel->getById($this->request->getIntegerParam('tag_id')); + + if (empty($tag)) { + throw new PageNotFoundException(); + } + + if ($tag['project_id'] != $project['id']) { + throw new AccessForbiddenException(); + } + + return $tag; + } + + protected function getAction(array $project) + { + $action = $this->actionModel->getById($this->request->getIntegerParam('action_id')); + + if (empty($action)) { + throw new PageNotFoundException(); + } + + if ($action['project_id'] != $project['id']) { + throw new AccessForbiddenException(); + } + + return $action; + } + + protected function getCustomFilter(array $project) + { + $filter = $this->customFilterModel->getById($this->request->getIntegerParam('filter_id')); + + if (empty($filter)) { + throw new PageNotFoundException(); + } + + if ($filter['project_id'] != $project['id']) { + throw new AccessForbiddenException(); + } + + return $filter; + } } diff --git a/app/Controller/CalendarController.php b/app/Controller/CalendarController.php deleted file mode 100644 index e764549d..00000000 --- a/app/Controller/CalendarController.php +++ /dev/null @@ -1,120 +0,0 @@ -<?php - -namespace Kanboard\Controller; - -use Kanboard\Filter\TaskAssigneeFilter; -use Kanboard\Filter\TaskProjectFilter; -use Kanboard\Filter\TaskStatusFilter; -use Kanboard\Model\TaskModel; - -/** - * Calendar Controller - * - * @package Kanboard\Controller - * @author Frederic Guillot - * @author Timo Litzbarski - */ -class CalendarController extends BaseController -{ - /** - * Show calendar view for a user - * - * @access public - */ - public function user() - { - $user = $this->getUser(); - - $this->response->html($this->helper->layout->app('calendar/user', array( - 'user' => $user, - ))); - } - - /** - * Show calendar view for a project - * - * @access public - */ - public function project() - { - $project = $this->getProject(); - - $this->response->html($this->helper->layout->app('calendar/project', array( - 'project' => $project, - 'title' => $project['name'], - 'description' => $this->helper->projectHeader->getDescription($project), - ))); - } - - /** - * Get tasks to display on the calendar (project view) - * - * @access public - */ - public function projectEvents() - { - $project_id = $this->request->getIntegerParam('project_id'); - $start = $this->request->getStringParam('start'); - $end = $this->request->getStringParam('end'); - $search = $this->userSession->getFilters($project_id); - $queryBuilder = $this->taskLexer->build($search)->withFilter(new TaskProjectFilter($project_id)); - - $events = $this->helper->calendar->getTaskDateDueEvents(clone($queryBuilder), $start, $end); - $events = array_merge($events, $this->helper->calendar->getTaskEvents(clone($queryBuilder), $start, $end)); - - $events = $this->hook->merge('controller:calendar:project:events', $events, array( - 'project_id' => $project_id, - 'start' => $start, - 'end' => $end, - )); - - $this->response->json($events); - } - - /** - * Get tasks to display on the calendar (user view) - * - * @access public - */ - public function userEvents() - { - $user_id = $this->request->getIntegerParam('user_id'); - $start = $this->request->getStringParam('start'); - $end = $this->request->getStringParam('end'); - $queryBuilder = $this->taskQuery - ->withFilter(new TaskAssigneeFilter($user_id)) - ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)); - - $events = $this->helper->calendar->getTaskDateDueEvents(clone($queryBuilder), $start, $end); - $events = array_merge($events, $this->helper->calendar->getTaskEvents(clone($queryBuilder), $start, $end)); - - if ($this->configModel->get('calendar_user_subtasks_time_tracking') == 1) { - $events = array_merge($events, $this->helper->calendar->getSubtaskTimeTrackingEvents($user_id, $start, $end)); - } - - $events = $this->hook->merge('controller:calendar:user:events', $events, array( - 'user_id' => $user_id, - 'start' => $start, - 'end' => $end, - )); - - $this->response->json($events); - } - - /** - * Update task due date - * - * @access public - */ - public function save() - { - if ($this->request->isAjax() && $this->request->isPost()) { - $values = $this->request->getJson(); - - $this->taskModificationModel->update(array( - 'id' => $values['task_id'], - 'date_due' => substr($values['date_due'], 0, 10), - )); - } - } -} diff --git a/app/Controller/CategoryController.php b/app/Controller/CategoryController.php index 69bbad5a..e3f2406b 100644 --- a/app/Controller/CategoryController.php +++ b/app/Controller/CategoryController.php @@ -13,24 +13,6 @@ use Kanboard\Core\Controller\PageNotFoundException; class CategoryController extends BaseController { /** - * Get the category (common method between actions) - * - * @access private - * @return array - * @throws PageNotFoundException - */ - private function getCategory() - { - $category = $this->categoryModel->getById($this->request->getIntegerParam('category_id')); - - if (empty($category)) { - throw new PageNotFoundException(); - } - - return $category; - } - - /** * List of categories for a given project * * @access public @@ -72,8 +54,9 @@ class CategoryController extends BaseController public function save() { $project = $this->getProject(); - $values = $this->request->getValues(); + $values['project_id'] = $project['id']; + list($valid, $errors) = $this->categoryValidator->validateCreation($values); if ($valid) { @@ -100,7 +83,7 @@ class CategoryController extends BaseController public function edit(array $values = array(), array $errors = array()) { $project = $this->getProject(); - $category = $this->getCategory(); + $category = $this->getCategory($project); $this->response->html($this->template->render('category/edit', array( 'values' => empty($values) ? $category : $values, @@ -117,8 +100,12 @@ class CategoryController extends BaseController public function update() { $project = $this->getProject(); + $category = $this->getCategory($project); $values = $this->request->getValues(); + $values['project_id'] = $project['id']; + $values['id'] = $category['id']; + list($valid, $errors) = $this->categoryValidator->validateModification($values); if ($valid) { @@ -141,7 +128,7 @@ class CategoryController extends BaseController public function confirm() { $project = $this->getProject(); - $category = $this->getCategory(); + $category = $this->getCategory($project); $this->response->html($this->helper->layout->project('category/remove', array( 'project' => $project, @@ -158,7 +145,7 @@ class CategoryController extends BaseController { $this->checkCSRFParam(); $project = $this->getProject(); - $category = $this->getCategory(); + $category = $this->getCategory($project); if ($this->categoryModel->remove($category['id'])) { $this->flash->success(t('Category removed successfully.')); diff --git a/app/Controller/ColumnController.php b/app/Controller/ColumnController.php index 7047d30e..8e4712d9 100644 --- a/app/Controller/ColumnController.php +++ b/app/Controller/ColumnController.php @@ -61,6 +61,7 @@ class ColumnController extends BaseController { $project = $this->getProject(); $values = $this->request->getValues() + array('hide_in_dashboard' => 0); + $values['project_id'] = $project['id']; list($valid, $errors) = $this->columnValidator->validateCreation($values); @@ -95,7 +96,7 @@ class ColumnController extends BaseController public function edit(array $values = array(), array $errors = array()) { $project = $this->getProject(); - $column = $this->columnModel->getById($this->request->getIntegerParam('column_id')); + $column = $this->getColumn($project); $this->response->html($this->helper->layout->project('column/edit', array( 'errors' => $errors, @@ -113,7 +114,11 @@ class ColumnController extends BaseController public function update() { $project = $this->getProject(); + $column = $this->getColumn($project); + $values = $this->request->getValues() + array('hide_in_dashboard' => 0); + $values['project_id'] = $project['id']; + $values['id'] = $column['id']; list($valid, $errors) = $this->columnValidator->validateModification($values); @@ -164,9 +169,10 @@ class ColumnController extends BaseController public function confirm() { $project = $this->getProject(); + $column = $this->getColumn($project); $this->response->html($this->helper->layout->project('column/remove', array( - 'column' => $this->columnModel->getById($this->request->getIntegerParam('column_id')), + 'column' => $column, 'project' => $project, ))); } @@ -178,11 +184,11 @@ class ColumnController extends BaseController */ public function remove() { - $project = $this->getProject(); $this->checkCSRFParam(); - $column_id = $this->request->getIntegerParam('column_id'); + $project = $this->getProject(); + $column = $this->getColumn($project); - if ($this->columnModel->remove($column_id)) { + if ($this->columnModel->remove($column['id'])) { $this->flash->success(t('Column removed successfully.')); } else { $this->flash->failure(t('Unable to remove this column.')); diff --git a/app/Controller/ColumnMoveRestrictionController.php b/app/Controller/ColumnMoveRestrictionController.php index b12f6b77..9a75bf75 100644 --- a/app/Controller/ColumnMoveRestrictionController.php +++ b/app/Controller/ColumnMoveRestrictionController.php @@ -49,7 +49,8 @@ class ColumnMoveRestrictionController extends BaseController $project['id'], $values['role_id'], $values['src_column_id'], - $values['dst_column_id'] + $values['dst_column_id'], + isset($values['only_assigned']) && $values['only_assigned'] == 1 ); if ($restriction_id !== false) { diff --git a/app/Controller/CommentController.php b/app/Controller/CommentController.php index 9a89103e..a29491a3 100644 --- a/app/Controller/CommentController.php +++ b/app/Controller/CommentController.php @@ -14,29 +14,6 @@ use Kanboard\Core\Controller\PageNotFoundException; class CommentController extends BaseController { /** - * Get the current comment - * - * @access protected - * @return array - * @throws PageNotFoundException - * @throws AccessForbiddenException - */ - protected function getComment() - { - $comment = $this->commentModel->getById($this->request->getIntegerParam('comment_id')); - - if (empty($comment)) { - throw new PageNotFoundException(); - } - - if (! $this->userSession->isAdmin() && $comment['user_id'] != $this->userSession->getId()) { - throw new AccessForbiddenException(); - } - - return $comment; - } - - /** * Add comment form * * @access public @@ -49,14 +26,6 @@ class CommentController extends BaseController { $project = $this->getProject(); $task = $this->getTask(); - - if (empty($values)) { - $values = array( - 'user_id' => $this->userSession->getId(), - 'task_id' => $task['id'], - ); - } - $values['project_id'] = $task['project_id']; $this->response->html($this->helper->layout->task('comment/create', array( @@ -106,7 +75,7 @@ class CommentController extends BaseController public function edit(array $values = array(), array $errors = array()) { $task = $this->getTask(); - $comment = $this->getComment(); + $comment = $this->getComment($task); if (empty($values)) { $values = $comment; @@ -130,9 +99,13 @@ class CommentController extends BaseController public function update() { $task = $this->getTask(); - $this->getComment(); + $comment = $this->getComment($task); $values = $this->request->getValues(); + $values['id'] = $comment['id']; + $values['task_id'] = $task['id']; + $values['user_id'] = $comment['user_id']; + list($valid, $errors) = $this->commentValidator->validateModification($values); if ($valid) { @@ -157,7 +130,7 @@ class CommentController extends BaseController public function confirm() { $task = $this->getTask(); - $comment = $this->getComment(); + $comment = $this->getComment($task); $this->response->html($this->template->render('comment/remove', array( 'comment' => $comment, @@ -175,7 +148,7 @@ class CommentController extends BaseController { $this->checkCSRFParam(); $task = $this->getTask(); - $comment = $this->getComment(); + $comment = $this->getComment($task); if ($this->commentModel->remove($comment['id'])) { $this->flash->success(t('Comment removed successfully.')); diff --git a/app/Controller/CommentMailController.php b/app/Controller/CommentMailController.php index e30d6a55..144c9a13 100644 --- a/app/Controller/CommentMailController.php +++ b/app/Controller/CommentMailController.php @@ -20,6 +20,7 @@ class CommentMailController extends BaseController 'errors' => $errors, 'task' => $task, 'project' => $project, + 'members' => $this->projectPermissionModel->getMembersWithEmail($project['id']), ))); } diff --git a/app/Controller/ConfigController.php b/app/Controller/ConfigController.php index 6b85d1f9..2ea04b35 100644 --- a/app/Controller/ConfigController.php +++ b/app/Controller/ConfigController.php @@ -46,12 +46,6 @@ class ConfigController extends BaseController 'disable_private_project' => 0, ); break; - case 'integrations': - $values += array('integration_gravatar' => 0); - break; - case 'calendar': - $values += array('calendar_user_subtasks_time_tracking' => 0); - break; } if ($this->configModel->save($values)) { @@ -128,18 +122,6 @@ class ConfigController extends BaseController } /** - * Display the calendar settings page - * - * @access public - */ - public function calendar() - { - $this->response->html($this->helper->layout->config('config/calendar', array( - 'title' => t('Settings').' > '.t('Calendar settings'), - ))); - } - - /** * Display the integration settings page * * @access public diff --git a/app/Controller/CustomFilterController.php b/app/Controller/CustomFilterController.php index dfe1ffc4..1bf1617e 100644 --- a/app/Controller/CustomFilterController.php +++ b/app/Controller/CustomFilterController.php @@ -59,6 +59,7 @@ class CustomFilterController extends BaseController $project = $this->getProject(); $values = $this->request->getValues(); + $values['project_id'] = $project['id']; $values['user_id'] = $this->userSession->getId(); list($valid, $errors) = $this->customFilterValidator->validateCreation($values); @@ -84,7 +85,7 @@ class CustomFilterController extends BaseController public function confirm() { $project = $this->getProject(); - $filter = $this->customFilterModel->getById($this->request->getIntegerParam('filter_id')); + $filter = $this->getCustomFilter($project); $this->response->html($this->helper->layout->project('custom_filter/remove', array( 'project' => $project, @@ -102,7 +103,7 @@ class CustomFilterController extends BaseController { $this->checkCSRFParam(); $project = $this->getProject(); - $filter = $this->customFilterModel->getById($this->request->getIntegerParam('filter_id')); + $filter = $this->getCustomFilter($project); $this->checkPermission($project, $filter); @@ -153,6 +154,8 @@ class CustomFilterController extends BaseController $this->checkPermission($project, $filter); $values = $this->request->getValues(); + $values['id'] = $filter['id']; + $values['project_id'] = $project['id']; if (! isset($values['is_shared'])) { $values += array('is_shared' => 0); diff --git a/app/Controller/DashboardController.php b/app/Controller/DashboardController.php index 7fdc53ff..6758381e 100644 --- a/app/Controller/DashboardController.php +++ b/app/Controller/DashboardController.php @@ -19,12 +19,11 @@ class DashboardController extends BaseController { $user = $this->getUser(); - $this->response->html($this->helper->layout->dashboard('dashboard/show', array( - 'title' => t('Dashboard for %s', $this->helper->user->getFullname($user)), - 'project_paginator' => $this->projectPagination->getDashboardPaginator($user['id'], 'show', 10), - 'task_paginator' => $this->taskPagination->getDashboardPaginator($user['id'], 'show', 10), - 'subtask_paginator' => $this->subtaskPagination->getDashboardPaginator($user['id'], 'show', 10), - 'user' => $user, + $this->response->html($this->helper->layout->dashboard('dashboard/overview', array( + 'title' => t('Dashboard for %s', $this->helper->user->getFullname($user)), + 'user' => $user, + 'overview_paginator' => $this->dashboardPagination->getOverview($user['id']), + 'project_paginator' => $this->projectPagination->getDashboardPaginator($user['id'], 'show', 10), ))); } @@ -55,8 +54,9 @@ class DashboardController extends BaseController $this->response->html($this->helper->layout->dashboard('dashboard/subtasks', array( 'title' => t('Subtasks overview for %s', $this->helper->user->getFullname($user)), - 'paginator' => $this->subtaskPagination->getDashboardPaginator($user['id'], 'subtasks', 50), + 'paginator' => $this->subtaskPagination->getDashboardPaginator($user['id']), 'user' => $user, + 'nb_subtasks' => $this->subtaskModel->countByAssigneeAndTaskStatus($user['id']), ))); } diff --git a/app/Controller/ICalendarController.php b/app/Controller/ICalendarController.php index 4fe8b78a..38475a32 100644 --- a/app/Controller/ICalendarController.php +++ b/app/Controller/ICalendarController.php @@ -5,6 +5,7 @@ namespace Kanboard\Controller; use Kanboard\Core\Controller\AccessForbiddenException; use Kanboard\Core\Filter\QueryBuilder; use Kanboard\Filter\TaskAssigneeFilter; +use Kanboard\Filter\TaskDueDateRangeFilter; use Kanboard\Filter\TaskProjectFilter; use Kanboard\Filter\TaskStatusFilter; use Kanboard\Model\TaskModel; @@ -18,81 +19,97 @@ use Eluceo\iCal\Component\Calendar as iCalendar; */ class ICalendarController extends BaseController { - /** - * Get user iCalendar - * - * @access public - */ public function user() { $token = $this->request->getStringParam('token'); $user = $this->userModel->getByToken($token); - // Token verification if (empty($user)) { throw AccessForbiddenException::getInstance()->withoutLayout(); } - // Common filter - $queryBuilder = new QueryBuilder(); - $queryBuilder - ->withQuery($this->taskFinderModel->getICalQuery()) - ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)) - ->withFilter(new TaskAssigneeFilter($user['id'])); + $startRange = strtotime('-2 months'); + $endRange = strtotime('+6 months'); + + $startColumn = $this->configModel->get('calendar_project_tasks', 'date_started'); - // Calendar properties $calendar = new iCalendar('Kanboard'); $calendar->setName($user['name'] ?: $user['username']); $calendar->setDescription($user['name'] ?: $user['username']); $calendar->setPublishedTTL('PT1H'); - $this->renderCalendar($queryBuilder, $calendar); + $queryDueDateOnly = QueryBuilder::create() + ->withQuery($this->taskFinderModel->getICalQuery()) + ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)) + ->withFilter(new TaskDueDateRangeFilter(array($startRange, $endRange))) + ->withFilter(new TaskAssigneeFilter($user['id'])) + ->getQuery(); + + $queryStartAndDueDate = QueryBuilder::create() + ->withQuery($this->taskFinderModel->getICalQuery()) + ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)) + ->withFilter(new TaskAssigneeFilter($user['id'])) + ->getQuery() + ->addCondition($this->getConditionForTasksWithStartAndDueDate($startRange, $endRange, $startColumn, 'date_due')); + + $this->response->ical($this->taskICalFormatter + ->setCalendar($calendar) + ->addTasksWithDueDateOnly($queryDueDateOnly) + ->addTasksWithStartAndDueDate($queryStartAndDueDate, $startColumn, 'date_due') + ->format()); } - /** - * Get project iCalendar - * - * @access public - */ public function project() { $token = $this->request->getStringParam('token'); $project = $this->projectModel->getByToken($token); - // Token verification if (empty($project)) { throw AccessForbiddenException::getInstance()->withoutLayout(); } - // Common filter - $queryBuilder = new QueryBuilder(); - $queryBuilder - ->withQuery($this->taskFinderModel->getICalQuery()) - ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)) - ->withFilter(new TaskProjectFilter($project['id'])); + $startRange = strtotime('-2 months'); + $endRange = strtotime('+6 months'); + + $startColumn = $this->configModel->get('calendar_project_tasks', 'date_started'); - // Calendar properties $calendar = new iCalendar('Kanboard'); $calendar->setName($project['name']); $calendar->setDescription($project['name']); $calendar->setPublishedTTL('PT1H'); - $this->renderCalendar($queryBuilder, $calendar); + $queryDueDateOnly = QueryBuilder::create() + ->withQuery($this->taskFinderModel->getICalQuery()) + ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)) + ->withFilter(new TaskProjectFilter($project['id'])) + ->withFilter(new TaskDueDateRangeFilter(array($startRange, $endRange))) + ->getQuery(); + + $queryStartAndDueDate = QueryBuilder::create() + ->withQuery($this->taskFinderModel->getICalQuery()) + ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)) + ->withFilter(new TaskProjectFilter($project['id'])) + ->getQuery() + ->addCondition($this->getConditionForTasksWithStartAndDueDate($startRange, $endRange, $startColumn, 'date_due')); + + $this->response->ical($this->taskICalFormatter + ->setCalendar($calendar) + ->addTasksWithDueDateOnly($queryDueDateOnly) + ->addTasksWithStartAndDueDate($queryStartAndDueDate, $startColumn, 'date_due') + ->format()); } - /** - * Common method to render iCal events - * - * @access private - * @param QueryBuilder $queryBuilder - * @param iCalendar $calendar - */ - private function renderCalendar(QueryBuilder $queryBuilder, iCalendar $calendar) + protected function getConditionForTasksWithStartAndDueDate($start_time, $end_time, $start_column, $end_column) { - $start = $this->request->getStringParam('start', strtotime('-2 month')); - $end = $this->request->getStringParam('end', strtotime('+6 months')); + $start_column = $this->db->escapeIdentifier($start_column); + $end_column = $this->db->escapeIdentifier($end_column); + + $conditions = array( + "($start_column >= '$start_time' AND $start_column <= '$end_time')", + "($start_column <= '$start_time' AND $end_column >= '$start_time')", + "($start_column <= '$start_time' AND ($end_column = '0' OR $end_column IS NULL))", + ); - $this->helper->ical->addTaskDateDueEvents($queryBuilder, $calendar, $start, $end); - $this->response->ical($this->taskICalFormatter->setCalendar($calendar)->format()); + return $start_column.' IS NOT NULL AND '.$start_column.' > 0 AND ('.implode(' OR ', $conditions).')'; } } diff --git a/app/Controller/PredefinedTaskDescriptionController.php b/app/Controller/PredefinedTaskDescriptionController.php new file mode 100644 index 00000000..1b7eca12 --- /dev/null +++ b/app/Controller/PredefinedTaskDescriptionController.php @@ -0,0 +1,116 @@ +<?php + +namespace Kanboard\Controller; + +use Kanboard\Core\Controller\PageNotFoundException; + +/** + * Predefined Task Description Controller + * + * @package Kanboard\Controller + * @author Frederic Guillot + */ +class PredefinedTaskDescriptionController extends BaseController +{ + public function create(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + + $this->response->html($this->template->render('predefined_task_description/create', array( + 'values' => $values, + 'errors' => $errors, + 'project' => $project, + ))); + } + + public function save() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + + list($valid, $errors) = $this->predefinedTaskDescriptionValidator->validate($values); + + if ($valid) { + if ($this->predefinedTaskDescriptionModel->create($project['id'], $values['title'], $values['description']) !== false) { + $this->flash->success(t('Template created successfully.')); + } else { + $this->flash->failure(t('Unable to create this template.')); + } + + $this->response->redirect($this->helper->url->to('ProjectPredefinedContentController', 'show', array('project_id' => $project['id'])), true); + } else { + $this->create($values, $errors); + } + } + + public function edit(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + $template = $this->predefinedTaskDescriptionModel->getById($project['id'], $this->request->getIntegerParam('id')); + + $this->response->html($this->template->render('predefined_task_description/edit', array( + 'values' => empty($values) ? $template : $values, + 'template' => $template, + 'errors' => $errors, + 'project' => $project, + ))); + } + + public function update() + { + $project = $this->getProject(); + $template = $this->getTemplate($project); + $values = $this->request->getValues(); + + list($valid, $errors) = $this->predefinedTaskDescriptionValidator->validate($values); + + if ($valid) { + if ($this->predefinedTaskDescriptionModel->update($project['id'], $template['id'], $values['title'], $values['description']) !== false) { + $this->flash->success(t('Template updated successfully.')); + } else { + $this->flash->failure(t('Unable to update this template.')); + } + + $this->response->redirect($this->helper->url->to('ProjectPredefinedContentController', 'show', array('project_id' => $project['id'])), true); + } else { + $this->edit($values, $errors); + } + } + + public function confirm() + { + $project = $this->getProject(); + $template = $this->getTemplate($project); + + $this->response->html($this->template->render('predefined_task_description/remove', array( + 'template' => $template, + 'project' => $project, + ))); + } + + public function remove() + { + $this->checkCSRFParam(); + $project = $this->getProject(); + $template = $this->getTemplate($project); + + if ($this->predefinedTaskDescriptionModel->remove($project['id'], $template['id'])) { + $this->flash->success(t('Template removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this template.')); + } + + $this->response->redirect($this->helper->url->to('ProjectPredefinedContentController', 'show', array('project_id' => $project['id'])), true); + } + + protected function getTemplate(array $project) + { + $template = $this->predefinedTaskDescriptionModel->getById($project['id'], $this->request->getIntegerParam('id')); + + if (empty($template)) { + throw new PageNotFoundException(); + } + + return $template; + } +}
\ No newline at end of file diff --git a/app/Controller/ProjectEditController.php b/app/Controller/ProjectEditController.php index ae39fdf3..dd534508 100644 --- a/app/Controller/ProjectEditController.php +++ b/app/Controller/ProjectEditController.php @@ -65,6 +65,8 @@ class ProjectEditController extends BaseController */ private function prepareValues(array $project, array $values) { + $values['id'] = $project['id']; + if (isset($values['is_private'])) { if (! $this->helper->user->hasProjectAccess('ProjectCreationController', 'create', $project['id'])) { unset($values['is_private']); diff --git a/app/Controller/ProjectGanttController.php b/app/Controller/ProjectGanttController.php deleted file mode 100644 index 8239005e..00000000 --- a/app/Controller/ProjectGanttController.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php - -namespace Kanboard\Controller; - -use Kanboard\Filter\ProjectIdsFilter; -use Kanboard\Filter\ProjectStatusFilter; -use Kanboard\Filter\ProjectTypeFilter; -use Kanboard\Model\ProjectModel; - -/** - * Projects Gantt Controller - * - * @package Kanboard\Controller - * @author Frederic Guillot - */ -class ProjectGanttController extends BaseController -{ - /** - * Show Gantt chart for all projects - */ - public function show() - { - $project_ids = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId()); - $filter = $this->projectQuery - ->withFilter(new ProjectTypeFilter(ProjectModel::TYPE_TEAM)) - ->withFilter(new ProjectStatusFilter(ProjectModel::ACTIVE)) - ->withFilter(new ProjectIdsFilter($project_ids)); - - $filter->getQuery()->asc(ProjectModel::TABLE.'.start_date'); - - $this->response->html($this->helper->layout->app('project_gantt/show', array( - 'projects' => $filter->format($this->projectGanttFormatter), - 'title' => t('Gantt chart for all projects'), - ))); - } - - /** - * Save new project start date and end date - */ - public function save() - { - $values = $this->request->getJson(); - - $result = $this->projectModel->update(array( - 'id' => $values['id'], - 'start_date' => $this->dateParser->getIsoDate(strtotime($values['start'])), - 'end_date' => $this->dateParser->getIsoDate(strtotime($values['end'])), - )); - - if (! $result) { - $this->response->json(array('message' => 'Unable to save project'), 400); - } else { - $this->response->json(array('message' => 'OK'), 201); - } - } -} diff --git a/app/Controller/ProjectPermissionController.php b/app/Controller/ProjectPermissionController.php index 56777b25..3fb6c090 100644 --- a/app/Controller/ProjectPermissionController.php +++ b/app/Controller/ProjectPermissionController.php @@ -60,25 +60,6 @@ class ProjectPermissionController extends BaseController } /** - * Allow everybody - * - * @access public - */ - public function allowEverybody() - { - $project = $this->getProject(); - $values = $this->request->getValues() + array('is_everybody_allowed' => 0); - - if ($this->projectModel->update($values)) { - $this->flash->success(t('Project updated successfully.')); - } else { - $this->flash->failure(t('Unable to update this project.')); - } - - $this->response->redirect($this->helper->url->to('ProjectPermissionController', 'index', array('project_id' => $project['id']))); - } - - /** * Add user to the project * * @access public @@ -88,6 +69,10 @@ class ProjectPermissionController extends BaseController $project = $this->getProject(); $values = $this->request->getValues(); + if (empty($values['user_id']) && ! empty($values['external_id']) && ! empty($values['external_id_column'])) { + $values['user_id'] = $this->userModel->getOrCreateExternalUserId($values['username'], $values['name'], $values['external_id_column'], $values['external_id']); + } + if (empty($values['user_id'])) { $this->flash->failure(t('User not found.')); } elseif ($this->projectUserRoleModel->addUser($values['project_id'], $values['user_id'], $values['role'])) { diff --git a/app/Controller/ProjectPredefinedContentController.php b/app/Controller/ProjectPredefinedContentController.php new file mode 100644 index 00000000..709d6b79 --- /dev/null +++ b/app/Controller/ProjectPredefinedContentController.php @@ -0,0 +1,50 @@ +<?php + +namespace Kanboard\Controller; + +/** + * Project Predefined Content Controller + * + * @package Kanboard\Controller + * @author Frederic Guillot + */ +class ProjectPredefinedContentController extends BaseController +{ + public function show(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + + $this->response->html($this->helper->layout->project('project_predefined_content/show', array( + 'values' => empty($values) ? $project : $values, + 'errors' => $errors, + 'project' => $project, + 'predefined_task_descriptions' => $this->predefinedTaskDescriptionModel->getAll($project['id']), + 'title' => t('Predefined Contents'), + ))); + } + + public function update() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + + $values = array( + 'id' => $project['id'], + 'name' => $project['name'], + 'predefined_email_subjects' => isset($values['predefined_email_subjects']) ? $values['predefined_email_subjects'] : '', + ); + + list($valid, $errors) = $this->projectValidator->validateModification($values); + + if ($valid) { + if ($this->projectModel->update($values)) { + $this->flash->success(t('Project updated successfully.')); + return $this->response->redirect($this->helper->url->to('ProjectPredefinedContentController', 'show', array('project_id' => $project['id'])), true); + } else { + $this->flash->failure(t('Unable to update this project.')); + } + } + + return $this->show($values, $errors); + } +} diff --git a/app/Controller/ProjectStatusController.php b/app/Controller/ProjectStatusController.php index 5bc445be..4fdbb6ea 100644 --- a/app/Controller/ProjectStatusController.php +++ b/app/Controller/ProjectStatusController.php @@ -95,6 +95,6 @@ class ProjectStatusController extends BaseController $this->flash->failure(t('Unable to remove this project.')); } - $this->response->redirect($this->helper->url->to('ProjectListController', 'show'), true); + $this->response->redirect($this->helper->url->to('ProjectListController', 'show')); } } diff --git a/app/Controller/ProjectTagController.php b/app/Controller/ProjectTagController.php index d225f0ca..c45e71e1 100644 --- a/app/Controller/ProjectTagController.php +++ b/app/Controller/ProjectTagController.php @@ -2,8 +2,6 @@ namespace Kanboard\Controller; -use Kanboard\Core\Controller\AccessForbiddenException; - /** * Class ProjectTagController * @@ -27,10 +25,6 @@ class ProjectTagController extends BaseController { $project = $this->getProject(); - if (empty($values)) { - $values['project_id'] = $project['id']; - } - $this->response->html($this->template->render('project_tag/create', array( 'project' => $project, 'values' => $values, @@ -42,6 +36,8 @@ class ProjectTagController extends BaseController { $project = $this->getProject(); $values = $this->request->getValues(); + $values['project_id'] = $project['id']; + list($valid, $errors) = $this->tagValidator->validateCreation($values); if ($valid) { @@ -60,8 +56,7 @@ class ProjectTagController extends BaseController public function edit(array $values = array(), array $errors = array()) { $project = $this->getProject(); - $tag_id = $this->request->getIntegerParam('tag_id'); - $tag = $this->tagModel->getById($tag_id); + $tag = $this->getProjectTag($project); if (empty($values)) { $values = $tag; @@ -78,14 +73,12 @@ class ProjectTagController extends BaseController public function update() { $project = $this->getProject(); - $tag_id = $this->request->getIntegerParam('tag_id'); - $tag = $this->tagModel->getById($tag_id); + $tag = $this->getProjectTag($project); $values = $this->request->getValues(); - list($valid, $errors) = $this->tagValidator->validateModification($values); + $values['project_id'] = $project['id']; + $values['id'] = $tag['id']; - if ($tag['project_id'] != $project['id']) { - throw new AccessForbiddenException(); - } + list($valid, $errors) = $this->tagValidator->validateModification($values); if ($valid) { if ($this->tagModel->update($values['id'], $values['name'])) { @@ -103,8 +96,7 @@ class ProjectTagController extends BaseController public function confirm() { $project = $this->getProject(); - $tag_id = $this->request->getIntegerParam('tag_id'); - $tag = $this->tagModel->getById($tag_id); + $tag = $this->getProjectTag($project); $this->response->html($this->template->render('project_tag/remove', array( 'tag' => $tag, @@ -116,14 +108,9 @@ class ProjectTagController extends BaseController { $this->checkCSRFParam(); $project = $this->getProject(); - $tag_id = $this->request->getIntegerParam('tag_id'); - $tag = $this->tagModel->getById($tag_id); - - if ($tag['project_id'] != $project['id']) { - throw new AccessForbiddenException(); - } + $tag = $this->getProjectTag($project); - if ($this->tagModel->remove($tag_id)) { + if ($this->tagModel->remove($tag['id'])) { $this->flash->success(t('Tag removed successfully.')); } else { $this->flash->failure(t('Unable to remove this tag.')); diff --git a/app/Controller/SubtaskController.php b/app/Controller/SubtaskController.php index 5fa55f6b..d5db9b2e 100644 --- a/app/Controller/SubtaskController.php +++ b/app/Controller/SubtaskController.php @@ -66,29 +66,51 @@ class SubtaskController extends BaseController { $task = $this->getTask(); $values = $this->request->getValues(); + $values['task_id'] = $task['id']; + $subtasks = explode("\r\n", isset($values['title']) ? $values['title'] : ''); + $subtasksAdded = 0; - list($valid, $errors) = $this->subtaskValidator->validateCreation($values); + foreach ($subtasks as $subtask) { + $subtask = trim($subtask); - if ($valid) { - if ($this->subtaskModel->create($values) !== false) { - $this->flash->success(t('Sub-task added successfully.')); - } else { - $this->flash->failure(t('Unable to create your sub-task.')); - } + if (! empty($subtask)) { + $subtaskValues = $values; + $subtaskValues['title'] = $subtask; + + list($valid, $errors) = $this->subtaskValidator->validateCreation($subtaskValues); + + if (! $valid) { + $this->create($values, $errors); + return false; + } - if (isset($values['another_subtask']) && $values['another_subtask'] == 1) { - return $this->create(array( - 'project_id' => $task['project_id'], - 'task_id' => $task['id'], - 'user_id' => $values['user_id'], - 'another_subtask' => 1 - )); + if (! $this->subtaskModel->create($subtaskValues)) { + $this->flash->failure(t('Unable to create your sub-task.')); + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks'), true); + return false; + } + + $subtasksAdded++; } + } - return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks'), true); + if (isset($values['another_subtask']) && $values['another_subtask'] == 1) { + return $this->create(array( + 'project_id' => $task['project_id'], + 'task_id' => $task['id'], + 'user_id' => $values['user_id'], + 'another_subtask' => 1, + 'subtasks_added' => $subtasksAdded, + )); + } else if ($subtasksAdded > 0) { + if ($subtasksAdded === 1) { + $this->flash->success(t('Subtask added successfully.')); + } else { + $this->flash->success(t('%d subtasks added successfully.', $subtasksAdded)); + } } - return $this->create($values, $errors); + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks'), true); } /** @@ -103,7 +125,7 @@ class SubtaskController extends BaseController public function edit(array $values = array(), array $errors = array()) { $task = $this->getTask(); - $subtask = $this->getSubtask(); + $subtask = $this->getSubtask($task); $this->response->html($this->template->render('subtask/edit', array( 'values' => empty($values) ? $subtask : $values, @@ -123,9 +145,12 @@ class SubtaskController extends BaseController public function update() { $task = $this->getTask(); - $this->getSubtask(); + $subtask = $this->getSubtask($task); $values = $this->request->getValues(); + $values['id'] = $subtask['id']; + $values['task_id'] = $task['id']; + list($valid, $errors) = $this->subtaskValidator->validateModification($values); if ($valid) { @@ -149,7 +174,7 @@ class SubtaskController extends BaseController public function confirm() { $task = $this->getTask(); - $subtask = $this->getSubtask(); + $subtask = $this->getSubtask($task); $this->response->html($this->template->render('subtask/remove', array( 'subtask' => $subtask, @@ -166,7 +191,7 @@ class SubtaskController extends BaseController { $this->checkCSRFParam(); $task = $this->getTask(); - $subtask = $this->getSubtask(); + $subtask = $this->getSubtask($task); if ($this->subtaskModel->remove($subtask['id'])) { $this->flash->success(t('Sub-task removed successfully.')); diff --git a/app/Controller/SubtaskConverterController.php b/app/Controller/SubtaskConverterController.php index 404c50d0..dafbb7e0 100644 --- a/app/Controller/SubtaskConverterController.php +++ b/app/Controller/SubtaskConverterController.php @@ -13,7 +13,7 @@ class SubtaskConverterController extends BaseController public function show() { $task = $this->getTask(); - $subtask = $this->getSubtask(); + $subtask = $this->getSubtask($task); $this->response->html($this->template->render('subtask_converter/show', array( 'subtask' => $subtask, @@ -24,7 +24,8 @@ class SubtaskConverterController extends BaseController public function save() { $project = $this->getProject(); - $subtask = $this->getSubtask(); + $task = $this->getTask(); + $subtask = $this->getSubtask($task); $task_id = $this->subtaskTaskConversionModel->convertToTask($project['id'], $subtask['id']); diff --git a/app/Controller/SubtaskRestrictionController.php b/app/Controller/SubtaskRestrictionController.php index 0e207674..315b023d 100644 --- a/app/Controller/SubtaskRestrictionController.php +++ b/app/Controller/SubtaskRestrictionController.php @@ -20,7 +20,7 @@ class SubtaskRestrictionController extends BaseController public function show() { $task = $this->getTask(); - $subtask = $this->getSubtask(); + $subtask = $this->getSubtask($task); $this->response->html($this->template->render('subtask_restriction/show', array( 'status_list' => array( @@ -41,21 +41,19 @@ class SubtaskRestrictionController extends BaseController public function save() { $task = $this->getTask(); - $subtask = $this->getSubtask(); + $subtask = $this->getSubtask($task); $values = $this->request->getValues(); // Change status of the previous "in progress" subtask $this->subtaskModel->update(array( 'id' => $values['id'], 'status' => $values['status'], - 'task_id' => $task['id'], )); // Set the current subtask to "in progress" $this->subtaskModel->update(array( 'id' => $subtask['id'], 'status' => SubtaskModel::STATUS_INPROGRESS, - 'task_id' => $task['id'], )); $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true); diff --git a/app/Controller/SubtaskStatusController.php b/app/Controller/SubtaskStatusController.php index 72feb685..c912848e 100644 --- a/app/Controller/SubtaskStatusController.php +++ b/app/Controller/SubtaskStatusController.php @@ -18,12 +18,21 @@ class SubtaskStatusController extends BaseController public function change() { $task = $this->getTask(); - $subtask = $this->getSubtask(); + $subtask = $this->getSubtask($task); + $fragment = $this->request->getStringParam('fragment'); $status = $this->subtaskStatusModel->toggleStatus($subtask['id']); $subtask['status'] = $status; - $this->response->html($this->helper->subtask->renderToggleStatus($task, $subtask)); + if ($fragment === 'table') { + $html = $this->renderTable($task); + } elseif ($fragment === 'rows') { + $html = $this->renderRows($task); + } else { + $html = $this->helper->subtask->renderToggleStatus($task, $subtask); + } + + $this->response->html($html); } /** @@ -34,19 +43,58 @@ class SubtaskStatusController extends BaseController public function timer() { $task = $this->getTask(); - $subtaskId = $this->request->getIntegerParam('subtask_id'); + $subtask = $this->getSubtask($task); $timer = $this->request->getStringParam('timer'); if ($timer === 'start') { - $this->subtaskTimeTrackingModel->logStartTime($subtaskId, $this->userSession->getId()); + $this->subtaskTimeTrackingModel->logStartTime($subtask['id'], $this->userSession->getId()); } elseif ($timer === 'stop') { - $this->subtaskTimeTrackingModel->logEndTime($subtaskId, $this->userSession->getId()); + $this->subtaskTimeTrackingModel->logEndTime($subtask['id'], $this->userSession->getId()); $this->subtaskTimeTrackingModel->updateTaskTimeTracking($task['id']); } $this->response->html($this->template->render('subtask/timer', array( 'task' => $task, - 'subtask' => $this->subtaskModel->getByIdWithDetails($subtaskId), + 'subtask' => $this->subtaskModel->getByIdWithDetails($subtask['id']), ))); } + + /** + * Render table + * + * @access protected + * @param array $task + * @return string + */ + protected function renderTable(array $task) + { + return $this->template->render('subtask/table', array( + 'task' => $task, + 'subtasks' => $this->subtaskModel->getAll($task['id']), + 'editable' => true, + )); + } + + /** + * Render task list rows + * + * @access protected + * @param array $task + * @return string + */ + protected function renderRows(array $task) + { + $userId = $this->request->getIntegerParam('user_id'); + + if ($userId > 0) { + $task['subtasks'] = $this->subtaskModel->getAllByTaskIdsAndAssignee(array($task['id']), $userId); + } else { + $task['subtasks'] = $this->subtaskModel->getAll($task['id']); + } + + return $this->template->render('task_list/task_subtasks', array( + 'task' => $task, + 'user_id' => $userId, + )); + } } diff --git a/app/Controller/SwimlaneController.php b/app/Controller/SwimlaneController.php index 0d81d83c..e6368b24 100644 --- a/app/Controller/SwimlaneController.php +++ b/app/Controller/SwimlaneController.php @@ -3,8 +3,6 @@ namespace Kanboard\Controller; use Kanboard\Core\Controller\AccessForbiddenException; -use Kanboard\Core\Controller\PageNotFoundException; -use Kanboard\Model\SwimlaneModel; /** * Swimlanes Controller @@ -15,24 +13,6 @@ use Kanboard\Model\SwimlaneModel; class SwimlaneController extends BaseController { /** - * Get the swimlane (common method between actions) - * - * @access private - * @return array - * @throws PageNotFoundException - */ - private function getSwimlane() - { - $swimlane = $this->swimlaneModel->getById($this->request->getIntegerParam('swimlane_id')); - - if (empty($swimlane)) { - throw new PageNotFoundException(); - } - - return $swimlane; - } - - /** * List of swimlanes for a given project * * @access public @@ -78,6 +58,8 @@ class SwimlaneController extends BaseController { $project = $this->getProject(); $values = $this->request->getValues(); + $values['project_id'] = $project['id']; + list($valid, $errors) = $this->swimlaneValidator->validateCreation($values); if ($valid) { @@ -104,7 +86,7 @@ class SwimlaneController extends BaseController public function edit(array $values = array(), array $errors = array()) { $project = $this->getProject(); - $swimlane = $this->getSwimlane(); + $swimlane = $this->getSwimlane($project); $this->response->html($this->helper->layout->project('swimlane/edit', array( 'values' => empty($values) ? $swimlane : $values, @@ -121,8 +103,11 @@ class SwimlaneController extends BaseController public function update() { $project = $this->getProject(); - + $swimlane = $this->getSwimlane($project); $values = $this->request->getValues(); + $values['project_id'] = $project['id']; + $values['id'] = $swimlane['id']; + list($valid, $errors) = $this->swimlaneValidator->validateModification($values); if ($valid) { @@ -145,7 +130,7 @@ class SwimlaneController extends BaseController public function confirm() { $project = $this->getProject(); - $swimlane = $this->getSwimlane(); + $swimlane = $this->getSwimlane($project); $this->response->html($this->helper->layout->project('swimlane/remove', array( 'project' => $project, @@ -162,9 +147,9 @@ class SwimlaneController extends BaseController { $this->checkCSRFParam(); $project = $this->getProject(); - $swimlane_id = $this->request->getIntegerParam('swimlane_id'); + $swimlane = $this->getSwimlane($project); - if ($this->swimlaneModel->remove($project['id'], $swimlane_id)) { + if ($this->swimlaneModel->remove($project['id'], $swimlane['id'])) { $this->flash->success(t('Swimlane removed successfully.')); } else { $this->flash->failure(t('Unable to remove this swimlane.')); @@ -182,9 +167,9 @@ class SwimlaneController extends BaseController { $this->checkCSRFParam(); $project = $this->getProject(); - $swimlane_id = $this->request->getIntegerParam('swimlane_id'); + $swimlane = $this->getSwimlane($project); - if ($this->swimlaneModel->disable($project['id'], $swimlane_id)) { + if ($this->swimlaneModel->disable($project['id'], $swimlane['id'])) { $this->flash->success(t('Swimlane updated successfully.')); } else { $this->flash->failure(t('Unable to update this swimlane.')); @@ -202,9 +187,9 @@ class SwimlaneController extends BaseController { $this->checkCSRFParam(); $project = $this->getProject(); - $swimlane_id = $this->request->getIntegerParam('swimlane_id'); + $swimlane = $this->getSwimlane($project); - if ($this->swimlaneModel->enable($project['id'], $swimlane_id)) { + if ($this->swimlaneModel->enable($project['id'], $swimlane['id'])) { $this->flash->success(t('Swimlane updated successfully.')); } else { $this->flash->failure(t('Unable to update this swimlane.')); diff --git a/app/Controller/TaskBulkController.php b/app/Controller/TaskBulkController.php index 4345a68f..0f49fb76 100644 --- a/app/Controller/TaskBulkController.php +++ b/app/Controller/TaskBulkController.php @@ -35,6 +35,7 @@ class TaskBulkController extends BaseController 'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true, false, $project['is_private'] == 1), 'colors_list' => $this->colorModel->getList(), 'categories_list' => $this->categoryModel->getList($project['id']), + 'task_description_templates' => $this->predefinedTaskDescriptionModel->getList($project['id']), ))); } @@ -85,8 +86,18 @@ class TaskBulkController extends BaseController 'owner_id' => empty($values['owner_id']) ? 0 : $values['owner_id'], 'color_id' => $values['color_id'], 'project_id' => $project['id'], + 'description' => $this->getTaskDescription($project, $values), )); } } } + + protected function getTaskDescription(array $project, array $values) + { + if (empty($values['task_description_template_id'])) { + return ''; + } + + return $this->predefinedTaskDescriptionModel->getDescriptionById($project['id'], $values['task_description_template_id']); + } } diff --git a/app/Controller/TaskCreationController.php b/app/Controller/TaskCreationController.php index 0d808c54..d050a60d 100644 --- a/app/Controller/TaskCreationController.php +++ b/app/Controller/TaskCreationController.php @@ -49,6 +49,7 @@ class TaskCreationController extends BaseController { $project = $this->getProject(); $values = $this->request->getValues(); + $values['project_id'] = $project['id']; list($valid, $errors) = $this->taskValidator->validateCreation($values); diff --git a/app/Controller/TaskExternalLinkController.php b/app/Controller/TaskExternalLinkController.php index df23f87b..946451fc 100644 --- a/app/Controller/TaskExternalLinkController.php +++ b/app/Controller/TaskExternalLinkController.php @@ -74,6 +74,8 @@ class TaskExternalLinkController extends BaseController { $task = $this->getTask(); $values = $this->request->getValues(); + $values['task_id'] = $task['id']; + list($valid, $errors) = $this->externalLinkValidator->validateCreation($values); if ($valid) { @@ -108,22 +110,14 @@ class TaskExternalLinkController extends BaseController public function edit(array $values = array(), array $errors = array()) { $task = $this->getTask(); - $link_id = $this->request->getIntegerParam('link_id'); - - if ($link_id > 0) { - $values = $this->taskExternalLinkModel->getById($link_id); - } - - if (empty($values)) { - throw new PageNotFoundException(); - } - - $provider = $this->externalLinkManager->getProvider($values['link_type']); + $link = $this->getExternalTaskLink($task); + $provider = $this->externalLinkManager->getProvider($link['link_type']); $this->response->html($this->template->render('task_external_link/edit', array( - 'values' => $values, - 'errors' => $errors, - 'task' => $task, + 'values' => empty($values) ? $link : $values, + 'errors' => $errors, + 'task' => $task, + 'link' => $link, 'dependencies' => $provider->getDependencies(), ))); } @@ -136,7 +130,12 @@ class TaskExternalLinkController extends BaseController public function update() { $task = $this->getTask(); + $link = $this->getExternalTaskLink($task); + $values = $this->request->getValues(); + $values['id'] = $link['id']; + $values['task_id'] = $link['task_id']; + list($valid, $errors) = $this->externalLinkValidator->validateModification($values); if ($valid && $this->taskExternalLinkModel->update($values)) { @@ -155,12 +154,7 @@ class TaskExternalLinkController extends BaseController public function confirm() { $task = $this->getTask(); - $link_id = $this->request->getIntegerParam('link_id'); - $link = $this->taskExternalLinkModel->getById($link_id); - - if (empty($link)) { - throw new PageNotFoundException(); - } + $link = $this->getExternalTaskLink($task); $this->response->html($this->template->render('task_external_link/remove', array( 'link' => $link, @@ -177,8 +171,9 @@ class TaskExternalLinkController extends BaseController { $this->checkCSRFParam(); $task = $this->getTask(); + $link = $this->getExternalTaskLink($task); - if ($this->taskExternalLinkModel->remove($this->request->getIntegerParam('link_id'))) { + if ($this->taskExternalLinkModel->remove($link['id'])) { $this->flash->success(t('Link removed successfully.')); } else { $this->flash->failure(t('Unable to remove this link.')); diff --git a/app/Controller/TaskGanttController.php b/app/Controller/TaskGanttController.php deleted file mode 100644 index b03b9d00..00000000 --- a/app/Controller/TaskGanttController.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php - -namespace Kanboard\Controller; - -use Kanboard\Filter\TaskProjectFilter; -use Kanboard\Model\TaskModel; - -/** - * Tasks Gantt Controller - * - * @package Kanboard\Controller - * @author Frederic Guillot - */ -class TaskGanttController extends BaseController -{ - /** - * Show Gantt chart for one project - */ - public function show() - { - $project = $this->getProject(); - $search = $this->helper->projectHeader->getSearchQuery($project); - $sorting = $this->request->getStringParam('sorting', 'board'); - $filter = $this->taskLexer->build($search)->withFilter(new TaskProjectFilter($project['id'])); - - if ($sorting === 'date') { - $filter->getQuery()->asc(TaskModel::TABLE.'.date_started')->asc(TaskModel::TABLE.'.date_creation'); - } else { - $filter->getQuery()->asc('column_position')->asc(TaskModel::TABLE.'.position'); - } - - $this->response->html($this->helper->layout->app('task_gantt/show', array( - 'project' => $project, - 'title' => $project['name'], - 'description' => $this->helper->projectHeader->getDescription($project), - 'sorting' => $sorting, - 'tasks' => $filter->format($this->taskGanttFormatter), - ))); - } - - /** - * Save new task start date and due date - */ - public function save() - { - $this->getProject(); - $values = $this->request->getJson(); - - $result = $this->taskModificationModel->update(array( - 'id' => $values['id'], - 'date_started' => strtotime($values['start']), - 'date_due' => strtotime($values['end']), - )); - - if (! $result) { - $this->response->json(array('message' => 'Unable to save task'), 400); - } else { - $this->response->json(array('message' => 'OK'), 201); - } - } -} diff --git a/app/Controller/TaskImportController.php b/app/Controller/TaskImportController.php index 2e323979..d6506880 100644 --- a/app/Controller/TaskImportController.php +++ b/app/Controller/TaskImportController.php @@ -3,6 +3,7 @@ namespace Kanboard\Controller; use Kanboard\Core\Csv; +use Kanboard\Import\TaskImport; /** * Task Import controller @@ -45,14 +46,15 @@ class TaskImportController extends BaseController if (! file_exists($filename)) { $this->show($values, array('file' => array(t('Unable to read your file')))); } else { - $this->taskImport->projectId = $project['id']; + $taskImport = new TaskImport($this->container); + $taskImport->setProjectId($project['id']); $csv = new Csv($values['delimiter'], $values['enclosure']); - $csv->setColumnMapping($this->taskImport->getColumnMapping()); - $csv->read($filename, array($this->taskImport, 'import')); + $csv->setColumnMapping($taskImport->getColumnMapping()); + $csv->read($filename, array($taskImport, 'importTask')); - if ($this->taskImport->counter > 0) { - $this->flash->success(t('%d task(s) have been imported successfully.', $this->taskImport->counter)); + if ($taskImport->getNumberOfImportedTasks() > 0) { + $this->flash->success(t('%d task(s) have been imported successfully.', $taskImport->getNumberOfImportedTasks())); } else { $this->flash->failure(t('Nothing have been imported!')); } @@ -67,7 +69,8 @@ class TaskImportController extends BaseController */ public function template() { + $taskImport = new TaskImport($this->container); $this->response->withFileDownload('tasks.csv'); - $this->response->csv(array($this->taskImport->getColumnMapping())); + $this->response->csv(array($taskImport->getColumnMapping())); } } diff --git a/app/Controller/TaskInternalLinkController.php b/app/Controller/TaskInternalLinkController.php index a140f1ff..02cc15c4 100644 --- a/app/Controller/TaskInternalLinkController.php +++ b/app/Controller/TaskInternalLinkController.php @@ -14,24 +14,6 @@ use Kanboard\Core\Controller\PageNotFoundException; class TaskInternalLinkController extends BaseController { /** - * Get the current link - * - * @access private - * @return array - * @throws PageNotFoundException - */ - private function getTaskLink() - { - $link = $this->taskLinkModel->getById($this->request->getIntegerParam('link_id')); - - if (empty($link)) { - throw new PageNotFoundException(); - } - - return $link; - } - - /** * Creation form * * @access public @@ -44,6 +26,11 @@ class TaskInternalLinkController extends BaseController { $task = $this->getTask(); + if (empty($values)) { + $values['another_tasklink'] = $this->request->getIntegerParam('another_tasklink', 0); + $values = $this->hook->merge('controller:tasklink:form:default', $values, array('default_values' => $values)); + } + $this->response->html($this->template->render('task_internal_link/create', array( 'values' => $values, 'errors' => $errors, @@ -61,12 +48,23 @@ class TaskInternalLinkController extends BaseController { $task = $this->getTask(); $values = $this->request->getValues(); + $values['task_id'] = $task['id']; list($valid, $errors) = $this->taskLinkValidator->validateCreation($values); if ($valid) { - if ($this->taskLinkModel->create($values['task_id'], $values['opposite_task_id'], $values['link_id'])) { + if ($this->taskLinkModel->create($values['task_id'], $values['opposite_task_id'], $values['link_id']) !== false) { $this->flash->success(t('Link added successfully.')); + + if (isset($values['another_tasklink']) && $values['another_tasklink'] == 1) { + return $this->create(array( + 'project_id' => $task['project_id'], + 'task_id' => $task['id'], + 'link_id' => $values['link_id'], + 'another_tasklink' => 1 + )); + } + return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true); } @@ -89,7 +87,7 @@ class TaskInternalLinkController extends BaseController public function edit(array $values = array(), array $errors = array()) { $task = $this->getTask(); - $task_link = $this->getTaskLink(); + $task_link = $this->getInternalTaskLink($task); if (empty($values)) { $opposite_task = $this->taskFinderModel->getById($task_link['opposite_task_id']); @@ -114,7 +112,11 @@ class TaskInternalLinkController extends BaseController public function update() { $task = $this->getTask(); + $task_link = $this->getInternalTaskLink($task); + $values = $this->request->getValues(); + $values['task_id'] = $task['id']; + $values['id'] = $task_link['id']; list($valid, $errors) = $this->taskLinkValidator->validateModification($values); @@ -138,7 +140,7 @@ class TaskInternalLinkController extends BaseController public function confirm() { $task = $this->getTask(); - $link = $this->getTaskLink(); + $link = $this->getInternalTaskLink($task); $this->response->html($this->template->render('task_internal_link/remove', array( 'link' => $link, @@ -155,8 +157,9 @@ class TaskInternalLinkController extends BaseController { $this->checkCSRFParam(); $task = $this->getTask(); + $link = $this->getInternalTaskLink($task); - if ($this->taskLinkModel->remove($this->request->getIntegerParam('link_id'))) { + if ($this->taskLinkModel->remove($link['id'])) { $this->flash->success(t('Link removed successfully.')); } else { $this->flash->failure(t('Unable to remove this link.')); diff --git a/app/Controller/TaskMailController.php b/app/Controller/TaskMailController.php index e95ddf03..feb96a68 100644 --- a/app/Controller/TaskMailController.php +++ b/app/Controller/TaskMailController.php @@ -20,6 +20,7 @@ class TaskMailController extends BaseController 'errors' => $errors, 'task' => $task, 'project' => $project, + 'members' => $this->projectPermissionModel->getMembersWithEmail($project['id']), ))); } @@ -33,6 +34,13 @@ class TaskMailController extends BaseController if ($valid) { $this->sendByEmail($values, $task); $this->flash->success(t('Task sent by email successfully.')); + + $this->commentModel->create(array( + 'comment' => t('This task was sent by email to "%s" with subject "%s".', $values['email'], $values['subject']), + 'user_id' => $this->userSession->getId(), + 'task_id' => $task['id'], + )); + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments'), true); } else { $this->create($values, $errors); diff --git a/app/Controller/TaskModificationController.php b/app/Controller/TaskModificationController.php index 520bf70e..338ed540 100644 --- a/app/Controller/TaskModificationController.php +++ b/app/Controller/TaskModificationController.php @@ -22,7 +22,11 @@ class TaskModificationController extends BaseController public function start() { $task = $this->getTask(); - $this->taskModificationModel->update(array('id' => $task['id'], 'date_started' => time())); + $values = array('id' => $task['id'], 'date_started' => time()); + if (! $this->helper->projectRole->canUpdateTask($task)) { + throw new AccessForbiddenException(t('You are not allowed to update tasks assigned to someone else.')); + } + $this->taskModificationModel->update($values); $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); } @@ -38,6 +42,11 @@ class TaskModificationController extends BaseController public function edit(array $values = array(), array $errors = array()) { $task = $this->getTask(); + + if (! $this->helper->projectRole->canUpdateTask($task)) { + throw new AccessForbiddenException(t('You are not allowed to update tasks assigned to someone else.')); + } + $project = $this->projectModel->getById($task['project_id']); if (empty($values)) { @@ -89,6 +98,8 @@ class TaskModificationController extends BaseController { $task = $this->getTask(); $values = $this->request->getValues(); + $values['id'] = $task['id']; + $values['project_id'] = $task['project_id']; list($valid, $errors) = $this->taskValidator->validateModification($values); @@ -103,6 +114,14 @@ class TaskModificationController extends BaseController protected function updateTask(array &$task, array &$values, array &$errors) { + if (isset($values['owner_id']) && $values['owner_id'] != $task['owner_id'] && !$this->helper->projectRole->canChangeAssignee($task)) { + throw new AccessForbiddenException(t('You are not allowed to change the assignee.')); + } + + if (! $this->helper->projectRole->canUpdateTask($task)) { + throw new AccessForbiddenException(t('You are not allowed to update tasks assigned to someone else.')); + } + $result = $this->taskModificationModel->update($values); if ($result && ! empty($task['external_uri'])) { diff --git a/app/Controller/TaskRecurrenceController.php b/app/Controller/TaskRecurrenceController.php index c6fdfa37..7f14cb53 100644 --- a/app/Controller/TaskRecurrenceController.php +++ b/app/Controller/TaskRecurrenceController.php @@ -47,6 +47,7 @@ class TaskRecurrenceController extends BaseController { $task = $this->getTask(); $values = $this->request->getValues(); + $values['id'] = $task['id']; list($valid, $errors) = $this->taskValidator->validateEditRecurrence($values); diff --git a/app/Controller/UserAjaxController.php b/app/Controller/UserAjaxController.php index 17567a00..57596202 100644 --- a/app/Controller/UserAjaxController.php +++ b/app/Controller/UserAjaxController.php @@ -2,9 +2,6 @@ namespace Kanboard\Controller; -use Kanboard\Filter\UserNameFilter; -use Kanboard\Model\UserModel; - /** * User Ajax Controller * @@ -21,9 +18,8 @@ class UserAjaxController extends BaseController public function autocomplete() { $search = $this->request->getStringParam('term'); - $filter = $this->userQuery->withFilter(new UserNameFilter($search)); - $filter->getQuery()->asc(UserModel::TABLE.'.name')->asc(UserModel::TABLE.'.username'); - $this->response->json($filter->format($this->userAutoCompleteFormatter)); + $users = $this->userManager->find($search); + $this->response->json($this->userAutoCompleteFormatter->withUsers($users)->format()); } /** diff --git a/app/Controller/UserApiAccessController.php b/app/Controller/UserApiAccessController.php index 0f1c891e..f9628ae6 100644 --- a/app/Controller/UserApiAccessController.php +++ b/app/Controller/UserApiAccessController.php @@ -32,7 +32,7 @@ class UserApiAccessController extends BaseController 'api_access_token' => Token::getToken(), )); - $this->renderResponse(); + $this->renderResponse($user); } public function remove() @@ -45,10 +45,10 @@ class UserApiAccessController extends BaseController 'api_access_token' => null, )); - $this->renderResponse(); + $this->renderResponse($user); } - protected function renderResponse() + protected function renderResponse(array $user) { if ($this->request->isAjax()) { $this->show(); diff --git a/app/Controller/UserCredentialController.php b/app/Controller/UserCredentialController.php index 23e7edba..a8b90b7b 100644 --- a/app/Controller/UserCredentialController.php +++ b/app/Controller/UserCredentialController.php @@ -43,6 +43,14 @@ class UserCredentialController extends BaseController list($valid, $errors) = $this->userValidator->validatePasswordModification($values); + if (! $this->userSession->isAdmin()) { + $values = array( + 'id' => $this->userSession->getId(), + 'password' => isset($values['password']) ? $values['password'] : '', + 'confirmation' => isset($values['confirmation']) ? $values['confirmation'] : '', + ); + } + if ($valid) { if ($this->userModel->update($values)) { $this->flash->success(t('Password modified successfully.')); diff --git a/app/Controller/UserModificationController.php b/app/Controller/UserModificationController.php index ed145921..f4916f6f 100644 --- a/app/Controller/UserModificationController.php +++ b/app/Controller/UserModificationController.php @@ -47,9 +47,14 @@ class UserModificationController extends BaseController $values = $this->request->getValues(); if (! $this->userSession->isAdmin()) { - if (isset($values['role'])) { - unset($values['role']); - } + $values = array( + 'id' => $this->userSession->getId(), + 'username' => isset($values['username']) ? $values['username'] : '', + 'name' => isset($values['name']) ? $values['name'] : '', + 'email' => isset($values['email']) ? $values['email'] : '', + 'timezone' => isset($values['timezone']) ? $values['timezone'] : '', + 'language' => isset($values['language']) ? $values['language'] : '', + ); } list($valid, $errors) = $this->userValidator->validateModification($values); diff --git a/app/Core/Base.php b/app/Core/Base.php index 28bbd534..a36828c4 100644 --- a/app/Core/Base.php +++ b/app/Core/Base.php @@ -7,7 +7,7 @@ use Pimple\Container; /** * Base Class * - * @package core + * @package Kanboard\Core * @author Frederic Guillot * * @property \Kanboard\Analytic\TaskDistributionAnalytic $taskDistributionAnalytic @@ -22,6 +22,7 @@ use Pimple\Container; * @property \Kanboard\Core\Cache\BaseCache $cacheDriver * @property \Kanboard\Core\Event\EventManager $eventManager * @property \Kanboard\Core\Group\GroupManager $groupManager + * @property \Kanboard\Core\User\UserManager $userManager * @property \Kanboard\Core\Http\Client $httpClient * @property \Kanboard\Core\Http\OAuth2 $oauth * @property \Kanboard\Core\Http\RememberMeCookie $rememberMeCookie @@ -68,15 +69,17 @@ use Pimple\Container; * @property \Kanboard\Formatter\BoardTaskFormatter $boardTaskFormatter * @property \Kanboard\Formatter\GroupAutoCompleteFormatter $groupAutoCompleteFormatter * @property \Kanboard\Formatter\ProjectActivityEventFormatter $projectActivityEventFormatter - * @property \Kanboard\Formatter\ProjectGanttFormatter $projectGanttFormatter + * @property \Kanboard\Formatter\ProjectApiFormatter $projectApiFormatter + * @property \Kanboard\Formatter\ProjectsApiFormatter $projectsApiFormatter * @property \Kanboard\Formatter\SubtaskListFormatter $subtaskListFormatter * @property \Kanboard\Formatter\SubtaskTimeTrackingCalendarFormatter $subtaskTimeTrackingCalendarFormatter + * @property \Kanboard\Formatter\TaskApiFormatter $taskApiFormatter + * @property \Kanboard\Formatter\TasksApiFormatter $tasksApiFormatter * @property \Kanboard\Formatter\TaskAutoCompleteFormatter $taskAutoCompleteFormatter - * @property \Kanboard\Formatter\TaskCalendarFormatter $taskCalendarFormatter - * @property \Kanboard\Formatter\TaskGanttFormatter $taskGanttFormatter * @property \Kanboard\Formatter\TaskICalFormatter $taskICalFormatter * @property \Kanboard\Formatter\TaskListFormatter $taskListFormatter * @property \Kanboard\Formatter\TaskListSubtaskFormatter $taskListSubtaskFormatter + * @property \Kanboard\Formatter\TaskListSubtaskAssigneeFormatter $taskListSubtaskAssigneeFormatter * @property \Kanboard\Formatter\TaskSuggestMenuFormatter $taskSuggestMenuFormatter * @property \Kanboard\Formatter\UserAutoCompleteFormatter $userAutoCompleteFormatter * @property \Kanboard\Formatter\UserMentionFormatter $userMentionFormatter @@ -103,6 +106,7 @@ use Pimple\Container; * @property \Kanboard\Model\LinkModel $linkModel * @property \Kanboard\Model\NotificationModel $notificationModel * @property \Kanboard\Model\PasswordResetModel $passwordResetModel + * @property \Kanboard\Model\PredefinedTaskDescriptionModel $predefinedTaskDescriptionModel * @property \Kanboard\Model\ProjectModel $projectModel * @property \Kanboard\Model\ProjectActivityModel $projectActivityModel * @property \Kanboard\Model\ProjectDuplicationModel $projectDuplicationModel @@ -151,9 +155,10 @@ use Pimple\Container; * @property \Kanboard\Model\UserNotificationFilterModel $userNotificationFilterModel * @property \Kanboard\Model\UserUnreadNotificationModel $userUnreadNotificationModel * @property \Kanboard\Model\UserMetadataModel $userMetadataModel + * @property \Kanboard\Pagination\DashboardPagination $dashboardPagination + * @property \Kanboard\Pagination\ProjectPagination $projectPagination * @property \Kanboard\Pagination\TaskPagination $taskPagination * @property \Kanboard\Pagination\SubtaskPagination $subtaskPagination - * @property \Kanboard\Pagination\ProjectPagination $projectPagination * @property \Kanboard\Pagination\UserPagination $userPagination * @property \Kanboard\Validator\ActionValidator $actionValidator * @property \Kanboard\Validator\AuthValidator $authValidator @@ -176,7 +181,7 @@ use Pimple\Container; * @property \Kanboard\Validator\TaskLinkValidator $taskLinkValidator * @property \Kanboard\Validator\TaskValidator $taskValidator * @property \Kanboard\Validator\UserValidator $userValidator - * @property \Kanboard\Import\TaskImport $taskImport + * @property \Kanboard\Validator\PredefinedTaskDescriptionValidator $predefinedTaskDescriptionValidator * @property \Kanboard\Import\UserImport $userImport * @property \Kanboard\Export\SubtaskExport $subtaskExport * @property \Kanboard\Export\TaskExport $taskExport diff --git a/app/Core/ExternalTask/ExternalTaskManager.php b/app/Core/ExternalTask/ExternalTaskManager.php index 102ec459..87a46b71 100644 --- a/app/Core/ExternalTask/ExternalTaskManager.php +++ b/app/Core/ExternalTask/ExternalTaskManager.php @@ -55,4 +55,14 @@ class ExternalTaskManager return array(); } + + /** + * Get all providers + * + * @return ExternalTaskProviderInterface[] + */ + public function getProviders() + { + return $this->providers; + } } diff --git a/app/Core/ExternalTask/ExternalTaskProviderInterface.php b/app/Core/ExternalTask/ExternalTaskProviderInterface.php index f67f7552..5678efb8 100644 --- a/app/Core/ExternalTask/ExternalTaskProviderInterface.php +++ b/app/Core/ExternalTask/ExternalTaskProviderInterface.php @@ -19,6 +19,22 @@ interface ExternalTaskProviderInterface public function getName(); /** + * Get provider icon + * + * @access public + * @return string + */ + public function getIcon(); + + /** + * Get label for adding a new task + * + * @access public + * @return string + */ + public function getMenuAddLabel(); + + /** * Retrieve task from external system or cache * * @access public diff --git a/app/Core/Filter/QueryBuilder.php b/app/Core/Filter/QueryBuilder.php index 3de82b63..bdd6b940 100644 --- a/app/Core/Filter/QueryBuilder.php +++ b/app/Core/Filter/QueryBuilder.php @@ -21,6 +21,18 @@ class QueryBuilder protected $query; /** + * Create a new class instance + * + * @static + * @access public + * @return static + */ + public static function create() + { + return new static(); + } + + /** * Set the query * * @access public diff --git a/app/Core/Group/GroupBackendProviderInterface.php b/app/Core/Group/GroupBackendProviderInterface.php index 74c5cb03..0b6e2985 100644 --- a/app/Core/Group/GroupBackendProviderInterface.php +++ b/app/Core/Group/GroupBackendProviderInterface.php @@ -5,7 +5,7 @@ namespace Kanboard\Core\Group; /** * Group Backend Provider Interface * - * @package group + * @package Kanboard\Core\Group * @author Frederic Guillot */ interface GroupBackendProviderInterface diff --git a/app/Core/Group/GroupManager.php b/app/Core/Group/GroupManager.php index 48b6c4f8..a0559139 100644 --- a/app/Core/Group/GroupManager.php +++ b/app/Core/Group/GroupManager.php @@ -5,7 +5,7 @@ namespace Kanboard\Core\Group; /** * Group Manager * - * @package group + * @package Kanboard\Core\Group * @author Frederic Guillot */ class GroupManager @@ -13,10 +13,10 @@ class GroupManager /** * List of backend providers * - * @access private + * @access protected * @var array */ - private $providers = array(); + protected $providers = array(); /** * Register a new group backend provider @@ -52,11 +52,11 @@ class GroupManager /** * Remove duplicated groups * - * @access private + * @access protected * @param array $groups * @return GroupProviderInterface[] */ - private function removeDuplicates(array $groups) + protected function removeDuplicates(array $groups) { $result = array(); diff --git a/app/Core/Group/GroupProviderInterface.php b/app/Core/Group/GroupProviderInterface.php index 4c7c16ec..f312f89b 100644 --- a/app/Core/Group/GroupProviderInterface.php +++ b/app/Core/Group/GroupProviderInterface.php @@ -5,7 +5,7 @@ namespace Kanboard\Core\Group; /** * Group Provider Interface * - * @package group + * @package Kanboard\Core\Group * @author Frederic Guillot */ interface GroupProviderInterface diff --git a/app/Core/Helper.php b/app/Core/Helper.php index 1b53ae2b..5f4772d5 100644 --- a/app/Core/Helper.php +++ b/app/Core/Helper.php @@ -14,13 +14,11 @@ use Pimple\Container; * @property \Kanboard\Helper\AssetHelper $asset * @property \Kanboard\Helper\AvatarHelper $avatar * @property \Kanboard\Helper\BoardHelper $board - * @property \Kanboard\Helper\CalendarHelper $calendar * @property \Kanboard\Helper\CommentHelper $comment * @property \Kanboard\Helper\DateHelper $dt * @property \Kanboard\Helper\FileHelper $file * @property \Kanboard\Helper\FormHelper $form * @property \Kanboard\Helper\HookHelper $hook - * @property \Kanboard\Helper\ICalHelper $ical * @property \Kanboard\Helper\ModalHelper $modal * @property \Kanboard\Helper\ModelHelper $model * @property \Kanboard\Helper\SubtaskHelper $subtask diff --git a/app/Core/Ldap/Query.php b/app/Core/Ldap/Query.php index 0f9abb5c..0fe7f2d3 100644 --- a/app/Core/Ldap/Query.php +++ b/app/Core/Ldap/Query.php @@ -44,9 +44,10 @@ class Query * @param string $baseDn * @param string $filter * @param array $attributes - * @return Query + * @param integer $limit + * @return $this */ - public function execute($baseDn, $filter, array $attributes) + public function execute($baseDn, $filter, array $attributes, $limit = 0) { if (DEBUG && $this->client->hasLogger()) { $this->client->getLogger()->debug('BaseDN='.$baseDn); @@ -54,7 +55,7 @@ class Query $this->client->getLogger()->debug('Attributes='.implode(', ', $attributes)); } - $sr = ldap_search($this->client->getConnection(), $baseDn, $filter, $attributes); + $sr = @ldap_search($this->client->getConnection(), $baseDn, $filter, $attributes, null, $limit); if ($sr === false) { return $this; } diff --git a/app/Core/Plugin/Installer.php b/app/Core/Plugin/Installer.php index b3618aeb..057ded94 100644 --- a/app/Core/Plugin/Installer.php +++ b/app/Core/Plugin/Installer.php @@ -100,7 +100,7 @@ class Installer extends \Kanboard\Core\Base { $zip = new ZipArchive(); $archiveData = $this->httpClient->get($archiveUrl); - $archiveFile = tempnam(sys_get_temp_dir(), 'kb_plugin'); + $archiveFile = tempnam(ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir(), 'kb_plugin'); if (empty($archiveData)) { unlink($archiveFile); diff --git a/app/Core/User/UserBackendProviderInterface.php b/app/Core/User/UserBackendProviderInterface.php new file mode 100644 index 00000000..5f8cab47 --- /dev/null +++ b/app/Core/User/UserBackendProviderInterface.php @@ -0,0 +1,21 @@ +<?php + +namespace Kanboard\Core\User; + +/** + * User Backend Provider Interface + * + * @package Kanboard\Core\User + * @author Frederic Guillot + */ +interface UserBackendProviderInterface +{ + /** + * Find a user from a search query + * + * @access public + * @param string $input + * @return UserProviderInterface[] + */ + public function find($input); +} diff --git a/app/Core/User/UserManager.php b/app/Core/User/UserManager.php new file mode 100644 index 00000000..e1692480 --- /dev/null +++ b/app/Core/User/UserManager.php @@ -0,0 +1,71 @@ +<?php + +namespace Kanboard\Core\User; + +/** + * User Manager + * + * @package Kanboard\Core\User + * @author Frederic Guillot + */ +class UserManager +{ + /** + * List of backend providers + * + * @access protected + * @var array + */ + protected $providers = array(); + + /** + * Register a new group backend provider + * + * @access public + * @param UserBackendProviderInterface $provider + * @return $this + */ + public function register(UserBackendProviderInterface $provider) + { + $this->providers[] = $provider; + return $this; + } + + /** + * Find a group from a search query + * + * @access public + * @param string $input + * @return UserProviderInterface[] + */ + public function find($input) + { + $groups = array(); + + foreach ($this->providers as $provider) { + $groups = array_merge($groups, $provider->find($input)); + } + + return $this->removeDuplicates($groups); + } + + /** + * Remove duplicated users + * + * @access protected + * @param array $users + * @return UserProviderInterface[] + */ + protected function removeDuplicates(array $users) + { + $result = array(); + + foreach ($users as $user) { + if (! isset($result[$user->getUsername()])) { + $result[$user->getUsername()] = $user; + } + } + + return array_values($result); + } +} diff --git a/app/Export/TaskExport.php b/app/Export/TaskExport.php index f47cd082..f0685576 100644 --- a/app/Export/TaskExport.php +++ b/app/Export/TaskExport.php @@ -30,11 +30,13 @@ class TaskExport extends Base public function export($project_id, $from, $to) { $tasks = $this->getTasks($project_id, $from, $to); + $taskIds = array_column($tasks, 'id'); + $tags = $this->taskTagModel->getTagsByTaskIds($taskIds); $colors = $this->colorModel->getList(); $results = array($this->getColumns()); foreach ($tasks as &$task) { - $task = $this->format($task, $colors); + $task = $this->format($task, $colors, $tags); $results[] = array_values($task); } @@ -83,7 +85,8 @@ class TaskExport extends Base TaskModel::TABLE . '.date_completed', TaskModel::TABLE . '.date_started', TaskModel::TABLE . '.time_estimated', - TaskModel::TABLE . '.time_spent' + TaskModel::TABLE . '.time_spent', + TaskModel::TABLE . '.priority' ) ->join(UserModel::TABLE, 'id', 'owner_id', TaskModel::TABLE) ->left(UserModel::TABLE, 'uc', 'id', TaskModel::TABLE, 'creator_id') @@ -103,14 +106,16 @@ class TaskExport extends Base * * @access protected * @param array $task - * @param array $colors + * @param array $colors + * @param array $tags * @return array */ - protected function format(array &$task, array $colors) + protected function format(array &$task, array $colors, array &$tags) { $task['is_active'] = $task['is_active'] == TaskModel::STATUS_OPEN ? e('Open') : e('Closed'); $task['color_id'] = $colors[$task['color_id']]; $task['score'] = $task['score'] ?: 0; + $task['tags'] = ''; $task = $this->dateParser->format( $task, @@ -118,6 +123,11 @@ class TaskExport extends Base $this->dateParser->getUserDateTimeFormat() ); + if (isset($tags[$task['id']])) { + $taskTags = array_column($tags[$task['id']], 'name'); + $task['tags'] = implode(', ', $taskTags); + } + return $task; } @@ -152,6 +162,8 @@ class TaskExport extends Base e('Start date'), e('Time estimated'), e('Time spent'), + e('Priority'), + e('Tags'), ); } } diff --git a/app/Filter/BaseComparisonFilter.php b/app/Filter/BaseComparisonFilter.php new file mode 100644 index 00000000..ba0c9c5e --- /dev/null +++ b/app/Filter/BaseComparisonFilter.php @@ -0,0 +1,48 @@ +<?php + +namespace Kanboard\Filter; + +/** + * Base comparison filter class + * + * @package filter + */ +abstract class BaseComparisonFilter extends BaseFilter +{ + /** + * Parse operator in the input string + * + * @access protected + * @return string + */ + protected function parseOperator() + { + $operators = array( + '<=' => 'lte', + '>=' => 'gte', + '<' => 'lt', + '>' => 'gt', + ); + + foreach ($operators as $operator => $method) { + if (strpos($this->value, $operator) === 0) { + $this->value = substr($this->value, strlen($operator)); + return $method; + } + } + + return 'eq'; + } + + /** + * Apply a comparison filter + * + * @access protected + * @param string $field + */ + protected function applyComparisonFilter($field) + { + $method = $this->parseOperator(); + $this->query->$method($field, $this->value); + } +} diff --git a/app/Filter/BaseFilter.php b/app/Filter/BaseFilter.php index e029f4e1..2687d903 100644 --- a/app/Filter/BaseFilter.php +++ b/app/Filter/BaseFilter.php @@ -51,7 +51,7 @@ abstract class BaseFilter * * @access public * @param Table $query - * @return \Kanboard\Core\Filter\FilterInterface + * @return $this */ public function withQuery(Table $query) { @@ -64,7 +64,7 @@ abstract class BaseFilter * * @access public * @param string $value - * @return \Kanboard\Core\Filter\FilterInterface + * @return $this */ public function withValue($value) { diff --git a/app/Filter/TaskDueDateRangeFilter.php b/app/Filter/TaskDueDateRangeFilter.php index a6aefbe2..a4250f80 100644 --- a/app/Filter/TaskDueDateRangeFilter.php +++ b/app/Filter/TaskDueDateRangeFilter.php @@ -32,6 +32,11 @@ class TaskDueDateRangeFilter extends BaseFilter implements FilterInterface */ public function apply() { + $this->query->beginOr(); + $this->query->isNull(TaskModel::TABLE.'.date_started'); + $this->query->eq(TaskModel::TABLE.'.date_started', 0); + $this->query->closeOr(); + $this->query->gte(TaskModel::TABLE.'.date_due', is_numeric($this->value[0]) ? $this->value[0] : strtotime($this->value[0])); $this->query->lte(TaskModel::TABLE.'.date_due', is_numeric($this->value[1]) ? $this->value[1] : strtotime($this->value[1])); return $this; diff --git a/app/Filter/TaskReferenceFilter.php b/app/Filter/TaskReferenceFilter.php index 27c838f8..d843b777 100644 --- a/app/Filter/TaskReferenceFilter.php +++ b/app/Filter/TaskReferenceFilter.php @@ -32,6 +32,11 @@ class TaskReferenceFilter extends BaseFilter implements FilterInterface */ public function apply() { + if (strpos($this->value, '*') >= 0) { + $this->query->like(TaskModel::TABLE.'.reference', str_replace('*', '%', $this->value)); + return $this; + } + $this->query->eq(TaskModel::TABLE.'.reference', $this->value); return $this; } diff --git a/app/Filter/TaskScoreFilter.php b/app/Filter/TaskScoreFilter.php new file mode 100644 index 00000000..2c4067f1 --- /dev/null +++ b/app/Filter/TaskScoreFilter.php @@ -0,0 +1,37 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TaskModel; + +/** + * Class TaskScoreFilter + * + * @package Kanboard\Filter + */ +class TaskScoreFilter extends BaseComparisonFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('score', 'complexity'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->applyComparisonFilter(TaskModel::TABLE.'.score'); + return $this; + } +} diff --git a/app/Formatter/BaseTaskCalendarFormatter.php b/app/Formatter/BaseTaskCalendarFormatter.php deleted file mode 100644 index 3d9ead4d..00000000 --- a/app/Formatter/BaseTaskCalendarFormatter.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php - -namespace Kanboard\Formatter; - -/** - * Common class to handle calendar events - * - * @package formatter - * @author Frederic Guillot - */ -abstract class BaseTaskCalendarFormatter extends BaseFormatter -{ - /** - * Column used for event start date - * - * @access protected - * @var string - */ - protected $startColumn = 'date_started'; - - /** - * Column used for event end date - * - * @access protected - * @var string - */ - protected $endColumn = 'date_completed'; - - /** - * Transform results to calendar events - * - * @access public - * @param string $start_column Column name for the start date - * @param string $end_column Column name for the end date - * @return $this - */ - public function setColumns($start_column, $end_column = '') - { - $this->startColumn = $start_column; - $this->endColumn = $end_column ?: $start_column; - return $this; - } -} diff --git a/app/Formatter/GroupAutoCompleteFormatter.php b/app/Formatter/GroupAutoCompleteFormatter.php index d811de7f..9d740b7f 100644 --- a/app/Formatter/GroupAutoCompleteFormatter.php +++ b/app/Formatter/GroupAutoCompleteFormatter.php @@ -4,12 +4,11 @@ namespace Kanboard\Formatter; use Kanboard\Core\Filter\FormatterInterface; use Kanboard\Core\Group\GroupProviderInterface; -use PicoDb\Table; /** * Auto-complete formatter for groups * - * @package formatter + * @package Kanboard\Formatter * @author Frederic Guillot */ class GroupAutoCompleteFormatter extends BaseFormatter implements FormatterInterface diff --git a/app/Formatter/ProjectApiFormatter.php b/app/Formatter/ProjectApiFormatter.php new file mode 100644 index 00000000..5521d57c --- /dev/null +++ b/app/Formatter/ProjectApiFormatter.php @@ -0,0 +1,39 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; + +/** + * Class ProjectApiFormatter + * + * @package Kanboard\Formatter + */ +class ProjectApiFormatter extends BaseFormatter implements FormatterInterface +{ + protected $project = null; + + public function withProject($project) + { + $this->project = $project; + return $this; + } + + /** + * Apply formatter + * + * @access public + * @return mixed + */ + public function format() + { + if (! empty($this->project)) { + $this->project['url'] = array( + 'board' => $this->helper->url->to('BoardViewController', 'show', array('project_id' => $this->project['id']), '', true), + 'list' => $this->helper->url->to('TaskListController', 'show', array('project_id' => $this->project['id']), '', true), + ); + } + + return $this->project; + } +} diff --git a/app/Formatter/ProjectGanttFormatter.php b/app/Formatter/ProjectGanttFormatter.php deleted file mode 100644 index af04f498..00000000 --- a/app/Formatter/ProjectGanttFormatter.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -namespace Kanboard\Formatter; - -use Kanboard\Core\Filter\FormatterInterface; - -/** - * Gantt chart formatter for projects - * - * @package formatter - * @author Frederic Guillot - */ -class ProjectGanttFormatter extends BaseFormatter implements FormatterInterface -{ - /** - * Format projects to be displayed in the Gantt chart - * - * @access public - * @return array - */ - public function format() - { - $projects = $this->query->findAll(); - $colors = $this->colorModel->getDefaultColors(); - $bars = array(); - - foreach ($projects as $project) { - $start = empty($project['start_date']) ? time() : strtotime($project['start_date']); - $end = empty($project['end_date']) ? $start : strtotime($project['end_date']); - $color = next($colors) ?: reset($colors); - - $bars[] = array( - 'type' => 'project', - 'id' => $project['id'], - 'title' => $project['name'], - 'start' => array( - (int) date('Y', $start), - (int) date('n', $start), - (int) date('j', $start), - ), - 'end' => array( - (int) date('Y', $end), - (int) date('n', $end), - (int) date('j', $end), - ), - 'link' => $this->helper->url->href('ProjectViewController', 'show', array('project_id' => $project['id'])), - 'board_link' => $this->helper->url->href('BoardViewController', 'show', array('project_id' => $project['id'])), - 'gantt_link' => $this->helper->url->href('TaskGanttController', 'show', array('project_id' => $project['id'])), - 'color' => $color, - 'not_defined' => empty($project['start_date']) || empty($project['end_date']), - 'users' => $this->projectUserRoleModel->getAllUsersGroupedByRole($project['id']), - ); - } - - return $bars; - } -} diff --git a/app/Formatter/ProjectsApiFormatter.php b/app/Formatter/ProjectsApiFormatter.php new file mode 100644 index 00000000..0bf97da4 --- /dev/null +++ b/app/Formatter/ProjectsApiFormatter.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; + +/** + * Class ProjectsApiFormatter + * + * @package Kanboard\Formatter + */ +class ProjectsApiFormatter extends BaseFormatter implements FormatterInterface +{ + protected $projects = array(); + + public function withProjects($projects) + { + $this->projects = $projects; + return $this; + } + + /** + * Apply formatter + * + * @access public + * @return mixed + */ + public function format() + { + if (! empty($this->projects)) { + foreach ($this->projects as &$project) { + $project = $this->projectApiFormatter->withProject($project)->format(); + } + } + + return $this->projects; + } +} diff --git a/app/Formatter/TaskApiFormatter.php b/app/Formatter/TaskApiFormatter.php new file mode 100644 index 00000000..60840beb --- /dev/null +++ b/app/Formatter/TaskApiFormatter.php @@ -0,0 +1,37 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; + +/** + * Class TaskApiFormatter + * + * @package Kanboard\Formatter + */ +class TaskApiFormatter extends BaseFormatter implements FormatterInterface +{ + protected $task = null; + + public function withTask($task) + { + $this->task = $task; + return $this; + } + + /** + * Apply formatter + * + * @access public + * @return mixed + */ + public function format() + { + if (! empty($this->task)) { + $this->task['url'] = $this->helper->url->to('TaskViewController', 'show', array('task_id' => $this->task['id'], 'project_id' => $this->task['project_id']), '', true); + $this->task['color'] = $this->colorModel->getColorProperties($this->task['color_id']); + } + + return $this->task; + } +} diff --git a/app/Formatter/TaskCalendarFormatter.php b/app/Formatter/TaskCalendarFormatter.php deleted file mode 100644 index 75d2a83e..00000000 --- a/app/Formatter/TaskCalendarFormatter.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php - -namespace Kanboard\Formatter; - -use Kanboard\Core\Filter\FormatterInterface; - -/** - * Calendar event formatter for task filter - * - * @package formatter - * @author Frederic Guillot - */ -class TaskCalendarFormatter extends BaseTaskCalendarFormatter implements FormatterInterface -{ - /** - * Full day event flag - * - * @access private - * @var boolean - */ - private $fullDay = false; - - /** - * When called calendar events will be full day - * - * @access public - * @return FormatterInterface - */ - public function setFullDay() - { - $this->fullDay = true; - return $this; - } - - /** - * Transform tasks to calendar events - * - * @access public - * @return array - */ - public function format() - { - $events = array(); - - foreach ($this->query->findAll() as $task) { - $events[] = array( - 'timezoneParam' => $this->timezoneModel->getCurrentTimezone(), - 'id' => $task['id'], - 'title' => t('#%d', $task['id']).' '.$task['title'], - 'backgroundColor' => $this->colorModel->getBackgroundColor($task['color_id']), - 'borderColor' => $this->colorModel->getBorderColor($task['color_id']), - 'textColor' => 'black', - 'url' => $this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), - 'start' => date($this->getDateTimeFormat(), $task[$this->startColumn]), - 'end' => date($this->getDateTimeFormat(), $task[$this->endColumn] ?: time()), - 'editable' => $this->fullDay, - 'allday' => $this->fullDay, - ); - } - - return $events; - } - - /** - * Get DateTime format for event - * - * @access private - * @return string - */ - private function getDateTimeFormat() - { - return $this->fullDay ? 'Y-m-d' : 'Y-m-d\TH:i:s'; - } -} diff --git a/app/Formatter/TaskGanttFormatter.php b/app/Formatter/TaskGanttFormatter.php deleted file mode 100644 index ddb3f93a..00000000 --- a/app/Formatter/TaskGanttFormatter.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php - -namespace Kanboard\Formatter; - -use Kanboard\Core\Filter\FormatterInterface; - -/** - * Task Gantt Formatter - * - * @package formatter - * @author Frederic Guillot - */ -class TaskGanttFormatter extends BaseFormatter implements FormatterInterface -{ - /** - * Local cache for project columns - * - * @access private - * @var array - */ - private $columns = array(); - - /** - * Apply formatter - * - * @access public - * @return array - */ - public function format() - { - $bars = array(); - - foreach ($this->query->findAll() as $task) { - $bars[] = $this->formatTask($task); - } - - return $bars; - } - - /** - * Format a single task - * - * @access private - * @param array $task - * @return array - */ - private function formatTask(array $task) - { - if (! isset($this->columns[$task['project_id']])) { - $this->columns[$task['project_id']] = $this->columnModel->getList($task['project_id']); - } - - $start = $task['date_started'] ?: time(); - $end = $task['date_due'] ?: $start; - - return array( - 'type' => 'task', - 'id' => $task['id'], - 'title' => $task['title'], - 'start' => array( - (int) date('Y', $start), - (int) date('n', $start), - (int) date('j', $start), - ), - 'end' => array( - (int) date('Y', $end), - (int) date('n', $end), - (int) date('j', $end), - ), - 'column_title' => $task['column_name'], - 'assignee' => $task['assignee_name'] ?: $task['assignee_username'], - 'progress' => $this->taskModel->getProgress($task, $this->columns[$task['project_id']]).'%', - 'link' => $this->helper->url->href('TaskViewController', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), - 'color' => $this->colorModel->getColorProperties($task['color_id']), - 'not_defined' => empty($task['date_due']) || empty($task['date_started']), - ); - } -} diff --git a/app/Formatter/TaskICalFormatter.php b/app/Formatter/TaskICalFormatter.php index ad2a4449..387074e5 100644 --- a/app/Formatter/TaskICalFormatter.php +++ b/app/Formatter/TaskICalFormatter.php @@ -8,6 +8,7 @@ use Eluceo\iCal\Component\Event; use Eluceo\iCal\Property\Event\Attendees; use Eluceo\iCal\Property\Event\Organizer; use Kanboard\Core\Filter\FormatterInterface; +use PicoDb\Table; /** * iCal event formatter for tasks @@ -15,15 +16,15 @@ use Kanboard\Core\Filter\FormatterInterface; * @package formatter * @author Frederic Guillot */ -class TaskICalFormatter extends BaseTaskCalendarFormatter implements FormatterInterface +class TaskICalFormatter extends BaseFormatter implements FormatterInterface { /** * Calendar object * - * @access private + * @access protected * @var \Eluceo\iCal\Component\Calendar */ - private $vCalendar; + protected $vCalendar; /** * Get Ical events @@ -41,7 +42,7 @@ class TaskICalFormatter extends BaseTaskCalendarFormatter implements FormatterIn * * @access public * @param \Eluceo\iCal\Component\Calendar $vCalendar - * @return FormatterInterface + * @return $this */ public function setCalendar(Calendar $vCalendar) { @@ -53,18 +54,21 @@ class TaskICalFormatter extends BaseTaskCalendarFormatter implements FormatterIn * Transform results to iCal events * * @access public - * @return FormatterInterface + * @param Table $query + * @param string $startColumn + * @param string $endColumn + * @return $this */ - public function addDateTimeEvents() + public function addTasksWithStartAndDueDate(Table $query, $startColumn, $endColumn) { - foreach ($this->query->findAll() as $task) { + foreach ($query->findAll() as $task) { $start = new DateTime; - $start->setTimestamp($task[$this->startColumn]); + $start->setTimestamp($task[$startColumn]); $end = new DateTime; - $end->setTimestamp($task[$this->endColumn] ?: time()); + $end->setTimestamp($task[$endColumn] ?: time()); - $vEvent = $this->getTaskIcalEvent($task, 'task-#'.$task['id'].'-'.$this->startColumn.'-'.$this->endColumn); + $vEvent = $this->getTaskIcalEvent($task, 'task-#'.$task['id'].'-'.$startColumn.'-'.$endColumn); $vEvent->setDtStart($start); $vEvent->setDtEnd($end); @@ -78,18 +82,22 @@ class TaskICalFormatter extends BaseTaskCalendarFormatter implements FormatterIn * Transform results to all day iCal events * * @access public - * @return FormatterInterface + * @param Table $query + * @return $this */ - public function addFullDayEvents() + public function addTasksWithDueDateOnly(Table $query) { - foreach ($this->query->findAll() as $task) { + foreach ($query->findAll() as $task) { $date = new DateTime; - $date->setTimestamp($task[$this->startColumn]); + $date->setTimestamp($task['date_due']); - $vEvent = $this->getTaskIcalEvent($task, 'task-#'.$task['id'].'-'.$this->startColumn); + $vEvent = $this->getTaskIcalEvent($task, 'task-#'.$task['id'].'-date_due'); $vEvent->setDtStart($date); $vEvent->setDtEnd($date); - $vEvent->setNoTime(true); + + if ($date->format('Hi') === '0000') { + $vEvent->setNoTime(true); + } $this->vCalendar->addComponent($vEvent); } diff --git a/app/Formatter/TaskListSubtaskAssigneeFormatter.php b/app/Formatter/TaskListSubtaskAssigneeFormatter.php new file mode 100644 index 00000000..73391766 --- /dev/null +++ b/app/Formatter/TaskListSubtaskAssigneeFormatter.php @@ -0,0 +1,56 @@ +<?php + +namespace Kanboard\Formatter; + +/** + * Class TaskListSubtaskAssigneeFormatter + * + * @package Kanboard\Formatter + * @author Frederic Guillot + */ +class TaskListSubtaskAssigneeFormatter extends TaskListFormatter +{ + protected $userId = 0; + protected $withoutEmptyTasks = false; + + /** + * Set assignee + * + * @param integer $userId + * @return $this + */ + public function withUserId($userId) + { + $this->userId = $userId; + return $this; + } + + public function withoutEmptyTasks() + { + $this->withoutEmptyTasks = true; + return $this; + } + + /** + * Apply formatter + * + * @access public + * @return array + */ + public function format() + { + $tasks = parent::format(); + $taskIds = array_column($tasks, 'id'); + $subtasks = $this->subtaskModel->getAllByTaskIdsAndAssignee($taskIds, $this->userId); + $subtasks = array_column_index($subtasks, 'task_id'); + array_merge_relation($tasks, $subtasks, 'subtasks', 'id'); + + if ($this->withoutEmptyTasks) { + $tasks = array_filter($tasks, function (array $task) { + return count($task['subtasks']) > 0; + }); + } + + return $tasks; + } +} diff --git a/app/Formatter/TasksApiFormatter.php b/app/Formatter/TasksApiFormatter.php new file mode 100644 index 00000000..95b14095 --- /dev/null +++ b/app/Formatter/TasksApiFormatter.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; + +/** + * Class TasksApiFormatter + * + * @package Kanboard\Formatter + */ +class TasksApiFormatter extends BaseFormatter implements FormatterInterface +{ + protected $tasks = array(); + + public function withTasks($tasks) + { + $this->tasks = $tasks; + return $this; + } + + /** + * Apply formatter + * + * @access public + * @return mixed + */ + public function format() + { + if (! empty($this->tasks)) { + foreach ($this->tasks as &$task) { + $task = $this->taskApiFormatter->withTask($task)->format(); + } + } + + return $this->tasks; + } +} diff --git a/app/Formatter/UserAutoCompleteFormatter.php b/app/Formatter/UserAutoCompleteFormatter.php index c81af00a..aa02cd22 100644 --- a/app/Formatter/UserAutoCompleteFormatter.php +++ b/app/Formatter/UserAutoCompleteFormatter.php @@ -2,37 +2,59 @@ namespace Kanboard\Formatter; -use Kanboard\Model\UserModel; +use Kanboard\Core\User\UserProviderInterface; use Kanboard\Core\Filter\FormatterInterface; /** - * Auto-complete formatter for user filter + * Auto-complete formatter for users * - * @package formatter + * @package Kanboard\Formatter * @author Frederic Guillot */ class UserAutoCompleteFormatter extends BaseFormatter implements FormatterInterface { /** - * Format the tasks for the ajax auto-completion + * Users found + * + * @access protected + * @var UserProviderInterface[] + */ + protected $users; + + /** + * Set users + * + * @access public + * @param UserProviderInterface[] $users + * @return $this + */ + public function withUsers(array $users) + { + $this->users = $users; + return $this; + } + + /** + * Format the users for the ajax auto-completion * * @access public * @return array */ public function format() { - $users = $this->query->columns(UserModel::TABLE.'.id', UserModel::TABLE.'.username', UserModel::TABLE.'.name')->findAll(); + $result = array(); - foreach ($users as &$user) { - if (empty($user['name'])) { - $user['value'] = $user['username'].' (#'.$user['id'].')'; - $user['label'] = $user['username']; - } else { - $user['value'] = $user['name'].' (#'.$user['id'].')'; - $user['label'] = $user['name'].' ('.$user['username'].')'; - } + foreach ($this->users as $user) { + $result[] = array( + 'id' => $user->getInternalId(), + 'username' => $user->getUsername(), + 'external_id' => $user->getExternalId(), + 'external_id_column' => $user->getExternalIdColumn(), + 'value' => $user->getName() === '' ? $user->getUsername() : $user->getName(), + 'label' => $user->getName() === '' ? $user->getUsername() : $user->getName().' ('.$user->getUsername().')', + ); } - return $users; + return $result; } } diff --git a/app/Formatter/UserMentionFormatter.php b/app/Formatter/UserMentionFormatter.php index 395fc463..9ea76881 100644 --- a/app/Formatter/UserMentionFormatter.php +++ b/app/Formatter/UserMentionFormatter.php @@ -2,13 +2,15 @@ namespace Kanboard\Formatter; +use Kanboard\Core\Filter\FormatterInterface; + /** * Class UserMentionFormatter * * @package Kanboard\Formatter * @author Frederic Guillot */ -class UserMentionFormatter extends BaseFormatter +class UserMentionFormatter extends BaseFormatter implements FormatterInterface { protected $users = array(); diff --git a/app/Helper/CalendarHelper.php b/app/Helper/CalendarHelper.php deleted file mode 100644 index 0942177d..00000000 --- a/app/Helper/CalendarHelper.php +++ /dev/null @@ -1,126 +0,0 @@ -<?php - -namespace Kanboard\Helper; - -use Kanboard\Core\Base; -use Kanboard\Core\Filter\QueryBuilder; -use Kanboard\Filter\TaskDueDateRangeFilter; - -/** - * Calendar Helper - * - * @package helper - * @author Frederic Guillot - */ -class CalendarHelper extends Base -{ - /** - * Render calendar component - * - * @param string $checkUrl - * @param string $saveUrl - * @return string - */ - public function render($checkUrl, $saveUrl) - { - $params = array( - 'checkUrl' => $checkUrl, - 'saveUrl' => $saveUrl, - ); - - return '<div class="js-calendar" data-params=\''.json_encode($params, JSON_HEX_APOS).'\'></div>'; - } - - /** - * Get formatted calendar task due events - * - * @access public - * @param QueryBuilder $queryBuilder - * @param string $start - * @param string $end - * @return array - */ - public function getTaskDateDueEvents(QueryBuilder $queryBuilder, $start, $end) - { - $formatter = $this->taskCalendarFormatter; - $formatter->setFullDay(); - $formatter->setColumns('date_due'); - - return $queryBuilder - ->withFilter(new TaskDueDateRangeFilter(array($start, $end))) - ->format($formatter); - } - - /** - * Get formatted calendar task events - * - * @access public - * @param QueryBuilder $queryBuilder - * @param string $start - * @param string $end - * @return array - */ - public function getTaskEvents(QueryBuilder $queryBuilder, $start, $end) - { - $startColumn = $this->configModel->get('calendar_project_tasks', 'date_started'); - - $queryBuilder->getQuery()->addCondition($this->getCalendarCondition( - $this->dateParser->getTimestampFromIsoFormat($start), - $this->dateParser->getTimestampFromIsoFormat($end), - $startColumn, - 'date_due' - )); - - $formatter = $this->taskCalendarFormatter; - $formatter->setColumns($startColumn, 'date_due'); - - return $queryBuilder->format($formatter); - } - - /** - * Get formatted calendar subtask time tracking events - * - * @access public - * @param integer $user_id - * @param string $start - * @param string $end - * @return array - */ - public function getSubtaskTimeTrackingEvents($user_id, $start, $end) - { - return $this->subtaskTimeTrackingCalendarFormatter - ->withQuery($this->subtaskTimeTrackingModel->getUserQuery($user_id) - ->addCondition($this->getCalendarCondition( - $this->dateParser->getTimestampFromIsoFormat($start), - $this->dateParser->getTimestampFromIsoFormat($end), - 'start', - 'end' - )) - ) - ->format(); - } - - /** - * Build SQL condition for a given time range - * - * @access public - * @param string $start_time Start timestamp - * @param string $end_time End timestamp - * @param string $start_column Start column name - * @param string $end_column End column name - * @return string - */ - public function getCalendarCondition($start_time, $end_time, $start_column, $end_column) - { - $start_column = $this->db->escapeIdentifier($start_column); - $end_column = $this->db->escapeIdentifier($end_column); - - $conditions = array( - "($start_column >= '$start_time' AND $start_column <= '$end_time')", - "($start_column <= '$start_time' AND $end_column >= '$start_time')", - "($start_column <= '$start_time' AND ($end_column = '0' OR $end_column IS NULL))", - ); - - return $start_column.' IS NOT NULL AND '.$start_column.' > 0 AND ('.implode(' OR ', $conditions).')'; - } -} diff --git a/app/Helper/ICalHelper.php b/app/Helper/ICalHelper.php deleted file mode 100644 index 95723417..00000000 --- a/app/Helper/ICalHelper.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -namespace Kanboard\Helper; - -use Kanboard\Core\Base; -use Kanboard\Core\Filter\QueryBuilder; -use Kanboard\Filter\TaskDueDateRangeFilter; -use Eluceo\iCal\Component\Calendar as iCalendar; - -/** - * ICal Helper - * - * @package helper - * @author Frederic Guillot - */ -class ICalHelper extends Base -{ - /** - * Get formatted calendar task due events - * - * @access public - * @param QueryBuilder $queryBuilder - * @param iCalendar $calendar - * @param string $start - * @param string $end - */ - public function addTaskDateDueEvents(QueryBuilder $queryBuilder, iCalendar $calendar, $start, $end) - { - $queryBuilder->withFilter(new TaskDueDateRangeFilter(array($start, $end))); - - $this->taskICalFormatter - ->setColumns('date_due') - ->setCalendar($calendar) - ->withQuery($queryBuilder->getQuery()) - ->addFullDayEvents(); - } -} diff --git a/app/Helper/ProjectHeaderHelper.php b/app/Helper/ProjectHeaderHelper.php index 9514f4f2..e4f96107 100644 --- a/app/Helper/ProjectHeaderHelper.php +++ b/app/Helper/ProjectHeaderHelper.php @@ -34,15 +34,17 @@ class ProjectHeaderHelper extends Base * @param string $controller * @param string $action * @param bool $boardView + * @param string $plugin * @return string */ - public function render(array $project, $controller, $action, $boardView = false) + public function render(array $project, $controller, $action, $boardView = false, $plugin = '') { $filters = array( 'controller' => $controller, 'action' => $action, 'project_id' => $project['id'], 'search' => $this->getSearchQuery($project), + 'plugin' => $plugin, ); return $this->template->render('project_header/header', array( diff --git a/app/Helper/ProjectRoleHelper.php b/app/Helper/ProjectRoleHelper.php index 6f9cf10c..a9f0596a 100644 --- a/app/Helper/ProjectRoleHelper.php +++ b/app/Helper/ProjectRoleHelper.php @@ -19,12 +19,12 @@ class ProjectRoleHelper extends Base * Get project role for the current user * * @access public - * @param integer $project_id + * @param integer $projectId * @return string */ - public function getProjectUserRole($project_id) + public function getProjectUserRole($projectId) { - return $this->memoryCache->proxy($this->projectUserRoleModel, 'getUserRole', $project_id, $this->userSession->getId()); + return $this->memoryCache->proxy($this->projectUserRoleModel, 'getUserRole', $projectId, $this->userSession->getId()); } /** @@ -36,7 +36,7 @@ class ProjectRoleHelper extends Base public function isDraggable(array &$task) { if ($task['is_active'] == 1 && $this->helper->user->hasProjectAccess('BoardAjaxController', 'save', $task['project_id'])) { - return $this->isSortableColumn($task['project_id'], $task['column_id']); + return $this->isSortableColumn($task['project_id'], $task['column_id'], $task['owner_id']); } return false; @@ -45,24 +45,29 @@ class ProjectRoleHelper extends Base /** * Return true is the column is sortable * - * @param int $project_id - * @param int $column_id + * @param int $projectId + * @param int $columnId + * @param int $assigneeId * @return bool */ - public function isSortableColumn($project_id, $column_id) + public function isSortableColumn($projectId, $columnId, $assigneeId = null) { - $role = $this->getProjectUserRole($project_id); + $role = $this->getProjectUserRole($projectId); if ($this->role->isCustomProjectRole($role)) { - $sortableColumns = $this->columnMoveRestrictionCacheDecorator->getSortableColumns($project_id, $role); + $sortableColumns = $this->columnMoveRestrictionCacheDecorator->getSortableColumns($projectId, $role); foreach ($sortableColumns as $column) { - if ($column['src_column_id'] == $column_id || $column['dst_column_id'] == $column_id) { + if ($column['src_column_id'] == $columnId || $column['dst_column_id'] == $columnId) { + if ($column['only_assigned'] == 1 && $assigneeId !== null && $assigneeId != $this->userSession->getId()) { + return false; + } + return true; } } - return empty($sortableColumns) && $this->isAllowedToMoveTask($project_id, $role); + return empty($sortableColumns) && $this->isAllowedToMoveTask($projectId, $role); } return true; @@ -71,33 +76,33 @@ class ProjectRoleHelper extends Base /** * Check if the user can move a task * - * @param int $project_id - * @param int $src_column_id - * @param int $dst_column_id + * @param int $projectId + * @param int $srcColumnId + * @param int $dstColumnId * @return bool|int */ - public function canMoveTask($project_id, $src_column_id, $dst_column_id) + public function canMoveTask($projectId, $srcColumnId, $dstColumnId) { - $role = $this->getProjectUserRole($project_id); + $role = $this->getProjectUserRole($projectId); if ($this->role->isCustomProjectRole($role)) { - if ($src_column_id == $dst_column_id) { + if ($srcColumnId == $dstColumnId) { return true; } - $sortableColumns = $this->columnMoveRestrictionCacheDecorator->getSortableColumns($project_id, $role); + $sortableColumns = $this->columnMoveRestrictionCacheDecorator->getSortableColumns($projectId, $role); foreach ($sortableColumns as $column) { - if ($column['src_column_id'] == $src_column_id && $column['dst_column_id'] == $dst_column_id) { + if ($column['src_column_id'] == $srcColumnId && $column['dst_column_id'] == $dstColumnId) { return true; } - if ($column['dst_column_id'] == $src_column_id && $column['src_column_id'] == $dst_column_id) { + if ($column['dst_column_id'] == $srcColumnId && $column['src_column_id'] == $dstColumnId) { return true; } } - return empty($sortableColumns) && $this->isAllowedToMoveTask($project_id, $role); + return empty($sortableColumns) && $this->isAllowedToMoveTask($projectId, $role); } return true; @@ -106,41 +111,41 @@ class ProjectRoleHelper extends Base /** * Return true if the user can create a task for the given column * - * @param int $project_id - * @param int $column_id + * @param int $projectId + * @param int $columnId * @return bool */ - public function canCreateTaskInColumn($project_id, $column_id) + public function canCreateTaskInColumn($projectId, $columnId) { - $role = $this->getProjectUserRole($project_id); + $role = $this->getProjectUserRole($projectId); if ($this->role->isCustomProjectRole($role)) { - if (! $this->isAllowedToCreateTask($project_id, $column_id, $role)) { + if (! $this->isAllowedToCreateTask($projectId, $columnId, $role)) { return false; } } - return $this->helper->user->hasProjectAccess('TaskCreationController', 'show', $project_id); + return $this->helper->user->hasProjectAccess('TaskCreationController', 'show', $projectId); } /** * Return true if the user can create a task for the given column * - * @param int $project_id - * @param int $column_id + * @param int $projectId + * @param int $columnId * @return bool */ - public function canChangeTaskStatusInColumn($project_id, $column_id) + public function canChangeTaskStatusInColumn($projectId, $columnId) { - $role = $this->getProjectUserRole($project_id); + $role = $this->getProjectUserRole($projectId); if ($this->role->isCustomProjectRole($role)) { - if (! $this->isAllowedToChangeTaskStatus($project_id, $column_id, $role)) { + if (! $this->isAllowedToChangeTaskStatus($projectId, $columnId, $role)) { return false; } } - return $this->helper->user->hasProjectAccess('TaskStatusController', 'close', $project_id); + return $this->helper->user->hasProjectAccess('TaskStatusController', 'close', $projectId); } /** @@ -154,6 +159,12 @@ class ProjectRoleHelper extends Base */ public function canRemoveTask(array $task) { + $role = $this->getProjectUserRole($task['project_id']); + + if ($this->hasRestriction($task['project_id'], $role, ProjectRoleRestrictionModel::RULE_TASK_SUPPRESSION)) { + return false; + } + if (isset($task['creator_id']) && $task['creator_id'] == $this->userSession->getId()) { return true; } @@ -166,14 +177,50 @@ class ProjectRoleHelper extends Base } /** + * Return true if the user can change assignee + * + * @public + * @param array $task + * @return bool + */ + public function canChangeAssignee(array $task) + { + $role = $this->getProjectUserRole($task['project_id']); + + if ($this->role->isCustomProjectRole($role) && $this->hasRestriction($task['project_id'], $role, ProjectRoleRestrictionModel::RULE_TASK_CHANGE_ASSIGNEE)) { + return false; + } + + return true; + } + + /** + * Return true if the user can update a task + * + * @public + * @param array $task + * @return bool + */ + public function canUpdateTask(array $task) + { + $role = $this->getProjectUserRole($task['project_id']); + + if ($this->role->isCustomProjectRole($role) && $task['owner_id'] != $this->userSession->getId() && $this->hasRestriction($task['project_id'], $role, ProjectRoleRestrictionModel::RULE_TASK_UPDATE_ASSIGNED)) { + return false; + } + + return true; + } + + /** * Check project access * * @param string $controller * @param string $action - * @param integer $project_id + * @param integer $projectId * @return bool */ - public function checkProjectAccess($controller, $action, $project_id) + public function checkProjectAccess($controller, $action, $projectId) { if (! $this->userSession->isLogged()) { return false; @@ -187,7 +234,7 @@ class ProjectRoleHelper extends Base return false; } - $role = $this->getProjectUserRole($project_id); + $role = $this->getProjectUserRole($projectId); if ($this->role->isCustomProjectRole($role)) { $result = $this->projectAuthorization->isAllowed($controller, $action, Role::PROJECT_MEMBER); @@ -201,17 +248,17 @@ class ProjectRoleHelper extends Base /** * Check authorization for a custom project role to change the task status * - * @param int $project_id - * @param int $column_id + * @param int $projectId + * @param int $columnId * @param string $role * @return bool */ - protected function isAllowedToChangeTaskStatus($project_id, $column_id, $role) + protected function isAllowedToChangeTaskStatus($projectId, $columnId, $role) { - $columnRestrictions = $this->columnRestrictionCacheDecorator->getAllByRole($project_id, $role); + $columnRestrictions = $this->columnRestrictionCacheDecorator->getAllByRole($projectId, $role); foreach ($columnRestrictions as $restriction) { - if ($restriction['column_id'] == $column_id) { + if ($restriction['column_id'] == $columnId) { if ($restriction['rule'] == ColumnRestrictionModel::RULE_ALLOW_TASK_OPEN_CLOSE) { return true; } else if ($restriction['rule'] == ColumnRestrictionModel::RULE_BLOCK_TASK_OPEN_CLOSE) { @@ -220,31 +267,23 @@ class ProjectRoleHelper extends Base } } - $projectRestrictions = $this->projectRoleRestrictionCacheDecorator->getAllByRole($project_id, $role); - - foreach ($projectRestrictions as $restriction) { - if ($restriction['rule'] == ProjectRoleRestrictionModel::RULE_TASK_OPEN_CLOSE) { - return false; - } - } - - return true; + return ! $this->hasRestriction($projectId, $role, ProjectRoleRestrictionModel::RULE_TASK_OPEN_CLOSE); } /** * Check authorization for a custom project role to create a task * - * @param int $project_id - * @param int $column_id + * @param int $projectId + * @param int $columnId * @param string $role * @return bool */ - protected function isAllowedToCreateTask($project_id, $column_id, $role) + protected function isAllowedToCreateTask($projectId, $columnId, $role) { - $columnRestrictions = $this->columnRestrictionCacheDecorator->getAllByRole($project_id, $role); + $columnRestrictions = $this->columnRestrictionCacheDecorator->getAllByRole($projectId, $role); foreach ($columnRestrictions as $restriction) { - if ($restriction['column_id'] == $column_id) { + if ($restriction['column_id'] == $columnId) { if ($restriction['rule'] == ColumnRestrictionModel::RULE_ALLOW_TASK_CREATION) { return true; } else if ($restriction['rule'] == ColumnRestrictionModel::RULE_BLOCK_TASK_CREATION) { @@ -253,10 +292,22 @@ class ProjectRoleHelper extends Base } } - $projectRestrictions = $this->projectRoleRestrictionCacheDecorator->getAllByRole($project_id, $role); + return ! $this->hasRestriction($projectId, $role, ProjectRoleRestrictionModel::RULE_TASK_CREATION); + } + + /** + * Check if the role can move task in the given project + * + * @param int $projectId + * @param string $role + * @return bool + */ + protected function isAllowedToMoveTask($projectId, $role) + { + $projectRestrictions = $this->projectRoleRestrictionCacheDecorator->getAllByRole($projectId, $role); foreach ($projectRestrictions as $restriction) { - if ($restriction['rule'] == ProjectRoleRestrictionModel::RULE_TASK_CREATION) { + if ($restriction['rule'] == ProjectRoleRestrictionModel::RULE_TASK_MOVE) { return false; } } @@ -265,22 +316,23 @@ class ProjectRoleHelper extends Base } /** - * Check if the role can move task in the given project + * Check if given role has a restriction * - * @param int $project_id + * @param integer $projectId * @param string $role + * @param string $rule * @return bool */ - protected function isAllowedToMoveTask($project_id, $role) + protected function hasRestriction($projectId, $role, $rule) { - $projectRestrictions = $this->projectRoleRestrictionCacheDecorator->getAllByRole($project_id, $role); + $projectRestrictions = $this->projectRoleRestrictionCacheDecorator->getAllByRole($projectId, $role); foreach ($projectRestrictions as $restriction) { - if ($restriction['rule'] == ProjectRoleRestrictionModel::RULE_TASK_MOVE) { - return false; + if ($restriction['rule'] == $rule) { + return true; } } - return true; + return false; } } diff --git a/app/Helper/SubtaskHelper.php b/app/Helper/SubtaskHelper.php index eea1ed63..67875a63 100644 --- a/app/Helper/SubtaskHelper.php +++ b/app/Helper/SubtaskHelper.php @@ -3,6 +3,7 @@ namespace Kanboard\Helper; use Kanboard\Core\Base; +use Kanboard\Model\SubtaskModel; /** * Subtask helpers @@ -45,26 +46,30 @@ class SubtaskHelper extends Base * Get the link to toggle subtask status * * @access public - * @param array $task - * @param array $subtask + * @param array $task + * @param array $subtask + * @param string $fragment + * @param int $userId * @return string */ - public function renderToggleStatus(array $task, array $subtask) + public function renderToggleStatus(array $task, array $subtask, $fragment = '', $userId = 0) { if (! $this->helper->user->hasProjectAccess('SubtaskController', 'edit', $task['project_id'])) { $html = $this->renderTitle($subtask); } else { $title = $this->renderTitle($subtask); $params = array( - 'project_id' => $task['project_id'], - 'task_id' => $subtask['task_id'], - 'subtask_id' => $subtask['id'], + 'project_id' => $task['project_id'], + 'task_id' => $subtask['task_id'], + 'subtask_id' => $subtask['id'], + 'user_id' => $userId, + 'fragment' => $fragment, ); if ($subtask['status'] == 0 && $this->hasSubtaskInProgress()) { - $html = $this->helper->url->link($title, 'SubtaskRestrictionController', 'show', $params, false, 'js-modal-confirm'); + $html = $this->helper->url->link($title, 'SubtaskRestrictionController', 'show', $params, false, 'js-modal-confirm', $this->getSubtaskTooltip($subtask)); } else { - $html = $this->helper->url->link($title, 'SubtaskStatusController', 'change', $params, false, 'js-subtask-toggle-status'); + $html = $this->helper->url->link($title, 'SubtaskStatusController', 'change', $params, false, 'js-subtask-toggle-status', $this->getSubtaskTooltip($subtask)); } } @@ -87,6 +92,17 @@ class SubtaskHelper extends Base return $html; } + public function renderBulkTitleField(array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="1"', 'required'), $attributes); + + $html = $this->helper->form->label(t('Title'), 'title'); + $html .= $this->helper->form->textarea('title', $values, $errors, $attributes); + $html .= '<p class="form-help">'.t('Enter one subtask by line.').'</p>'; + + return $html; + } + public function renderTitleField(array $values, array $errors = array(), array $attributes = array()) { $attributes = array_merge(array('tabindex="1"', 'required', 'maxlength="255"'), $attributes); @@ -132,4 +148,18 @@ class SubtaskHelper extends Base return $html; } + + public function getSubtaskTooltip(array $subtask) + { + switch ($subtask['status']) { + case SubtaskModel::STATUS_TODO: + return t('Subtask not started'); + case SubtaskModel::STATUS_INPROGRESS: + return t('Subtask currently in progress'); + case SubtaskModel::STATUS_DONE: + return t('Subtask completed'); + } + + return ''; + } } diff --git a/app/Helper/TaskHelper.php b/app/Helper/TaskHelper.php index 69520c03..cc164ce0 100644 --- a/app/Helper/TaskHelper.php +++ b/app/Helper/TaskHelper.php @@ -61,6 +61,30 @@ class TaskHelper extends Base return $this->helper->form->textEditor('description', $values, $errors, array('tabindex' => 2)); } + public function renderDescriptionTemplateDropdown($projectId) + { + $templates = $this->predefinedTaskDescriptionModel->getAll($projectId); + + if (! empty($templates)) { + $html = '<div class="dropdown dropdown-smaller">'; + $html .= '<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-floppy-o fa-fw" aria-hidden="true"></i>'.t('Template for the task description').' <i class="fa fa-caret-down" aria-hidden="true"></i></a>'; + $html .= '<ul>'; + + foreach ($templates as $template) { + $html .= '<li>'; + $html .= '<a href="#" data-template-target="textarea[name=description]" data-template="'.$this->helper->text->e($template['description']).'" class="js-template">'; + $html .= $this->helper->text->e($template['title']); + $html .= '</a>'; + $html .= '</li>'; + } + + $html .= '</ul></div>'; + return $html; + } + + return ''; + } + public function renderTagField(array $project, array $tags = array()) { $options = $this->tagModel->getAssignableList($project['id']); @@ -93,6 +117,10 @@ class TaskHelper extends Base public function renderAssigneeField(array $users, array $values, array $errors = array(), array $attributes = array()) { + if (isset($values['project_id']) && ! $this->helper->projectRole->canChangeAssignee($values)) { + return ''; + } + $attributes = array_merge(array('tabindex="3"'), $attributes); $html = $this->helper->form->label(t('Assignee'), 'owner_id'); @@ -204,7 +232,7 @@ class TaskHelper extends Base public function renderDueDateField(array $values, array $errors = array(), array $attributes = array()) { $attributes = array_merge(array('tabindex="9"'), $attributes); - return $this->helper->form->date(t('Due Date'), 'date_due', $values, $errors, $attributes); + return $this->helper->form->datetime(t('Due Date'), 'date_due', $values, $errors, $attributes); } public function renderPriority($priority) @@ -216,6 +244,21 @@ class TaskHelper extends Base return $html; } + public function renderReference(array $task) + { + if (! empty($task['reference'])) { + $reference = $this->helper->text->e($task['reference']); + + if (filter_var($task['reference'], FILTER_VALIDATE_URL) !== false) { + return sprintf('<a href="%s" target=_blank">%s</a>', $reference, $reference); + } + + return $reference; + } + + return ''; + } + public function getProgress($task) { if (! isset($this->columns[$task['project_id']])) { @@ -225,31 +268,57 @@ class TaskHelper extends Base return $this->taskModel->getProgress($task, $this->columns[$task['project_id']]); } - public function getNewTaskDropdown($projectId, $swimlaneId, $columnId) + public function getNewBoardTaskButton(array $swimlane, array $column) { - $providers = $this->externalTaskManager->getProvidersList(); + $html = '<div class="board-add-icon">'; + $providers = $this->externalTaskManager->getProviders(); if (empty($providers)) { - return ''; - } - - $html = '<small class="pull-right"><div class="dropdown">'; - $html .= '<a href="#" class="dropdown-menu"><i class="fa fa-cloud-download" aria-hidden="true"></i> <i class="fa fa-caret-down"></i></a><ul>'; - - foreach ($providers as $providerName) { - $link = $this->helper->url->link( - t('New External Task: %s', $providerName), - 'ExternalTaskCreationController', - 'step1', - array('project_id' => $projectId, 'swimlane_id' => $swimlaneId, 'column_id' => $columnId, 'provider_name' => $providerName), - false, - 'js-modal-replace' + $html .= $this->helper->modal->largeIcon( + 'plus', + t('Add a new task'), + 'TaskCreationController', + 'show', array( + 'project_id' => $column['project_id'], + 'column_id' => $column['id'], + 'swimlane_id' => $swimlane['id'], + ) ); + } else { + $html .= '<div class="dropdown">'; + $html .= '<a href="#" class="dropdown-menu"><i class="fa fa-plus" aria-hidden="true"></i></a><ul>'; + + $link = $this->helper->modal->large( + 'plus', + t('Add a new Kanboard task'), + 'TaskCreationController', + 'show', array( + 'project_id' => $column['project_id'], + 'column_id' => $column['id'], + 'swimlane_id' => $swimlane['id'], + ) + ); + + $html .= '<li>'.$link.'</li>'; + + foreach ($providers as $provider) { + $link = $this->helper->url->link( + $provider->getMenuAddLabel(), + 'ExternalTaskCreationController', + 'step1', + array('project_id' => $column['project_id'], 'swimlane_id' => $swimlane['id'], 'column_id' => $column['id'], 'provider_name' => $provider->getName()), + false, + 'js-modal-large' + ); - $html .= '<li><i class="fa fa-fw fa-plus-square" aria-hidden="true"></i> '.$link.'</li>'; + $html .= '<li>'.$provider->getIcon().' '.$link.'</li>'; + } + + $html .= '</ul></div>'; } - $html .= '</ul></div></small>'; + $html .= '</div>'; + return $html; } } diff --git a/app/Helper/TextHelper.php b/app/Helper/TextHelper.php index 89c1a8f3..698bef6d 100644 --- a/app/Helper/TextHelper.php +++ b/app/Helper/TextHelper.php @@ -90,19 +90,19 @@ class TextHelper extends Base */ public function phpToBytes($val) { - $val = trim($val); - $last = strtolower($val[strlen($val)-1]); + $size = (int) substr($val, 0, -1); + $last = strtolower(substr($val, -1)); switch ($last) { case 'g': - $val *= 1024; + $size *= 1024; case 'm': - $val *= 1024; + $size *= 1024; case 'k': - $val *= 1024; + $size *= 1024; } - return $val; + return $size; } /** diff --git a/app/Import/TaskImport.php b/app/Import/TaskImport.php index f5ca9b0e..4f89962b 100644 --- a/app/Import/TaskImport.php +++ b/app/Import/TaskImport.php @@ -4,90 +4,86 @@ namespace Kanboard\Import; use Kanboard\Core\Base; use Kanboard\Core\Csv; +use Kanboard\Core\ExternalLink\ExternalLinkManager; +use Kanboard\Core\ExternalLink\ExternalLinkProviderNotFound; use SimpleValidator\Validator; use SimpleValidator\Validators; /** - * Task Import + * Task CSV Import * - * @package import + * @package Kanboard\Import * @author Frederic Guillot */ class TaskImport extends Base { - /** - * Number of successful import - * - * @access public - * @var integer - */ - public $counter = 0; - - /** - * Project id to import tasks - * - * @access public - * @var integer - */ - public $projectId; - - /** - * Get mapping between CSV header and SQL columns - * - * @access public - * @return array - */ + protected $nbImportedTasks = 0; + protected $projectId = 0; + + public function setProjectId($projectId) + { + $this->projectId = $projectId; + return $this; + } + + public function getNumberOfImportedTasks() + { + return $this->nbImportedTasks; + } + public function getColumnMapping() { return array( - 'reference' => 'Reference', - 'title' => 'Title', - 'description' => 'Description', - 'assignee' => 'Assignee Username', - 'creator' => 'Creator Username', - 'color' => 'Color Name', - 'column' => 'Column Name', - 'category' => 'Category Name', - 'swimlane' => 'Swimlane Name', - 'score' => 'Complexity', - 'time_estimated' => 'Time Estimated', - 'time_spent' => 'Time Spent', - 'date_due' => 'Due Date', - 'is_active' => 'Closed', + 'reference' => e('Reference'), + 'title' => e('Title'), + 'description' => e('Description'), + 'assignee' => e('Assignee Username'), + 'creator' => e('Creator Username'), + 'color' => e('Color Name'), + 'column' => e('Column Name'), + 'category' => e('Category Name'), + 'swimlane' => e('Swimlane Name'), + 'score' => e('Complexity'), + 'time_estimated' => e('Time Estimated'), + 'time_spent' => e('Time Spent'), + 'date_started' => e('Start Date'), + 'date_due' => e('Due Date'), + 'priority' => e('Priority'), + 'is_active' => e('Status'), + 'tags' => e('Tags'), + 'external_link' => e('External Link'), ); } - /** - * Import a single row - * - * @access public - * @param array $row - * @param integer $line_number - */ - public function import(array $row, $line_number) + public function importTask(array $row, $lineNumber) { - $row = $this->prepare($row); + $task = $this->prepareTask($row); + + if ($this->validateCreation($task)) { + $taskId = $this->taskCreationModel->create($task); + + if ($taskId > 0) { + $this->logger->debug(__METHOD__.': imported successfully line '.$lineNumber); + $this->nbImportedTasks++; - if ($this->validateCreation($row)) { - if ($this->taskCreationModel->create($row) > 0) { - $this->logger->debug('TaskImport: imported successfully line '.$line_number); - $this->counter++; + if (! empty($row['tags'])) { + $tagsList = explode(',', $row['tags']); + array_walk($tagsList, function (&$value) { $value = trim($value); }); + $this->taskTagModel->save($this->projectId, $taskId, $tagsList); + } + + if (! empty($row['external_link'])) { + $this->createExternalLink($taskId, $row['external_link']); + } } else { - $this->logger->error('TaskImport: creation error at line '.$line_number); + $this->logger->error(__METHOD__.': creation error at line '.$lineNumber); } } else { - $this->logger->error('TaskImport: validation error at line '.$line_number); + $this->logger->error(__METHOD__.': validation error at line '.$lineNumber); } } - /** - * Format row before validation - * - * @access public - * @param array $row - * @return array - */ - public function prepare(array $row) + public function prepareTask(array $row) { $values = array(); $values['project_id'] = $this->projectId; @@ -96,6 +92,7 @@ class TaskImport extends Base $values['description'] = $row['description']; $values['is_active'] = Csv::getBooleanValue($row['is_active']) == 1 ? 0 : 1; $values['score'] = (int) $row['score']; + $values['priority'] = (int) $row['priority']; $values['time_estimated'] = (float) $row['time_estimated']; $values['time_spent'] = (float) $row['time_spent']; @@ -124,25 +121,22 @@ class TaskImport extends Base } if (! empty($row['date_due'])) { - $values['date_due'] = $this->dateParser->getTimestampFromIsoFormat($row['date_due']); + $values['date_due'] = $this->dateParser->getTimestamp($row['date_due']); + } + + if (! empty($row['date_started'])) { + $values['date_started'] = $this->dateParser->getTimestamp($row['date_started']); } $this->helper->model->removeEmptyFields( $values, - array('owner_id', 'creator_id', 'color_id', 'column_id', 'category_id', 'swimlane_id', 'date_due') + array('owner_id', 'creator_id', 'color_id', 'column_id', 'category_id', 'swimlane_id', 'date_due', 'date_started', 'priority') ); return $values; } - /** - * Validate user creation - * - * @access public - * @param array $values - * @return boolean - */ - public function validateCreation(array $values) + protected function validateCreation(array $values) { $v = new Validator($values, array( new Validators\Integer('project_id', t('This value must be an integer')), @@ -154,4 +148,34 @@ class TaskImport extends Base return $v->execute(); } + + protected function createExternalLink($taskId, $externalLink) + { + try { + $provider = $this->externalLinkManager + ->setUserInputText($externalLink) + ->setUserInputType(ExternalLinkManager::TYPE_AUTO) + ->find(); + + $link = $provider->getLink(); + $dependencies = $provider->getDependencies(); + $values = array( + 'task_id' => $taskId, + 'title' => $link->getTitle() ?: $link->getUrl(), + 'url' => $link->getUrl(), + 'link_type' => $provider->getType(), + 'dependency' => key($dependencies), + ); + + list($valid, $errors) = $this->externalLinkValidator->validateCreation($values); + + if ($valid) { + $this->taskExternalLinkModel->create($values); + } else { + $this->logger->error(__METHOD__.': '.var_export($errors, true)); + } + } catch (ExternalLinkProviderNotFound $e) { + $this->logger->error(__METHOD__.': '.$e->getMessage()); + } + } } diff --git a/app/Job/HttpAsyncJob.php b/app/Job/HttpAsyncJob.php index 9e5cf107..f2e9b3f5 100644 --- a/app/Job/HttpAsyncJob.php +++ b/app/Job/HttpAsyncJob.php @@ -34,7 +34,6 @@ class HttpAsyncJob extends BaseJob * @param string $url * @param string $content * @param array $headers - * @return $this */ public function execute($method, $url, $content, array $headers) { diff --git a/app/Job/ProjectFileEventJob.php b/app/Job/ProjectFileEventJob.php index 45e6ece3..7c0fd695 100644 --- a/app/Job/ProjectFileEventJob.php +++ b/app/Job/ProjectFileEventJob.php @@ -30,7 +30,6 @@ class ProjectFileEventJob extends BaseJob * * @param int $fileId * @param string $eventName - * @return $this */ public function execute($fileId, $eventName) { diff --git a/app/Job/SubtaskEventJob.php b/app/Job/SubtaskEventJob.php index 85c4d73e..36d268dd 100644 --- a/app/Job/SubtaskEventJob.php +++ b/app/Job/SubtaskEventJob.php @@ -32,7 +32,6 @@ class SubtaskEventJob extends BaseJob * @param int $subtaskId * @param string $eventName * @param array $values - * @return $this */ public function execute($subtaskId, $eventName, array $values = array()) { diff --git a/app/Job/TaskEventJob.php b/app/Job/TaskEventJob.php index acc7fca3..4626999c 100644 --- a/app/Job/TaskEventJob.php +++ b/app/Job/TaskEventJob.php @@ -38,7 +38,6 @@ class TaskEventJob extends BaseJob * @param array $changes * @param array $values * @param array $task - * @return $this */ public function execute($taskId, array $eventNames, array $changes = array(), array $values = array(), array $task = array()) { diff --git a/app/Job/TaskFileEventJob.php b/app/Job/TaskFileEventJob.php index 293dbf27..b9f1cd58 100644 --- a/app/Job/TaskFileEventJob.php +++ b/app/Job/TaskFileEventJob.php @@ -30,7 +30,6 @@ class TaskFileEventJob extends BaseJob * * @param int $fileId * @param string $eventName - * @return $this */ public function execute($fileId, $eventName) { diff --git a/app/Job/TaskLinkEventJob.php b/app/Job/TaskLinkEventJob.php index 31f62f07..4498bea3 100644 --- a/app/Job/TaskLinkEventJob.php +++ b/app/Job/TaskLinkEventJob.php @@ -30,7 +30,6 @@ class TaskLinkEventJob extends BaseJob * * @param int $taskLinkId * @param string $eventName - * @return $this */ public function execute($taskLinkId, $eventName) { diff --git a/app/Locale/bs_BA/translations.php b/app/Locale/bs_BA/translations.php index d1314ef3..62aaea64 100644 --- a/app/Locale/bs_BA/translations.php +++ b/app/Locale/bs_BA/translations.php @@ -39,7 +39,6 @@ return array( 'Administrator' => 'Administrator', 'Sign in' => 'Prijava', 'Users' => 'Korisnici', - 'No user' => 'Nema korisnika', 'Forbidden' => 'Zabranjeno', 'Access Forbidden' => 'Pristup zabranjen', 'Edit user' => 'Uredi korisnika', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'Pod-zadatak uspjeÅ¡no ažuriran.', 'Unable to update your sub-task.' => 'Nemoguće ažurirati pod-zadatak.', 'Unable to create your sub-task.' => 'Nemoguće dodati pod-zadatak.', - 'Sub-task added successfully.' => 'Pod-zadatak uspjeÅ¡no dodan.', 'Maximum size: ' => 'Maksimalna veliÄina: ', 'Display another project' => 'Prikaži drugi projekat', 'Created by %s' => 'Kreirao %s', @@ -396,22 +394,17 @@ return array( 'Activity stream' => 'Spisak aktivnosti', 'Dashboard' => 'Panel', 'Confirmation' => 'Potvrda', - 'Allow everybody to access to this project' => 'Dozvoli svima pristup ovom projektu', - 'Everybody have access to this project.' => 'Svima je dozvoljen pristup ovom projektu.', 'Webhooks' => 'Webhooks', 'API' => 'API', 'Create a comment from an external provider' => 'Napravi komentar preko vanjskog posrednika', 'Project management' => 'Upravljanje projektima', - 'My projects' => 'Moji projekti', 'Columns' => 'Kolone', 'Task' => 'Zadatak', - 'Your are not member of any project.' => 'Nisi Älan ni jednog projekta', 'Percentage' => 'Procenat', 'Number of tasks' => 'Broj zadataka', 'Task distribution' => 'Podjela zadataka', 'Analytics' => 'Analiza', 'Subtask' => 'Pod-zadatak', - 'My subtasks' => 'Moji pod-zadaci', 'User repartition' => 'Zaduženja korisnika', 'Clone this project' => 'Kloniraj ovaj projekat', 'Column removed successfully.' => 'Kolona uspjeÅ¡no uklonjena.', @@ -459,7 +452,6 @@ return array( 'Language:' => 'Jezik:', 'Timezone:' => 'Vremenska zona:', 'All columns' => 'Sve kolone', - 'Calendar' => 'Kalendar', 'Next' => 'Slijedeći', '#%d' => '#%d', 'All swimlanes' => 'Sve swimlane trake', @@ -557,7 +549,6 @@ return array( 'Unable to add this currency rate.' => 'Nemoguće dodati stopu valute.', 'Webhook URL' => 'Webhook URL', '%s removed the assignee of the task %s' => '%s je uklonio izvrÅ¡ioca zadatka %s', - 'Enable Gravatar images' => 'Omogući Gravatar slike', 'Information' => 'Informacije', 'Check two factor authentication code' => 'Provjera "Dva faktora" autentifikacionog koda', 'The two factor authentication code is not valid.' => '"Dva faktora" autentifikacionog koda nije validan.', @@ -611,14 +602,7 @@ return array( 'When task is moved from first column' => 'Kada je zadatak premjeÅ¡ten iz prve kolone', 'When task is moved to last column' => 'Kada je zadatak premjeÅ¡ten u posljednju kolonu', 'Year(s)' => 'Godina/e', - 'Calendar settings' => 'Postavke kalendara', - 'Project calendar view' => 'Pregled kalendara projekta', 'Project settings' => 'Postavke projekta', - 'Show subtasks based on the time tracking' => 'Prikaži pod-zadatke bazirano na vremenskom praćenju', - 'Show tasks based on the creation date' => 'Prikaži zadatke bazirano na vremenu otvaranja', - 'Show tasks based on the start date' => 'Prikaži zadatke bazirano na vremenu poÄetka rada', - 'Subtasks time tracking' => 'Vremensko praćenje pod-zadataka', - 'User calendar view' => 'Pregled korisniÄkog kalendara', 'Automatically update the start date' => 'Automatski ažuriraj poÄetni datum', 'iCal feed' => 'iCal kanal', 'Preferences' => 'Postavke', @@ -637,7 +621,6 @@ return array( 'Notification' => 'ObavjeÅ¡tenja', '%s moved the task #%d to the first swimlane' => '%s je premjestio zadatak #%d u prvu swimlane traku', 'Swimlane' => 'Swimlane traka', - 'Gravatar' => 'Gravatar', '%s moved the task %s to the first swimlane' => '%s je premjestio zadatak %s u prvi swimlane traku', '%s moved the task %s to the swimlane "%s"' => '%s je premjestio zadatak %s u swimlane traku "%s"', 'This report contains all subtasks information for the given date range.' => 'Ovaj izvjeÅ¡taj sadržava sve informacije o pod-zadacima za dati period', @@ -674,7 +657,6 @@ return array( 'Stop timer' => 'Zaustavi tajmer', 'Start timer' => 'Pokreni tajmer', 'My activity stream' => 'Tok mojih aktivnosti', - 'My calendar' => 'Moj kalendar', 'Search tasks' => 'Pretraga zadataka', 'Reset filters' => 'Vrati filtere na poÄetno', 'My tasks due tomorrow' => 'Moji zadaci koje treba zavrÅ¡iti sutra', @@ -688,7 +670,6 @@ return array( 'Overview' => 'OpÅ¡ti pregled', 'Board/Calendar/List view' => 'Pregled PloÄe/Kalendara/Liste', 'Switch to the board view' => 'Promijeni da vidim ploÄu', - 'Switch to the calendar view' => 'Promijeni da vidim kalendar', 'Switch to the list view' => 'Promijeni da vidim listu', 'Go to the search/filter box' => 'Idi na kutiju s pretragom/filterima', 'There is no activity yet.' => 'JoÅ¡ uvijek nema aktivnosti.', @@ -743,15 +724,8 @@ return array( 'License:' => 'Licenca:', 'License' => 'Licenca', 'Enter the text below' => 'Unesi tekst ispod', - 'Sort by position' => 'Sortiraj po poziciji', - 'Sort by date' => 'Sortiraj po datumu', - 'Add task' => 'Dodaj zadatak', 'Start date:' => 'PoÄetno vrijeme:', 'Due date:' => 'Vrijeme do kada treba zavrÅ¡iti:', - 'There is no start date or due date for this task.' => 'Nema poÄetnog datuma ili datuma do kada treba zavrÅ¡iti ovaj zadatak.', - 'Moving or resizing a task will change the start and due date of the task.' => 'PremjeÅ¡tanje ili promjena veliÄine zadatka će promijeniti datum poÄetka i datum do kada treba zavrÅ¡iti zadatak.', - 'There is no task in your project.' => 'Nema zadataka u projektu.', - 'Gantt chart' => 'Gantogram', 'People who are project managers' => 'Osobe koji su menadžeri projekta', 'People who are project members' => 'Osobe koje su Älanovi projekta', 'NOK - Norwegian Krone' => 'NOK - NorveÅ¡ka kruna', @@ -763,22 +737,15 @@ return array( 'Members' => 'ÄŒlanovi', 'Shared project' => 'Dijeljeni projekti', 'Project managers' => 'Menadžeri projekta', - 'Gantt chart for all projects' => 'Gantogram za sve projekte', 'Projects list' => 'Lista projekata', - 'Gantt chart for this project' => 'Gantogram za ovaj projekat', - 'Project board' => 'PloÄa projekta', 'End date:' => 'Datum zavrÅ¡etka:', - 'There is no start date or end date for this project.' => 'Nema poÄetnog ili krajnjeg datuma za ovaj projekat.', - 'Projects Gantt chart' => 'Gantogram projekata', 'Change task color when using a specific task link' => 'Promijeni boju zadatka kada se koristi odreÄ‘ena veza na zadatku', 'Task link creation or modification' => 'Veza na zadatku je napravljena ili izmijenjena', 'Milestone' => 'Prekretnica', 'Documentation: %s' => 'Dokumentacija: %s', - 'Switch to the Gantt chart view' => 'Promijeni u gantogram pregled', 'Reset the search/filter box' => 'Vrati na poÄetno pretragu/filtere', 'Documentation' => 'Dokumentacija', 'Table of contents' => 'Sadržaj', - 'Gantt' => 'Gantogram', 'Author' => 'Autor', 'Version' => 'Verzija', 'Plugins' => 'Dodaci', @@ -889,7 +856,6 @@ return array( 'There is no user available.' => 'Trenutno nema dostupnih korisnika.', 'Do you really want to remove the user "%s" from the group "%s"?' => 'Da li zaista želiÅ¡ ukloniti korisnika "%s" iz grupe "%s"?', 'There is no group.' => 'Trenutno nema grupa.', - 'External Id' => 'Vanjski Id', 'Add group member' => 'Dodaj Älana grupe', 'Do you really want to remove this group: "%s"?' => 'Da li zaista želiÅ¡ ukloniti ovu grupu: "%s"?', 'There is no user in this group.' => 'Trenutno nema korisnika u grupi.', @@ -955,7 +921,6 @@ return array( 'Default priority' => 'Podrazumijevani prioritet', 'Lowest priority' => 'Najmanji prioritet', 'Highest priority' => 'Najveći prioritet', - 'If you put zero to the low and high priority, this feature will be disabled.' => 'Ako upiÅ¡eÅ¡ nulu za najmanji i najveći prioritet, ova opcija će biti onemogućena.', 'Close a task when there is no activity' => 'Zatvori zadatak kada nema aktivnosti', 'Duration in days' => 'Dužina trajanja u danima', 'Send email when there is no activity on a task' => 'PoÅ¡alji email kada nema aktivnosti na zadatku', @@ -1169,8 +1134,6 @@ return array( 'Subtasks overview for %s' => 'Pregled pod-zadataka za %s', 'Projects overview for %s' => 'Pregled projekata za %s', 'Activity stream for %s' => 'Tok aktivnosti za %s', - 'Calendar for %s' => 'Kalendar za %s', - 'Notifications for %s' => 'Obavijesti za %s', 'Assign a color when the task is moved to a specific swimlane' => 'Dodijeli boju kada je zadatak pomjeren u odreÄ‘enu swimlane traku', 'Assign a priority when the task is moved to a specific swimlane' => 'Dodijeli prioritet kada je zadatak pomjeren u odreÄ‘enu swimlane traku', 'User unlocked successfully.' => 'Korisnik je uspjeÅ¡no otkljuÄan.', @@ -1241,7 +1204,6 @@ return array( 'Preview' => 'Pregled', 'Write' => 'PiÅ¡i', 'Write your text in Markdown' => 'UpiÅ¡i tekst u Markdown', - 'New External Task: %s' => 'Novi vanjski zadatak: %s', 'No personal API access token registered.' => 'Nema registrovanog liÄnog pristupnog API tokena', 'Your personal API access token is "%s"' => 'Tvoj liÄni pristupni API token je: "%s"', 'Remove your token' => 'Ukloni svoj token', @@ -1316,7 +1278,6 @@ return array( 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'MožeÅ¡ priložiti prethodno preuzetu Sqlite bazu podataka (Gzip format).', 'Database file' => 'Datoteka baze podataka', 'Upload' => 'Priloži', - 'Remove this user from group' => 'Ukloni ovog korisnika iz grupe', 'Your project must have at least one active swimlane.' => 'Projekat mora imati barem jednu aktivnu swimlane traku.', 'Project: %s' => 'Projekat: %s', 'Automatic action not found: "%s"' => 'Automatska akcija nije pronaÄ‘ena: "%s"', @@ -1334,4 +1295,75 @@ return array( 'Assign automatically a color when due date is expired' => 'Automatski dodaj boju kada je krajnje vrijeme izvrÅ¡enja isteklo', 'Total score in this column across all swimlanes' => 'Ukupni rezultat u ovoj koloni za sve swimlane trake', 'HRK - Kuna' => 'HRK - Hrvatska Kuna', + // 'ARS - Argentine Peso' => '', + // 'COP - Colombian Peso' => '', + // '%d groups' => '', + // '%d group' => '', + // 'Group ID' => '', + // 'External ID' => '', + // '%d users' => '', + // '%d user' => '', + // 'Hide subtasks' => '', + // 'Show subtasks' => '', + // 'Authentication Parameters' => '', + // 'API Access' => '', + // 'No users found.' => '', + // 'User ID' => '', + // 'Notifications are activated' => '', + // 'Notifications are disabled' => '', + // 'User disabled' => '', + // '%d notifications' => '', + // '%d notification' => '', + // 'There is no external integration installed.' => '', + // 'You are not allowed to update tasks assigned to someone else.' => '', + // 'You are not allowed to change the assignee.' => '', + // 'Task suppression is not permitted' => '', + // 'Changing assignee is not permitted' => '', + // 'Update only assigned tasks is permitted' => '', + // 'Only for tasks assigned to the current user' => '', + // 'My projects' => '', + // 'Your are not member of any project.' => '', + // 'My subtasks' => '', + // '%d subtasks' => '', + // '%d subtask' => '', + // 'Only moving task between those columns is permitted for tasks assigned to the current user' => '', + // '[DUPLICATE]' => '', + // 'DKK - Danish Krona' => '', + // 'Remove user from group' => '', + // 'Assign the task to its creator' => '', + // 'This task was sent by email to "%s" with subject "%s".' => '', + // 'Predefined Email Subjects' => '', + // 'Write one subject by line.' => '', + // 'Create another link' => '', + // 'BRL - Brazilian Real' => '', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/ca_ES/translations.php b/app/Locale/ca_ES/translations.php new file mode 100644 index 00000000..1240d560 --- /dev/null +++ b/app/Locale/ca_ES/translations.php @@ -0,0 +1,1369 @@ +<?php + +return array( + 'number.decimals_separator' => ',', + 'number.thousands_separator' => ' ', + 'None' => 'Cap', + 'Edit' => 'Edita', + 'Remove' => 'Elimina', + 'Yes' => 'Si', + 'No' => 'No', + 'cancel' => 'cancel·la', + 'or' => 'o', + 'Yellow' => 'Groc', + 'Blue' => 'Blau', + 'Green' => 'Verd', + 'Purple' => 'Lil·la', + 'Red' => 'Vermell', + 'Orange' => 'Taronja', + 'Grey' => 'Gris', + 'Brown' => 'Marró', + 'Deep Orange' => 'Taronja fosc', + 'Dark Grey' => 'Gris fosc', + 'Pink' => 'Rosa', + 'Teal' => 'Turquessa', + 'Cyan' => 'Cian', + 'Lime' => 'Lima', + 'Light Green' => 'Verd clar', + 'Amber' => 'Ambre', + 'Save' => 'Desa', + 'Login' => 'Inicia la sessió', + 'Official website:' => 'Pà gina web oficial:', + 'Unassigned' => 'Sense assignar', + 'View this task' => 'Veure aquesta tasca', + 'Remove user' => 'Eliminar usuari', + 'Do you really want to remove this user: "%s"?' => 'Vols eliminar aquest usuari: "%s"?', + 'All users' => 'Tots els usuaris', + 'Username' => 'Nom d\'usuari', + 'Password' => 'Contrasenya', + 'Administrator' => 'Administrador', + 'Sign in' => 'Accedeix', + 'Users' => 'Usuaris', + 'Forbidden' => 'Prohibit', + 'Access Forbidden' => 'Accés prohibit', + 'Edit user' => 'Edita usuaris', + 'Logout' => 'Tanca sessió', + 'Bad username or password' => 'Usuari o contrasenya no existeix', + 'Edit project' => 'Edita el projecte', + 'Name' => 'Nom', + 'Projects' => 'Projectes', + 'No project' => 'Cap projecte', + 'Project' => 'Projecte', + 'Status' => 'Estat', + 'Tasks' => 'Tasques', + 'Board' => 'Tauler', + 'Actions' => 'Accions', + 'Inactive' => 'Inactiu', + 'Active' => 'Actiu', + 'Unable to update this board.' => 'No es pot actualitzar aquest tauler.', + 'Disable' => 'Desactivar', + 'Enable' => 'Habilita', + 'New project' => 'Nou projecte', + 'Do you really want to remove this project: "%s"?' => 'Vols eliminar aquest projecte: "%s"?', + 'Remove project' => 'Elimina projecte', + 'Edit the board for "%s"' => 'Edita el tauler de "%s"', + 'Add a new column' => 'Afegeix una nova columna', + 'Title' => 'TÃtol', + 'Assigned to %s' => 'Assignat a %s', + 'Remove a column' => 'Eliminar una columna', + 'Unable to remove this column.' => 'No es pot eliminar aquesta columna.', + 'Do you really want to remove this column: "%s"?' => 'Vols eliminar aquesta columna: "%s"?', + 'Settings' => 'Configuració', + 'Application settings' => 'Configuració de l\'aplicació', + 'Language' => 'Idioma', + 'Webhook token:' => 'Web hook token:', + 'API token:' => 'Token de l\'API:', + 'Database size:' => 'Mida de la base de dades:', + 'Download the database' => 'Descarregar la base de dades', + 'Optimize the database' => 'Optimitzar la base de dades', + '(VACUUM command)' => '(Comanda VACUUM)', + '(Gzip compressed Sqlite file)' => '(Arxiu comprimit gzip SQLite)', + 'Close a task' => 'Tancar una tasca', + 'Column' => 'Columna', + 'Color' => 'Color', + 'Assignee' => 'Assignat a', + 'Create another task' => 'Crear una altra tasca', + 'New task' => 'Nova tasca', + 'Open a task' => 'Obrir una tasca', + 'Do you really want to open this task: "%s"?' => 'Vols obrir aquesta tasca: "%s"?', + 'Back to the board' => 'Torna al tauler', + 'There is nobody assigned' => 'No hi ha ningú assignat', + 'Column on the board:' => 'Columna al tauler:', + 'Close this task' => 'Tanca aquesta tasca', + 'Open this task' => 'Obre aquesta tasca', + 'There is no description.' => 'No hi ha cap descripció.', + 'Add a new task' => 'Afegeix una nova tasca', + 'The username is required' => 'Es requereix el nom d\'usuari', + 'The maximum length is %d characters' => 'La longitud mà xima és de %d carà cters', + 'The minimum length is %d characters' => 'La longitud mÃnima és %d carà cters', + 'The password is required' => 'La contrasenya és necessà ria', + 'This value must be an integer' => 'Aquest valor ha de ser un nombre enter', + 'The username must be unique' => 'El nom d\'usuari ha de ser únic', + 'The user id is required' => 'Es requereix que l\'ID d\'usuari', + 'Passwords don\'t match' => 'Les contrasenyes no coincideixen', + 'The confirmation is required' => 'Es requereix la confirmació', + 'The project is required' => 'Es requereix el projecte', + 'The id is required' => 'Es requereix que l\'ID', + 'The project id is required' => 'Es requereix que l\'identificador del projecte', + 'The project name is required' => 'Es requereix el nom del projecte', + 'The title is required' => 'Es requereix el tÃtol', + 'Settings saved successfully.' => 'La configuració es va guardar amb èxit.', + 'Unable to save your settings.' => 'No es pot desar la configuració.', + 'Database optimization done.' => 'Optimització de bases de dades realitza.', + 'Your project have been created successfully.' => 'El seu projecte s\'han creat amb èxit.', + 'Unable to create your project.' => 'No es pot crear el projecte.', + 'Project updated successfully.' => 'Projecte actualitzat correctament.', + 'Unable to update this project.' => 'No es pot actualitzar aquest projecte.', + 'Unable to remove this project.' => 'No es pot eliminar aquest projecte.', + 'Project removed successfully.' => 'Projecte eliminat correctament.', + 'Project activated successfully.' => 'Projecte activat correctament.', + 'Unable to activate this project.' => 'No es pot activar aquest projecte.', + 'Project disabled successfully.' => 'Projecte deshabilitat amb èxit.', + 'Unable to disable this project.' => 'No és possible desactivar aquest projecte.', + 'Unable to open this task.' => 'No es pot obrir aquesta tasca.', + 'Task opened successfully.' => 'Tasca oberta amb èxit.', + 'Unable to close this task.' => 'No es pot tancar aquesta tasca.', + 'Task closed successfully.' => 'Tasca tancada amb èxit.', + 'Unable to update your task.' => 'No es pot actualitzar la seva tasca.', + 'Task updated successfully.' => 'Tasca actualitzada correctament.', + 'Unable to create your task.' => 'No es pot crear la tasca.', + 'Task created successfully.' => 'Tasca creada correctament.', + 'User created successfully.' => 'L\'usuari ha creat correctament.', + 'Unable to create your user.' => 'No es pot crear l\'usuari.', + 'User updated successfully.' => 'Usuari actualitzat correctament.', + 'User removed successfully.' => 'Usuari eliminat correctament.', + 'Unable to remove this user.' => 'No es pot eliminar aquest usuari.', + 'Board updated successfully.' => 'Tauler actualitzat correctament.', + 'Ready' => 'Preparat', + 'Backlog' => 'Pendent', + 'Work in progress' => 'En curs', + 'Done' => 'Fet', + 'Application version:' => 'Versió de l\'aplicació:', + 'Id' => 'Identificació', + 'Public link' => 'Enllaç públic', + 'Timezone' => 'Zona horà ria', + 'Sorry, I didn\'t find this information in my database!' => 'No s\'ha trobat la informació a la base de dades!', + 'Page not found' => 'Pà gina no trobada', + 'Complexity' => 'Dificultat', + 'Task limit' => 'LÃmit de tasques', + 'Task count' => 'Recompte de tasques', + 'User' => 'Usuari', + 'Comments' => 'Comentaris', + 'Comment is required' => 'Es requereix comentari', + 'Comment added successfully.' => 'Comentari afegit amb èxit.', + 'Unable to create your comment.' => 'No es pot crear el seu comentari.', + 'Due Date' => 'Data de venciment', + 'Invalid date' => 'Data no và lida', + 'Automatic actions' => 'Accions automà tiques', + 'Your automatic action have been created successfully.' => 'La seva acció automà tica s\'han creat amb èxit.', + 'Unable to create your automatic action.' => 'No es pot crear la seva acció automà tica.', + 'Remove an action' => 'Eliminar una acció', + 'Unable to remove this action.' => 'No es pot eliminar aquesta acció.', + 'Action removed successfully.' => 'Acció eliminat correctament.', + 'Automatic actions for the project "%s"' => 'Accions automà tiques per al projecte "%s"', + 'Add an action' => 'Afegeix una acció', + 'Event name' => 'Nom de l\'esdeveniment', + 'Action' => 'Acció', + 'Event' => 'Esdeveniment', + 'When the selected event occurs execute the corresponding action.' => 'Quan es produeix l\'esdeveniment seleccionat executar l\'acció corresponent.', + 'Next step' => 'Següent pas', + 'Define action parameters' => 'Definir parà metres d\'acció', + 'Do you really want to remove this action: "%s"?' => 'Vols eliminar aquesta acció: "%s"?', + 'Remove an automatic action' => 'Eliminar una acció automà tica', + 'Assign the task to a specific user' => 'Assignar la tasca a un usuari especÃfic', + 'Assign the task to the person who does the action' => 'Assignar la tasca a la persona que fa l\'acció', + 'Duplicate the task to another project' => 'Duplica la tasca a un altre projecte', + 'Move a task to another column' => 'Mou una tasca a una altra columna', + 'Task modification' => 'Modificació de tasques', + 'Task creation' => 'Creació de tasques', + 'Closing a task' => 'El tancament d\'una tasca', + 'Assign a color to a specific user' => 'Assignar un color a un usuari especÃfic', + 'Position' => 'Posició', + 'Duplicate to another project' => 'Duplica a un altre projecte', + 'Duplicate' => 'Duplica', + 'Link' => 'Enllaç', + 'Comment updated successfully.' => 'Comentari actualitzat correctament.', + 'Unable to update your comment.' => 'No es pot actualitzar el seu comentari.', + 'Remove a comment' => 'Retirar un comentari', + 'Comment removed successfully.' => 'Comentari eliminat correctament.', + 'Unable to remove this comment.' => 'No es pot eliminar aquest comentari.', + 'Do you really want to remove this comment?' => 'Vols eliminar aquest comentari?', + 'Current password for the user "%s"' => 'Contrasenya actual per a l\'usuari "%s"', + 'The current password is required' => 'Es requereix la contrasenya actual', + 'Wrong password' => 'Contrasenya incorrecta', + 'Unknown' => 'Desconegut', + 'Last logins' => 'Últims inicis de sessió', + 'Login date' => 'Data d\'inici de sessió', + 'Authentication method' => 'Mètode d\'autenticació', + 'IP address' => 'Adreça IP', + 'User agent' => 'Agent d\'usuari', + 'Persistent connections' => 'Les connexions persistents', + 'No session.' => 'Cap sessió.', + 'Expiration date' => 'Data de caducitat', + 'Remember Me' => 'Recorda\'m', + 'Creation date' => 'Data de creació', + 'Everybody' => 'Tothom', + 'Open' => 'Obert', + 'Closed' => 'Tancat', + 'Search' => 'Cerca', + 'Nothing found.' => 'No s\'ha trobat res.', + 'Due date' => 'Data de venciment', + 'Description' => 'Descripció', + '%d comments' => '%d comentaris', + '%d comment' => '%d comentari', + 'Email address invalid' => 'Adreça de correu electrònic no và lida', + 'Your external account is not linked anymore to your profile.' => 'El seu compte extern no està vinculada més al seu perfil.', + 'Unable to unlink your external account.' => 'No és possible desvincular el compte extern.', + 'External authentication failed' => 'L\'autenticació externa va fallar', + 'Your external account is linked to your profile successfully.' => 'El seu compte extern està vinculada al seu perfil correctament.', + 'Email' => 'E-mail', + 'Task removed successfully.' => 'Tasca eliminat correctament.', + 'Unable to remove this task.' => 'No es pot eliminar aquesta tasca.', + 'Remove a task' => 'Treure una tasca', + 'Do you really want to remove this task: "%s"?' => 'Vols eliminar aquesta tasca: "%s"?', + 'Assign automatically a color based on a category' => 'Assignar automà ticament un color basat en una categoria', + 'Assign automatically a category based on a color' => 'Assignar automà ticament una categoria basada en un color', + 'Task creation or modification' => 'Creació o modificació de tasques', + 'Category' => 'Categoria', + 'Category:' => 'Categoria:', + 'Categories' => 'Categories', + 'Your category have been created successfully.' => 'La seva categoria s\'han creat amb èxit.', + 'This category has been updated successfully.' => 'Aquesta categoria s\'ha actualitzat correctament.', + 'Unable to update this category.' => 'No es pot actualitzar aquesta categoria.', + 'Remove a category' => 'Eliminar una categoria', + 'Category removed successfully.' => 'Categoria eliminat correctament.', + 'Unable to remove this category.' => 'No es pot eliminar aquesta categoria.', + 'Category modification for the project "%s"' => 'Modificació de la categoria pel projecte "%s"', + 'Category Name' => 'Nom de categoria', + 'Add a new category' => 'Afegeix una nova categoria', + 'Do you really want to remove this category: "%s"?' => 'Vols eliminar aquesta categoria: "%s"?', + 'All categories' => 'Totes les categories', + 'No category' => 'sense categoria', + 'The name is required' => 'Es requereix el nom', + 'Remove a file' => 'Suprimir un fitxer', + 'Unable to remove this file.' => 'No es pot eliminar aquest arxiu.', + 'File removed successfully.' => 'Arxiu eliminat amb èxit.', + 'Attach a document' => 'Adjunta un document', + 'Do you really want to remove this file: "%s"?' => 'Vols eliminar aquesta imatge: "%s"?', + 'Attachments' => 'Adjunts', + 'Edit the task' => 'Edita la tasca', + 'Add a comment' => 'Afegeix un comentari', + 'Edit a comment' => 'Edita un comentari', + 'Summary' => 'Resum', + 'Time tracking' => 'Control d\'hores', + 'Estimate:' => 'Estimació:', + 'Spent:' => 'Utilitzat:', + 'Do you really want to remove this sub-task?' => 'Realment voleu eliminar aquesta sub-tasca?', + 'Remaining:' => 'Restant:', + 'hours' => 'hores', + 'spent' => 'gastat', + 'estimated' => 'estimat', + 'Sub-Tasks' => 'Subtasques', + 'Add a sub-task' => 'Afegeix una subtasca', + 'Original estimate' => 'Estimació original', + 'Create another sub-task' => 'Crear una altra subtasca', + 'Time spent' => 'El temps dedicat', + 'Edit a sub-task' => 'Editar una subtasca', + 'Remove a sub-task' => 'Suprimir una subtasca', + 'The time must be a numeric value' => 'El temps ha de ser un valor numèric', + 'Todo' => 'Fer', + 'In progress' => 'En progrés', + 'Sub-task removed successfully.' => 'Subtasca eliminat correctament.', + 'Unable to remove this sub-task.' => 'No es pot eliminar aquesta sub-tasques.', + 'Sub-task updated successfully.' => 'Subtasca actualitzat correctament.', + 'Unable to update your sub-task.' => 'No es pot actualitzar el seu sub-tasques.', + 'Unable to create your sub-task.' => 'No es pot crear el sub-tasques.', + 'Maximum size: ' => 'Mida mà xima: ', + 'Display another project' => 'Mostra un altre projecte', + 'Created by %s' => 'Creat per %s', + 'Tasks Export' => 'Exportació de tasques', + 'Start Date' => 'Data d\'inici', + 'Execute' => 'Executar', + 'Task Id' => 'ID de tasca', + 'Creator' => 'Creador', + 'Modification date' => 'Data de modificació', + 'Completion date' => 'Data de finalització', + 'Clone' => 'Clon', + 'Project cloned successfully.' => 'Projecte clonat amb èxit.', + 'Unable to clone this project.' => 'No és possible clonar aquest projecte.', + 'Enable email notifications' => 'Activa notificacions per correu electrònic', + 'Task position:' => 'Posició de la tasca:', + 'The task #%d have been opened.' => 'La tasca #%d s\'ha obert.', + 'The task #%d have been closed.' => 'La tasca #%d s\'ha tancat.', + 'Sub-task updated' => 'Subtasca actualitza', + 'Title:' => 'TÃtol:', + 'Status:' => 'Estat:', + 'Assignee:' => 'Assignat a:', + 'Time tracking:' => 'Control d\'hores:', + 'New sub-task' => 'Nova subtasca', + 'New attachment added "%s"' => 'Nou adjunt afegit "%s"', + 'New comment posted by %s' => 'Nou comentari Publicat per %s', + 'New comment' => 'Nou comentari', + 'Comment updated' => 'Comentari actualitzat', + 'New subtask' => 'Nova subtasca', + 'I want to receive notifications only for those projects:' => 'Vull rebre notificacions només per a aquells projectes:', + 'view the task on Kanboard' => 'Veure la tasca en Kanboard', + 'Public access' => 'Accés públic', + 'Disable public access' => 'Deshabilitar l\'accés del públic', + 'Enable public access' => 'Permetre l\'accés del públic', + 'Public access disabled' => 'L\'accés públic deshabilitat', + 'Move the task to another project' => 'Mou la tasca a un altre projecte', + 'Move to another project' => 'Mou a un altre projecte', + 'Do you really want to duplicate this task?' => 'És el que realment desitja duplicar aquesta tasca?', + 'Duplicate a task' => 'Duplica una tasca', + 'External accounts' => 'Els comptes externes', + 'Account type' => 'Tipus de compte', + 'Local' => 'Local', + 'Remote' => 'Remot', + 'Enabled' => 'Habilitat', + 'Disabled' => 'Inhabilitat', + 'Login:' => 'Iniciar Sessió:', + 'Full Name:' => 'Nom complet:', + 'Email:' => 'E-mail:', + 'Notifications:' => 'Notificacions:', + 'Notifications' => 'Notificacions', + 'Account type:' => 'Tipus de compte:', + 'Edit profile' => 'Editar el perfil', + 'Change password' => 'Canvia la contrasenya', + 'Password modification' => 'Modificació de la contrasenya', + 'External authentications' => 'Autenticacions externes', + 'Never connected.' => 'Mai connectada.', + 'No external authentication enabled.' => 'No s\'habilita l\'autenticació externa.', + 'Password modified successfully.' => 'Contrasenya modificat correctament.', + 'Unable to change the password.' => 'No es pot canviar la contrasenya.', + 'Change category' => 'Canvia la categoria', + '%s updated the task %s' => '%s ha actualitzat la tasca %s', + '%s opened the task %s' => '%s ha obert la tasca %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s ha mogut la tasca %s a la posició #%d de la columna "%s"', + '%s moved the task %s to the column "%s"' => '%s ha mogut la tasca %s a la columna "%s"', + '%s created the task %s' => '%s creat la tasca %s', + '%s closed the task %s' => '%s tancada la tasca %s', + '%s created a subtask for the task %s' => '%s creat una subtasca de la tasca %s', + '%s updated a subtask for the task %s' => '%s actualitzada una subtasca de la tasca %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Assignat a %s amb un temps estimat de %s/%sh', + 'Not assigned, estimate of %sh' => 'No assignat, estimat de %sh', + '%s updated a comment on the task %s' => '%s ha actualitzat un comentari a la tasca %s', + '%s commented the task %s' => '%s ha comentat la tasca %s', + '%s\'s activity' => 'L\'activitat de %s', + 'RSS feed' => 'RSS feed', + '%s updated a comment on the task #%d' => '%s s\'actualitzen un comentari a la tasca #%d', + '%s commented on the task #%d' => '%s comentat a la tasca #%d', + '%s updated a subtask for the task #%d' => '%s s\'actualitzen un subtasca de la tasca #%d', + '%s created a subtask for the task #%d' => '%s creat una subtasca de la tasca #%d', + '%s updated the task #%d' => '%s actualitzada la tasca #%d', + '%s created the task #%d' => '%s creat la tasca #%d', + '%s closed the task #%d' => '%s van tancar la tasca #%d', + '%s opened the task #%d' => '%s va obrir la tasca #%d', + 'Activity' => 'Activitat', + 'Default values are "%s"' => 'Els valors per defecte son "%s"', + 'Default columns for new projects (Comma-separated)' => 'Columnes predeterminades per a nous projectes (separats per comes)', + 'Task assignee change' => 'Tasca de canvi del assignat', + '%s changed the assignee of the task #%d to %s' => '%s va canviar el assignat de la tasca #%d de %s', + '%s changed the assignee of the task %s to %s' => '%s va canviar el assignat de la tasca %s %s', + 'New password for the user "%s"' => 'Nova contrasenya per a l\'usuari "%s"', + 'Choose an event' => 'Tria un esdeveniment', + 'Create a task from an external provider' => 'Crear una tasca d\'un proveïdor extern', + 'Change the assignee based on an external username' => 'Canviar el assignat basat en un nom d\'usuari extern', + 'Change the category based on an external label' => 'Canviar la categoria en funció d\'una etiqueta externa', + 'Reference' => 'Referència', + 'Label' => 'Etiqueta', + 'Database' => 'Base de dades', + 'About' => 'Quant a', + 'Database driver:' => 'Controlador de base de dades:', + 'Board settings' => 'Parà metres del tauler', + 'Webhook settings' => 'Configuració web hook', + 'Reset token' => 'Reinicia el token', + 'API endpoint:' => 'API de punt final:', + 'Refresh interval for private board' => 'Interval d\'actualització per al tauler privada', + 'Refresh interval for public board' => 'Interval d\'actualització per al tauler pública', + 'Task highlight period' => 'PerÃode culminant de tasques', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'PerÃode (en segons) per considerar una tasca es va modificar recentment (0 per desactivar, 2 dies de defecte)', + 'Frequency in second (60 seconds by default)' => 'Freqüència en segons (60 segons per defecte)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Freqüència en segons (0 per desactivar aquesta caracterÃstica, 10 segons per defecte)', + 'Application URL' => 'URL de l\'aplicació', + 'Token regenerated.' => 'Token regenerat.', + 'Date format' => 'Format de dates', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Sempre s\'accepta el format ISO, exemple "%s" i "%s"', + 'New private project' => 'Nou projecte privat', + 'This project is private' => 'Aquest projecte és privat', + 'Add' => 'Afegeix', + 'Start date' => 'Data d\'inici', + 'Time estimated' => 'Temps estimat', + 'There is nothing assigned to you.' => 'No tens res assignat.', + 'My tasks' => 'Les meves tasques', + 'Activity stream' => 'Fluxe d\'activitat', + 'Dashboard' => 'Panell', + 'Confirmation' => 'Confirmació', + 'Webhooks' => 'WebHooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Crear un comentari d\'un proveïdor extern', + 'Project management' => 'Gestió de projectes', + 'Columns' => 'Columnes', + 'Task' => 'Tasca', + 'Percentage' => 'Percentatge', + 'Number of tasks' => 'Nombre de tasques', + 'Task distribution' => 'Distribució de tasques', + 'Analytics' => 'AnalÃtica', + 'Subtask' => 'Subtasca', + 'User repartition' => 'Repartiment d\'usuari', + 'Clone this project' => 'Clonar aquest projecte', + 'Column removed successfully.' => 'Columna eliminat correctament.', + 'Not enough data to show the graph.' => 'No hi ha dades suficients per mostrar el grà fic.', + 'Previous' => 'Anterior', + 'The id must be an integer' => 'L\'identificador ha de ser un nombre enter', + 'The project id must be an integer' => 'L\'identificador de projecte ha de ser un enter', + 'The status must be an integer' => 'L\'estat ha de ser un enter', + 'The subtask id is required' => 'Es requereix que l\'ID subtasca', + 'The subtask id must be an integer' => 'L\'identificador de subtasca ha de ser un enter', + 'The task id is required' => 'Es requereix que l\'ID de tasca', + 'The task id must be an integer' => 'L\'identificador de tasca ha de ser un enter', + 'The user id must be an integer' => 'L\'identificador d\'usuari ha de ser un nombre enter', + 'This value is required' => 'Aquest valor és necessari', + 'This value must be numeric' => 'Aquest valor ha de ser numèric', + 'Unable to create this task.' => 'No es pot crear aquesta tasca.', + 'Cumulative flow diagram' => 'Diagrama de flux acumulat', + 'Daily project summary' => 'Resum diari projecte', + 'Daily project summary export' => 'Exportació del resum diari del projecte', + 'Exports' => 'Exportacions', + 'This export contains the number of tasks per column grouped per day.' => 'Aquesta exportació conté el nombre de tasques per columna agrupada per dia.', + 'Active swimlanes' => 'Swimlanes actius', + 'Add a new swimlane' => 'Afegeix un nou swimlane', + 'Default swimlane' => 'Swimlane per defecte', + 'Do you really want to remove this swimlane: "%s"?' => 'Segur que vols eliminar la swimlane: "%s"', + 'Inactive swimlanes' => 'Swimlanes inactius', + 'Remove a swimlane' => 'Traieu un carril', + 'Swimlane modification for the project "%s"' => 'Modificació del swimlane pel projecte "%s"', + 'Swimlane removed successfully.' => 'Swimlane eliminat correctament.', + 'Swimlanes' => 'Swimlanes', + 'Swimlane updated successfully.' => 'Swimlane actualitzat correctament.', + 'Unable to remove this swimlane.' => 'No es pot eliminar aquest swimlane.', + 'Unable to update this swimlane.' => 'No es pot actualitzar aquest swimlane.', + 'Your swimlane have been created successfully.' => 'La seva swimlane s\'han creat amb èxit.', + 'Example: "Bug, Feature Request, Improvement"' => 'Exemple: "Bug, Comanda de funcions, Millora"', + 'Default categories for new projects (Comma-separated)' => 'Categories per defecte per a nous projectes (separats per comes)', + 'Integrations' => 'Integracions', + 'Integration with third-party services' => 'Integració amb els serveis de tercers', + 'Subtask Id' => 'ID de subtasca', + 'Subtasks' => 'Subtasques', + 'Subtasks Export' => 'Exportació de subtasques', + 'Task Title' => 'TÃtol de la tasca', + 'Untitled' => 'Sense tÃtol', + 'Application default' => 'Per defecte de l\'aplicació', + 'Language:' => 'Llengua:', + 'Timezone:' => 'Zona horà ria:', + 'All columns' => 'Totes les columnes', + 'Next' => 'Pròxim', + '#%d' => '#%d', + 'All swimlanes' => 'Tots swimlanes', + 'All colors' => 'Tots els colors', + 'Moved to column %s' => 'Traslladat a la columna %s', + 'User dashboard' => 'Tauler d\'usuari', + 'Allow only one subtask in progress at the same time for a user' => 'Permetre només una subtasca en curs al mateix temps per a un usuari', + 'Edit column "%s"' => 'Modifica la columna "%s"', + 'Select the new status of the subtask: "%s"' => 'Sel·lecciona el nou estat per la subtasca: "%s"', + 'Subtask timesheet' => 'Part d\'hores de subtasca', + 'There is nothing to show.' => 'Res per mostrar.', + 'Time Tracking' => 'Control d\'hores', + 'You already have one subtask in progress' => 'Ja tens una subtasca en curs', + 'Which parts of the project do you want to duplicate?' => 'Quines parts del projecte vols duplicar?', + 'Disallow login form' => 'No permetre formulari d\'accés', + 'Start' => 'Començar', + 'End' => 'Final', + 'Task age in days' => 'L\'edat de tasques en dies', + 'Days in this column' => 'Dies en aquesta columna', + '%dd' => '%dd', + 'Add a new link' => 'Afegeix un nou enllaç', + 'Do you really want to remove this link: "%s"?' => 'Segur que vols eliminar aquest enllaç: "%s"?', + 'Do you really want to remove this link with task #%d?' => 'Segur que vols eliminar aquest enllaç amb la tasca #%d?', + 'Field required' => 'Camp requerit', + 'Link added successfully.' => 'Enllaç afegit correctament.', + 'Link updated successfully.' => 'Enllaç actualitzat correctament.', + 'Link removed successfully.' => 'Enllaç eliminat amb èxit.', + 'Link labels' => 'Etiquetes d\'enllaç', + 'Link modification' => 'Modificació d\'enllaç', + 'Links' => 'Enllaços', + 'Opposite label' => 'Etiqueta oposada', + 'Remove a link' => 'Suprimir un enllaç', + 'The labels must be different' => 'Les etiquetes han de ser diferents', + 'There is no link.' => 'No hi ha cap relació.', + 'This label must be unique' => 'Aquest avÃs ha de ser únic', + 'Unable to create your link.' => 'No es pot crear l\'enllaç.', + 'Unable to update your link.' => 'No es pot actualitzar el seu enllaç.', + 'Unable to remove this link.' => 'No es pot eliminar aquest enllaç.', + 'relates to' => 'es refereix a', + 'blocks' => 'bloqueja', + 'is blocked by' => 'està bloquejada per', + 'duplicates' => 'duplica', + 'is duplicated by' => 'es duplica per', + 'is a child of' => 'és fill de', + 'is a parent of' => 'és pare de', + 'targets milestone' => 'objectiu de la fita', + 'is a milestone of' => 'és una fita de', + 'fixes' => 'corregeix', + 'is fixed by' => 'corregit per', + 'This task' => 'Aquesta tasca', + '<1h' => '<1h', + // '%dh' => '', + 'Expand tasks' => 'Amplia les tasques', + 'Collapse tasks' => 'Col·lapsa les tasques', + 'Expand/collapse tasks' => 'Amplia les tasques / col·lapsa', + 'Close dialog box' => 'Quadre de dià leg tancar', + 'Submit a form' => 'Envia un formulari', + 'Board view' => 'Mostra el tauler', + 'Keyboard shortcuts' => 'Dreceres de teclat', + 'Open board switcher' => 'Obre commutador de tauler', + 'Application' => 'Aplicació', + 'Compact view' => 'Vista compacta', + 'Horizontal scrolling' => 'Desplaçament horitzontal', + 'Compact/wide view' => 'Vista compacta / ample', + 'Currency' => 'Moneda', + 'Private project' => 'Projecte privat', + 'AUD - Australian Dollar' => 'MXN - Dòlar australià ', + 'CAD - Canadian Dollar' => 'CAD - Dòlar canadenc', + 'CHF - Swiss Francs' => 'CHF - Franc suÃs', + 'Custom Stylesheet' => 'Estil personalitzat', + 'download' => 'descarrega', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Lliura brità nica', + 'INR - Indian Rupee' => 'INR - Rupia Ãndia', + 'JPY - Japanese Yen' => 'JPY - El ien japonès', + 'NZD - New Zealand Dollar' => 'NZD - Dòlar de Nova Zelanda', + 'RSD - Serbian dinar' => 'RSD - Dinar serbi', + 'CNY - Chinese Yuan' => 'CNY - Yuan xinès', + 'USD - US Dollar' => 'USD - El dòlar dels EUA', + 'Destination column' => 'Columna de destinació', + 'Move the task to another column when assigned to a user' => 'Mou la tasca a una altra columna quan s\'assigna a un usuari', + 'Move the task to another column when assignee is cleared' => 'Mou la tasca a una altra columna quan s\'esborra assignat', + 'Source column' => 'Columna d\'origen', + 'Transitions' => 'Transicions', + 'Executer' => 'Executor', + 'Time spent in the column' => 'El temps emprat a la columna', + 'Task transitions' => 'transicions de tasques', + 'Task transitions export' => 'Tasca transicions d\'exportació', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Aquest informe conté tots els moviments de columna per a cada tasca amb la data, l\'usuari i el temps per a cada transició.', + 'Currency rates' => 'Monedes', + 'Rate' => 'Preu', + 'Change reference currency' => 'Moneda de canvi de referència', + 'Reference currency' => 'Moneda de referència', + 'The currency rate have been added successfully.' => 'El tipus de canvi s\'ha afegit amb èxit.', + 'Unable to add this currency rate.' => 'No es pot afegeix aquest tipus de moneda.', + 'Webhook URL' => 'URL web hook', + '%s removed the assignee of the task %s' => '%s ha eliminat l\'assignat de la tasca %s', + 'Information' => 'Informació', + 'Check two factor authentication code' => 'Comprovar el codi d\'autenticació de dos factors', + 'The two factor authentication code is not valid.' => 'El codi d\'autenticació de dos factors no és và lid.', + 'The two factor authentication code is valid.' => 'El codi d\'autenticació de dos factors és và lid.', + 'Code' => 'Codi', + 'Two factor authentication' => 'Autenticació de dos factors', + // 'This QR code contains the key URI: ' => '', + 'Check my code' => 'Comprovar el meu codi', + // 'Secret key: ' => '', + 'Test your device' => 'Prova el teu dispositiu', + 'Assign a color when the task is moved to a specific column' => 'Assignar un color quan la tasca es mou a una columna especÃfica', + '%s via Kanboard' => '%s a través del Kanboard', + 'Burndown chart' => 'Burndown chart', + 'This chart show the task complexity over the time (Work Remaining).' => 'Aquesta grà fica mostra la dificultat de la tasca en el temps (Treball restant).', + 'Screenshot taken %s' => 'Imatge presa %s', + 'Add a screenshot' => 'Afegeix una captura de pantalla', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Prengui una captura de pantalla i premeu CTRL + V o ⌘ + V per enganxar aquÃ.', + 'Screenshot uploaded successfully.' => 'Captura carregat correctament.', + 'SEK - Swedish Krona' => 'SEK - Corona sueca', + 'Identifier' => 'Identificador', + 'Disable two factor authentication' => 'Desactiva l\'autenticació de dos factors', + // 'Do you really want to disable the two factor authentication for this user: "%s"?' => '', + 'Edit link' => 'Edita enllaç', + 'Start to type task title...' => 'Comenceu a tÃtol de la tasca de tipus ...', + 'A task cannot be linked to itself' => 'Una tasca no pot vincular-se a si mateix', + 'The exact same link already exists' => 'La mateixa relació exacta ja existeix', + 'Recurrent task is scheduled to be generated' => 'La tasca recurrent està programada per ser generada', + 'Score' => 'Puntuació', + 'The identifier must be unique' => 'L\'identificador ha de ser únic', + // 'This linked task id doesn\'t exists' => '', + 'This value must be alphanumeric' => 'Aquest valor ha de ser alfanumèric', + 'Edit recurrence' => 'Edita recurrència', + 'Generate recurrent task' => 'Generar tasca recurrent', + 'Trigger to generate recurrent task' => 'Desencadenar per generar tasca recurrent', + 'Factor to calculate new due date' => 'Factor per calcular la nova data de venciment', + 'Timeframe to calculate new due date' => 'Marc de temps per calcular la nova data de venciment', + 'Base date to calculate new due date' => 'Data de referència per al cà lcul de la nova data de venciment', + 'Action date' => 'data d\'acció', + 'Base date to calculate new due date: ' => 'Data base per a calcular nova data de venciment: ', + 'This task has created this child task: ' => 'Aquesta tasca ha creat aquesta tasca filla: ', + 'Day(s)' => 'Dia (Dies)', + 'Existing due date' => 'Data de venciment existent', + // 'Factor to calculate new due date: ' => '', + 'Month(s)' => 'Mes (Mesos)', + 'Recurrence' => 'Recurrència', + 'This task has been created by: ' => 'Tasca creada per: ', + 'Recurrent task has been generated:' => 'La tasca recurrent s\'ha generat:', + // 'Timeframe to calculate new due date: ' => '', + // 'Trigger to generate recurrent task: ' => '', + 'When task is closed' => 'Quan la tasca està tancada', + 'When task is moved from first column' => 'Quan la tasca es mou de la primera columna', + 'When task is moved to last column' => 'Quan la tasca es mou a última columna', + 'Year(s)' => 'Any (s)', + 'Project settings' => 'Configuració del projecte', + 'Automatically update the start date' => 'Actualitzar automà ticament la data d\'inici', + 'iCal feed' => 'Origen iCal', + 'Preferences' => 'Configuració', + 'Security' => 'Seguretat', + 'Two factor authentication disabled' => 'L\'autenticació de dos factors deshabilitat', + 'Two factor authentication enabled' => 'Autenticació de dos factors activada', + 'Unable to update this user.' => 'No es pot actualitzar aquest usuari.', + 'There is no user management for private projects.' => 'No hi ha una gestió d\'usuaris per a projectes privats.', + 'User that will receive the email' => 'L\'usuari que rebrà el correu electrònic', + 'Email subject' => 'assumpte del correu electrònic', + 'Date' => 'Data', + 'Add a comment log when moving the task between columns' => 'Afegeix un registre comentari en moure la tasca entre les columnes', + 'Move the task to another column when the category is changed' => 'Mou la tasca a una altra columna quan es canvia la categoria', + 'Send a task by email to someone' => 'Envia una tasca per correu electrònic a algú', + 'Reopen a task' => 'Torneu a obrir una tasca', + 'Notification' => 'Notificació', + '%s moved the task #%d to the first swimlane' => '%s van moure la tasca #%d per al primer swimlane', + 'Swimlane' => 'Swimlane', + '%s moved the task %s to the first swimlane' => '%s es va traslladar la tasca %s per al primer swimlane', + // '%s moved the task %s to the swimlane "%s"' => '', + 'This report contains all subtasks information for the given date range.' => 'Aquest informe conté tota la informació subtasques per al perÃode de temps.', + 'This report contains all tasks information for the given date range.' => 'Aquest informe conté tota la informació de tasques per al perÃode de temps.', + 'Project activities for %s' => 'Les activitats del projecte per a %s', + 'view the board on Kanboard' => 'Veure el tauler en Kanboard', + 'The task have been moved to the first swimlane' => 'La tasca ha estat mogut a la primera swimlane', + 'The task have been moved to another swimlane:' => 'La tasca s\'han traslladat a un altre swimlane:', + 'New title: %s' => 'Nou tÃtol: %s', + 'The task is not assigned anymore' => 'La tasca no s\'ha assignat més', + 'New assignee: %s' => 'Nova assignat:%s', + 'There is no category now' => 'No hi ha una categoria ara', + 'New category: %s' => 'Nova categoria:%s', + 'New color: %s' => 'Nou color: %s', + 'New complexity: %d' => 'Nova dificultat: %d', + 'The due date have been removed' => 'La data de venciment s\'han eliminat', + 'There is no description anymore' => 'No hi ha una descripció més', + 'Recurrence settings have been modified' => 'ajustos de recurrència s\'han modificat', + // 'Time spent changed: %sh' => '', + // 'Time estimated changed: %sh' => '', + // 'The field "%s" have been updated' => '', + 'The description has been modified:' => 'La descripció s\'ha modificat:', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Vols tancar la tasca "%s", aixà com totes les subtasques?', + 'I want to receive notifications for:' => 'Vull rebre notificacions per:', + 'All tasks' => 'totes les tasques', + 'Only for tasks assigned to me' => 'Només per a les tasques que se m\'ha s\'assignin', + 'Only for tasks created by me' => 'Només per a les tasques creades per mi', + 'Only for tasks created by me and assigned to me' => 'Només per a tasques creades per mi i em assignin', + // '%%Y-%%m-%%d' => '', + 'Total for all columns' => 'Total per a totes les columnes', + 'You need at least 2 days of data to show the chart.' => 'Es necessita com a mÃnim 2 dies de dades per mostrar el grà fic.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Atura el temporitzador', + 'Start timer' => 'Inicia el temporitzador', + 'My activity stream' => 'El meu fluxe d\'activitat', + 'Search tasks' => 'Cerca de tasques', + 'Reset filters' => 'Restableix filtres', + 'My tasks due tomorrow' => 'Les meves tasques per demà ', + 'Tasks due today' => 'Tasques que vencen avui', + 'Tasks due tomorrow' => 'Tasques per demà ', + 'Tasks due yesterday' => 'Tasques a causa ahir', + 'Closed tasks' => 'Tasques tancades', + 'Open tasks' => 'Tasques obertes', + 'Not assigned' => 'No assignat', + 'View advanced search syntax' => 'Veure sintaxi de cerca avançada', + 'Overview' => 'Visió de conjunt', + 'Board/Calendar/List view' => 'Tauler / Calendari / Vista de llista', + 'Switch to the board view' => 'Canviar a la vista de tauler', + 'Switch to the list view' => 'Canviar a la vista de llista', + 'Go to the search/filter box' => 'Anar al quadre de cerca / filtre', + 'There is no activity yet.' => 'No hi ha cap activitat encara.', + 'No tasks found.' => 'No s\'han trobat tasques.', + 'Keyboard shortcut: "%s"' => 'Drecera de teclat: "%s"', + 'List' => 'Llista', + 'Filter' => 'Filtre', + 'Advanced search' => 'Cerca avançada', + 'Example of query: ' => 'Exemple de consulta: ', + 'Search by project: ' => 'Cerca per projecte: ', + 'Search by column: ' => 'Cerca per columna: ', + 'Search by assignee: ' => 'Cerca per assignat: ', + 'Search by color: ' => 'Cerca per color: ', + 'Search by category: ' => 'Cerca per categoria: ', + 'Search by description: ' => 'Cerca per descripció: ', + 'Search by due date: ' => 'Cerca per data de venciment: ', + 'Average time spent into each column' => 'Temps mitjà de permanència en cada columna', + 'Average time spent' => 'Temps mitjà de permanència', + 'This chart show the average time spent into each column for the last %d tasks.' => 'Aquest grà fic mostra el temps mitjà de permanència en cada columna per a les últimes %d tasques.', + 'Average Lead and Cycle time' => 'El plom i la mitjana del temps de cicle', + 'Average lead time: ' => 'Mitjana de temps de lliurament: ', + 'Average cycle time: ' => 'Mitjana de temps de cicle: ', + 'Cycle Time' => 'Temps de cicle', + 'Lead Time' => 'Temps de lliurament', + 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Aquesta taula mostra la mitjana de plom i el temps de cicle per a les últimes tasques %d respecte al temps.', + 'Average time into each column' => 'Temps mitjà en cada columna', + 'Lead and cycle time' => 'Temps de lliurament i cicle', + 'Lead time: ' => 'Temps de lliurament: ', + 'Cycle time: ' => 'Temps del cicle: ', + 'Time spent into each column' => 'El temps invertit en cada columna', + 'The lead time is the duration between the task creation and the completion.' => 'El termini d\'execució és la durada entre la creació de la tasca i la finalització.', + 'The cycle time is the duration between the start date and the completion.' => 'El temps de cicle és la durada entre la data d\'inici i la finalització.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Si la tasca no està tancat el moment actual s\'utilitza en lloc de la data de finalització.', + 'Set automatically the start date' => 'S\'ajusta automà ticament la data d\'inici', + 'Edit Authentication' => 'Edita autenticació', + 'Remote user' => 'Usuari remot', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Els usuaris remots no emmagatzemen la contrasenya a la base de dades Kanboard, exemples: els comptes LDAP, Google i Github.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Si marca la casella "No permetre formulari d\'accés", s\'ignoraran les credencials introduïdes al formulari d\'inici de sessió.', + 'Default task color' => 'Color per defecte de les tasques', + 'This feature does not work with all browsers.' => 'Aquesta caracterÃstica no funciona amb tots els navegadors.', + 'There is no destination project available.' => 'No hi ha cap projecte de destinació disponibles.', + 'Trigger automatically subtask time tracking' => 'Desencadenar automà ticament control d\'hores de subtasca', + 'Include closed tasks in the cumulative flow diagram' => 'Incloure tasques tancades en el diagrama de flux acumulat', + 'Current swimlane: %s' => 'Swimlane actual: %s', + 'Current column: %s' => 'Columna actual: %s', + 'Current category: %s' => 'Categoria actual: %s', + 'no category' => 'cap categoria', + 'Current assignee: %s' => 'Assignat actual: %s', + 'not assigned' => 'no assignat', + 'Author:' => 'Autor:', + 'contributors' => 'contribuents', + 'License:' => 'Llicència:', + 'License' => 'Llicència', + 'Enter the text below' => 'Introduïu el text a continuació', + 'Start date:' => 'Data d\'inici:', + 'Due date:' => 'Data de venciment:', + 'People who are project managers' => 'Les persones que són caps de projecte', + 'People who are project members' => 'Les persones que són membres del projecte', + 'NOK - Norwegian Krone' => 'NOK - Corona noruega', + 'Show this column' => 'Mostra aquesta columna', + 'Hide this column' => 'Amaga aquesta columna', + 'open file' => 'Arxiu obert', + 'End date' => 'Data de finalització', + 'Users overview' => 'Visió general dels usuaris', + 'Members' => 'Membres', + 'Shared project' => 'Projecte compartit', + 'Project managers' => 'Els gerents de projecte', + 'Projects list' => 'Llista de projectes', + 'End date:' => 'Data de finalització:', + 'Change task color when using a specific task link' => 'Canviar de color quan la tasca utilitzant un enllaç tasca especÃfica', + 'Task link creation or modification' => 'La creació o modificació de l\'enllaç de tasques', + 'Milestone' => 'Fita', + 'Documentation: %s' => 'Documentació: %s', + 'Reset the search/filter box' => 'Restablir el quadre de cerca / filtre', + 'Documentation' => 'Documentació', + 'Table of contents' => 'Taula de continguts', + 'Author' => 'Autor', + 'Version' => 'Versió', + 'Plugins' => 'Connectors', + 'There is no plugin loaded.' => 'No hi ha cap complement carregat.', + 'My notifications' => 'Les meves notificacions', + 'Custom filters' => 'Filtres personalitzats', + 'Your custom filter have been created successfully.' => 'El seu filtre a mida s\'han creat amb èxit.', + 'Unable to create your custom filter.' => 'No es pot crear el filtre personalitzat.', + 'Custom filter removed successfully.' => 'Filtre a mida eliminat correctament.', + 'Unable to remove this custom filter.' => 'No es pot eliminar aquest filtre personalitzat.', + 'Edit custom filter' => 'Edita filtre a mida', + 'Your custom filter have been updated successfully.' => 'El seu filtre a mida s\'han actualitzat correctament.', + 'Unable to update custom filter.' => 'No es pot actualitzar filtre a mida.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Nou adjunt a la tasca #%d: %s', + 'New comment on task #%d' => 'Nou comentari a la tasca #%d', + 'Comment updated on task #%d' => 'Comentari actualitzat a la tasca #%d', + 'New subtask on task #%d' => 'Nova subtasca a la tasca #%d', + 'Subtask updated on task #%d' => 'Subtasca s\'actualitza a la tasca #%d', + 'New task #%d: %s' => 'Nova tasca #%d: %s', + 'Task updated #%d' => 'Tasca actualitzada #%d', + 'Task #%d closed' => 'Tasca #%d tancada', + 'Task #%d opened' => 'Tasca #%d obrir', + 'Column changed for task #%d' => 'Columna canviada per la tasca #%d', + 'New position for task #%d' => 'Nova posició per la tasca #%d', + 'Swimlane changed for task #%d' => 'El swimlane va canviat per la tasca #%d', + 'Assignee changed on task #%d' => 'L\'assignat va canviar a la tasca #%d', + '%d overdue tasks' => '%d tasques endarrerides', + 'Task #%d is overdue' => 'La tasca #%d està vençuda', + 'No notification.' => 'Sense notificacions.', + 'Mark all as read' => 'Marca totes com llegides', + 'Mark as read' => 'Marca com llegida', + 'Total number of tasks in this column across all swimlanes' => 'Nombre total de tasques en aquesta columna a través de tots swimlanes', + 'Collapse swimlane' => 'Col·lapsa swimlane', + 'Expand swimlane' => 'Ampliar swimlane', + 'Add a new filter' => 'Afegeix un nou filtre', + 'Share with all project members' => 'Compartir amb tots els membres del projecte', + 'Shared' => 'Compartit', + 'Owner' => 'Propietari', + 'Unread notifications' => 'Notificacions no llegides', + 'Notification methods:' => 'Els mètodes de notificació:', + 'Unable to read your file' => 'No es pot llegir el fitxer', + '%d task(s) have been imported successfully.' => '%d tasca(s) s\'han importat correctament.', + 'Nothing have been imported!' => 'Res s\'han importat!', + 'Import users from CSV file' => 'Importa usuaris des d\'arxiu CSV', + '%d user(s) have been imported successfully.' => '%d usuari(s) s\'han importat correctament.', + 'Comma' => 'Coma', + 'Semi-colon' => 'Punt i coma', + 'Tab' => 'Llengüeta', + 'Vertical bar' => 'Barra vertical', + 'Double Quote' => 'Cometes dobles', + 'Single Quote' => 'Cometes simples', + '%s attached a file to the task #%d' => '%s ha adjuntat un arxiu a la tasca #%d', + 'There is no column or swimlane activated in your project!' => 'No hi ha cap columna o swimlane activat en el seu projecte!', + 'Append filter (instead of replacement)' => 'Annexar filtre (en lloc de reemplaçament)', + 'Append/Replace' => 'Annexar / Reemplaçar', + 'Append' => 'Annexar', + 'Replace' => 'Reemplaçar', + 'Import' => 'Importació', + 'Change sorting' => 'Canvia la ordenació', + 'Tasks Importation' => 'Importació de tasques', + 'Delimiter' => 'Delimitador', + 'Enclosure' => 'Recinte', + 'CSV File' => 'Fitxer CSV', + 'Instructions' => 'Instruccions', + 'Your file must use the predefined CSV format' => 'El seu arxiu ha d\'utilitzar el format CSV predefinit', + 'Your file must be encoded in UTF-8' => 'El seu arxiu ha d\'estar codificat en UTF-8', + 'The first row must be the header' => 'La primera fila ha de ser la capçalera', + 'Duplicates are not verified for you' => 'Els duplicats no es verifiquen per a vostè', + 'The due date must use the ISO format: YYYY-MM-DD' => 'La data de venciment ha d\'utilitzar el format ISO: AAAA-MM-DD', + 'Download CSV template' => 'Descarregar plantilla CSV', + 'No external integration registered.' => 'No es va registrar la integració externa.', + 'Duplicates are not imported' => 'Els duplicats no s\'importen', + 'Usernames must be lowercase and unique' => 'Els noms d\'usuari han d\'estar en minúscules i únic', + 'Passwords will be encrypted if present' => 'Les contrasenyes es xifraran si està present', + '%s attached a new file to the task %s' => '%s adjunta un arxiu nou a la tasca %s', + 'Link type' => 'Tipus d\'enllaç', + 'Assign automatically a category based on a link' => 'Assignar automà ticament una categoria basada en un enllaç', + 'BAM - Konvertible Mark' => 'BAM - Konvertible Mark', + 'Assignee Username' => 'Nom d\'usuari assignat', + 'Assignee Name' => 'Nom assignat', + 'Groups' => 'Grups', + 'Members of %s' => 'Els membres de %s', + 'New group' => 'Nou grup', + 'Group created successfully.' => 'Grup creat correctament.', + 'Unable to create your group.' => 'No es pot crear el grup.', + 'Edit group' => 'Edita grup', + 'Group updated successfully.' => 'Grup actualitzat correctament.', + 'Unable to update your group.' => 'No es pot actualitzar el seu grup.', + // 'Add group member to "%s"' => '', + 'Group member added successfully.' => 'Membre del grup afegit correctament.', + 'Unable to add group member.' => 'No es pot afegeix membre del grup.', + // 'Remove user from group "%s"' => '', + 'User removed successfully from this group.' => 'Usuari eliminat amb èxit d\'aquest grup.', + 'Unable to remove this user from the group.' => 'No es pot eliminar aquest usuari del grup.', + 'Remove group' => 'Elimina el grup', + 'Group removed successfully.' => 'Grup eliminat correctament.', + 'Unable to remove this group.' => 'No es pot eliminar aquest grup.', + 'Project Permissions' => 'Permisos de projectes', + 'Manager' => 'Gerent', + 'Project Manager' => 'Gerent de projectes', + 'Project Member' => 'Membres del projecte', + 'Project Viewer' => 'Visor de projectes', + 'Your account is locked for %d minutes' => 'El seu compte està bloquejada per %d minuts', + 'Invalid captcha' => 'codi d\'imatge no và lida', + 'The name must be unique' => 'El nom ha de ser únic', + 'View all groups' => 'Veure tots els grups', + 'There is no user available.' => 'No hi ha cap usuari disponible.', + // 'Do you really want to remove the user "%s" from the group "%s"?' => '', + 'There is no group.' => 'No hi ha cap grup.', + 'Add group member' => 'Afegeix membre del grup', + // 'Do you really want to remove this group: "%s"?' => '', + 'There is no user in this group.' => 'No hi ha cap usuari en aquest grup.', + 'Permissions' => 'Permisos', + 'Allowed Users' => 'Els usuaris autoritzats', + 'No user have been allowed specifically.' => 'Cap usuari s\'hagi establert.', + 'Role' => 'Rol', + 'Enter user name...' => 'Introdueix el nom d\'usuari ...', + 'Allowed Groups' => 'Grups permesos', + 'No group have been allowed specifically.' => 'Cap grup s\'hagi establert.', + 'Group' => 'Grup', + 'Group Name' => 'Nom del grup', + 'Enter group name...' => 'Introduïu el nom del grup ...', + 'Role:' => 'Rol:', + 'Project members' => 'Els membres del projecte', + '%s mentioned you in the task #%d' => '%s t\'ha esmentat a la tasca #%d', + '%s mentioned you in a comment on the task #%d' => '%s t\'ha mencionat en un comentari a la tasca #%d', + 'You were mentioned in the task #%d' => 'Se t\'ha esmentat a la tasca #%d', + 'You were mentioned in a comment on the task #%d' => 'Se t\'ha mencionat en un comentari sobre la tasca #%d', + // 'Estimated hours: ' => '', + // 'Actual hours: ' => '', + 'Hours Spent' => 'Les hores invertides', + 'Hours Estimated' => 'hores estimat', + 'Estimated Time' => 'Temps estimat', + 'Actual Time' => 'temps real', + 'Estimated vs actual time' => 'Estimat en funció del temps real', + 'RUB - Russian Ruble' => 'RUB - Russia rus', + 'Assign the task to the person who does the action when the column is changed' => 'Assignar la tasca a la persona que fa l\'acció quan es canvia la columna', + 'Close a task in a specific column' => 'Tancar una tasca en una columna especÃfica', + 'Time-based One-time Password Algorithm' => 'Algoritme de contrasenya basat en el temps d\'una sola vegada', + // 'Two-Factor Provider: ' => '', + 'Disable two-factor authentication' => 'Deshabilita l\'autenticació de dos factors', + 'Enable two-factor authentication' => 'Habilitar l\'autenticació de dos factors', + 'There is no integration registered at the moment.' => 'No hi ha integració registrada en el moment.', + 'Password Reset for Kanboard' => 'Restableix contrasenya per Kanboard', + 'Forgot password?' => 'Has oblidat la contrasenya?', + 'Enable "Forget Password"' => 'Habilita "he oblidat la contrasenya"', + 'Password Reset' => 'Restablir contrasenya', + 'New password' => 'Nova contrasenya', + 'Change Password' => 'Canvia la contrasenya', + 'To reset your password click on this link:' => 'Per restablir la contrasenya, feu clic en aquest enllaç:', + 'Last Password Reset' => 'Darrer restabliment de contrasenya', + 'The password has never been reinitialized.' => 'La contrasenya mai ha estat reiniciada.', + 'Creation' => 'Creació', + 'Expiration' => 'Caducitat', + 'Password reset history' => 'Històric de restabliment de contrasenya', + // 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => '', + 'Do you really want to close all tasks of this column?' => 'Vols tancar totes les tasques d\'aquesta columna?', + // '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '', + 'Close all tasks of this column' => 'Tanqueu totes les tasques d\'aquesta columna', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'No s\'ha registrat plugin d\'un mètode de notificació del projecte. Encara es pot configurar les notificacions individuals en el seu perfil d\'usuari.', + 'My dashboard' => 'El meu tauler', + 'My profile' => 'El meu perfil', + 'Project owner: ' => 'Propietari del projecte: ', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'L\'identificador de projecte és opcional i ha de ser alfanumèric, exemple: MYPROJECT.', + 'Project owner' => 'Propietari del projecte', + 'Private projects do not have users and groups management.' => 'projectes privats no tenen els usuaris i grups de gestió.', + 'There is no project member.' => 'No hi ha cap membre del projecte.', + 'Priority' => 'Prioritat', + 'Task priority' => 'Prioritat de la tasca', + 'General' => 'General', + 'Dates' => 'Dates', + 'Default priority' => 'Prioritat per defecte', + 'Lowest priority' => 'La prioritat més baixa', + 'Highest priority' => 'La prioritat més alta', + 'Close a task when there is no activity' => 'Tancar una tasca quan no hi ha activitat', + 'Duration in days' => 'Durada en dies', + 'Send email when there is no activity on a task' => 'Envia correu electrònic quan no hi ha activitat en una tasca', + 'Unable to fetch link information.' => 'No és possible obtenir informació d\'enllaç.', + 'Daily background job for tasks' => 'Feina en segon pla diaria per a les tasques', + 'Auto' => 'Auto', + 'Related' => 'Relacionat', + 'Attachment' => 'Adjunt', + 'Title not found' => 'TÃtol no trobat', + 'Web Link' => 'Enllaç web', + 'External links' => 'Enllaços externs', + 'Add external link' => 'Afegeix un enllaç extern', + 'Type' => 'Tipus', + 'Dependency' => 'Dependència', + 'Add internal link' => 'Afegeix un enllaç intern', + 'Add a new external link' => 'Afegeix un nou enllaç extern', + 'Edit external link' => 'Edita enllaç extern', + 'External link' => 'URL', + 'Copy and paste your link here...' => 'Copia i enganxa l\'enllaç aquà ...', + 'URL' => 'URL', + 'Internal links' => 'Els enllaços interns', + 'Assign to me' => 'Assignar a mi', + 'Me' => 'jo', + 'Do not duplicate anything' => 'No dupliquis res', + 'Projects management' => 'Gestió de projectes', + 'Users management' => 'Gestió d\'usuaris', + 'Groups management' => 'Gestió de grups', + 'Create from another project' => 'Crea a partir d\'un altre projecte', + 'open' => 'obert', + 'closed' => 'tancat', + 'Priority:' => 'Prioritat:', + 'Reference:' => 'Referència:', + 'Complexity:' => 'Dificultat:', + 'Swimlane:' => 'Swimlane:', + 'Column:' => 'Columna:', + 'Position:' => 'Posició:', + 'Creator:' => 'Creador:', + 'Time estimated:' => 'Temps estimat:', + '%s hours' => '%s hora', + 'Time spent:' => 'El temps passat:', + 'Created:' => 'Creat:', + 'Modified:' => 'Modificat:', + 'Completed:' => 'Completat:', + 'Started:' => 'Començat:', + 'Moved:' => 'Mogut:', + 'Task #%d' => 'Tasca #%d', + 'Time format' => 'Format d\'hora', + 'Start date: ' => 'Data d\'inici: ', + 'End date: ' => 'Data de fi: ', + 'New due date: ' => 'Nova data de venciment: ', + 'Start date changed: ' => 'Data d\'inici canviada: ', + 'Disable private projects' => 'Desactivar els projectes privats', + // 'Do you really want to remove this custom filter: "%s"?' => '', + 'Remove a custom filter' => 'Treure un filtre a mida', + 'User activated successfully.' => 'L\'usuari ha activat correctament.', + 'Unable to enable this user.' => 'No és possible permetre que aquest usuari.', + 'User disabled successfully.' => 'L\'usuari deshabilitat amb èxit.', + 'Unable to disable this user.' => 'No és possible desactivar aquest usuari.', + 'All files have been uploaded successfully.' => 'Tots els arxius s\'hagin carregat correctament.', + // 'The maximum allowed file size is %sB.' => '', + 'Drag and drop your files here' => 'Arrossegar i deixar anar els arxius aquÃ', + 'choose files' => 'triar els arxius', + 'View profile' => 'Veure el perfil', + 'Two Factor' => 'dos factors', + 'Disable user' => 'desactivar usuari', + // 'Do you really want to disable this user: "%s"?' => '', + 'Enable user' => 'Permetre a l\'usuari', + // 'Do you really want to enable this user: "%s"?' => '', + 'Download' => 'Descarregar', + 'Uploaded: %s' => 'Pujat: %s', + 'Size: %s' => 'Mida: %s', + 'Uploaded by %s' => 'Pujada per %s', + 'Filename' => 'Nom de l\'arxiu', + 'Size' => 'Mida', + 'Column created successfully.' => 'Columna creat correctament.', + 'Another column with the same name exists in the project' => 'Una altra columna amb el mateix nom existeix en el projecte', + 'Default filters' => 'filtres predeterminats', + // 'Your board doesn\'t have any columns!' => '', + 'Change column position' => 'Canvi de posició de columna', + 'Switch to the project overview' => 'Canviar a la visió general del projecte', + 'User filters' => 'Els filtres d\'usuari', + 'Category filters' => 'filtres de categories', + 'Upload a file' => 'Pujar un arxiu', + 'View file' => 'Veure arxiu', + 'Last activity' => 'Última activitat', + 'Change subtask position' => 'Subtasca posició de canvi', + 'This value must be greater than %d' => 'Aquest valor ha de ser més gran que %d', + 'Another swimlane with the same name exists in the project' => 'Una altra swimlane amb el mateix nom existeix en el projecte', + 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Exemple: http://example.kanboard.net/ (utilitzat per generar URL absoluta)', + 'Actions duplicated successfully.' => 'Accions dupliquen amb èxit.', + 'Unable to duplicate actions.' => 'No s\'ha pogut duplicar accions.', + 'Add a new action' => 'Afegeix una nova acció', + 'Import from another project' => 'Importa d\'un altre projecte', + 'There is no action at the moment.' => 'No hi ha cap acció en aquest moment.', + 'Import actions from another project' => 'accions d\'importació d\'un altre projecte', + 'There is no available project.' => 'No hi ha un projecte disponible.', + 'Local File' => 'Fitxer local', + 'Configuration' => 'Configuració', + 'PHP version:' => 'Versió de PHP:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'Versió del sistema operatiu:', + 'Database version:' => 'Versió de la base de dades:', + 'Browser:' => 'Navegador:', + 'Task view' => 'Vista de tasques', + 'Edit task' => 'Edició de tasques', + 'Edit description' => 'Modifica la descripció', + 'New internal link' => 'Nou enllaç intern', + 'Display list of keyboard shortcuts' => 'Visualització de la llista de dreceres de teclat', + 'Menu' => 'Menú', + 'Set start date' => 'Comença', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'Puja la imatge meu avatar', + 'Remove my image' => 'Traieu la meva imatge', + 'The OAuth2 state parameter is invalid' => 'El parà metre d\'estat OAuth2 no és và lid', + 'User not found.' => 'Usuari no trobat.', + 'Search in activity stream' => 'Buscar en el Trà fic d\'activitat', + 'My activities' => 'Les meves activitats', + 'Activity until yesterday' => 'Activitat fins ahir', + 'Activity until today' => 'Activitat fins avui', + // 'Search by creator: ' => '', + // 'Search by creation date: ' => '', + // 'Search by task status: ' => '', + // 'Search by task title: ' => '', + 'Activity stream search' => 'Cerca al fluxe d\'activitat', + // 'Projects where "%s" is manager' => '', + // 'Projects where "%s" is member' => '', + // 'Open tasks assigned to "%s"' => '', + // 'Closed tasks assigned to "%s"' => '', + 'Assign automatically a color based on a priority' => 'Assignar automà ticament un color basat en una prioritat', + // 'Overdue tasks for the project(s) "%s"' => '', + 'Upload files' => 'Pujar arxius', + 'Installed Plugins' => 'Els connectors instal·lats', + 'Plugin Directory' => 'Directori de complements', + 'Plugin installed successfully.' => 'Plug-in instal·lat correctament.', + 'Plugin updated successfully.' => 'Plugin actualitzat correctament.', + 'Plugin removed successfully.' => 'Plugin eliminat correctament.', + 'Subtask converted to task successfully.' => 'Subtasca converteix en tasca amb èxit.', + 'Unable to convert the subtask.' => 'No es pot convertir la subtasca.', + 'Unable to extract plugin archive.' => 'No es pot extreure arxiu connector.', + 'Plugin not found.' => 'Plugin no trobat.', + // 'You don\'t have the permission to remove this plugin.' => '', + 'Unable to download plugin archive.' => 'No es pot descarregar arxiu connector.', + 'Unable to write temporary file for plugin.' => 'No es pot escriure el fitxer temporal per al connector.', + 'Unable to open plugin archive.' => 'No es pot obrir arxiu de plugin.', + 'There is no file in the plugin archive.' => 'No hi ha cap arxiu a l\'arxiu connector.', + 'Create tasks in bulk' => 'Crear tasques a granel', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'La instà ncia Kanboard no està configurat per a instal·lar plugins des de la interfÃcie d\'usuari.', + 'There is no plugin available.' => 'No hi ha cap complement disponible.', + 'Install' => 'Instal·la', + 'Update' => 'Actualitza', + 'Up to date' => 'Actualitzat', + 'Not available' => 'No disponible', + 'Remove plugin' => 'Eliminar connector', + // 'Do you really want to remove this plugin: "%s"?' => '', + 'Uninstall' => 'Suprimeix', + 'Listing' => 'Llistat', + 'Metadata' => 'Metadades', + 'Manage projects' => 'Gestió de projectes', + 'Convert to task' => 'Convertir la tasca', + 'Convert sub-task to task' => 'Converteix sub-tasca a una altra', + 'Do you really want to convert this sub-task to a task?' => 'Vols convertir aquesta sub-tasca a una tasca?', + 'My task title' => 'TÃtol de la tasca', + 'Enter one task by line.' => 'Introduïu una tasca per lÃnia.', + 'Number of failed login:' => 'Nombre d\'inici de sessió fallit:', + 'Account locked until:' => 'Compte bloquejada fins que:', + 'Email settings' => 'Configuració de correu electrònic', + 'Email sender address' => 'Adreça de correu electrònic del remitent', + 'Email transport' => 'Transport de correu electrònic', + 'Webhook token' => 'SÃmbol web hook', + 'Project tags management' => 'Gestió de projectes etiquetes', + 'Tag created successfully.' => 'Tag creat correctament.', + 'Unable to create this tag.' => 'No es pot crear aquesta etiqueta.', + 'Tag updated successfully.' => 'Tag actualitzat correctament.', + 'Unable to update this tag.' => 'No es pot actualitzar aquesta etiqueta.', + 'Tag removed successfully.' => 'Tag eliminat correctament.', + 'Unable to remove this tag.' => 'No es pot eliminar aquesta etiqueta.', + 'Global tags management' => 'Gestió global d\'etiquetes', + 'Tags' => 'Etiquetes', + 'Tags management' => 'Gestió d\'etiquetes', + 'Add new tag' => 'Afegeix nova etiqueta', + 'Edit a tag' => 'Edita una etiqueta', + 'Project tags' => 'Variables del projecte', + 'There is no specific tag for this project at the moment.' => 'No hi ha una etiqueta especÃfica per a aquest projecte en aquest moment.', + 'Tag' => 'Etiqueta', + 'Remove a tag' => 'Elimina una etiqueta', + // 'Do you really want to remove this tag: "%s"?' => '', + 'Global tags' => 'etiquetes globals', + 'There is no global tag at the moment.' => 'No hi ha cap variable global en el moment.', + 'This field cannot be empty' => 'Aquest camp no pot estar buit', + 'Close a task when there is no activity in an specific column' => 'Tancar una tasca quan no hi ha activitat en una columna especÃfica', + '%s removed a subtask for the task #%d' => '%s extret una subtasca per a la tasca #%d', + '%s removed a comment on the task #%d' => '%s remogut un comentari a la tasca #%d', + 'Comment removed on task #%d' => 'Comentari eliminat a la tasca #%d', + 'Subtask removed on task #%d' => 'Subtasca eliminat a la tasca #%d', + 'Hide tasks in this column in the dashboard' => 'Amaga tasques en aquesta columna al tauler d\'instruments', + '%s removed a comment on the task %s' => '%s remogut un comentari a la tasca %s', + '%s removed a subtask for the task %s' => '%s extret una subtasca per a la tasca %s', + 'Comment removed' => 'Es va eliminar el comentari', + 'Subtask removed' => 'subtasca retira', + '%s set a new internal link for the task #%d' => '%s va establir un nou enllaç intern per a la tasca #%d', + '%s removed an internal link for the task #%d' => '%s eliminat un enllaç intern per a la tasca #%d', + 'A new internal link for the task #%d have been defined' => 'Un nou enllaç intern per a la tasca #%d s\'han definit', + 'Internal link removed for the task #%d' => 'Enllaç intern eliminat la tasca #%d', + '%s set a new internal link for the task %s' => '%s va establir un nou enllaç intern per a la tasca %s', + '%s removed an internal link for the task %s' => '%s eliminat un enllaç intern per a la tasca %s', + 'Automatically set the due date on task creation' => 'S\'ajusta automà ticament la data de venciment a la creació de tasques', + 'Move the task to another column when closed' => 'Mou la tasca a una altra columna quan es tanca', + 'Move the task to another column when not moved during a given period' => 'Mou la tasca a una altra columna quan no es va moure durant un perÃode determinat', + 'Dashboard for %s' => 'Tauler de %s', + 'Tasks overview for %s' => 'Resum de tasques de %s', + 'Subtasks overview for %s' => 'Visió general de subtasques de %s', + 'Projects overview for %s' => 'Visió general del projecte de %s', + 'Activity stream for %s' => 'Fluxe d\'activitat per a %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Assignar un color quan la tasca es mou a un carril especÃfic', + 'Assign a priority when the task is moved to a specific swimlane' => 'Assignar una prioritat quan la tasca es mou a un carril especÃfic', + 'User unlocked successfully.' => 'Usuari desbloquejat amb èxit.', + 'Unable to unlock the user.' => 'No és possible desbloquejar l\'usuari.', + 'Move a task to another swimlane' => 'Mou una tasca a una altra swimlane', + 'Creator Name' => 'nom creador', + 'Time spent and estimated' => 'El temps dedicat i estima', + 'Move position' => 'Mou la posició', + 'Move task to another position on the board' => 'Mou tasca a una altra posició al tauler', + 'Insert before this task' => 'Insereix abans aquesta tasca', + 'Insert after this task' => 'Inserir, després d\'aquesta tasca', + 'Unlock this user' => 'Desbloquejar aquest usuari', + 'Custom Project Roles' => 'Rols d\'encà rrec del projecte', + 'Add a new custom role' => 'Afegeix un nou rol', + // 'Restrictions for the role "%s"' => '', + 'Add a new project restriction' => 'Afegeix una nova restricció projecte', + 'Add a new drag and drop restriction' => 'Afegeix un nou arrossegar i deixar anar restricció', + 'Add a new column restriction' => 'Afegeix una nova restricció de columna', + 'Edit this role' => 'Edita aquest rol', + 'Remove this role' => 'Elimina aquest rol', + 'There is no restriction for this role.' => 'No hi ha cap restricció per a aquest rol.', + 'Only moving task between those columns is permitted' => 'Només es mou tasques entre aquestes columnes es permet', + 'Close a task in a specific column when not moved during a given period' => 'Tancar una tasca en una columna especÃfica quan no es va moure durant un perÃode determinat', + 'Edit columns' => 'Edita columnes', + 'The column restriction has been created successfully.' => 'La restricció de la columna s\'ha creat correctament.', + 'Unable to create this column restriction.' => 'No es pot crear aquesta restricció columna.', + 'Column restriction removed successfully.' => 'restricció Columna eliminat correctament.', + 'Unable to remove this restriction.' => 'No es pot eliminar aquesta restricció.', + 'Your custom project role has been created successfully.' => 'El seu rol projecte personalitzat s\'ha creat correctament.', + 'Unable to create custom project role.' => 'No es pot crear el rol de projecte personalitzat.', + 'Your custom project role has been updated successfully.' => 'El seu rol projecte personalitzat s\'ha actualitzat correctament.', + 'Unable to update custom project role.' => 'No es pot actualitzar rol projecte personalitzat.', + 'Custom project role removed successfully.' => 'rol projecte personalitzat eliminat correctament.', + 'Unable to remove this project role.' => 'No es pot treure aquest rol projecte.', + 'The project restriction has been created successfully.' => 'La restricció projecte ha estat creat amb èxit.', + 'Unable to create this project restriction.' => 'No es pot crear aquesta restricció projecte.', + 'Project restriction removed successfully.' => 'Projecte restricció eliminat correctament.', + 'You cannot create tasks in this column.' => 'No es poden crear tasques en aquesta columna.', + 'Task creation is permitted for this column' => 'Es permet la creació de tasques per a aquesta columna', + 'Closing or opening a task is permitted for this column' => 'Tancant o obrint una tasca està permesa per a aquesta columna', + 'Task creation is blocked for this column' => 'creació de la tasca està bloquejat per a aquesta columna', + 'Closing or opening a task is blocked for this column' => 'Tancant o obrint una tasca està bloquejat per a aquesta columna', + 'Task creation is not permitted' => 'No es permet la creació de tasques', + 'Closing or opening a task is not permitted' => 'No es permet tancar o obrir una tasca', + // 'New drag and drop restriction for the role "%s"' => '', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Les persones que pertanyen a aquest rol seran capaços de moure les tasques només entre la font i la columna de destinació.', + 'Remove a column restriction' => 'Eliminar una restricció columna', + // 'Do you really want to remove this column restriction: "%s" to "%s"?' => '', + // 'New column restriction for the role "%s"' => '', + 'Rule' => 'regla', + 'Do you really want to remove this column restriction?' => 'Vols eliminar aquest criteri de la columna?', + 'Custom roles' => 'Rols personalitzats', + 'New custom project role' => 'Nou rol projecte personalitzat', + 'Edit custom project role' => 'Edita rol projecte personalitzat', + 'Remove a custom role' => 'Eliminar una funció personalitzada', + // 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '', + 'There is no custom role for this project.' => 'No hi ha una funció personalitzada per a aquest projecte.', + // 'New project restriction for the role "%s"' => '', + 'Restriction' => 'Restricció', + 'Remove a project restriction' => 'Eliminar una restricció projecte', + // 'Do you really want to remove this project restriction: "%s"?' => '', + 'Duplicate to multiple projects' => 'Duplica a múltiples projectes', + 'This field is required' => 'Aquest camp és obligatori', + 'Moving a task is not permitted' => 'No està permès moure una tasca', + 'This value must be in the range %d to %d' => 'Aquest valor ha d\'estar en el rang %d a %d', + 'You are not allowed to move this task.' => 'No se li permet moure a aquesta tasca.', + 'API User Access' => 'L\'accés d\'usuari d\'API', + 'Preview' => 'Vista prèvia', + 'Write' => 'escriure', + 'Write your text in Markdown' => 'Escriure el text en Markdown', + 'No personal API access token registered.' => 'Sense accés a l\'API personals sÃmbol registrat.', + // 'Your personal API access token is "%s"' => '', + 'Remove your token' => 'Traieu el testimoni', + 'Generate a new token' => 'Generar un nou token', + // 'Showing %d-%d of %d' => '', + 'Outgoing Emails' => 'Els correus electrònics sortints', + 'Add or change currency rate' => 'Afegeix o canviar el valor de moneda', + 'Reference currency: %s' => 'moneda de referència: %s', + 'Add custom filters' => 'Afegeix filtres personalitzats', + 'Export' => 'exportació', + 'Add link label' => 'Afegeix etiqueta d\'enllaç', + 'Incompatible Plugins' => 'Connectors incompatibles', + 'Compatibility' => 'Compatibilitat', + 'Permissions and ownership' => 'Permisos i propietat', + 'Priorities' => 'Prioritats', + 'Close this window' => 'Tanca aquesta finestra', + 'Unable to upload this file.' => 'No es pot carregar aquest arxiu.', + 'Import tasks' => 'Importació de tasques', + 'Choose a project' => 'Tria un projecte', + 'Profile' => 'Perfil', + 'Application role' => 'Rol d\'aplicació', + '%d invitations were sent.' => 'S\'han enviat %d invitacions.', + '%d invitation was sent.' => 'S\'han enviat %d invitacions.', + 'Unable to create this user.' => 'No es pot crear aquest usuari.', + 'Kanboard Invitation' => 'Invitació Kanboard', + 'Visible on dashboard' => 'Visible al tauler', + 'Created at:' => 'Creat el:', + 'Updated at:' => 'Actualitzat a:', + 'There is no custom filter.' => 'No hi ha cap filtre personal.', + 'New User' => 'Nou usuari', + 'Authentication' => 'Autenticació', + 'If checked, this user will use a third-party system for authentication.' => 'Si s\'activa, aquest usuari utilitzarà un sistema de tercers per a l\'autenticació.', + 'The password is necessary only for local users.' => 'La contrasenya és necessà ria només per als usuaris locals.', + 'You have been invited to register on Kanboard.' => 'T\'han convidat a inscriure\'t al Kanboard.', + 'Click here to join your team' => 'Fes clic aquà per unir-te a l\'equip', + 'Invite people' => 'Convidar a més gent', + 'Emails' => 'Correus electrònics', + 'Enter one email address by line.' => 'Introdueix una adreça de correu electrònic per lÃnia.', + 'Add these people to this project' => 'Afegeix a aquestes persones a aquest projecte', + 'Add this person to this project' => 'Afegeix aquesta persona a aquest projecte', + 'Sign-up' => 'Registra\'t', + 'Credentials' => 'Credencials', + 'New user' => 'Nou usuari', + 'This username is already taken' => 'Aquest nom d\'usuari ja està en ús', + 'A link to reset your password has been sent by email.' => 'S\'ha enviat per correu eletrònic un enllaç per restablir la contrasenya.', + 'Your profile must have a valid email address.' => 'El teu perfil ha de tenir una adreça de correu electrònic và lida.', + 'Unfortunately, we are unable to reset your password. Did you entered a valid username? Do you have an email address in your profile?' => 'Per desgrà cia, no som capaços de restablir la contrasenya. ¿S\'ha introduït un nom d\'usuari và lid? Té una adreça de correu electrònic en el seu perfil?', + 'TRL - Turkish Lira' => 'TRL - Lira turca', + 'The project email is optional and could be used by several plugins.' => 'El correu electrònic del projecte és opcional i pot ser utilitzat per diversos connectors.', + 'The project email must be unique across all projects' => 'El correu electrònic del projecte ha de ser únic en tots els projectes', + 'The email configuration has been disabled by the administrator.' => 'La configuració de correu electrònic ha estat desactivat per l\'administrador.', + 'Close this project' => 'Tanca aquest projecte', + 'Open this project' => 'Obre aquest projecte', + 'Close a project' => 'Tanca un projecte', + // 'Do you really want to close this project: "%s"?' => '', + 'Reopen a project' => 'Torna a obrir un projecte', + // 'Do you really want to reopen this project: "%s"?' => '', + 'This project is open' => 'Aquest projecte està obert', + 'This project is closed' => 'Aquest projecte està tancat', + 'Unable to upload files, check the permissions of your data folder.' => 'No es pot pujar arxius, comprovar els permisos de la carpeta de dades.', + 'Another category with the same name exists in this project' => 'Una altra categoria amb el mateix nom existeix en aquest projecte', + 'Comment sent by email successfully.' => 'Comentar enviat per correu electrònic amb èxit.', + // 'Sent by email to [%s](mailto:%s) (%s)' => '', + 'Unable to read uploaded file.' => 'No es pot llegir el fitxer pujat.', + 'Database uploaded successfully.' => 'Base de dades carregat correctament.', + 'Task sent by email successfully.' => 'Tasca enviat per correu electrònic amb èxit.', + 'There is no category in this project.' => 'No hi ha una categoria en aquest projecte.', + 'Send by email' => 'Envia per correu electrònic', + 'Create and send a comment by email' => 'Per crear i enviar un comentari per correu electrònic', + 'Subject' => 'Tema', + 'Upload the database' => 'Puja la base de dades', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Es podia pujar la base de dades SQLite prèviament descarregat (format gzip).', + 'Database file' => 'arxiu de base', + 'Upload' => 'Pujar', + 'Your project must have at least one active swimlane.' => 'El seu projecte ha de tenir com a mÃnim un swimlane actiu.', + 'Project: %s' => 'Projecte: %s', + // 'Automatic action not found: "%s"' => '', + '%d projects' => '%d projectes', + '%d project' => '%d projecte', + 'There is no project.' => 'No hi ha cap projecte.', + 'Sort' => 'Ordena', + 'Project ID' => 'identificació del projecte', + 'Project name' => 'Nom del projecte', + 'Public' => 'Públic', + 'Private' => 'Privat', + '%d tasks' => '%d tasques', + '%d task' => '%d tasca', + 'Task ID' => 'ID de tasca', + 'Assign automatically a color when due date is expired' => 'Assignar automà ticament un color quan ha caducat la data de venciment', + 'Total score in this column across all swimlanes' => 'Puntuació total d\'aquesta columna a través de tots swimlanes', + 'HRK - Kuna' => 'HRK - Kuna', + 'ARS - Argentine Peso' => 'ARS - Pes ArgentÃ', + 'COP - Colombian Peso' => 'COP - Pes colombià ', + '%d groups' => '%d grups', + '%d group' => '%d grup', + 'Group ID' => 'ID de grup', + 'External ID' => 'Identificació externa', + '%d users' => '%d usuaris', + '%d user' => '%d usuari', + 'Hide subtasks' => 'Amaga subtasques', + 'Show subtasks' => 'Mostra subtasques', + 'Authentication Parameters' => 'Parà metres d\'autenticació', + 'API Access' => 'Accés API', + 'No users found.' => 'No es van trobar usuaris.', + 'User ID' => 'ID d\'usuari', + 'Notifications are activated' => 'Notificacions habilitades', + 'Notifications are disabled' => 'Notificacions inhabilitades', + 'User disabled' => 'Usuari deshabilitat', + '%d notifications' => '%d notificacions', + '%d notification' => '%d notificació', + 'There is no external integration installed.' => 'No hi ha integració externa instal·lada.', + 'You are not allowed to update tasks assigned to someone else.' => 'No se li permet posar al dia les tasques assignades a una altra persona.', + 'You are not allowed to change the assignee.' => 'No se li permet canviar el assignat.', + 'Task suppression is not permitted' => 'No es permet la supressió de tasques', + 'Changing assignee is not permitted' => 'No es permet el canvi de assignatari', + 'Update only assigned tasks is permitted' => 'Actualitzar les tasques assignades només es permet', + 'Only for tasks assigned to the current user' => 'Només per a les tasques assignades a l\'usuari actual', + 'My projects' => 'Els meus projectes', + 'Your are not member of any project.' => 'No és membre de cap projecte.', + 'My subtasks' => 'Les meves subtasques', + '%d subtasks' => '%d subtasques', + '%d subtask' => '%d subtasca', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Only moving task between those columns is permitted for tasks assigned to the current user', + '[DUPLICATE]' => '[DUPLICATE]', + // 'DKK - Danish Krona' => '', + // 'Remove user from group' => '', + // 'Assign the task to its creator' => '', + // 'This task was sent by email to "%s" with subject "%s".' => '', + // 'Predefined Email Subjects' => '', + // 'Write one subject by line.' => '', + // 'Create another link' => '', + // 'BRL - Brazilian Real' => '', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', +); diff --git a/app/Locale/cs_CZ/translations.php b/app/Locale/cs_CZ/translations.php index f32453a1..dce15afe 100644 --- a/app/Locale/cs_CZ/translations.php +++ b/app/Locale/cs_CZ/translations.php @@ -3,7 +3,7 @@ return array( 'number.decimals_separator' => ',', 'number.thousands_separator' => '.', - 'None' => 'žádné', + 'None' => 'Žádné', 'Edit' => 'Editovat', 'Remove' => 'Odstranit', 'Yes' => 'Ano', @@ -17,15 +17,15 @@ return array( 'Red' => 'ÄŒervená', 'Orange' => 'Oranžová', 'Grey' => 'Å edá', - // 'Brown' => '', - // 'Deep Orange' => '', - // 'Dark Grey' => '', - // 'Pink' => '', - // 'Teal' => '', - // 'Cyan' => '', - // 'Lime' => '', - // 'Light Green' => '', - // 'Amber' => '', + 'Brown' => 'HnÄ›dá', + 'Deep Orange' => 'TmavÄ› oranžová', + 'Dark Grey' => 'TmavÄ› Å¡edá', + 'Pink' => 'Růžová', + 'Teal' => 'Modrozelená', + 'Cyan' => 'Tyrkysová', + 'Lime' => 'Limetková', + 'Light Green' => 'SvÄ›tle zelená', + 'Amber' => 'Jantarová', 'Save' => 'Uložit', 'Login' => 'PÅ™ihlásit se', 'Official website:' => 'Oficiálnà stránky:', @@ -37,9 +37,8 @@ return array( 'Username' => 'Uživatelské jméno', 'Password' => 'Heslo', 'Administrator' => 'Administrátor', - 'Sign in' => 'Registrace', + 'Sign in' => 'PÅ™ihlásit', 'Users' => 'Uživatelé', - 'No user' => 'Žádný uživatel', 'Forbidden' => 'Zakázat projekt', 'Access Forbidden' => 'PÅ™Ãstup zakázán', 'Edit user' => 'Upravit uživatele', @@ -182,7 +181,7 @@ return array( 'Position' => 'Pozice', 'Duplicate to another project' => 'VytvoÅ™it kopii v jiném projektu', 'Duplicate' => 'VytvoÅ™it kopii', - 'Link' => 'Link', + 'Link' => 'Odkaz', 'Comment updated successfully.' => 'Komentář byl úspěšnÄ› aktualizován.', 'Unable to update your comment.' => 'Nelze upravit Váš komentář.', 'Remove a comment' => 'Odebrat komentář', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'DÃlÄà úkol byl aktualizován.', 'Unable to update your sub-task.' => 'Nelze aktualizovat dÃlÄà úkol.', 'Unable to create your sub-task.' => 'Nelze vytvoÅ™it dÃlÄà úkol.', - 'Sub-task added successfully.' => 'DÃlÄà úkol byl úspěšnÄ› pÅ™idán.', 'Maximum size: ' => 'Maximálnà velikost: ', 'Display another project' => 'Zobrazit jiný projekt', 'Created by %s' => 'VytvoÅ™eno uživatelem %s', @@ -293,8 +291,8 @@ return array( 'The task #%d have been opened.' => 'Úkol #%d byl znovu otevÅ™en.', 'The task #%d have been closed.' => 'Úkol #%d byl uzavÅ™en.', 'Sub-task updated' => 'DÃlÄà úkol byl aktualizován', - 'Title:' => 'Nadpis', - 'Status:' => 'Stav', + 'Title:' => 'Nadpis:', + 'Status:' => 'Stav:', 'Assignee:' => 'PÅ™iÅ™azeno:', 'Time tracking:' => 'Sledovánà Äasu:', 'New sub-task' => 'Nový dÃlÄà úkol', @@ -368,10 +366,10 @@ return array( 'Change the assignee based on an external username' => 'ZmÄ›na pÅ™iÅ™azenà uživatele závislá na externÃm uživateli', 'Change the category based on an external label' => 'ZmÄ›na kategorie závislá na externÃm popisku', 'Reference' => 'Reference', - // 'Label' => '', - 'Database' => 'Datenbank', + 'Label' => 'ZnaÄka', + 'Database' => 'Databáze', 'About' => 'O projektu', - 'Database driver:' => 'Databáze', + 'Database driver:' => 'Databázový ovladaÄ', 'Board settings' => 'Nastavenà nástÄ›nky', 'Webhook settings' => 'Webhook nastavenÃ', 'Reset token' => 'Token reset', @@ -396,22 +394,17 @@ return array( 'Activity stream' => 'PÅ™ehled aktivit', 'Dashboard' => 'NástÄ›nka', 'Confirmation' => 'PotvrzenÃ', - 'Allow everybody to access to this project' => 'Umožnà pÅ™Ãstup komukoliv k tomuto projektu', - 'Everybody have access to this project.' => 'PÅ™Ãstup k tomuto projektu má kdokoliv.', 'Webhooks' => 'Webhooks', 'API' => 'API', 'Create a comment from an external provider' => 'VytvoÅ™it komentář pomocà externÃho poskytovatele', 'Project management' => 'Správa projektů', - 'My projects' => 'Moje projekty', 'Columns' => 'Sloupce', 'Task' => 'Úkol', - 'Your are not member of any project.' => 'V žádném projektu nejste Älenem.', 'Percentage' => 'Procenta', 'Number of tasks' => 'PoÄet úkolů', 'Task distribution' => 'RozdÄ›lenà úkolů', 'Analytics' => 'Analýza', 'Subtask' => 'DÃlÄà úkoly', - 'My subtasks' => 'Moje dÃlÄà úkoly', 'User repartition' => 'RozdÄ›lenà podle uživatelů', 'Clone this project' => 'Duplokovat projekt', 'Column removed successfully.' => 'Sloupec byl odstranÄ›n.', @@ -459,7 +452,6 @@ return array( 'Language:' => 'Jazyk:', 'Timezone:' => 'ÄŒasová zóna:', 'All columns' => 'VÅ¡echny sloupce', - 'Calendar' => 'Kalendář', 'Next' => 'DalÅ¡Ã', // '#%d' => '', 'All swimlanes' => 'Alle Swimlanes', @@ -557,7 +549,6 @@ return array( 'Unable to add this currency rate.' => 'Nelze pÅ™idat tento smÄ›nný kurz', 'Webhook URL' => 'Webhook URL', '%s removed the assignee of the task %s' => '%s odstranil pÅ™iÅ™azenà úkolu %s ', - 'Enable Gravatar images' => 'Aktiviere Gravatar Bilder', 'Information' => 'Informace', 'Check two factor authentication code' => 'Zkontrolujte dvouúrovňový autentifikaÄnà klÃÄ', 'The two factor authentication code is not valid.' => 'Dvouúrovňový autentifikaÄnà klÃÄ nenà platný.', @@ -572,7 +563,7 @@ return array( '%s via Kanboard' => '%s via Kanboard', 'Burndown chart' => 'Burndown-Chart', 'This chart show the task complexity over the time (Work Remaining).' => 'Graf zobrazuje složitost úkolů v Äase (ZbývajÃcà práce).', - 'Screenshot taken %s' => 'Screenshot aufgenommen %s ', + 'Screenshot taken %s' => 'SnÃmek obrazovky poÅ™Ãzen %s ', 'Add a screenshot' => 'PÅ™idat snÃmek obrazovky', 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'PoÅ™iÄte snÃmek obrazovky a v tomto poli stisknÄ›te Ctrl+V nebo ⌘+V ', 'Screenshot uploaded successfully.' => 'SnÃmek obrazovky byl úspěšnÄ› nahrán.', @@ -580,45 +571,38 @@ return array( 'Identifier' => 'Identifikátor', 'Disable two factor authentication' => 'ZruÅ¡it dvouúrovňovou autorizaci', 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Opravdu chcete vypnout dvouúrovňovou autentifikaci pro uživatele: "%s"?', - // 'Edit link' => '', + 'Edit link' => 'Upravit odkaz', // 'Start to type task title...' => '', // 'A task cannot be linked to itself' => '', - // 'The exact same link already exists' => '', + 'The exact same link already exists' => 'Stejný odkaz již existuje', // 'Recurrent task is scheduled to be generated' => '', - // 'Score' => '', - // 'The identifier must be unique' => '', - // 'This linked task id doesn\'t exists' => '', - // 'This value must be alphanumeric' => '', + 'Score' => 'Skóre', + 'The identifier must be unique' => 'Identifikátor musà být unikátnÃ', + 'This linked task id doesn\'t exists' => 'Tento odkazovaný úkol neexistuje', + 'This value must be alphanumeric' => 'Tato hodnota musà být alfanumerická', 'Edit recurrence' => 'Upravit opakovánÃ', - // 'Generate recurrent task' => '', - // 'Trigger to generate recurrent task' => '', - // 'Factor to calculate new due date' => '', - // 'Timeframe to calculate new due date' => '', - // 'Base date to calculate new due date' => '', - // 'Action date' => '', - // 'Base date to calculate new due date: ' => '', - // 'This task has created this child task: ' => '', - // 'Day(s)' => '', - // 'Existing due date' => '', - // 'Factor to calculate new due date: ' => '', - // 'Month(s)' => '', - // 'Recurrence' => '', - // 'This task has been created by: ' => '', - // 'Recurrent task has been generated:' => '', - // 'Timeframe to calculate new due date: ' => '', - // 'Trigger to generate recurrent task: ' => '', - // 'When task is closed' => '', - // 'When task is moved from first column' => '', - // 'When task is moved to last column' => '', - // 'Year(s)' => '', - 'Calendar settings' => 'Nastavenà kalendáře', - // 'Project calendar view' => '', + 'Generate recurrent task' => 'Generovat opakujÃcà se úkol', + 'Trigger to generate recurrent task' => 'SpouÅ¡tÄ›Ä pro generovánà opakujÃcÃho se úkolu', + 'Factor to calculate new due date' => 'Faktor pro výpoÄet nového data splnÄ›nÃ', + 'Timeframe to calculate new due date' => 'ÄŒasové okno pro výpoÄet nového data splnÄ›nÃ', + 'Base date to calculate new due date' => 'Výchozà datum pro výpoÄet nového data splnÄ›nÃ', + 'Action date' => 'Datum akce', + 'Base date to calculate new due date: ' => 'Výchozà datum pro výpoÄet nového data splnÄ›nÃ: ', + 'This task has created this child task: ' => 'Tento úkol vygeneroval následujÃcà podúkol: ', + 'Day(s)' => 'Dny', + 'Existing due date' => 'ExistujÃcà datum splnÄ›nÃ', + 'Factor to calculate new due date: ' => 'Faktor pro výpoÄet nového data splnÄ›nÃ: ', + 'Month(s)' => 'MÄ›sÃce', + 'Recurrence' => 'OpakovánÃ', + 'This task has been created by: ' => 'Tento úkol vytvoÅ™il: ', + 'Recurrent task has been generated:' => 'OpakujÃcà se úkol vygeneroval: ', + 'Timeframe to calculate new due date: ' => 'ÄŒasové okno pro výpoÄet nového data splnÄ›nÃ: ', + 'Trigger to generate recurrent task: ' => 'SpouÅ¡tÄ›Ä pro generovánà opakujÃcÃho se úkolu: ', + 'When task is closed' => 'Když je úkol uzavÅ™en', + 'When task is moved from first column' => 'Když je úkol pÅ™esunut z prvnÃho sloupce', + 'When task is moved to last column' => 'Když je úkol pÅ™esunut do poslenÃho sloupce', + 'Year(s)' => 'Roky', 'Project settings' => 'Nastavenà projektu', - 'Show subtasks based on the time tracking' => 'Zobrazit dÃlÄà úkoly závislé na sledovánà Äasu', - 'Show tasks based on the creation date' => 'Zobrazit úkoly podle datumu vytvoÅ™enÃ', - 'Show tasks based on the start date' => 'Zobrazit úkoly podle datumu zahájenÃ', - 'Subtasks time tracking' => 'DÃlÄà úkoly s ÄasovaÄem', - 'User calendar view' => 'Zobrazenà kalendáře uživatele', 'Automatically update the start date' => 'Automaticky aktualizovat poÄáteÄnà datum', // 'iCal feed' => '', 'Preferences' => 'PÅ™edvolby', @@ -636,8 +620,7 @@ return array( 'Reopen a task' => 'Znovu otevÅ™Ãt úkol', 'Notification' => 'UpozornÄ›nÃ', '%s moved the task #%d to the first swimlane' => '%s pÅ™esunul úkol #%d do prvnà dráhy', - // 'Swimlane' => '', - // 'Gravatar' => '', + 'Swimlane' => 'Dráha', '%s moved the task %s to the first swimlane' => '%s pÅ™esunul úkol %s do prvnà dráhy', '%s moved the task %s to the swimlane "%s"' => '%s pÅ™esunul úkol %s do dráhy "%s"', 'This report contains all subtasks information for the given date range.' => 'Report obsahuje vÅ¡echny informace o dÃlÄÃch úkolech pro daný Äasový úsek', @@ -674,7 +657,6 @@ return array( 'Stop timer' => 'Zastavit ÄasovaÄ', 'Start timer' => 'Spustit ÄasovaÄ', 'My activity stream' => 'PÅ™ehled mých aktivit', - 'My calendar' => 'Můj kalendář', 'Search tasks' => 'Hledánà úkolů', 'Reset filters' => 'Resetovat filtry', 'My tasks due tomorrow' => 'Moje zÃtÅ™ejšà úkoly', @@ -688,7 +670,6 @@ return array( 'Overview' => 'PÅ™ehled', 'Board/Calendar/List view' => 'NástÄ›nka/Kalendář/Zobrazenà seznamu', 'Switch to the board view' => 'PÅ™epnout na nástÄ›nku', - 'Switch to the calendar view' => 'PÅ™epnout na kalendář', 'Switch to the list view' => 'PÅ™epnout na seznam zobrazenÃ', 'Go to the search/filter box' => 'Zobrazit vyhledávánÃ/filtrovánÃ', 'There is no activity yet.' => 'Doposud nejsou žádné aktivity.', @@ -743,47 +724,33 @@ return array( 'License:' => 'Licence:', 'License' => 'Licence', 'Enter the text below' => 'Zadejte text nÞe', - 'Sort by position' => 'TÅ™Ãdit podle pozice', - 'Sort by date' => 'TÅ™Ãdit podle datumu', - 'Add task' => 'PÅ™idat úkol', 'Start date:' => 'TermÃn zahájenÃ:', 'Due date:' => 'TermÃn dokonÄenÃ:', - 'There is no start date or due date for this task.' => 'Úkol nemá nastaven termÃn zahájenà a dokonÄenÃ.', - 'Moving or resizing a task will change the start and due date of the task.' => 'PosunutÃm nebo prodlouženÃm úkolu se zmÄ›nà poÄáteÄnà a koneÄné datum úkolu. ', - 'There is no task in your project.' => 'Projekt neobsahuje žádné úkoly.', - 'Gantt chart' => 'Gantt graf', - // 'People who are project managers' => '', - // 'People who are project members' => '', + 'People who are project managers' => 'Lidé, kteřà jsou správci projektu', + 'People who are project members' => 'Lidé, kteřà jsou Älenové projektu', // 'NOK - Norwegian Krone' => '', - // 'Show this column' => '', - // 'Hide this column' => '', - // 'open file' => '', - // 'End date' => '', - // 'Users overview' => '', - // 'Members' => '', - // 'Shared project' => '', - // 'Project managers' => '', - // 'Gantt chart for all projects' => '', - // 'Projects list' => '', - // 'Gantt chart for this project' => '', - // 'Project board' => '', - // 'End date:' => '', - // 'There is no start date or end date for this project.' => '', - // 'Projects Gantt chart' => '', - // 'Change task color when using a specific task link' => '', - // 'Task link creation or modification' => '', - // 'Milestone' => '', - // 'Documentation: %s' => '', - // 'Switch to the Gantt chart view' => '', - // 'Reset the search/filter box' => '', - // 'Documentation' => '', - // 'Table of contents' => '', - // 'Gantt' => '', + 'Show this column' => 'Zobrazit tento sloupec', + 'Hide this column' => 'Skrýt tento sloupec', + 'open file' => 'otevÅ™Ãt soubor', + 'End date' => 'Datum konce', + 'Users overview' => 'PÅ™ehled uživatelů', + 'Members' => 'ÄŒlenové', + 'Shared project' => 'SdÃlený projekt', + 'Project managers' => 'Správci projektu', + 'Projects list' => 'Seznam projektů', + 'End date:' => 'Datum konce: ', + 'Change task color when using a specific task link' => 'ZmÄ›nit barvu úkolu pÅ™i použità konkrétnÃho odkazu na úkol', + 'Task link creation or modification' => 'VytvoÅ™enÃ, nebo zmÄ›na odkazu na úkol', + 'Milestone' => 'MilnÃk', + 'Documentation: %s' => 'Dokumentace %s', + 'Reset the search/filter box' => 'Vyresetovat pole pro vyhledávánÃ/filtrovánÃ', + 'Documentation' => 'Dokumentace', + 'Table of contents' => 'Obsah', // 'Author' => '', // 'Version' => '', // 'Plugins' => '', // 'There is no plugin loaded.' => '', - // 'My notifications' => '', + 'My notifications' => 'Moje oznámenÃ', // 'Custom filters' => '', // 'Your custom filter have been created successfully.' => '', // 'Unable to create your custom filter.' => '', @@ -793,34 +760,34 @@ return array( // 'Your custom filter have been updated successfully.' => '', // 'Unable to update custom filter.' => '', // 'Web' => '', - // 'New attachment on task #%d: %s' => '', - // 'New comment on task #%d' => '', - // 'Comment updated on task #%d' => '', - // 'New subtask on task #%d' => '', - // 'Subtask updated on task #%d' => '', - // 'New task #%d: %s' => '', - // 'Task updated #%d' => '', - // 'Task #%d closed' => '', - // 'Task #%d opened' => '', - // 'Column changed for task #%d' => '', - // 'New position for task #%d' => '', - // 'Swimlane changed for task #%d' => '', - // 'Assignee changed on task #%d' => '', - // '%d overdue tasks' => '', - // 'Task #%d is overdue' => '', - // 'No notification.' => '', - // 'Mark all as read' => '', - // 'Mark as read' => '', - // 'Total number of tasks in this column across all swimlanes' => '', - // 'Collapse swimlane' => '', - // 'Expand swimlane' => '', - // 'Add a new filter' => '', - // 'Share with all project members' => '', - // 'Shared' => '', - // 'Owner' => '', - // 'Unread notifications' => '', - // 'Notification methods:' => '', - // 'Unable to read your file' => '', + 'New attachment on task #%d: %s' => 'Nová pÅ™Ãloha u úkolu #%d: %s', + 'New comment on task #%d' => 'Nový komentář u úkolu #%d', + 'Comment updated on task #%d' => 'Komentář u úkolu #%d byl aktualizován', + 'New subtask on task #%d' => 'Nový dÃlÄà úkol u úkolu #%d', + 'Subtask updated on task #%d' => 'DÃlÄà úkol u úkolu #%d byl aktualizován', + 'New task #%d: %s' => 'Nový úkol #%d: %s', + 'Task updated #%d' => 'Úkol #%d byl aktualizován', + 'Task #%d closed' => 'Úkol #%d byl uzavÅ™en', + 'Task #%d opened' => 'Úkol #%d byl otevÅ™en', + 'Column changed for task #%d' => 'Sloupec úkolu #%d byl zmÄ›nÄ›n', + 'New position for task #%d' => 'Nová pozice úkolu #%d', + 'Swimlane changed for task #%d' => 'Dráha úkolu #%d byla zmÄ›nÄ›na', + 'Assignee changed on task #%d' => 'VlastnÃk úkolu #%d byl zmÄ›nÄ›n', + '%d overdue tasks' => '%d úkolů po datu splnÄ›nÃ', + 'Task #%d is overdue' => 'Úkol #%d je po datu splnÄ›nÃ', + 'No notification.' => 'Žádná notifikace.', + 'Mark all as read' => 'OznaÄit vÅ¡e jako pÅ™eÄtené', + 'Mark as read' => 'OznaÄit jako pÅ™eÄtené', + 'Total number of tasks in this column across all swimlanes' => 'Celkový poÄet úkolů v tomto sloupci napÅ™ÃÄ vÅ¡emi dráhami', + 'Collapse swimlane' => 'Zabalit dráhu', + 'Expand swimlane' => 'Rozbalit dráhu', + 'Add a new filter' => 'PÅ™idat nový filtr', + 'Share with all project members' => 'SdÃlet se vÅ¡emi Äleny projektu', + 'Shared' => 'SdÃleno', + 'Owner' => 'VlastnÃk', + 'Unread notifications' => 'NepÅ™eÄtené notifikace', + 'Notification methods:' => 'Způsoby notifikacÃ:', + 'Unable to read your file' => 'Nelze pÅ™eÄÃst soubor', // '%d task(s) have been imported successfully.' => '', // 'Nothing have been imported!' => '', // 'Import users from CSV file' => '', @@ -877,45 +844,44 @@ return array( // 'Remove group' => '', // 'Group removed successfully.' => '', // 'Unable to remove this group.' => '', - // 'Project Permissions' => '', - // 'Manager' => '', - // 'Project Manager' => '', - // 'Project Member' => '', - // 'Project Viewer' => '', + 'Project Permissions' => 'OprávnÄ›nà projektu', + 'Manager' => 'Správce', + 'Project Manager' => 'Správce projektu', + 'Project Member' => 'ÄŒlen projektu', + 'Project Viewer' => 'ÄŒtenář projektu', // 'Your account is locked for %d minutes' => '', // 'Invalid captcha' => '', // 'The name must be unique' => '', - // 'View all groups' => '', + 'View all groups' => 'Zobrazit vÅ¡echny skupiny', // 'There is no user available.' => '', // 'Do you really want to remove the user "%s" from the group "%s"?' => '', // 'There is no group.' => '', - // 'External Id' => '', // 'Add group member' => '', // 'Do you really want to remove this group: "%s"?' => '', // 'There is no user in this group.' => '', - // 'Permissions' => '', - // 'Allowed Users' => '', + 'Permissions' => 'OprávnÄ›nÃ', + 'Allowed Users' => 'Povolenà uživatelé', // 'No user have been allowed specifically.' => '', - // 'Role' => '', - // 'Enter user name...' => '', - // 'Allowed Groups' => '', + 'Role' => 'Role', + 'Enter user name...' => 'Zadejte uživatelské jméno...', + 'Allowed Groups' => 'Povolené skupiny', // 'No group have been allowed specifically.' => '', - // 'Group' => '', - // 'Group Name' => '', + 'Group' => 'Skupina', + 'Group Name' => 'Jméno skupiny', // 'Enter group name...' => '', // 'Role:' => '', - // 'Project members' => '', + 'Project members' => 'ÄŒlenové projektu', // '%s mentioned you in the task #%d' => '', // '%s mentioned you in a comment on the task #%d' => '', // 'You were mentioned in the task #%d' => '', // 'You were mentioned in a comment on the task #%d' => '', // 'Estimated hours: ' => '', // 'Actual hours: ' => '', - // 'Hours Spent' => '', - // 'Hours Estimated' => '', - // 'Estimated Time' => '', - // 'Actual Time' => '', - // 'Estimated vs actual time' => '', + 'Hours Spent' => 'Hodin stráveno', + 'Hours Estimated' => 'Hodin odhadováno', + 'Estimated Time' => 'Odhadovaný Äas', + 'Actual Time' => 'Aktuálnà Äas', + 'Estimated vs actual time' => 'RozdÃl mezi odhadovaným a aktuálnÃm Äasem', // 'RUB - Russian Ruble' => '', // 'Assign the task to the person who does the action when the column is changed' => '', // 'Close a task in a specific column' => '', @@ -925,7 +891,7 @@ return array( // 'Enable two-factor authentication' => '', // 'There is no integration registered at the moment.' => '', // 'Password Reset for Kanboard' => '', - // 'Forgot password?' => '', + 'Forgot password?' => 'Zapomenuté heslo?', // 'Enable "Forget Password"' => '', // 'Password Reset' => '', // 'New password' => '', @@ -933,81 +899,80 @@ return array( // 'To reset your password click on this link:' => '', // 'Last Password Reset' => '', // 'The password has never been reinitialized.' => '', - // 'Creation' => '', + 'Creation' => 'VytvoÅ™enÃ', // 'Expiration' => '', // 'Password reset history' => '', // 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => '', // 'Do you really want to close all tasks of this column?' => '', // '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '', - // 'Close all tasks of this column' => '', + 'Close all tasks of this column' => 'UzavÅ™Ãt vÅ¡echny úkoly v tomto sloupci', // 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => '', - // 'My dashboard' => '', - // 'My profile' => '', - // 'Project owner: ' => '', + 'My dashboard' => 'Moje nástÄ›nka', + 'My profile' => 'Můj profil', + 'Project owner: ' => 'VlastnÃk projektu: ', // 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '', - // 'Project owner' => '', - // 'Private projects do not have users and groups management.' => '', - // 'There is no project member.' => '', - // 'Priority' => '', - // 'Task priority' => '', - // 'General' => '', - // 'Dates' => '', - // 'Default priority' => '', - // 'Lowest priority' => '', - // 'Highest priority' => '', - // 'If you put zero to the low and high priority, this feature will be disabled.' => '', - // 'Close a task when there is no activity' => '', - // 'Duration in days' => '', - // 'Send email when there is no activity on a task' => '', + 'Project owner' => 'VlastnÃk projektu', + 'Private projects do not have users and groups management.' => 'Soukromé projekty nemajà správu uživatelů a skupin', + 'There is no project member.' => 'Projekt nemá žádného Älena', + 'Priority' => 'Priorita', + 'Task priority' => 'Priorita úkolu', + 'General' => 'VÅ¡eobecné', + 'Dates' => 'TermÃny', + 'Default priority' => 'Výchozà priorita', + 'Lowest priority' => 'Nejnižšà priorita', + 'Highest priority' => 'Nejvyžšà priorita', + 'Close a task when there is no activity' => 'UzavÅ™Ãt úkol pokud nenà žádná aktivita', + 'Duration in days' => 'Trvánà ve dnech', + 'Send email when there is no activity on a task' => 'Odeslat mail pokud nenà žádná aktivita u úkolu', // 'Unable to fetch link information.' => '', // 'Daily background job for tasks' => '', // 'Auto' => '', - // 'Related' => '', - // 'Attachment' => '', - // 'Title not found' => '', - // 'Web Link' => '', - // 'External links' => '', - // 'Add external link' => '', - // 'Type' => '', - // 'Dependency' => '', - // 'Add internal link' => '', - // 'Add a new external link' => '', - // 'Edit external link' => '', - // 'External link' => '', - // 'Copy and paste your link here...' => '', + 'Related' => 'SouvisejÃcÃ', + 'Attachment' => 'PÅ™Ãloha', + 'Title not found' => 'Název nenalezen', + 'Web Link' => 'Webový odkaz', + 'External links' => 'Externà odkazy', + 'Add external link' => 'PÅ™idat externà odkaz', + 'Type' => 'Typ', + 'Dependency' => 'Závislost', + 'Add internal link' => 'PÅ™idat internà odkaz', + 'Add a new external link' => 'PÅ™idat nový externà odkaz', + 'Edit external link' => 'Upravit externà odkaz', + 'External link' => 'Externà odkaz', + 'Copy and paste your link here...' => 'Zde vložte váš odkaz...', // 'URL' => '', - // 'Internal links' => '', + 'Internal links' => 'Internà odkazy', // 'Assign to me' => '', // 'Me' => '', // 'Do not duplicate anything' => '', - // 'Projects management' => '', - // 'Users management' => '', - // 'Groups management' => '', - // 'Create from another project' => '', - // 'open' => '', - // 'closed' => '', - // 'Priority:' => '', + 'Projects management' => 'Správa projektů', + 'Users management' => 'Správa uživatelů', + 'Groups management' => 'Správa skupin', + 'Create from another project' => 'VytvoÅ™it z jiného projektu', + 'open' => 'otevÅ™eno', + 'closed' => 'uzavÅ™eno', + 'Priority:' => 'Priorita:', // 'Reference:' => '', - // 'Complexity:' => '', - // 'Swimlane:' => '', - // 'Column:' => '', - // 'Position:' => '', - // 'Creator:' => '', - // 'Time estimated:' => '', - // '%s hours' => '', - // 'Time spent:' => '', - // 'Created:' => '', - // 'Modified:' => '', - // 'Completed:' => '', - // 'Started:' => '', - // 'Moved:' => '', - // 'Task #%d' => '', - // 'Time format' => '', - // 'Start date: ' => '', - // 'End date: ' => '', - // 'New due date: ' => '', - // 'Start date changed: ' => '', - // 'Disable private projects' => '', + 'Complexity:' => 'Složitost:', + 'Swimlane:' => 'Dráha:', + 'Column:' => 'Sloupec:', + 'Position:' => 'Pozice:', + 'Creator:' => 'Autor:', + 'Time estimated:' => 'Odhadovaný Äas:', + '%s hours' => '%s hodin', + 'Time spent:' => 'Strávený Äas:', + 'Created:' => 'VytvoÅ™eno:', + 'Modified:' => 'ZmÄ›nÄ›no:', + 'Completed:' => 'DokonÄeno:', + 'Started:' => 'ZapoÄato:', + 'Moved:' => 'PÅ™esunuto', + 'Task #%d' => 'Úkol #%d', + 'Time format' => 'ÄŒasový formát', + 'Start date: ' => 'PoÄáteÄnà datum', + 'End date: ' => 'Koncové datum', + 'New due date: ' => 'Nové datum splnÄ›nÃ', + 'Start date changed: ' => 'PoÄáteÄnà datum zmÄ›nÄ›no: ', + 'Disable private projects' => 'Zakázat soukromé projekty', // 'Do you really want to remove this custom filter: "%s"?' => '', // 'Remove a custom filter' => '', // 'User activated successfully.' => '', @@ -1133,13 +1098,13 @@ return array( // 'Tag removed successfully.' => '', // 'Unable to remove this tag.' => '', // 'Global tags management' => '', - // 'Tags' => '', - // 'Tags management' => '', - // 'Add new tag' => '', - // 'Edit a tag' => '', - // 'Project tags' => '', + 'Tags' => 'Å tÃtky', + 'Tags management' => 'Správa Å¡tÃtků', + 'Add new tag' => 'PÅ™idat nový Å¡tÃtek', + 'Edit a tag' => 'Upravit Å¡tÃtek', + 'Project tags' => 'Å tÃtky projektu', // 'There is no specific tag for this project at the moment.' => '', - // 'Tag' => '', + 'Tag' => 'Å tÃtek', // 'Remove a tag' => '', // 'Do you really want to remove this tag: "%s"?' => '', // 'Global tags' => '', @@ -1169,8 +1134,6 @@ return array( // 'Subtasks overview for %s' => '', // 'Projects overview for %s' => '', // 'Activity stream for %s' => '', - // 'Calendar for %s' => '', - // 'Notifications for %s' => '', // 'Assign a color when the task is moved to a specific swimlane' => '', // 'Assign a priority when the task is moved to a specific swimlane' => '', // 'User unlocked successfully.' => '', @@ -1241,7 +1204,6 @@ return array( // 'Preview' => '', // 'Write' => '', // 'Write your text in Markdown' => '', - // 'New External Task: %s' => '', // 'No personal API access token registered.' => '', // 'Your personal API access token is "%s"' => '', // 'Remove your token' => '', @@ -1293,9 +1255,9 @@ return array( // 'The project email is optional and could be used by several plugins.' => '', // 'The project email must be unique across all projects' => '', // 'The email configuration has been disabled by the administrator.' => '', - // 'Close this project' => '', - // 'Open this project' => '', - // 'Close a project' => '', + 'Close this project' => 'UzavÅ™Ãt tento projekt', + 'Open this project' => 'OtevÅ™Ãt tento projekt', + 'Close a project' => 'UzavÅ™Ãt projekt', // 'Do you really want to close this project: "%s"?' => '', // 'Reopen a project' => '', // 'Do you really want to reopen this project: "%s"?' => '', @@ -1316,14 +1278,13 @@ return array( // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', // 'Database file' => '', // 'Upload' => '', - // 'Remove this user from group' => '', // 'Your project must have at least one active swimlane.' => '', // 'Project: %s' => '', // 'Automatic action not found: "%s"' => '', // '%d projects' => '', // '%d project' => '', // 'There is no project.' => '', - // 'Sort' => '', + 'Sort' => 'SeÅ™adit', // 'Project ID' => '', // 'Project name' => '', // 'Public' => '', @@ -1334,4 +1295,75 @@ return array( // 'Assign automatically a color when due date is expired' => '', // 'Total score in this column across all swimlanes' => '', // 'HRK - Kuna' => '', + // 'ARS - Argentine Peso' => '', + // 'COP - Colombian Peso' => '', + // '%d groups' => '', + // '%d group' => '', + // 'Group ID' => '', + // 'External ID' => '', + // '%d users' => '', + // '%d user' => '', + // 'Hide subtasks' => '', + // 'Show subtasks' => '', + // 'Authentication Parameters' => '', + // 'API Access' => '', + // 'No users found.' => '', + // 'User ID' => '', + // 'Notifications are activated' => '', + // 'Notifications are disabled' => '', + // 'User disabled' => '', + // '%d notifications' => '', + // '%d notification' => '', + // 'There is no external integration installed.' => '', + // 'You are not allowed to update tasks assigned to someone else.' => '', + // 'You are not allowed to change the assignee.' => '', + // 'Task suppression is not permitted' => '', + // 'Changing assignee is not permitted' => '', + // 'Update only assigned tasks is permitted' => '', + // 'Only for tasks assigned to the current user' => '', + 'My projects' => 'Moje projekty', + // 'Your are not member of any project.' => '', + 'My subtasks' => 'Moje dÃlÄà úkoly', + // '%d subtasks' => '', + // '%d subtask' => '', + // 'Only moving task between those columns is permitted for tasks assigned to the current user' => '', + '[DUPLICATE]' => '[KOPIE]', + // 'DKK - Danish Krona' => '', + // 'Remove user from group' => '', + // 'Assign the task to its creator' => '', + // 'This task was sent by email to "%s" with subject "%s".' => '', + // 'Predefined Email Subjects' => '', + // 'Write one subject by line.' => '', + // 'Create another link' => '', + // 'BRL - Brazilian Real' => '', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/da_DK/translations.php b/app/Locale/da_DK/translations.php index fd4a7ecb..2149920d 100644 --- a/app/Locale/da_DK/translations.php +++ b/app/Locale/da_DK/translations.php @@ -39,7 +39,6 @@ return array( 'Administrator' => 'Administrator', 'Sign in' => 'Log ind', 'Users' => 'Brugere', - 'No user' => 'Ingen bruger', 'Forbidden' => 'Forbudt', 'Access Forbidden' => 'Adgang nægtet', 'Edit user' => 'Rediger bruger', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'Under-opgaven er opdateret.', 'Unable to update your sub-task.' => 'Under-opgaven kunne ikke opdateres.', 'Unable to create your sub-task.' => 'Under-opgaven kunne ikke oprettes.', - 'Sub-task added successfully.' => 'Under-opgaven er tilføjet.', 'Maximum size: ' => 'Maksimum størrelse: ', 'Display another project' => 'Vis et andet projekt...', 'Created by %s' => 'Oprettet af %s', @@ -396,22 +394,17 @@ return array( 'Activity stream' => 'Aktivitets strøm', 'Dashboard' => 'Dashboard', 'Confirmation' => 'Bekræftelse', - 'Allow everybody to access to this project' => 'Tillad adgang for alle til dette projekt', - 'Everybody have access to this project.' => 'Alle har adgang til dette projekt', // 'Webhooks' => '', // 'API' => '', // 'Create a comment from an external provider' => '', 'Project management' => 'Projektstyrring', - 'My projects' => 'Mine projekter', 'Columns' => 'Kolonner', 'Task' => 'Opgave', - 'Your are not member of any project.' => 'Du er ikke medlem af nogen projekter.', 'Percentage' => 'Procent', // 'Number of tasks' => '', 'Task distribution' => 'Opgave distribution', 'Analytics' => 'Analyse', 'Subtask' => 'Under-opgave', - 'My subtasks' => 'Mine under-opgaver', // 'User repartition' => '', 'Clone this project' => 'Kopier dette projekt', // 'Column removed successfully.' => '', @@ -459,7 +452,6 @@ return array( 'Language:' => 'Sprog', 'Timezone:' => 'Tidszone', 'All columns' => 'Alle kolonner', - 'Calendar' => 'Kalender', 'Next' => 'Næste', // '#%d' => '', 'All swimlanes' => 'Alle spor', @@ -557,7 +549,6 @@ return array( // 'Unable to add this currency rate.' => '', // 'Webhook URL' => '', // '%s removed the assignee of the task %s' => '', - // 'Enable Gravatar images' => '', // 'Information' => '', // 'Check two factor authentication code' => '', // 'The two factor authentication code is not valid.' => '', @@ -611,14 +602,7 @@ return array( // 'When task is moved from first column' => '', // 'When task is moved to last column' => '', 'Year(s)' => 'Ã…r', - 'Calendar settings' => 'Kalender indstillinger', - 'Project calendar view' => 'Projekt kalender visning', 'Project settings' => 'Projekt indstillinger', - // 'Show subtasks based on the time tracking' => '', - // 'Show tasks based on the creation date' => '', - // 'Show tasks based on the start date' => '', - // 'Subtasks time tracking' => '', - 'User calendar view' => 'Bruger kalender visning', // 'Automatically update the start date' => '', // 'iCal feed' => '', 'Preferences' => 'Præferencer', @@ -637,7 +621,6 @@ return array( 'Notification' => 'Notifikation', // '%s moved the task #%d to the first swimlane' => '', 'Swimlane' => 'Spor', - // 'Gravatar' => '', // '%s moved the task %s to the first swimlane' => '', // '%s moved the task %s to the swimlane "%s"' => '', // 'This report contains all subtasks information for the given date range.' => '', @@ -674,7 +657,6 @@ return array( // 'Stop timer' => '', // 'Start timer' => '', 'My activity stream' => 'Min aktivitets strøm', - 'My calendar' => 'Min kalender', 'Search tasks' => 'Søg opgaver', // 'Reset filters' => '', // 'My tasks due tomorrow' => '', @@ -688,7 +670,6 @@ return array( 'Overview' => 'Overblik', 'Board/Calendar/List view' => 'Tavle/Kalender/Liste visning', 'Switch to the board view' => 'Skift til tavle visning', - 'Switch to the calendar view' => 'Skift til kalender visning', 'Switch to the list view' => 'Skift til liste visning', // 'Go to the search/filter box' => '', // 'There is no activity yet.' => '', @@ -743,15 +724,8 @@ return array( // 'License:' => '', // 'License' => '', // 'Enter the text below' => '', - 'Sort by position' => 'Sorter udfra position', - 'Sort by date' => 'Sorter ud fra dato', - 'Add task' => 'Tilføj opgave', 'Start date:' => 'Start dato:', 'Due date:' => 'Forfaldsdato:', - // 'There is no start date or due date for this task.' => '', - 'Moving or resizing a task will change the start and due date of the task.' => 'Hvis du flytter eller trækker i en opgave vil det ændre start -og forfaldsdato', - 'There is no task in your project.' => 'Der er ikke nogle opgaver i dette projekt', - // 'Gantt chart' => '', // 'People who are project managers' => '', // 'People who are project members' => '', // 'NOK - Norwegian Krone' => '', @@ -763,22 +737,15 @@ return array( 'Members' => 'Medlemmer', 'Shared project' => 'Delt projekt', 'Project managers' => 'Projekt managers', - 'Gantt chart for all projects' => 'Gantt chart for alle projekter', 'Projects list' => 'Projekt liste', - 'Gantt chart for this project' => 'Gantt chart for dette projekt', - 'Project board' => 'Projekt tavle', 'End date:' => 'Slut dato:', - 'There is no start date or end date for this project.' => 'Der er ikke nogen start eller slut dato for dette projekt.', - 'Projects Gantt chart' => 'Gantt chart pÃ¥ Projekter', // 'Change task color when using a specific task link' => '', // 'Task link creation or modification' => '', 'Milestone' => 'Milepæl', // 'Documentation: %s' => '', - // 'Switch to the Gantt chart view' => '', // 'Reset the search/filter box' => '', // 'Documentation' => '', // 'Table of contents' => '', - // 'Gantt' => '', 'Author' => 'Forfatter', // 'Version' => '', // 'Plugins' => '', @@ -889,7 +856,6 @@ return array( // 'There is no user available.' => '', // 'Do you really want to remove the user "%s" from the group "%s"?' => '', // 'There is no group.' => '', - // 'External Id' => '', 'Add group member' => 'Tilføj gruppe medlem', // 'Do you really want to remove this group: "%s"?' => '', // 'There is no user in this group.' => '', @@ -955,7 +921,6 @@ return array( 'Default priority' => 'Standard prioritet', 'Lowest priority' => 'Laveste prioritet', 'Highest priority' => 'Højeste prioritet', - 'If you put zero to the low and high priority, this feature will be disabled.' => 'Hvis du sætter et nul i bÃ¥de højelse og laveste prioritet, vil denne funktion blive deaktiveret', // 'Close a task when there is no activity' => '', // 'Duration in days' => '', // 'Send email when there is no activity on a task' => '', @@ -1169,8 +1134,6 @@ return array( // 'Subtasks overview for %s' => '', // 'Projects overview for %s' => '', // 'Activity stream for %s' => '', - // 'Calendar for %s' => '', - // 'Notifications for %s' => '', // 'Assign a color when the task is moved to a specific swimlane' => '', // 'Assign a priority when the task is moved to a specific swimlane' => '', // 'User unlocked successfully.' => '', @@ -1241,7 +1204,6 @@ return array( 'Preview' => 'ForhÃ¥ndsvisning', 'Write' => 'Skriv', 'Write your text in Markdown' => 'Skriv din tekst i Markdown', - // 'New External Task: %s' => '', // 'No personal API access token registered.' => '', // 'Your personal API access token is "%s"' => '', // 'Remove your token' => '', @@ -1316,7 +1278,6 @@ return array( // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', // 'Database file' => '', // 'Upload' => '', - // 'Remove this user from group' => '', // 'Your project must have at least one active swimlane.' => '', // 'Project: %s' => '', // 'Automatic action not found: "%s"' => '', @@ -1334,4 +1295,75 @@ return array( // 'Assign automatically a color when due date is expired' => '', // 'Total score in this column across all swimlanes' => '', // 'HRK - Kuna' => '', + // 'ARS - Argentine Peso' => '', + // 'COP - Colombian Peso' => '', + // '%d groups' => '', + // '%d group' => '', + // 'Group ID' => '', + // 'External ID' => '', + // '%d users' => '', + // '%d user' => '', + // 'Hide subtasks' => '', + // 'Show subtasks' => '', + // 'Authentication Parameters' => '', + // 'API Access' => '', + // 'No users found.' => '', + // 'User ID' => '', + // 'Notifications are activated' => '', + // 'Notifications are disabled' => '', + // 'User disabled' => '', + // '%d notifications' => '', + // '%d notification' => '', + // 'There is no external integration installed.' => '', + // 'You are not allowed to update tasks assigned to someone else.' => '', + // 'You are not allowed to change the assignee.' => '', + // 'Task suppression is not permitted' => '', + // 'Changing assignee is not permitted' => '', + // 'Update only assigned tasks is permitted' => '', + // 'Only for tasks assigned to the current user' => '', + // 'My projects' => '', + // 'Your are not member of any project.' => '', + // 'My subtasks' => '', + // '%d subtasks' => '', + // '%d subtask' => '', + // 'Only moving task between those columns is permitted for tasks assigned to the current user' => '', + // '[DUPLICATE]' => '', + // 'DKK - Danish Krona' => '', + // 'Remove user from group' => '', + // 'Assign the task to its creator' => '', + // 'This task was sent by email to "%s" with subject "%s".' => '', + // 'Predefined Email Subjects' => '', + // 'Write one subject by line.' => '', + // 'Create another link' => '', + // 'BRL - Brazilian Real' => '', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/de_DE/translations.php b/app/Locale/de_DE/translations.php index 2b554c9d..1499fcdc 100644 --- a/app/Locale/de_DE/translations.php +++ b/app/Locale/de_DE/translations.php @@ -39,7 +39,6 @@ return array( 'Administrator' => 'Administrator', 'Sign in' => 'Anmelden', 'Users' => 'Benutzer', - 'No user' => 'Kein Benutzer', 'Forbidden' => 'Verboten', 'Access Forbidden' => 'Zugriff verboten', 'Edit user' => 'Benutzer bearbeiten', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'Teilaufgabe erfolgreich aktualisiert.', 'Unable to update your sub-task.' => 'Aktualisieren der Teilaufgabe nicht möglich.', 'Unable to create your sub-task.' => 'Erstellen der Teilaufgabe nicht möglich.', - 'Sub-task added successfully.' => 'Teilaufgabe erfolgreich angelegt.', 'Maximum size: ' => 'Maximalgröße: ', 'Display another project' => 'Zu Projekt wechseln', 'Created by %s' => 'Erstellt durch %s', @@ -360,8 +358,8 @@ return array( 'Default values are "%s"' => 'Die Standardwerte sind "%s"', 'Default columns for new projects (Comma-separated)' => 'Standardspalten für neue Projekte (komma-getrennt)', 'Task assignee change' => 'Zuständigkeit geändert', - '%s changed the assignee of the task #%d to %s' => '%s hat die Zusständigkeit der Aufgabe #%d geändert um %s', - '%s changed the assignee of the task %s to %s' => '%s hat die Zuständigkeit der Aufgabe %s geändert um %s', + '%s changed the assignee of the task #%d to %s' => '%s hat die Zuständigkeit der Aufgabe #%d geändert zu %s', + '%s changed the assignee of the task %s to %s' => '%s hat die Zuständigkeit der Aufgabe %s geändert zu %s', 'New password for the user "%s"' => 'Neues Passwort des Benutzers "%s"', 'Choose an event' => 'Aktion wählen', 'Create a task from an external provider' => 'Eine Aufgabe durch einen externen Provider hinzufügen', @@ -396,35 +394,30 @@ return array( 'Activity stream' => 'Letzte Aktivitäten', 'Dashboard' => 'Dashboard', 'Confirmation' => 'Wiederholung', - 'Allow everybody to access to this project' => 'Jedem Zugriff zu diesem Projekt gewähren', - 'Everybody have access to this project.' => 'Jeder hat Zugriff zu diesem Projekt', 'Webhooks' => 'Webhooks', 'API' => 'API', 'Create a comment from an external provider' => 'Kommentar eines externen Providers hinzufügen', 'Project management' => 'Projektmanagement', - 'My projects' => 'Meine Projekte', 'Columns' => 'Spalten', 'Task' => 'Aufgabe', - 'Your are not member of any project.' => 'Sie sind nicht Mitglied eines Projekts.', 'Percentage' => 'Prozentsatz', 'Number of tasks' => 'Anzahl an Aufgaben', 'Task distribution' => 'Aufgabenverteilung', 'Analytics' => 'Analyse', 'Subtask' => 'Teilaufgabe', - 'My subtasks' => 'Meine Teilaufgaben', 'User repartition' => 'Benutzerverteilung', 'Clone this project' => 'Projekt kopieren', 'Column removed successfully.' => 'Spalte erfolgreich entfernt.', 'Not enough data to show the graph.' => 'Nicht genügend Daten, um die Grafik zu zeigen.', 'Previous' => 'Vorherige', - 'The id must be an integer' => 'Die Id muss eine ganze Zahl sein', + 'The id must be an integer' => 'Die ID muss eine ganze Zahl sein', 'The project id must be an integer' => 'Der Projekt-ID muss eine ganze Zahl sein', 'The status must be an integer' => 'Der Status muss eine ganze Zahl sein', 'The subtask id is required' => 'Die Teilaufgaben-ID ist benötigt', 'The subtask id must be an integer' => 'Die Teilaufgaben-ID muss eine ganze Zahl sein', 'The task id is required' => 'Die Aufgaben-ID ist benötigt', - 'The task id must be an integer' => 'Die Aufgaben-ID muss eine ganze Zahl sein', - 'The user id must be an integer' => 'Die User-ID muss eine ganze Zahl sein', + 'The task id must be an integer' => 'Die Aufgaben ID muss eine ganze Zahl sein', + 'The user id must be an integer' => 'Die Benutzer ID muss eine ganze Zahl sein', 'This value is required' => 'Dieser Wert ist erforderlich', 'This value must be numeric' => 'Dieser Wert muss nummerisch sein', 'Unable to create this task.' => 'Diese Aufgabe kann nicht erstellt werden', @@ -459,7 +452,6 @@ return array( 'Language:' => 'Sprache:', 'Timezone:' => 'Zeitzone:', 'All columns' => 'Alle Spalten', - 'Calendar' => 'Kalender', 'Next' => 'Nächste', '#%d' => 'Nr %d', 'All swimlanes' => 'Alle Swimlanes', @@ -540,7 +532,7 @@ return array( // 'CNY - Chinese Yuan' => '', 'USD - US Dollar' => 'USD - US-Dollar', 'Destination column' => 'Zielspalte', - 'Move the task to another column when assigned to a user' => 'Aufgabe in eine andere Spalte verschieben, wenn ein User zugeordnet wurde.', + 'Move the task to another column when assigned to a user' => 'Aufgabe in eine andere Spalte verschieben, wenn ein Benutzer zugeordnet wurde.', 'Move the task to another column when assignee is cleared' => 'Aufgabe in eine andere Spalte verschieben, wenn die Zuordnung gelöscht wurde.', 'Source column' => 'Quellspalte', 'Transitions' => 'Übergänge', @@ -557,7 +549,6 @@ return array( 'Unable to add this currency rate.' => 'Währungskurs konnte nicht hinzugefügt werden', 'Webhook URL' => 'Webhook-URL', '%s removed the assignee of the task %s' => '%s Zuordnung für die Aufgabe %s entfernen', - 'Enable Gravatar images' => 'Aktiviere Gravatar-Bilder', 'Information' => 'Information', 'Check two factor authentication code' => 'Prüfe Zwei-Faktor-Authentifizierungscode', 'The two factor authentication code is not valid.' => 'Der Zwei-Faktor-Authentifizierungscode ist ungültig.', @@ -572,14 +563,14 @@ return array( '%s via Kanboard' => '%s via Kanboard', 'Burndown chart' => 'Burndown-Diagramm', 'This chart show the task complexity over the time (Work Remaining).' => 'Dieses Diagramm zeigt die Aufgabenkomplexität über den Faktor Zeit (Verbleibende Arbeit).', - 'Screenshot taken %s' => 'Screenshot aufgenommen %s ', + 'Screenshot taken %s' => 'Screenshot aufgenommen %s', 'Add a screenshot' => 'Screenshot hinzufügen', 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Nimm einen Screenshot auf und drücke STRG+V oder ⌘+V um ihn hier einzufügen.', 'Screenshot uploaded successfully.' => 'Screenshot erfolgreich hochgeladen.', 'SEK - Swedish Krona' => 'SEK - Schwedische Kronen', 'Identifier' => 'Identifikator', 'Disable two factor authentication' => 'Deaktiviere Zwei-Faktor-Authentifizierung', - 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Willst du wirklich für folgenden Nutzer die Zwei-Faktor-Authentifizierung deaktivieren: "%s"?', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Willst du wirklich für folgenden Benutzer die Zwei-Faktor-Authentifizierung deaktivieren: "%s"?', 'Edit link' => 'Verbindung bearbeiten', 'Start to type task title...' => 'Beginne mit der Titeleingabe...', 'A task cannot be linked to itself' => 'Eine Aufgabe kann nicht mit sich selber verbunden werden', @@ -611,14 +602,7 @@ return array( 'When task is moved from first column' => 'Wenn Aufgabe von erster Spalte verschoben wird', 'When task is moved to last column' => 'Wenn Aufgabe in letzte Spalte verschoben wird', 'Year(s)' => 'Jahr(e)', - 'Calendar settings' => 'Kalender-Einstellungen', - 'Project calendar view' => 'Projekt-Kalendarsicht', 'Project settings' => 'Projekteinstellungen', - 'Show subtasks based on the time tracking' => 'Zeige Teilaufgaben basierend auf Zeiterfassung', - 'Show tasks based on the creation date' => 'Zeige Aufgaben basierend auf Erstelldatum', - 'Show tasks based on the start date' => 'Zeige Aufgaben basierend auf Beginndatum', - 'Subtasks time tracking' => 'Teilaufgaben-Zeiterfassung', - 'User calendar view' => 'Benutzer-Kalendersicht', 'Automatically update the start date' => 'Beginndatum automatisch aktualisieren', 'iCal feed' => 'iCal Feed', 'Preferences' => 'Einstellungen', @@ -637,7 +621,6 @@ return array( 'Notification' => 'Benachrichtigungen', '%s moved the task #%d to the first swimlane' => '%s hat die Aufgabe #%d in die erste Swimlane verschoben', 'Swimlane' => 'Swimlane', - 'Gravatar' => 'Gravatar', '%s moved the task %s to the first swimlane' => '%s hat die Aufgabe %s in die erste Swimlane verschoben', '%s moved the task %s to the swimlane "%s"' => '%s hat die Aufgaben %s in die Swimlane "%s" verschoben', 'This report contains all subtasks information for the given date range.' => 'Der Bericht beinhaltet alle Teilaufgaben im gewählten Zeitraum', @@ -674,7 +657,6 @@ return array( 'Stop timer' => 'Stoppe Timer', 'Start timer' => 'Starte Timer', 'My activity stream' => 'Aktivitätsstream', - 'My calendar' => 'Mein Kalender', 'Search tasks' => 'Suche nach Aufgaben', 'Reset filters' => 'Filter zurücksetzen', 'My tasks due tomorrow' => 'Meine morgen fälligen Aufgaben', @@ -688,7 +670,6 @@ return array( 'Overview' => 'Überblick', 'Board/Calendar/List view' => 'Board-/Kalender-/Listen-Ansicht', 'Switch to the board view' => 'Zur Board-Ansicht', - 'Switch to the calendar view' => 'Zur Kalender-Ansicht', 'Switch to the list view' => 'Zur Listen-Ansicht', 'Go to the search/filter box' => 'Zum Such- und Filterfeld', 'There is no activity yet.' => 'Es gibt bislang keine Aktivitäten.', @@ -743,15 +724,8 @@ return array( 'License:' => 'Lizenz:', 'License' => 'Lizenz', 'Enter the text below' => 'Text unten eingeben', - 'Sort by position' => 'Nach Position sortieren', - 'Sort by date' => 'Nach Datum sortieren', - 'Add task' => 'Aufgabe hinzufügen', 'Start date:' => 'Startdatum:', 'Due date:' => 'Ablaufdatum:', - 'There is no start date or due date for this task.' => 'Diese Aufgabe hat kein Start oder Ablaufdatum.', - 'Moving or resizing a task will change the start and due date of the task.' => 'Aufgabe verschieben/ändern, ändert auch Start- und Ablaufdatum der Aufgabe.', - 'There is no task in your project.' => 'Es gibt keine Aufgabe in deinem Projekt', - 'Gantt chart' => 'Gantt Diagramm', 'People who are project managers' => 'Benutzer die Projektmanager sind', 'People who are project members' => 'Benutzer die Projektmitglieder sind', 'NOK - Norwegian Krone' => 'NOK - Norwegische Kronen', @@ -763,22 +737,15 @@ return array( 'Members' => 'Mitglieder', 'Shared project' => 'Geteiltes Projekt', 'Project managers' => 'Projektmanager', - 'Gantt chart for all projects' => 'Gantt Diagramm für alle Projekte', 'Projects list' => 'Projektliste', - 'Gantt chart for this project' => 'Gantt Diagramm für dieses Projekt', - 'Project board' => 'Projekt Pinnwand', 'End date:' => 'Endedatum:', - 'There is no start date or end date for this project.' => 'Es gibt kein Startdatum oder Endedatum für dieses Projekt', - 'Projects Gantt chart' => 'Projekt Gantt Diagramm', 'Change task color when using a specific task link' => 'Aufgabefarbe ändern bei bestimmter Aufgabenverbindung', 'Task link creation or modification' => 'Aufgabenverbindung erstellen oder bearbeiten', 'Milestone' => 'Meilenstein', 'Documentation: %s' => 'Dokumentation: %s', - 'Switch to the Gantt chart view' => 'Zur Gantt-Diagramm Ansicht wechseln', 'Reset the search/filter box' => 'Suche/Filter-Box zurücksetzen', 'Documentation' => 'Dokumentation', 'Table of contents' => 'Inhaltsverzeichnis', - 'Gantt' => 'Gantt', 'Author' => 'Autor', 'Version' => 'Version', 'Plugins' => 'Plugins', @@ -889,7 +856,6 @@ return array( 'There is no user available.' => 'Es ist kein Benutzer verfügbar.', 'Do you really want to remove the user "%s" from the group "%s"?' => 'Wollen Sie den Benutzer "%s" wirklich aus der Gruppe "%s" löschen?', 'There is no group.' => 'Es gibt keine Gruppe.', - 'External Id' => 'Externe ID', 'Add group member' => 'Gruppenmitglied hinzufügen', 'Do you really want to remove this group: "%s"?' => 'Wollen Sie die Gruppe "%s" wirklich löschen?', 'There is no user in this group.' => 'Es gibt keinen Benutzer in dieser Gruppe.', @@ -910,8 +876,8 @@ return array( 'You were mentioned in the task #%d' => 'Sie wurden in der Aufgabe #%d erwähnt', 'You were mentioned in a comment on the task #%d' => 'Sie wurden in einem Kommentar zur Aufgabe #%d erwähnt', 'Estimated hours: ' => 'Erwarteter Zeitaufwand (Stunden): ', - 'Actual hours: ' => 'Tatsächlich aufgewändete Stunden: ', - 'Hours Spent' => 'Stunden aufgewändet', + 'Actual hours: ' => 'Tatsächlich aufgewendete Stunden: ', + 'Hours Spent' => 'Stunden aufgewendet', 'Hours Estimated' => 'Stunden erwartet', 'Estimated Time' => 'Erwartete Zeit', 'Actual Time' => 'Aktuelle Zeit', @@ -955,7 +921,6 @@ return array( 'Default priority' => 'Standard-Priorität', 'Lowest priority' => 'Niedrigste Priorität', 'Highest priority' => 'Höchste Priorität', - 'If you put zero to the low and high priority, this feature will be disabled.' => 'Wenn Sie Null bei höchster und niedrigster Priorität eintragen, wird diese Funktion deaktiviert.', 'Close a task when there is no activity' => 'Schliesse eine Aufgabe, wenn keine Aktivitäten vorhanden sind', 'Duration in days' => 'Dauer in Tagen', 'Send email when there is no activity on a task' => 'Versende eine Email, wenn keine Aktivitäten an einer Aufgabe vorhanden sind', @@ -1041,7 +1006,7 @@ return array( 'Upload a file' => 'Eine Datei hochladen', 'View file' => 'Datei ansehen', 'Last activity' => 'Letzte Aktivität', - 'Change subtask position' => 'Position der Unteraufgabe ändern', + 'Change subtask position' => 'Position der Teilaufgabe ändern', 'This value must be greater than %d' => 'Dieser Wert muss größer als %d sein', 'Another swimlane with the same name exists in the project' => 'Es gibt bereits eine Swimlane mit diesem Namen im Projekt', 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Beispiel: http://example.kanboard.net (wird zum Erstellen absoluter URLs genutzt)', @@ -1169,8 +1134,6 @@ return array( 'Subtasks overview for %s' => 'Teilaufgaben-Übersicht für %s', 'Projects overview for %s' => 'Projekt-Übersicht für %s', 'Activity stream for %s' => 'Aktivitätenstrom für %s', - 'Calendar for %s' => 'Kalender für %s', - 'Notifications for %s' => 'Benachrichtigungen für %s', 'Assign a color when the task is moved to a specific swimlane' => 'Einer Aufgabe eine Farbe zuweisen, wenn diese in eine bestimmte Swimlane verschoben wird', 'Assign a priority when the task is moved to a specific swimlane' => 'Einer Aufgabe eine Priorität zuweisen, wenn diese in eine bestimmte Swimlane verschoben wird', 'User unlocked successfully.' => 'Benutzer erfolgreich entsperrt.', @@ -1240,8 +1203,7 @@ return array( 'API User Access' => 'API Benutzerzugriff', 'Preview' => 'Vorschau', 'Write' => 'Schreiben', - 'Write your text in Markdown' => 'Schreiben Sie iHren Text in Markdown', - 'New External Task: %s' => 'Neue externe Aufgabe: %s', + 'Write your text in Markdown' => 'Schreiben Sie Ihren Text in Markdown', 'No personal API access token registered.' => 'Keine persönlichen API-Zugriffsinformationen registriert', 'Your personal API access token is "%s"' => 'Ihre persönlichen API-Zugriffsinformationen: "%s"', 'Remove your token' => 'Ihre Zugriffsinformationen entfernen', @@ -1309,29 +1271,99 @@ return array( 'Database uploaded successfully.' => 'Die Datenbank wurde erfolgreich hochgeladen.', 'Task sent by email successfully.' => 'Aufgabe wurde erfolgreich per E-Mail gesendet.', 'There is no category in this project.' => 'Es gibt keine Kategorie in diesem Projekt', - 'Send by email' => 'Per E-Mail gesendet', + 'Send by email' => 'Per E-Mail senden', 'Create and send a comment by email' => 'Erstellen und senden Sie einen Kommentar per E-Mail', 'Subject' => 'Betreff', 'Upload the database' => 'Datenbank hochladen', 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Sie können die zuvor heruntergeladene Sqlite-Datenbank (Gzip-Format) hochladen.', 'Database file' => 'Datenbankdatei', 'Upload' => 'Hochladen', - 'Remove this user from group' => 'Diesen Benutzer aus der Gruppe entfernen', 'Your project must have at least one active swimlane.' => 'Ihr Projekt muss mindestens eine aktive Swimlane haben.', 'Project: %s' => 'Projekt: %s', 'Automatic action not found: "%s"' => 'Automatische Aktion nicht gefunden: "%s"', - // '%d projects' => '', - // '%d project' => '', - // 'There is no project.' => '', - // 'Sort' => '', - // 'Project ID' => '', - // 'Project name' => '', - // 'Public' => '', - // 'Private' => '', - // '%d tasks' => '', - // '%d task' => '', - // 'Task ID' => '', - // 'Assign automatically a color when due date is expired' => '', - // 'Total score in this column across all swimlanes' => '', - // 'HRK - Kuna' => '', + '%d projects' => '%d Projekte', + '%d project' => '%d Projekt', + 'There is no project.' => 'Es gibt kein Projekt.', + 'Sort' => 'Sortieren', + 'Project ID' => 'Projekt ID', + 'Project name' => 'Projekt Name', + 'Public' => 'Öffentlich', + 'Private' => 'Privat', + '%d tasks' => '%d Aufgaben', + '%d task' => '%d Aufgabe', + 'Task ID' => 'Aufgaben ID', + 'Assign automatically a color when due date is expired' => 'Automatisch eine Farbe zuweisen, wenn das Fälligkeitsdatum abgelaufen ist', + 'Total score in this column across all swimlanes' => 'Gesamtpunktzahl in dieser Spalte über alle Swimlanes', + 'HRK - Kuna' => 'HRK - Kuna', + 'ARS - Argentine Peso' => 'ARS - Argentinische Peso', + 'COP - Colombian Peso' => 'COP - Kolumbianische Peso', + '%d groups' => '%d Gruppen', + '%d group' => '%d Gruppe', + 'Group ID' => 'Gruppen ID', + 'External ID' => 'Externe ID', + '%d users' => '%d Benutzer', + '%d user' => '%d Benutzer', + 'Hide subtasks' => 'Teilaufgaben verstecken', + 'Show subtasks' => 'Teilaufgaben anzeigen', + 'Authentication Parameters' => 'Authentifizierungsparameter', + 'API Access' => 'API-Zugriff', + 'No users found.' => 'Keine Benutzer gefunden.', + 'User ID' => 'Benutzer ID', + 'Notifications are activated' => 'Benachrichtigungen sind aktiviert', + 'Notifications are disabled' => 'Benachrichtigungen sind deaktiviert', + 'User disabled' => 'Benutzer deaktiviert', + '%d notifications' => '%d Benachrichtigungen', + '%d notification' => '%d Benachrichtigung', + 'There is no external integration installed.' => 'Es ist keine externe Integration installiert.', + 'You are not allowed to update tasks assigned to someone else.' => 'Sie sind nicht berechtigt, Aufgaben zu aktualisieren, die jemand anderem zugewiesen wurden.', + 'You are not allowed to change the assignee.' => 'Sie dürfen den Zuständigen nicht ändern.', + 'Task suppression is not permitted' => 'Taskausblendung ist nicht zulässig', + 'Changing assignee is not permitted' => 'Änderung des Zuständigen ist nicht zulässig', + 'Update only assigned tasks is permitted' => 'Nur zugeordnete Aufgaben dürfen aktualisiert werden', + 'Only for tasks assigned to the current user' => 'Nur für Aufgaben, die dem aktuellen Benutzer zugeordnet sind', + 'My projects' => 'Meine Projekte', + 'Your are not member of any project.' => 'Sie sind nicht Mitglied eines Projektes.', + 'My subtasks' => 'Meine Teilaufgaben', + '%d subtasks' => '%d Teilaufgaben', + '%d subtask' => '%d Teilaufgabe', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Das Bewegen einer Aufgabe zwischen diesen Spalten ist nur für Aufgaben zulässig, die dem aktuellen Benutzer zugewiesen sind', + '[DUPLICATE]' => '[DUPLIKAT]', + 'DKK - Danish Krona' => 'DKK - Dänische Krone', + 'Remove user from group' => 'Benutzer aus Gruppe löschen', + 'Assign the task to its creator' => 'Aufgabe dem Ersteller zuordnen', + 'This task was sent by email to "%s" with subject "%s".' => 'Diese Aufgabe wurde per Mail an "%s" mit dem Betreff "%s" gesendet.', + 'Predefined Email Subjects' => 'Vordefinierte E-Mail Betreffzeilen', + 'Write one subject by line.' => 'Schreibe ein Betreff pro Zeile.', + // 'Create another link' => '', + // 'BRL - Brazilian Real' => '', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/el_GR/translations.php b/app/Locale/el_GR/translations.php index bad43233..c0e1e4e1 100644 --- a/app/Locale/el_GR/translations.php +++ b/app/Locale/el_GR/translations.php @@ -39,7 +39,6 @@ return array( 'Administrator' => 'ΔιαχειÏιστής', 'Sign in' => 'Είσοδος', 'Users' => 'ΧÏήστες', - 'No user' => 'Δεν υπάÏχει χÏήστης', 'Forbidden' => 'Δεν επιτÏÎπεται η Ï€Ïόσβαση', 'Access Forbidden' => 'Δεν επιτÏÎπεται η Ï€Ïόσβαση', 'Edit user' => 'ΔιόÏθωση χÏήστη', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'Η υπο-εÏγασία ενημεÏώθηκε με επιτυχία.', 'Unable to update your sub-task.' => 'ΑδÏνατο να ενημεÏωθεί η υπο-εÏγασία.', 'Unable to create your sub-task.' => 'ΑδÏνατο να δημιουÏγηθεί η υπο-εÏγασία.', - 'Sub-task added successfully.' => 'Η υπο-εÏγασία Ï€ÏοστÎθηκε με επιτυχία.', 'Maximum size: ' => 'ΜÎγιστο μÎγεθος : ', 'Display another project' => 'Εμφάνιση άλλου ÎÏγου', 'Created by %s' => 'ΔημιουÏγήθηκε από %s', @@ -396,22 +394,17 @@ return array( 'Activity stream' => 'Ροή δÏαστηÏιότητας', 'Dashboard' => 'ΚεντÏικό ταμπλό', 'Confirmation' => 'Επιβεβαίωση', - 'Allow everybody to access to this project' => 'Îα επιτÏÎπετε σε όλους να Îχουν Ï€Ïόσβαση σε αυτό το ÎÏγο', - 'Everybody have access to this project.' => 'Όλοι Îχουν Ï€Ïόσβαση σε αυτό το ÎÏγο.', 'Webhooks' => 'Webhooks', 'API' => 'API', 'Create a comment from an external provider' => 'ΔημιουÏγήστε Îνα σχόλιο από Îναν εξωτεÏικό πάÏοχο', 'Project management' => 'ΔιαχείÏιση ÎÏγων', - 'My projects' => 'Τα ÎÏγα μου', 'Columns' => 'Στήλες', 'Task' => 'ΕÏγασία', - 'Your are not member of any project.' => 'Δεν είστε μÎλος κάποιου ÎÏγου.', 'Percentage' => 'Ποσοστό', 'Number of tasks' => 'ΑÏιθμός εÏγασιών', 'Task distribution' => 'Κατανομή εÏγασιών', 'Analytics' => 'ΑναλÏσεις', 'Subtask' => 'Υπο-ΕÏγασία', - 'My subtasks' => 'Οι υπο-εÏγασίες μου', 'User repartition' => 'Επαναλήψεις χÏηστών', 'Clone this project' => 'Κλωνοποίηση ÎÏγου', 'Column removed successfully.' => 'Η στήλη αφαιÏÎθηκε με επιτυχία.', @@ -459,7 +452,6 @@ return array( 'Language:' => 'Γλώσσα:', 'Timezone:' => 'Timezone:', 'All columns' => 'Όλες οι στήλες', - 'Calendar' => 'ΗμεÏολόγιο', 'Next' => 'Επόμενο', '#%d' => 'nËš%d', 'All swimlanes' => 'Όλες οι λωÏίδες', @@ -557,7 +549,6 @@ return array( 'Unable to add this currency rate.' => 'ΑδÏνατο να Ï€Ïοστεθεί αυτή η ισοτιμία.', 'Webhook URL' => 'Webhook URL', '%s removed the assignee of the task %s' => '%s αφαίÏεσε τον εκδοχÎα της εÏγασίας %s', - 'Enable Gravatar images' => 'ΕνεÏγοποίηση εικόνων Gravatar', 'Information' => 'ΠληÏοφοÏίες', 'Check two factor authentication code' => 'ΕλÎγξτε δÏο παÏάγοντες ελÎγχου ταυτότητας κωδικοÏ', 'The two factor authentication code is not valid.' => 'Ο κωδικός ελÎγχου ταυτότητας δÏο παÏαγόντων δεν είναι σωστός.', @@ -611,14 +602,7 @@ return array( 'When task is moved from first column' => 'Όταν το ÎÏγο Îχει μετακινηθεί στην 1η στήλη', 'When task is moved to last column' => 'Όταν το ÎÏγο Îχει μετακινηθεί στην τελευταία στήλη', 'Year(s)' => 'ΧÏόνος(οι)', - 'Calendar settings' => 'Ρυθμίσεις ημεÏολογίου', - 'Project calendar view' => 'Î Ïοβολή ημεÏολογίου ÎÏγων', 'Project settings' => 'Ρυθμίσεις ÎÏγου', - 'Show subtasks based on the time tracking' => 'Εμφάνιση υπο-εÏγασίων με βάση την παÏακολοÏθηση του χÏόνου', - 'Show tasks based on the creation date' => 'Εμφάνιση ÎÏγων με βάση την ημεÏομηνία δημιουÏγίας', - 'Show tasks based on the start date' => 'Εμφάνιση ÎÏγων με βάση την ημεÏομηνία δημιουÏγίας ', - 'Subtasks time tracking' => 'ΠαÏακολοÏθηση χÏόνου υπο-εÏγασίων', - 'User calendar view' => 'Î Ïοβολή του ημεÏολογίου του χÏήστη', 'Automatically update the start date' => 'Αυτόματη ενημÎÏωση της ημεÏομηνίας ÎναÏξης', 'iCal feed' => 'iCal feed', 'Preferences' => 'Î Ïοτιμήσεις', @@ -637,7 +621,6 @@ return array( 'Notification' => 'Κοινοποίηση', '%s moved the task #%d to the first swimlane' => '%s μετÎφεÏε την εÏγασία n°%d στην 1η λωÏίδα', 'Swimlane' => 'ΛωÏίδα', - 'Gravatar' => 'Gravatar', '%s moved the task %s to the first swimlane' => '%s μετÎφεÏε την εÏγασία %s στην 1η λωÏίδα', '%s moved the task %s to the swimlane "%s"' => '%s μετÎφεÏε την εÏγασία %s στη λωÏίδα « %s »', 'This report contains all subtasks information for the given date range.' => 'Η Îκθεση αυτή πεÏιÎχει όλες τις υπο-εÏγασίες για το συγκεκÏιμÎνο εÏÏος ημεÏομηνιών.', @@ -674,7 +657,6 @@ return array( 'Stop timer' => 'Διακοπή ÏολογιοÏ', 'Start timer' => 'ΈναÏξη ÏολογιοÏ', 'My activity stream' => 'Η Ïοή δÏαστηÏιοτήτων μου', - 'My calendar' => 'Το ημεÏολόγιο μου', 'Search tasks' => 'Αναζήτηση εÏγασιών', 'Reset filters' => 'ΕπαναφοÏά φίλτÏων', 'My tasks due tomorrow' => 'Οι εÏγασίες καθηκόντων μου αÏÏιο', @@ -688,7 +670,6 @@ return array( 'Overview' => 'Επισκόπηση', 'Board/Calendar/List view' => 'Πίνακας / ΗμεÏολόγιο / Î Ïοβολή λίστας', 'Switch to the board view' => 'Εναλλαγή στην Ï€Ïοβολή του πίνακα', - 'Switch to the calendar view' => 'Εναλλαγή στην Ï€Ïοβολή ημεÏολογίου', 'Switch to the list view' => 'Εναλλαγή στην Ï€Ïοβολή λίστας', 'Go to the search/filter box' => 'Μετάβαση στο πλαίσιο αναζήτησης / φίλτÏο', 'There is no activity yet.' => 'Δεν υπάÏχει καμία δÏαστηÏιότητα ακόμα.', @@ -743,15 +724,8 @@ return array( 'License:' => 'Άδεια:', 'License' => 'Άδεια', 'Enter the text below' => 'ΠληκτÏολογήστε το παÏακάτω κείμενο', - 'Sort by position' => 'Ταξινόμηση κατά ΘÎση', - 'Sort by date' => 'Ταξινόμηση κατά ημÎÏα', - 'Add task' => 'Î Ïοσθήκη εÏγασίας', 'Start date:' => 'ΗμεÏομηνία εκκίνησης :', 'Due date:' => 'ΗμεÏομηνία λήξης:', - 'There is no start date or due date for this task.' => 'Δεν υπάÏχει ημεÏομηνία ÎναÏξης ή ημεÏομηνία λήξης καθηκόντων για το ÎÏγο αυτό.', - 'Moving or resizing a task will change the start and due date of the task.' => 'Μετακίνηση ή αλλαγή μεγÎθους μιας εÏγασίας θα αλλάξει την ÏŽÏα ÎναÏξης και ημεÏομηνία λήξης της εÏγασίας.', - 'There is no task in your project.' => 'Δεν υπάÏχει καμία εÏγασία στο ÎÏγο σας.', - 'Gantt chart' => 'ΔιαγÏάμματα Gantt', 'People who are project managers' => 'Οι άνθÏωποι που είναι οι διευθυντÎÏ‚ ÎÏγων', 'People who are project members' => 'Οι άνθÏωποι που είναι μÎλη των ÎÏγων', 'NOK - Norwegian Krone' => 'NOK - Norwegian Krone', @@ -763,22 +737,15 @@ return array( 'Members' => 'ΜÎλη', 'Shared project' => 'ΚοινόχÏηστο ÎÏγο', 'Project managers' => 'ΔιευθυντÎÏ‚ ÎÏγου', - 'Gantt chart for all projects' => 'ΔιάγÏαμμα Gantt για όλα τα ÎÏγα', 'Projects list' => 'Λίστα ÎÏγων', - 'Gantt chart for this project' => 'ΔιάγÏαμμα Gantt για το ÎÏγο', - 'Project board' => 'ΚεντÏικός πίνακας ÎÏγου', 'End date:' => 'ΗμεÏομηνία λήξης :', - 'There is no start date or end date for this project.' => 'Δεν υπάÏχει ημεÏομηνία ÎναÏξης ή λήξης για το ÎÏγο αυτό.', - 'Projects Gantt chart' => 'ΔιάγÏαμμα Gantt ÎÏγων', 'Change task color when using a specific task link' => 'Αλλαγή χÏώματος εÏγασίας χÏησιμοποιώντας συγκεκÏιμÎνο σÏνδεσμο εÏγασίας', 'Task link creation or modification' => 'ΣÏνδεσμος δημιουÏγίας ή Ï„Ïοποποίησης εÏγασίας', 'Milestone' => 'ΟÏόσημο', 'Documentation: %s' => 'ΤεκμηÏίωση: %s', - 'Switch to the Gantt chart view' => 'ΜεταφοÏά σε Ï€Ïοβολή διαγÏάμματος Gantt', 'Reset the search/filter box' => 'ΑÏχικοποίηση του πεδίου αναζήτησης/φιλτÏαÏίσματος', 'Documentation' => 'ΤεκμηÏίωση', 'Table of contents' => 'Πίνακας πεÏιεχομÎνων', - 'Gantt' => 'Gantt', 'Author' => 'ΔημιουÏγός', 'Version' => 'Έκδοση', 'Plugins' => 'Î Ïόσθετα', @@ -889,7 +856,6 @@ return array( 'There is no user available.' => 'Δεν υπάÏχει διαθÎσιμος χÏήστης', 'Do you really want to remove the user "%s" from the group "%s"?' => 'ΑφαίÏεση του χÏήστη « %s » από την ομάδα « %s » ?', 'There is no group.' => 'Δεν υπάÏχει ομάδα.', - 'External Id' => 'ΕξωτεÏικό αναγνωÏιστικό', 'Add group member' => 'Î Ïοσθήκη μÎλους ομάδας', 'Do you really want to remove this group: "%s"?' => 'ΑφαίÏεση της ομάδας: « %s » ?', 'There is no user in this group.' => 'Δεν υπάÏχει χÏήστης σε αυτήν την ομάδα', @@ -955,7 +921,6 @@ return array( 'Default priority' => 'Εξ οÏÎ¹ÏƒÎ¼Î¿Ï Ï€ÏοτεÏαιότητα', 'Lowest priority' => 'η χαμηλότεÏη Ï€ÏοτεÏαιότητα', 'Highest priority' => 'η υψηλότεÏη Ï€ÏοτεÏαιότητα', - 'If you put zero to the low and high priority, this feature will be disabled.' => 'Αν βάλετε μηδÎν στη χαμηλή και στην υψηλή Ï€ÏοτεÏαιότητα, το χαÏακτηÏιστικό αυτό απενεÏγοποιείται.', 'Close a task when there is no activity' => 'Κλείσιμο εÏγασίας όταν δεν υπάÏχει δÏαστηÏιότητα', 'Duration in days' => 'ΔιάÏκεια σε ημÎÏες', 'Send email when there is no activity on a task' => 'Αποστολή email όταν δεν υπάÏχει δÏαστηÏιότητα σε εÏγασία', @@ -1169,8 +1134,6 @@ return array( // 'Subtasks overview for %s' => '', // 'Projects overview for %s' => '', // 'Activity stream for %s' => '', - // 'Calendar for %s' => '', - // 'Notifications for %s' => '', // 'Assign a color when the task is moved to a specific swimlane' => '', // 'Assign a priority when the task is moved to a specific swimlane' => '', // 'User unlocked successfully.' => '', @@ -1241,7 +1204,6 @@ return array( // 'Preview' => '', // 'Write' => '', // 'Write your text in Markdown' => '', - // 'New External Task: %s' => '', // 'No personal API access token registered.' => '', // 'Your personal API access token is "%s"' => '', // 'Remove your token' => '', @@ -1316,7 +1278,6 @@ return array( // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', // 'Database file' => '', // 'Upload' => '', - // 'Remove this user from group' => '', // 'Your project must have at least one active swimlane.' => '', // 'Project: %s' => '', // 'Automatic action not found: "%s"' => '', @@ -1334,4 +1295,75 @@ return array( // 'Assign automatically a color when due date is expired' => '', // 'Total score in this column across all swimlanes' => '', // 'HRK - Kuna' => '', + // 'ARS - Argentine Peso' => '', + // 'COP - Colombian Peso' => '', + // '%d groups' => '', + // '%d group' => '', + // 'Group ID' => '', + // 'External ID' => '', + // '%d users' => '', + // '%d user' => '', + // 'Hide subtasks' => '', + // 'Show subtasks' => '', + // 'Authentication Parameters' => '', + // 'API Access' => '', + // 'No users found.' => '', + // 'User ID' => '', + // 'Notifications are activated' => '', + // 'Notifications are disabled' => '', + // 'User disabled' => '', + // '%d notifications' => '', + // '%d notification' => '', + // 'There is no external integration installed.' => '', + // 'You are not allowed to update tasks assigned to someone else.' => '', + // 'You are not allowed to change the assignee.' => '', + // 'Task suppression is not permitted' => '', + // 'Changing assignee is not permitted' => '', + // 'Update only assigned tasks is permitted' => '', + // 'Only for tasks assigned to the current user' => '', + // 'My projects' => '', + // 'Your are not member of any project.' => '', + // 'My subtasks' => '', + // '%d subtasks' => '', + // '%d subtask' => '', + // 'Only moving task between those columns is permitted for tasks assigned to the current user' => '', + // '[DUPLICATE]' => '', + // 'DKK - Danish Krona' => '', + // 'Remove user from group' => '', + // 'Assign the task to its creator' => '', + // 'This task was sent by email to "%s" with subject "%s".' => '', + // 'Predefined Email Subjects' => '', + // 'Write one subject by line.' => '', + // 'Create another link' => '', + // 'BRL - Brazilian Real' => '', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/es_ES/translations.php b/app/Locale/es_ES/translations.php index 8f76d92c..0d01ee7a 100644 --- a/app/Locale/es_ES/translations.php +++ b/app/Locale/es_ES/translations.php @@ -1,8 +1,8 @@ <?php return array( - // 'number.decimals_separator' => '', - // 'number.thousands_separator' => '', + 'number.decimals_separator' => ',', + 'number.thousands_separator' => '.', 'None' => 'Ninguno', 'Edit' => 'Modificar', 'Remove' => 'Suprimir', @@ -17,15 +17,15 @@ return array( 'Red' => 'Rojo', 'Orange' => 'Naranja', 'Grey' => 'Gris', - // 'Brown' => '', - // 'Deep Orange' => '', - // 'Dark Grey' => '', - // 'Pink' => '', - // 'Teal' => '', - // 'Cyan' => '', - // 'Lime' => '', - // 'Light Green' => '', - // 'Amber' => '', + 'Brown' => 'Marron', + 'Deep Orange' => 'Naranja', + 'Dark Grey' => 'Gris Oscuro', + 'Pink' => 'Rosado', + 'Teal' => 'Verde Azulado', + 'Cyan' => 'Azul Claro', + 'Lime' => 'Lima', + 'Light Green' => 'Verde Claro', + 'Amber' => 'Ambar', 'Save' => 'Guardar', 'Login' => 'Iniciar sesión (Ingresar)', 'Official website:' => 'Página web oficial :', @@ -39,7 +39,6 @@ return array( 'Administrator' => 'Administrador', 'Sign in' => 'Iniciar sesión', 'Users' => 'Usuarios', - 'No user' => 'Ningún usuario', 'Forbidden' => 'Acceso denegado', 'Access Forbidden' => 'Acceso denegado', 'Edit user' => 'Editar un usuario', @@ -153,7 +152,7 @@ return array( 'Comment is required' => 'El comentario es obligatorio', 'Comment added successfully.' => 'El comentario ha sido añadido correctamente.', 'Unable to create your comment.' => 'No se puede crear este comentario.', - 'Due Date' => 'Fecha lÃmite', + 'Due Date' => 'Fecha LÃmite', 'Invalid date' => 'Fecha no válida', 'Automatic actions' => 'Acciones automatizadas', 'Your automatic action have been created successfully.' => 'La acción automatizada ha sido creada correctamente.', @@ -182,7 +181,7 @@ return array( 'Position' => 'Posición', 'Duplicate to another project' => 'Duplicar a otro proyecto', 'Duplicate' => 'Duplicar', - // 'Link' => '', + 'Link' => 'Enlace', 'Comment updated successfully.' => 'El comentario ha sido actualizado correctamente.', 'Unable to update your comment.' => 'No se puede actualizar este comentario.', 'Remove a comment' => 'Suprimir un comentario', @@ -208,15 +207,15 @@ return array( 'Closed' => 'Cerrado', 'Search' => 'Buscar', 'Nothing found.' => 'Nada hallado.', - 'Due date' => 'Fecha lÃmite', + 'Due date' => 'Fecha LÃmite', 'Description' => 'Descripción', '%d comments' => '%d comentarios', '%d comment' => '%d comentario', 'Email address invalid' => 'Dirección de correo inválida', - // 'Your external account is not linked anymore to your profile.' => '', - // 'Unable to unlink your external account.' => '', - // 'External authentication failed' => '', - // 'Your external account is linked to your profile successfully.' => '', + 'Your external account is not linked anymore to your profile.' => 'Tu cuenta externa no está vinculada a tu perfil.', + 'Unable to unlink your external account.' => 'No se puede desvincular su cuenta externa.', + 'External authentication failed' => 'Error de autenticación externa', + 'Your external account is linked to your profile successfully.' => 'Su cuenta externa está vinculada a su perfil correctamente.', 'Email' => 'Correo', 'Task removed successfully.' => 'Tarea suprimida correctamente.', 'Unable to remove this task.' => 'No pude suprimir esta tarea.', @@ -229,8 +228,8 @@ return array( 'Category:' => 'CategorÃa:', 'Categories' => 'CategorÃas', 'Your category have been created successfully.' => 'Se ha creado tu categorÃa correctamente.', - // 'This category has been updated successfully.' => '', - // 'Unable to update this category.' => '', + 'This category has been updated successfully.' => 'Esta categorÃa se ha actualizado correctamente.', + 'Unable to update this category.' => 'No se puede actualizar esta categorÃa.', 'Remove a category' => 'Suprimir una categorÃa', 'Category removed successfully.' => 'CategorÃa suprimida correctamente.', 'Unable to remove this category.' => 'No pude suprimir esta categorÃa.', @@ -254,37 +253,36 @@ return array( 'Time tracking' => 'Control de tiempo', 'Estimate:' => 'Estimado:', 'Spent:' => 'Transcurrido:', - 'Do you really want to remove this sub-task?' => '¿Realmente quiere suprimir esta sub-tarea?', + 'Do you really want to remove this sub-task?' => '¿Realmente quiere suprimir esta subtarea?', 'Remaining:' => 'Quedando', 'hours' => 'horas', 'spent' => 'transcurrido', 'estimated' => 'estimado', 'Sub-Tasks' => 'Sub-Tareas', - 'Add a sub-task' => 'Añadir una sub-tarea', - 'Original estimate' => 'Estimado Original', - 'Create another sub-task' => 'Crear otra sub-tarea', - 'Time spent' => 'Tiempo Transcurrido', - 'Edit a sub-task' => 'Editar una sub-tarea', - 'Remove a sub-task' => 'Suprimir una sub-tarea', + 'Add a sub-task' => 'Añadir una subtarea', + 'Original estimate' => 'Horas Presupuestadas', + 'Create another sub-task' => 'Crear otra subtarea', + 'Time spent' => 'Horas Ejecutadas', + 'Edit a sub-task' => 'Editar una subtarea', + 'Remove a sub-task' => 'Suprimir una subtarea', 'The time must be a numeric value' => 'El tiempo debe de ser un valor numérico', 'Todo' => 'Por hacer', 'In progress' => 'En progreso', 'Sub-task removed successfully.' => 'Sub-tarea suprimida correctamente.', - 'Unable to remove this sub-task.' => 'No pude suprimir esta sub-tarea.', + 'Unable to remove this sub-task.' => 'No pude suprimir esta subtarea.', 'Sub-task updated successfully.' => 'Sub-tarea actualizada correctamente.', - 'Unable to update your sub-task.' => 'No pude actualizar tu sub-tarea.', - 'Unable to create your sub-task.' => 'No pude crear tu sub-tarea.', - 'Sub-task added successfully.' => 'Sub-tarea añadida correctamente.', + 'Unable to update your sub-task.' => 'No pude actualizar tu subtarea.', + 'Unable to create your sub-task.' => 'No pude crear tu subtarea.', 'Maximum size: ' => 'Tamaño máximo', 'Display another project' => 'Mostrar otro proyecto', 'Created by %s' => 'Creado por %s', 'Tasks Export' => 'Exportar tareas', - 'Start Date' => 'Fecha de inicio', + 'Start Date' => 'Fecha de Inicio', 'Execute' => 'Ejecutar', 'Task Id' => 'ID de tarea', 'Creator' => 'Creador', 'Modification date' => 'Fecha de modificación', - 'Completion date' => 'Fecha de terminación', + 'Completion date' => 'Fecha de Fin', 'Clone' => 'Clonar', 'Project cloned successfully.' => 'Proyecto clonado correctamente', 'Unable to clone this project.' => 'Impsible clonar proyecto', @@ -319,8 +317,8 @@ return array( 'Remote' => 'Remota', 'Enabled' => 'Activada', 'Disabled' => 'Deactivada', - // 'Login:' => '', - // 'Full Name:' => '', + 'Login:' => 'Login:', + 'Full Name:' => 'Nombre completo:', 'Email:' => 'Correo electrónico:', 'Notifications:' => 'Notificaciones:', 'Notifications' => 'Notificaciones', @@ -355,12 +353,12 @@ return array( '%s updated the task #%d' => '%s actualizó la tarea #%d', '%s created the task #%d' => '%s creó la tarea #%d', '%s closed the task #%d' => '%s cerró la tarea #%d', - // '%s opened the task #%d' => '', + '%s opened the task #%d' => '%s abrió la tarea #%d', 'Activity' => 'Actividad', 'Default values are "%s"' => 'Los valores por defecto son "%s"', 'Default columns for new projects (Comma-separated)' => 'Columnas por defecto de los nuevos proyectos (Separadas mediante comas)', 'Task assignee change' => 'Cambiar persona asignada a la tarea', - // '%s changed the assignee of the task #%d to %s' => '', + '%s changed the assignee of the task #%d to %s' => '%s cambió al asignado de la tarea #%d a %s', '%s changed the assignee of the task %s to %s' => '%s cambió la persona asignada de la tarea de %s a %s', 'New password for the user "%s"' => 'Nueva contraseña para el usuario "%s"', 'Choose an event' => 'Escoja un evento', @@ -390,28 +388,23 @@ return array( 'This project is private' => 'Este proyecto es privado', 'Add' => 'Añadir', 'Start date' => 'Fecha de inicio', - 'Time estimated' => 'Tiempo estimado', + 'Time estimated' => 'Horas Presupuestadas', 'There is nothing assigned to you.' => 'Esto no le está asignado', 'My tasks' => 'Mis tareas', 'Activity stream' => 'Flujo de actividad', 'Dashboard' => 'Tablero', 'Confirmation' => 'Confirmación', - 'Allow everybody to access to this project' => 'Permitir a cualquier acceder a este proyecto', - 'Everybody have access to this project.' => 'Cualquier tiene acceso a este proyecto', 'Webhooks' => 'Webhooks', 'API' => 'API', 'Create a comment from an external provider' => 'Crear un comentario a partir de un proveedor externo', - 'Project management' => 'Administración del proyecto', - 'My projects' => 'Mis proyectos', + 'Project management' => 'Gestión de proyectos', 'Columns' => 'Columnas', 'Task' => 'Tarea', - 'Your are not member of any project.' => 'No es miembro de ningún proyecto', 'Percentage' => 'Porcentaje', 'Number of tasks' => 'Número de tareas', 'Task distribution' => 'Distribución de tareas', 'Analytics' => 'AnalÃtica', 'Subtask' => 'Subtarea', - 'My subtasks' => 'Mis subtareas', 'User repartition' => 'Repartición de usuarios', 'Clone this project' => 'Clonar este proyecto', 'Column removed successfully.' => 'Columna removida correctamente', @@ -429,7 +422,7 @@ return array( 'This value must be numeric' => 'Este valor debe ser numérico', 'Unable to create this task.' => 'Imposible crear esta tarea', 'Cumulative flow diagram' => 'Diagrama de flujo acumulativo', - 'Daily project summary' => 'Sumario diario del proyecto', + 'Daily project summary' => 'Resumen diario del proyecto', 'Daily project summary export' => 'Exportar sumario diario del proyecto', 'Exports' => 'Exportar', 'This export contains the number of tasks per column grouped per day.' => 'Esta exportación contiene el número de tereas por columna agrupada por dÃa', @@ -446,7 +439,7 @@ return array( 'Unable to remove this swimlane.' => 'Imposible remover este carril', 'Unable to update this swimlane.' => 'Imposible actualizar este carril', 'Your swimlane have been created successfully.' => 'Su carril ha sido creado correctamente', - 'Example: "Bug, Feature Request, Improvement"' => 'Ejemplo: "Error, Solicitud de caracterÃstica, Mejora', + 'Example: "Bug, Feature Request, Improvement"' => 'Ejemplo: "Error, Solicitud de caracterÃstica, Mejora"', 'Default categories for new projects (Comma-separated)' => 'CategorÃas por defecto de los nuevos proyectos (Separadas mediante comas)', 'Integrations' => 'Integraciones', 'Integration with third-party services' => 'Integraciones para servicios de terceros', @@ -459,9 +452,8 @@ return array( 'Language:' => 'Idioma', 'Timezone:' => 'Zona horaria', 'All columns' => 'Todas las columnas', - 'Calendar' => 'Calendario', 'Next' => 'Siguiente', - // '#%d' => '', + '#%d' => '#%d', 'All swimlanes' => 'Todos los carriles', 'All colors' => 'Todos los colores', 'Moved to column %s' => 'Movido a columna %s', @@ -474,12 +466,12 @@ return array( 'Time Tracking' => 'Control de Tiempo', 'You already have one subtask in progress' => 'Ya tiene una subtarea en progreso', 'Which parts of the project do you want to duplicate?' => '¿Que partes del proyecto quiere duplicar?', - // 'Disallow login form' => '', + 'Disallow login form' => 'Deshabilitar el formulario de inicio de sesión', 'Start' => 'Inicio', 'End' => 'Fin', 'Task age in days' => 'Edad de la tarea en dÃas', 'Days in this column' => 'DÃas en esta columna', - // '%dd' => '', + '%dd' => '%dd', 'Add a new link' => 'Añadir nuevo vÃnculo', 'Do you really want to remove this link: "%s"?' => '¿Realmente quiere suprimir este vÃnculo: "%s"?', 'Do you really want to remove this link with task #%d?' => '¿Realmente quiere suprimir este vÃnculo con la tarea #%d?', @@ -510,8 +502,8 @@ return array( 'fixes' => 'arregla', 'is fixed by' => 'arreglado por', 'This task' => 'Esta tarea', - // '<1h' => '', - // '%dh' => '', + '<1h' => '<1h', + '%dh' => '%dh', 'Expand tasks' => 'Expandir tareas', 'Collapse tasks' => 'Colapsar tareas', 'Expand/collapse tasks' => 'Expandir/colapsar tareas', @@ -519,7 +511,7 @@ return array( 'Submit a form' => 'Enviar un formulario', 'Board view' => 'Vista de tablero', 'Keyboard shortcuts' => 'Atajos del teclado', - // 'Open board switcher' => '', + 'Open board switcher' => 'Conmutador de tablero abierto', 'Application' => 'Aplicación', 'Compact view' => 'Vista compacta', 'Horizontal scrolling' => 'Desplazamiento horizontal', @@ -531,21 +523,21 @@ return array( 'CHF - Swiss Francs' => 'CHF - Franco suizo', 'Custom Stylesheet' => 'Hoja de estilo personalizada', 'download' => 'descargar', - // 'EUR - Euro' => '', + 'EUR - Euro' => 'EUR - Euro', 'GBP - British Pound' => 'GBP - Libra británica', 'INR - Indian Rupee' => 'INR - Rupia india', 'JPY - Japanese Yen' => 'JPY - Yen japonés', 'NZD - New Zealand Dollar' => 'NZD - Dólar de Nueva Zelanda', 'RSD - Serbian dinar' => 'RSD - Dinar serbio', - // 'CNY - Chinese Yuan' => '', + 'CNY - Chinese Yuan' => 'CNY - Yuan Chino', 'USD - US Dollar' => 'USD - Dólar estadounidense', 'Destination column' => 'Columna destino', - 'Move the task to another column when assigned to a user' => 'Mover la tarea a otra columan cuando sea asignada a un usuario', + 'Move the task to another column when assigned to a user' => 'Mover la tarea a otra columna cuando sea asignada a un usuario', 'Move the task to another column when assignee is cleared' => 'Mover la tarea a otra columna cuando se elimine la persona asignada', 'Source column' => 'Columna de origen', 'Transitions' => 'Transiciones', 'Executer' => 'Ejecutor', - 'Time spent in the column' => 'Tiempo transcurrido en la columna', + 'Time spent in the column' => 'Horas Ejecutadas en la columna', 'Task transitions' => 'Transiciones de las tareas', 'Task transitions export' => 'Exportar transiciones de las tareas', 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Este reporte contiene todos los movimientos de columna por cada tarea con la fecha, el usuario y el tiempo transcurrido para cada transición', @@ -556,8 +548,7 @@ return array( 'The currency rate have been added successfully.' => 'El tipo de cambio ha sido añadido correctamente.', 'Unable to add this currency rate.' => 'No se puede añadir este tipo de cambio.', 'Webhook URL' => 'URL del Webhook', - // '%s removed the assignee of the task %s' => '', - 'Enable Gravatar images' => 'Activar imágenes Gravatar', + '%s removed the assignee of the task %s' => '%s eliminó al asignado de la tarea %s', 'Information' => 'Información', 'Check two factor authentication code' => 'Verificar el código de autenticación de dos factores', 'The two factor authentication code is not valid.' => 'El código de autenticación de dos factores no es válido', @@ -570,768 +561,809 @@ return array( 'Test your device' => 'Pruebe su dispositivo', 'Assign a color when the task is moved to a specific column' => 'Asignar un color cuando la tarea se mueve a una columna especÃfica', '%s via Kanboard' => '%s vÃa Kanboard', - // 'Burndown chart' => '', - // 'This chart show the task complexity over the time (Work Remaining).' => '', + 'Burndown chart' => 'Gráfico de Tareas Pendientes', + 'This chart show the task complexity over the time (Work Remaining).' => 'Este gráfico muestra la complejidad de la tarea durante el tiempo (Trabajo restante)', 'Screenshot taken %s' => 'Captura de pantalla tomada %s', 'Add a screenshot' => 'Añadir una captura de pantalla', 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Tomar una captura de pantalla y presione CTRL+V o ⌘+V para pegar aquÃ.', 'Screenshot uploaded successfully.' => 'Captura de pantalla subida correctamente', 'SEK - Swedish Krona' => 'SEK - Corona sueca', - // 'Identifier' => '', - // 'Disable two factor authentication' => '', - // 'Do you really want to disable the two factor authentication for this user: "%s"?' => '', - // 'Edit link' => '', - // 'Start to type task title...' => '', - // 'A task cannot be linked to itself' => '', - // 'The exact same link already exists' => '', - // 'Recurrent task is scheduled to be generated' => '', - // 'Score' => '', - // 'The identifier must be unique' => '', - // 'This linked task id doesn\'t exists' => '', - // 'This value must be alphanumeric' => '', - // 'Edit recurrence' => '', - // 'Generate recurrent task' => '', - // 'Trigger to generate recurrent task' => '', - // 'Factor to calculate new due date' => '', - // 'Timeframe to calculate new due date' => '', - // 'Base date to calculate new due date' => '', - // 'Action date' => '', - // 'Base date to calculate new due date: ' => '', - // 'This task has created this child task: ' => '', - // 'Day(s)' => '', - // 'Existing due date' => '', - // 'Factor to calculate new due date: ' => '', - // 'Month(s)' => '', - // 'Recurrence' => '', - // 'This task has been created by: ' => '', - // 'Recurrent task has been generated:' => '', - // 'Timeframe to calculate new due date: ' => '', - // 'Trigger to generate recurrent task: ' => '', - // 'When task is closed' => '', - // 'When task is moved from first column' => '', - // 'When task is moved to last column' => '', - // 'Year(s)' => '', - // 'Calendar settings' => '', - // 'Project calendar view' => '', - // 'Project settings' => '', - // 'Show subtasks based on the time tracking' => '', - // 'Show tasks based on the creation date' => '', - // 'Show tasks based on the start date' => '', - // 'Subtasks time tracking' => '', - // 'User calendar view' => '', - // 'Automatically update the start date' => '', - // 'iCal feed' => '', - // 'Preferences' => '', - // 'Security' => '', - // 'Two factor authentication disabled' => '', - // 'Two factor authentication enabled' => '', - // 'Unable to update this user.' => '', - // 'There is no user management for private projects.' => '', - // 'User that will receive the email' => '', - // 'Email subject' => '', - // 'Date' => '', - // 'Add a comment log when moving the task between columns' => '', - // 'Move the task to another column when the category is changed' => '', - // 'Send a task by email to someone' => '', - // 'Reopen a task' => '', - // 'Notification' => '', - // '%s moved the task #%d to the first swimlane' => '', - // 'Swimlane' => '', - // 'Gravatar' => '', - // '%s moved the task %s to the first swimlane' => '', - // '%s moved the task %s to the swimlane "%s"' => '', - // 'This report contains all subtasks information for the given date range.' => '', - // 'This report contains all tasks information for the given date range.' => '', - // 'Project activities for %s' => '', - // 'view the board on Kanboard' => '', - // 'The task have been moved to the first swimlane' => '', - // 'The task have been moved to another swimlane:' => '', - // 'New title: %s' => '', - // 'The task is not assigned anymore' => '', - // 'New assignee: %s' => '', - // 'There is no category now' => '', - // 'New category: %s' => '', - // 'New color: %s' => '', - // 'New complexity: %d' => '', - // 'The due date have been removed' => '', - // 'There is no description anymore' => '', - // 'Recurrence settings have been modified' => '', - // 'Time spent changed: %sh' => '', - // 'Time estimated changed: %sh' => '', - // 'The field "%s" have been updated' => '', - // 'The description has been modified:' => '', - // 'Do you really want to close the task "%s" as well as all subtasks?' => '', - // 'I want to receive notifications for:' => '', - // 'All tasks' => '', - // 'Only for tasks assigned to me' => '', - // 'Only for tasks created by me' => '', - // 'Only for tasks created by me and assigned to me' => '', - // '%%Y-%%m-%%d' => '', - // 'Total for all columns' => '', - // 'You need at least 2 days of data to show the chart.' => '', - // '<15m' => '', - // '<30m' => '', - // 'Stop timer' => '', - // 'Start timer' => '', - // 'My activity stream' => '', - // 'My calendar' => '', - // 'Search tasks' => '', - // 'Reset filters' => '', - // 'My tasks due tomorrow' => '', - // 'Tasks due today' => '', - // 'Tasks due tomorrow' => '', - // 'Tasks due yesterday' => '', - // 'Closed tasks' => '', - // 'Open tasks' => '', - // 'Not assigned' => '', - // 'View advanced search syntax' => '', - // 'Overview' => '', - // 'Board/Calendar/List view' => '', - // 'Switch to the board view' => '', - // 'Switch to the calendar view' => '', - // 'Switch to the list view' => '', - // 'Go to the search/filter box' => '', - // 'There is no activity yet.' => '', - // 'No tasks found.' => '', - // 'Keyboard shortcut: "%s"' => '', - // 'List' => '', - // 'Filter' => '', - // 'Advanced search' => '', - // 'Example of query: ' => '', - // 'Search by project: ' => '', - // 'Search by column: ' => '', - // 'Search by assignee: ' => '', - // 'Search by color: ' => '', - // 'Search by category: ' => '', - // 'Search by description: ' => '', - // 'Search by due date: ' => '', - // 'Average time spent into each column' => '', - // 'Average time spent' => '', - // 'This chart show the average time spent into each column for the last %d tasks.' => '', - // 'Average Lead and Cycle time' => '', - // 'Average lead time: ' => '', - // 'Average cycle time: ' => '', - // 'Cycle Time' => '', - // 'Lead Time' => '', - // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', - // 'Average time into each column' => '', - // 'Lead and cycle time' => '', - // 'Lead time: ' => '', - // 'Cycle time: ' => '', - // 'Time spent into each column' => '', - // 'The lead time is the duration between the task creation and the completion.' => '', - // 'The cycle time is the duration between the start date and the completion.' => '', - // 'If the task is not closed the current time is used instead of the completion date.' => '', - // 'Set automatically the start date' => '', - // 'Edit Authentication' => '', - // 'Remote user' => '', - // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', - // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', - // 'Default task color' => '', - // 'This feature does not work with all browsers.' => '', - // 'There is no destination project available.' => '', - // 'Trigger automatically subtask time tracking' => '', - // 'Include closed tasks in the cumulative flow diagram' => '', - // 'Current swimlane: %s' => '', - // 'Current column: %s' => '', - // 'Current category: %s' => '', - // 'no category' => '', - // 'Current assignee: %s' => '', - // 'not assigned' => '', - // 'Author:' => '', - // 'contributors' => '', - // 'License:' => '', - // 'License' => '', - // 'Enter the text below' => '', - // 'Sort by position' => '', - // 'Sort by date' => '', - // 'Add task' => '', - // 'Start date:' => '', - // 'Due date:' => '', - // 'There is no start date or due date for this task.' => '', - // 'Moving or resizing a task will change the start and due date of the task.' => '', - // 'There is no task in your project.' => '', - // 'Gantt chart' => '', - // 'People who are project managers' => '', - // 'People who are project members' => '', - // 'NOK - Norwegian Krone' => '', - // 'Show this column' => '', - // 'Hide this column' => '', - // 'open file' => '', - // 'End date' => '', - // 'Users overview' => '', - // 'Members' => '', - // 'Shared project' => '', - // 'Project managers' => '', - // 'Gantt chart for all projects' => '', - // 'Projects list' => '', - // 'Gantt chart for this project' => '', - // 'Project board' => '', - // 'End date:' => '', - // 'There is no start date or end date for this project.' => '', - // 'Projects Gantt chart' => '', - // 'Change task color when using a specific task link' => '', - // 'Task link creation or modification' => '', - // 'Milestone' => '', - // 'Documentation: %s' => '', - // 'Switch to the Gantt chart view' => '', - // 'Reset the search/filter box' => '', - // 'Documentation' => '', - // 'Table of contents' => '', - // 'Gantt' => '', - // 'Author' => '', - // 'Version' => '', - // 'Plugins' => '', - // 'There is no plugin loaded.' => '', - // 'My notifications' => '', - // 'Custom filters' => '', - // 'Your custom filter have been created successfully.' => '', - // 'Unable to create your custom filter.' => '', - // 'Custom filter removed successfully.' => '', - // 'Unable to remove this custom filter.' => '', - // 'Edit custom filter' => '', - // 'Your custom filter have been updated successfully.' => '', - // 'Unable to update custom filter.' => '', - // 'Web' => '', - // 'New attachment on task #%d: %s' => '', - // 'New comment on task #%d' => '', - // 'Comment updated on task #%d' => '', - // 'New subtask on task #%d' => '', - // 'Subtask updated on task #%d' => '', - // 'New task #%d: %s' => '', - // 'Task updated #%d' => '', - // 'Task #%d closed' => '', - // 'Task #%d opened' => '', - // 'Column changed for task #%d' => '', - // 'New position for task #%d' => '', - // 'Swimlane changed for task #%d' => '', - // 'Assignee changed on task #%d' => '', - // '%d overdue tasks' => '', - // 'Task #%d is overdue' => '', - // 'No notification.' => '', - // 'Mark all as read' => '', - // 'Mark as read' => '', - // 'Total number of tasks in this column across all swimlanes' => '', - // 'Collapse swimlane' => '', - // 'Expand swimlane' => '', - // 'Add a new filter' => '', - // 'Share with all project members' => '', - // 'Shared' => '', - // 'Owner' => '', - // 'Unread notifications' => '', - // 'Notification methods:' => '', - // 'Unable to read your file' => '', - // '%d task(s) have been imported successfully.' => '', - // 'Nothing have been imported!' => '', - // 'Import users from CSV file' => '', - // '%d user(s) have been imported successfully.' => '', - // 'Comma' => '', - // 'Semi-colon' => '', - // 'Tab' => '', - // 'Vertical bar' => '', - // 'Double Quote' => '', - // 'Single Quote' => '', - // '%s attached a file to the task #%d' => '', - // 'There is no column or swimlane activated in your project!' => '', - // 'Append filter (instead of replacement)' => '', - // 'Append/Replace' => '', - // 'Append' => '', - // 'Replace' => '', - // 'Import' => '', - // 'Change sorting' => '', - // 'Tasks Importation' => '', - // 'Delimiter' => '', - // 'Enclosure' => '', - // 'CSV File' => '', - // 'Instructions' => '', - // 'Your file must use the predefined CSV format' => '', - // 'Your file must be encoded in UTF-8' => '', - // 'The first row must be the header' => '', - // 'Duplicates are not verified for you' => '', - // 'The due date must use the ISO format: YYYY-MM-DD' => '', - // 'Download CSV template' => '', - // 'No external integration registered.' => '', - // 'Duplicates are not imported' => '', - // 'Usernames must be lowercase and unique' => '', - // 'Passwords will be encrypted if present' => '', - // '%s attached a new file to the task %s' => '', - // 'Link type' => '', - // 'Assign automatically a category based on a link' => '', - // 'BAM - Konvertible Mark' => '', - // 'Assignee Username' => '', - // 'Assignee Name' => '', - // 'Groups' => '', - // 'Members of %s' => '', - // 'New group' => '', - // 'Group created successfully.' => '', - // 'Unable to create your group.' => '', - // 'Edit group' => '', - // 'Group updated successfully.' => '', - // 'Unable to update your group.' => '', - // 'Add group member to "%s"' => '', - // 'Group member added successfully.' => '', - // 'Unable to add group member.' => '', - // 'Remove user from group "%s"' => '', - // 'User removed successfully from this group.' => '', - // 'Unable to remove this user from the group.' => '', - // 'Remove group' => '', - // 'Group removed successfully.' => '', - // 'Unable to remove this group.' => '', - // 'Project Permissions' => '', - // 'Manager' => '', - // 'Project Manager' => '', - // 'Project Member' => '', - // 'Project Viewer' => '', - // 'Your account is locked for %d minutes' => '', - // 'Invalid captcha' => '', - // 'The name must be unique' => '', - // 'View all groups' => '', - // 'There is no user available.' => '', - // 'Do you really want to remove the user "%s" from the group "%s"?' => '', - // 'There is no group.' => '', - // 'External Id' => '', - // 'Add group member' => '', - // 'Do you really want to remove this group: "%s"?' => '', - // 'There is no user in this group.' => '', - // 'Permissions' => '', - // 'Allowed Users' => '', - // 'No user have been allowed specifically.' => '', - // 'Role' => '', - // 'Enter user name...' => '', - // 'Allowed Groups' => '', - // 'No group have been allowed specifically.' => '', - // 'Group' => '', - // 'Group Name' => '', - // 'Enter group name...' => '', - // 'Role:' => '', - // 'Project members' => '', - // '%s mentioned you in the task #%d' => '', - // '%s mentioned you in a comment on the task #%d' => '', - // 'You were mentioned in the task #%d' => '', - // 'You were mentioned in a comment on the task #%d' => '', - // 'Estimated hours: ' => '', - // 'Actual hours: ' => '', - // 'Hours Spent' => '', - // 'Hours Estimated' => '', - // 'Estimated Time' => '', - // 'Actual Time' => '', - // 'Estimated vs actual time' => '', - // 'RUB - Russian Ruble' => '', - // 'Assign the task to the person who does the action when the column is changed' => '', - // 'Close a task in a specific column' => '', - // 'Time-based One-time Password Algorithm' => '', - // 'Two-Factor Provider: ' => '', - // 'Disable two-factor authentication' => '', - // 'Enable two-factor authentication' => '', - // 'There is no integration registered at the moment.' => '', - // 'Password Reset for Kanboard' => '', - // 'Forgot password?' => '', - // 'Enable "Forget Password"' => '', - // 'Password Reset' => '', - // 'New password' => '', - // 'Change Password' => '', - // 'To reset your password click on this link:' => '', - // 'Last Password Reset' => '', - // 'The password has never been reinitialized.' => '', - // 'Creation' => '', - // 'Expiration' => '', - // 'Password reset history' => '', - // 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => '', - // 'Do you really want to close all tasks of this column?' => '', - // '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '', - // 'Close all tasks of this column' => '', - // 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => '', - // 'My dashboard' => '', - // 'My profile' => '', - // 'Project owner: ' => '', - // 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '', - // 'Project owner' => '', - // 'Private projects do not have users and groups management.' => '', - // 'There is no project member.' => '', - // 'Priority' => '', - // 'Task priority' => '', - // 'General' => '', - // 'Dates' => '', - // 'Default priority' => '', - // 'Lowest priority' => '', - // 'Highest priority' => '', - // 'If you put zero to the low and high priority, this feature will be disabled.' => '', - // 'Close a task when there is no activity' => '', - // 'Duration in days' => '', - // 'Send email when there is no activity on a task' => '', - // 'Unable to fetch link information.' => '', - // 'Daily background job for tasks' => '', - // 'Auto' => '', - // 'Related' => '', - // 'Attachment' => '', - // 'Title not found' => '', - // 'Web Link' => '', - // 'External links' => '', - // 'Add external link' => '', - // 'Type' => '', - // 'Dependency' => '', - // 'Add internal link' => '', - // 'Add a new external link' => '', - // 'Edit external link' => '', - // 'External link' => '', - // 'Copy and paste your link here...' => '', - // 'URL' => '', - // 'Internal links' => '', - // 'Assign to me' => '', - // 'Me' => '', - // 'Do not duplicate anything' => '', - // 'Projects management' => '', - // 'Users management' => '', - // 'Groups management' => '', - // 'Create from another project' => '', - 'open' => 'abrir', - // 'closed' => '', - // 'Priority:' => '', - // 'Reference:' => '', - // 'Complexity:' => '', - // 'Swimlane:' => '', - // 'Column:' => '', - // 'Position:' => '', - // 'Creator:' => '', - // 'Time estimated:' => '', - // '%s hours' => '', - // 'Time spent:' => '', - // 'Created:' => '', - // 'Modified:' => '', - // 'Completed:' => '', - // 'Started:' => '', - // 'Moved:' => '', + 'Identifier' => 'Identificador', + 'Disable two factor authentication' => 'Deshabilitar la autenticación de dos factores', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => '¿Realmente desea desactivar la autenticación de dos factores para este usuario: "%s"?', + 'Edit link' => 'Editar enlace', + 'Start to type task title...' => 'Comenzar a escribir el tÃtulo de la tarea ...', + 'A task cannot be linked to itself' => 'Una tarea no se puede vincular a sà misma', + 'The exact same link already exists' => 'Ya existe un enlace idéntico', + 'Recurrent task is scheduled to be generated' => 'La tarea periódica está programada para ser generada', + 'Score' => 'Puntuación', + 'The identifier must be unique' => 'El identificador debe ser único', + 'This linked task id doesn\'t exists' => 'Este ID de la tarea enlazada no existe', + 'This value must be alphanumeric' => 'Este valor debe ser alfanumérico', + 'Edit recurrence' => 'Editar recurrencia', + 'Generate recurrent task' => 'Generar tarea recurrente', + 'Trigger to generate recurrent task' => 'Disparador para generar tarea recurrente', + 'Factor to calculate new due date' => 'Factor para calcular la nueva fecha de vencimiento', + 'Timeframe to calculate new due date' => 'Plazo para calcular la nueva fecha de vencimiento', + 'Base date to calculate new due date' => 'Fecha base para calcular la nueva fecha de vencimiento', + 'Action date' => 'Fecha de acción', + 'Base date to calculate new due date: ' => 'Fecha base para calcular la nueva fecha de vencimiento: ', + 'This task has created this child task: ' => 'Esta tarea ha creado esta tarea hija: ', + 'Day(s)' => 'DÃa(s)', + 'Existing due date' => 'Fecha de vencimiento existente', + 'Factor to calculate new due date: ' => 'Factor para calcular la nueva fecha de vencimiento: ', + 'Month(s)' => 'Mes(es)', + 'Recurrence' => 'Recurrencia', + 'This task has been created by: ' => 'Esta tarea ha sido creada por: ', + 'Recurrent task has been generated:' => 'Se ha generado una tarea recurrente:', + 'Timeframe to calculate new due date: ' => 'Plazo para calcular la nueva fecha de vencimiento: ', + 'Trigger to generate recurrent task: ' => 'Disparador para generar la tarea recurrente: ', + 'When task is closed' => 'Cuando la tarea está cerrada', + 'When task is moved from first column' => 'Cuando se mueve la tarea desde la primera columna', + 'When task is moved to last column' => 'Cuando la tarea se mueve a la última columna', + 'Year(s)' => 'Año(s)', + 'Project settings' => 'Configuración del proyecto', + 'Automatically update the start date' => 'Actualizar automáticamente la fecha de inicio', + 'iCal feed' => 'Alimentador de iCal', + 'Preferences' => 'Preferencias', + 'Security' => 'Seguridad', + 'Two factor authentication disabled' => 'Se ha inhabilitado la autenticación de dos factores.', + 'Two factor authentication enabled' => 'Autenticación de dos factores habilitada', + 'Unable to update this user.' => 'No se puede actualizar este usuario.', + 'There is no user management for private projects.' => 'No hay gestión de usuarios para proyectos privados.', + 'User that will receive the email' => 'Usuario que recibirá el correo electrónico', + 'Email subject' => 'Asunto del email', + 'Date' => 'Fecha', + 'Add a comment log when moving the task between columns' => 'Añadir un registro en los comentarios al mover la tarea entre columnas', + 'Move the task to another column when the category is changed' => 'Mover la tarea a otra columna cuando se cambia la categorÃa', + 'Send a task by email to someone' => 'Enviar una tarea por correo electrónico a alguien', + 'Reopen a task' => 'Reabrir una tarea', + 'Notification' => 'Notificación', + '%s moved the task #%d to the first swimlane' => '%s movió la tarea #%d al primer carril', + 'Swimlane' => 'Carril', + '%s moved the task %s to the first swimlane' => '%s movió la tarea %s al primer carril', + '%s moved the task %s to the swimlane "%s"' => '%s trasladó la tarea %s al carril "%s"', + 'This report contains all subtasks information for the given date range.' => 'Este informe contiene toda la información de las subtareas para el intervalo de fechas especificado.', + 'This report contains all tasks information for the given date range.' => 'Este informe contiene la información de todas las tareas para el intervalo de fechas dado.', + 'Project activities for %s' => 'Actividades del proyecto para %s', + 'view the board on Kanboard' => 'ver el tablero en Kanboard', + 'The task have been moved to the first swimlane' => 'La tarea se ha movido al primer carril', + 'The task have been moved to another swimlane:' => 'La tarea se ha trasladado a otro carril:', + 'New title: %s' => 'Nuevo tÃtulo: %s', + 'The task is not assigned anymore' => 'La tarea ya no está asignada', + 'New assignee: %s' => 'Nuevo asignado: %s', + 'There is no category now' => 'No hay categorÃa ahora', + 'New category: %s' => 'Nueva categorÃa: %s', + 'New color: %s' => 'Nuevo color: %s', + 'New complexity: %d' => 'Nueva complejidad: %d', + 'The due date have been removed' => 'Se ha eliminado la fecha de vencimiento', + 'There is no description anymore' => 'Ya no hay ninguna descripción', + 'Recurrence settings have been modified' => 'Los parámetros de recurrencia se han modificado', + 'Time spent changed: %sh' => 'Tiempo ejecutado cambiado: %sh', + 'Time estimated changed: %sh' => 'Horas Presupuestadas cambiado: %sh', + 'The field "%s" have been updated' => 'Se ha actualizado el campo "%s"', + 'The description has been modified:' => 'La descripción ha sido modificada:', + 'Do you really want to close the task "%s" as well as all subtasks?' => '¿Realmente desea cerrar la tarea "%s", asà como todas sus subtareas?', + 'I want to receive notifications for:' => 'Quiero recibir notificaciones para:', + 'All tasks' => 'Todas las tareas', + 'Only for tasks assigned to me' => 'Sólo para tareas asignadas a mÃ', + 'Only for tasks created by me' => 'Sólo para tareas creadas por mÃ', + 'Only for tasks created by me and assigned to me' => 'Sólo para las tareas creadas por mà y asignadas a mÃ', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'Total para todas las columnas', + 'You need at least 2 days of data to show the chart.' => 'Necesita al menos 2 dÃas de datos para mostrar el gráfico.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Detener temporizador', + 'Start timer' => 'Iniciar el temporizador', + 'My activity stream' => 'Mi flujo de actividad', + 'Search tasks' => 'Buscar tareas', + 'Reset filters' => 'Restablecer filtros', + 'My tasks due tomorrow' => 'Mis tareas para mañana', + 'Tasks due today' => 'Tareas pendientes hoy', + 'Tasks due tomorrow' => 'Tareas para mañana', + 'Tasks due yesterday' => 'Tareas vencidas ayer', + 'Closed tasks' => 'Tareas cerradas', + 'Open tasks' => 'Tareas abiertas', + 'Not assigned' => 'No asignado', + 'View advanced search syntax' => 'Ver sintaxis de búsqueda avanzada', + 'Overview' => 'Visión general', + 'Board/Calendar/List view' => 'Vista de Tablero/Calendario/Lista', + 'Switch to the board view' => 'Cambiar a la vista de tablero', + 'Switch to the list view' => 'Cambiar a la vista de lista', + 'Go to the search/filter box' => 'Ir a la caja de búsqueda/filtro', + 'There is no activity yet.' => 'No hay actividad todavÃa.', + 'No tasks found.' => 'No se han encontrado tareas.', + 'Keyboard shortcut: "%s"' => 'Atajo de teclado: "%s"', + 'List' => 'Lista', + 'Filter' => 'Filtrar', + 'Advanced search' => 'Búsqueda Avanzada', + 'Example of query: ' => 'Ejemplo de consulta: ', + 'Search by project: ' => 'Buscar por proyecto: ', + 'Search by column: ' => 'Buscar por columna: ', + 'Search by assignee: ' => 'Buscar por asignado:', + 'Search by color: ' => 'Buscar por color: ', + 'Search by category: ' => 'Buscar por categorÃa: ', + 'Search by description: ' => 'Buscar por descripción: ', + 'Search by due date: ' => 'Buscar por fecha de vencimiento: ', + 'Average time spent into each column' => 'Tiempo de permanencia promedio en cada columna', + 'Average time spent' => 'Tiempo ejecutado promedio', + 'This chart show the average time spent into each column for the last %d tasks.' => 'Este gráfico muestra el tiempo promedio invertido en cada columna para las últimas %d tareas.', + 'Average Lead and Cycle time' => 'Tiempo de Espera y de Ciclo promedio', + 'Average lead time: ' => 'Tiempo de espera promedio: ', + 'Average cycle time: ' => 'Tiempo del ciclo promedio: ', + 'Cycle Time' => 'Tiempo del Ciclo', + 'Lead Time' => 'Tiempo de Espera', + 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Este gráfico muestra el tiempo promedio de espera y de ciclo para las últimas %d tareas.', + 'Average time into each column' => 'Tiempo promedio en cada columna', + 'Lead and cycle time' => 'Tiempo de espera y de ciclo', + 'Lead time: ' => 'Tiempo de Espera: ', + 'Cycle time: ' => 'Tiempo del Ciclo: ', + 'Time spent into each column' => 'Tiempo empleado en cada columna', + 'The lead time is the duration between the task creation and the completion.' => 'El tiempo de espera es la duración entre la creación de la tarea y la finalización.', + 'The cycle time is the duration between the start date and the completion.' => 'El tiempo de ciclo es la duración entre la fecha de inicio y la finalización.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Si la tarea no está cerrada, se utiliza la hora actual en lugar de la fecha de finalización.', + 'Set automatically the start date' => 'Establecer automáticamente la fecha de inicio', + 'Edit Authentication' => 'Editar autenticación', + 'Remote user' => 'Usuario remoto', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Los usuarios remotos no almacenan su contraseña en la base de datos Kanboard, ejemplos: cuentas LDAP, Google y Github.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Si marca la casilla "Deshabilitar el formulario de inicio de sesión", las credenciales ingresadas en el formulario de inicio de sesión serán ignoradas.', + 'Default task color' => 'Color predeterminado de la tarea', + 'This feature does not work with all browsers.' => 'Esta función no funciona con todos los navegadores.', + 'There is no destination project available.' => 'No hay proyecto de destino disponible.', + 'Trigger automatically subtask time tracking' => 'Activar automáticamente el seguimiento de tiempo de la subtarea', + 'Include closed tasks in the cumulative flow diagram' => 'Incluir tareas cerradas en el diagrama de flujo acumulativo', + 'Current swimlane: %s' => 'Carril actual: %s', + 'Current column: %s' => 'Columna actual: %s', + 'Current category: %s' => 'CategorÃa actual: %s', + 'no category' => 'sin categoria', + 'Current assignee: %s' => 'Asignado actual: %s', + 'not assigned' => 'no asignado', + 'Author:' => 'Autor:', + 'contributors' => 'colaboradores', + 'License:' => 'Licencia:', + 'License' => 'Licencia', + 'Enter the text below' => 'Introduzca el texto a continuación', + 'Start date:' => 'Fecha de inicio:', + 'Due date:' => 'Fecha de vencimiento:', + 'People who are project managers' => 'Personas que son gerentes de proyecto', + 'People who are project members' => 'Personas que son miembros del proyecto', + 'NOK - Norwegian Krone' => 'NOK - Corona Noruega', + 'Show this column' => 'Mostrar esta columna', + 'Hide this column' => 'Ocultar esta columna', + 'open file' => 'abrir archivo', + 'End date' => 'Fecha final', + 'Users overview' => 'Visión general de los usuarios', + 'Members' => 'Miembros', + 'Shared project' => 'Proyecto compartido', + 'Project managers' => 'Gerentes de Proyecto', + 'Projects list' => 'Lista de proyectos', + 'End date:' => 'Fecha final:', + 'Change task color when using a specific task link' => 'Cambiar el color de la tarea cuando se utiliza un enlace de tarea especÃfico', + 'Task link creation or modification' => 'Creación o modificación de enlaces de tareas', + 'Milestone' => 'Hito', + 'Documentation: %s' => 'Documentación: %s', + 'Reset the search/filter box' => 'Restablecer la caja de búsqueda/filtro', + 'Documentation' => 'Documentación', + 'Table of contents' => 'Tabla de contenido', + 'Author' => 'Autor', + 'Version' => 'Versión', + 'Plugins' => 'Plugins', + 'There is no plugin loaded.' => 'No hay ningún plugin cargado.', + 'My notifications' => 'Mis Notificaciones', + 'Custom filters' => 'Filtros personalizados', + 'Your custom filter have been created successfully.' => 'Su filtro personalizado se ha creado correctamente.', + 'Unable to create your custom filter.' => 'No se puede crear el filtro personalizado.', + 'Custom filter removed successfully.' => 'Filtro personalizado eliminado correctamente.', + 'Unable to remove this custom filter.' => 'No se puede quitar este filtro personalizado.', + 'Edit custom filter' => 'Editar filtro personalizado', + 'Your custom filter have been updated successfully.' => 'Su filtro personalizado se ha actualizado correctamente.', + 'Unable to update custom filter.' => 'No se puede actualizar el filtro personalizado.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Nuevo archivo adjunto en la tarea #%d: %s', + 'New comment on task #%d' => 'Nuevo comentario sobre la tarea #%d', + 'Comment updated on task #%d' => 'Comentario actualizado en la tarea #%d', + 'New subtask on task #%d' => 'Nueva subtarea en la tarea #%d', + 'Subtask updated on task #%d' => 'Subtarea actualizada en la tarea #%d', + 'New task #%d: %s' => 'Nueva tarea #%d: %s', + 'Task updated #%d' => 'Tarea actualizada #%d', + 'Task #%d closed' => 'Tarea #%d cerrada', + 'Task #%d opened' => 'Tarea #%d abierta', + 'Column changed for task #%d' => 'Columna modificada para la tarea #%d', + 'New position for task #%d' => 'Nueva posición para la tarea #%d', + 'Swimlane changed for task #%d' => 'Carril cambiado para la tarea #%d', + 'Assignee changed on task #%d' => 'Asignado cambiado en la tarea #%d', + '%d overdue tasks' => '%d tareas pendientes', + 'Task #%d is overdue' => 'La tarea #%d está atrasada', + 'No notification.' => 'Sin notificación.', + 'Mark all as read' => 'marcar todo como leido', + 'Mark as read' => 'Marcar como leÃdo', + 'Total number of tasks in this column across all swimlanes' => 'Número total de tareas en esta columna contando todos los carriles', + 'Collapse swimlane' => 'Colapsar el carril', + 'Expand swimlane' => 'Expandir el carril', + 'Add a new filter' => 'Añadir un nuevo filtro', + 'Share with all project members' => 'Compartir con todos los miembros del proyecto', + 'Shared' => 'Compartido', + 'Owner' => 'Propietario', + 'Unread notifications' => 'Notificaciones no leÃdas', + 'Notification methods:' => 'Métodos de notificación:', + 'Unable to read your file' => 'No se puede leer el archivo', + '%d task(s) have been imported successfully.' => '%d tarea(s) se han importado correctamente.', + 'Nothing have been imported!' => '¡Nada se ha importado!', + 'Import users from CSV file' => 'Importar usuarios del archivo CSV', + '%d user(s) have been imported successfully.' => '%d usuario(s) se han importado correctamente.', + 'Comma' => 'Coma', + 'Semi-colon' => 'Punto y coma', + 'Tab' => 'Tabulación', + 'Vertical bar' => 'Barra vertical', + 'Double Quote' => 'Comillas dobles', + 'Single Quote' => 'Comillas simples', + '%s attached a file to the task #%d' => '%s adjuntó un archivo a la tarea #%d', + 'There is no column or swimlane activated in your project!' => '¡No hay columna ni carril activado en tu proyecto!', + 'Append filter (instead of replacement)' => 'Agregar el filtro (en lugar del reemplazo)', + 'Append/Replace' => 'Añadir/Reemplazar', + 'Append' => 'Adjuntar', + 'Replace' => 'Reemplazar', + 'Import' => 'Importar', + 'Change sorting' => 'Cambio de orden', + 'Tasks Importation' => 'Importación de Tareas', + 'Delimiter' => 'Delimitador', + 'Enclosure' => 'Contenedor', + 'CSV File' => 'Archivo CSV', + 'Instructions' => 'Instrucciones', + 'Your file must use the predefined CSV format' => 'Su archivo debe utilizar el formato CSV predefinido', + 'Your file must be encoded in UTF-8' => 'Su archivo debe estar codificado en UTF-8', + 'The first row must be the header' => 'La primera fila debe ser el encabezado', + 'Duplicates are not verified for you' => 'Los duplicados no se verifican', + 'The due date must use the ISO format: YYYY-MM-DD' => 'La fecha de vencimiento debe utilizar el formato ISO: AAAA-MM-DD', + 'Download CSV template' => 'Descargar plantilla CSV', + 'No external integration registered.' => 'No se ha registrado ninguna integración externa.', + 'Duplicates are not imported' => 'Los duplicados no se importan', + 'Usernames must be lowercase and unique' => 'Los nombres de usuario deben estar en minúsculas y ser únicos', + 'Passwords will be encrypted if present' => 'Las contraseñas se cifrarán si están presentes', + '%s attached a new file to the task %s' => '%s adjunto un nuevo archivo a la tarea %s', + 'Link type' => 'Tipo de enlace', + 'Assign automatically a category based on a link' => 'Asignar automáticamente una categorÃa basada en un enlace', + 'BAM - Konvertible Mark' => 'BAM - Marco Convertible', + 'Assignee Username' => 'Usuario asignado', + 'Assignee Name' => 'Nombre del asignado', + 'Groups' => 'Grupos', + 'Members of %s' => 'Miembros de %s', + 'New group' => 'Nuevo grupo', + 'Group created successfully.' => 'Grupo creado correctamente.', + 'Unable to create your group.' => 'No se puede crear su grupo.', + 'Edit group' => 'Editar grupo', + 'Group updated successfully.' => 'Grupo actualizado correctamente.', + 'Unable to update your group.' => 'No se puede actualizar su grupo.', + 'Add group member to "%s"' => 'Agregue el miembro del grupo a "%s"', + 'Group member added successfully.' => 'Miembro del grupo agregado correctamente.', + 'Unable to add group member.' => 'No se puede agregar el miembro del grupo.', + 'Remove user from group "%s"' => 'Quitar usuario del grupo "%s"', + 'User removed successfully from this group.' => 'Usuario eliminado correctamente de este grupo.', + 'Unable to remove this user from the group.' => 'No se puede eliminar este usuario del grupo.', + 'Remove group' => 'Eliminar grupo', + 'Group removed successfully.' => 'Grupo eliminado correctamente.', + 'Unable to remove this group.' => 'No se pudo eliminar este grupo.', + 'Project Permissions' => 'Permisos del proyecto', + 'Manager' => 'Gerente', + 'Project Manager' => 'Gerente de proyecto', + 'Project Member' => 'Miembro del proyecto', + 'Project Viewer' => 'Visor de proyectos', + 'Your account is locked for %d minutes' => 'Tu cuenta está bloqueada durante %d minutos', + 'Invalid captcha' => 'Captcha inválido', + 'The name must be unique' => 'El nombre debe ser único', + 'View all groups' => 'Ver todos los grupos', + 'There is no user available.' => 'No hay usuarios disponibles.', + 'Do you really want to remove the user "%s" from the group "%s"?' => '¿Realmente desea eliminar el usuario "%s" del grupo "%s"?', + 'There is no group.' => 'No hay grupo.', + 'Add group member' => 'Agregar miembro del grupo', + 'Do you really want to remove this group: "%s"?' => '¿Desea realmente eliminar este grupo: "%s"?', + 'There is no user in this group.' => 'No hay ningún usuario en este grupo.', + 'Permissions' => 'Permisos', + 'Allowed Users' => 'Usuarios permitidos', + 'No user have been allowed specifically.' => 'Ningún usuario ha sido autorizado especÃficamente.', + 'Role' => 'Rol', + 'Enter user name...' => 'Introduzca su nombre de usuario...', + 'Allowed Groups' => 'Grupos permitidos', + 'No group have been allowed specifically.' => 'Ningún grupo se ha permitido especÃficamente.', + 'Group' => 'Grupo', + 'Group Name' => 'Nombre del grupo', + 'Enter group name...' => 'Introduzca el nombre del grupo ...', + 'Role:' => 'Rol:', + 'Project members' => 'Miembros del proyecto', + '%s mentioned you in the task #%d' => '%s te mencionó en la tarea #%d', + '%s mentioned you in a comment on the task #%d' => '%s te ha mencionado en un comentario sobre la tarea #%d', + 'You were mentioned in the task #%d' => 'Te mencionaron en la tarea #%d', + 'You were mentioned in a comment on the task #%d' => 'Usted fue mencionado en un comentario en la tarea #%d', + 'Estimated hours: ' => 'Horas estimadas: ', + 'Actual hours: ' => 'Horas reales: ', + 'Hours Spent' => 'Horas Gastadas', + 'Hours Estimated' => 'Horas Estimadas', + 'Estimated Time' => 'Tiempo Estimado', + 'Actual Time' => 'Tiempo Real', + 'Estimated vs actual time' => 'Horas Presupuestadas vs. tiempo real', + 'RUB - Russian Ruble' => 'RUB - Rublo Ruso', + 'Assign the task to the person who does the action when the column is changed' => 'Asigne la tarea a la persona que realiza la acción cuando se cambia la columna', + 'Close a task in a specific column' => 'Cerrar una tarea en una columna especÃfica', + 'Time-based One-time Password Algorithm' => 'Algoritmo de contraseña de una sola vez basado en el tiempo', + 'Two-Factor Provider: ' => 'Proveedor de dos factores: ', + 'Disable two-factor authentication' => 'Deshabilitar la autenticación de dos factores', + 'Enable two-factor authentication' => 'Habilitar la autenticación de dos factores', + 'There is no integration registered at the moment.' => 'No hay ninguna integración registrada en este momento.', + 'Password Reset for Kanboard' => 'Restablecer contraseña para Kanboard', + 'Forgot password?' => '¿Olvidó su contraseña?', + 'Enable "Forget Password"' => 'Habilitar "Olvidar contraseña"', + 'Password Reset' => 'Restablecimiento de contraseña', + 'New password' => 'Nueva contraseña', + 'Change Password' => 'Cambiar la contraseña', + 'To reset your password click on this link:' => 'Para restablecer su contraseña, haga clic en este enlace:', + 'Last Password Reset' => 'Restablecer la última contraseña', + 'The password has never been reinitialized.' => 'La contraseña nunca ha sido reinicializada.', + 'Creation' => 'Creación', + 'Expiration' => 'Vencimiento', + 'Password reset history' => 'Historial de restablecimiento de contraseñas', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Todas las tareas de la columna "%s" y el carril "%s" han sido cerradas con éxito.', + 'Do you really want to close all tasks of this column?' => '¿Realmente desea cerrar todas las tareas de esta columna?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d tarea(s) en la columna "%s" y el carril "%s" se cerrará.', + 'Close all tasks of this column' => 'Cierre todas las tareas de esta columna', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Ningún plugin ha registrado un método de notificación de proyecto. TodavÃa puede configurar notificaciones individuales en su perfil de usuario.', + 'My dashboard' => 'Mi tablero', + 'My profile' => 'Mi perfil', + 'Project owner: ' => 'Propietario del proyecto: ', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'El identificador del proyecto es opcional y debe ser alfanumérico, por ejemplo: MIPROYECTO.', + 'Project owner' => 'Propietario del proyecto', + 'Private projects do not have users and groups management.' => 'Los proyectos privados no tienen gestión de usuarios y grupos.', + 'There is no project member.' => 'No hay ningún miembro del proyecto.', + 'Priority' => 'Prioridad', + 'Task priority' => 'Prioridad de la tarea', + 'General' => 'General', + 'Dates' => 'Fechas', + 'Default priority' => 'Prioridad predeterminada', + 'Lowest priority' => 'Menor prioridad', + 'Highest priority' => 'Mayor prioridad', + 'Close a task when there is no activity' => 'Cierra una tarea cuando no hay actividad', + 'Duration in days' => 'Duración en dÃas', + 'Send email when there is no activity on a task' => 'Enviar correo electrónico cuando no hay actividad en una tarea', + 'Unable to fetch link information.' => 'No es posible obtener información de enlace.', + 'Daily background job for tasks' => 'Trabajo en segundo plano diario para las tareas', + 'Auto' => 'Auto', + 'Related' => 'Relacionado', + 'Attachment' => 'Archivo adjunto', + 'Title not found' => 'TÃtulo no encontrado', + 'Web Link' => 'Enlace web', + 'External links' => 'Enlaces externos', + 'Add external link' => 'Añadir enlace externo', + 'Type' => 'Tipo', + 'Dependency' => 'Dependencia', + 'Add internal link' => 'Añadir enlace interno', + 'Add a new external link' => 'Añadir un nuevo enlace externo', + 'Edit external link' => 'Editar enlace externo', + 'External link' => 'Enlace externo', + 'Copy and paste your link here...' => 'Copia y pega el enlace aquà ...', + 'URL' => 'URL', + 'Internal links' => 'Enlaces internos', + 'Assign to me' => 'Asignármelo', + 'Me' => 'Yo', + 'Do not duplicate anything' => 'No duplique nada', + 'Projects management' => 'Gestión de proyectos', + 'Users management' => 'Gestión de usuarios', + 'Groups management' => 'Gestión de grupos', + 'Create from another project' => 'Crear desde otro proyecto', + 'open' => 'abierto', + 'closed' => 'cerrado', + 'Priority:' => 'Prioridad:', + 'Reference:' => 'Referencia:', + 'Complexity:' => 'Complejidad:', + 'Swimlane:' => 'Carril:', + 'Column:' => 'Columna:', + 'Position:' => 'Posición:', + 'Creator:' => 'Creador:', + 'Time estimated:' => 'Horas Presupuestadas:', + '%s hours' => '%s horas', + 'Time spent:' => 'Tiempo usado:', + 'Created:' => 'Creado:', + 'Modified:' => 'Modificado:', + 'Completed:' => 'Terminado:', + 'Started:' => 'Empezado:', + 'Moved:' => 'Movido:', 'Task #%d' => 'Tarea número %d', - // 'Time format' => '', - // 'Start date: ' => '', - // 'End date: ' => '', - // 'New due date: ' => '', - // 'Start date changed: ' => '', - // 'Disable private projects' => '', - // 'Do you really want to remove this custom filter: "%s"?' => '', - // 'Remove a custom filter' => '', - // 'User activated successfully.' => '', - // 'Unable to enable this user.' => '', - // 'User disabled successfully.' => '', - // 'Unable to disable this user.' => '', - // 'All files have been uploaded successfully.' => '', - // 'The maximum allowed file size is %sB.' => '', - // 'Drag and drop your files here' => '', - // 'choose files' => '', - // 'View profile' => '', - // 'Two Factor' => '', - // 'Disable user' => '', - // 'Do you really want to disable this user: "%s"?' => '', - // 'Enable user' => '', - // 'Do you really want to enable this user: "%s"?' => '', - // 'Download' => '', - // 'Uploaded: %s' => '', - // 'Size: %s' => '', - // 'Uploaded by %s' => '', - // 'Filename' => '', - // 'Size' => '', - // 'Column created successfully.' => '', - // 'Another column with the same name exists in the project' => '', - // 'Default filters' => '', - // 'Your board doesn\'t have any columns!' => '', - // 'Change column position' => '', - // 'Switch to the project overview' => '', - // 'User filters' => '', - // 'Category filters' => '', - // 'Upload a file' => '', - // 'View file' => '', - // 'Last activity' => '', - // 'Change subtask position' => '', + 'Time format' => 'Formato de tiempo', + 'Start date: ' => 'Fecha de inicio: ', + 'End date: ' => 'Fecha final: ', + 'New due date: ' => 'Nueva fecha de vencimiento: ', + 'Start date changed: ' => 'Fecha de inicio cambiada: ', + 'Disable private projects' => 'Inhabilitar proyectos privados', + 'Do you really want to remove this custom filter: "%s"?' => '¿Desea realmente eliminar este filtro personalizado: "%s"?', + 'Remove a custom filter' => 'Eliminar un filtro personalizado', + 'User activated successfully.' => 'Usuario activado correctamente.', + 'Unable to enable this user.' => 'No se puede habilitar este usuario.', + 'User disabled successfully.' => 'El usuario ha sido desactivado correctamente.', + 'Unable to disable this user.' => 'No se puede deshabilitar este usuario.', + 'All files have been uploaded successfully.' => 'Todos los archivos se han cargado correctamente.', + 'The maximum allowed file size is %sB.' => 'El tamaño máximo de archivo permitido es %sB.', + 'Drag and drop your files here' => 'Arrastra y suelta tus archivos aquÃ', + 'choose files' => 'Elija el archivo', + 'View profile' => 'Ver perfil', + 'Two Factor' => 'Dos factores', + 'Disable user' => 'Deshabilitar usuario', + 'Do you really want to disable this user: "%s"?' => '¿Desea realmente desactivar este usuario: "%s"?', + 'Enable user' => 'Habilitar usuario', + 'Do you really want to enable this user: "%s"?' => '¿De verdad quieres habilitar a este usuario: "%s"?', + 'Download' => 'Descargar', + 'Uploaded: %s' => 'Subido: %s', + 'Size: %s' => 'Tamaño: %s', + 'Uploaded by %s' => 'Subido por %s', + 'Filename' => 'Nombre del archivo', + 'Size' => 'Tamaño', + 'Column created successfully.' => 'Columna creada correctamente.', + 'Another column with the same name exists in the project' => 'Otra columna con el mismo nombre existe en el proyecto', + 'Default filters' => 'Filtros predeterminados', + 'Your board doesn\'t have any columns!' => '¡Su tablero no tiene columnas!', + 'Change column position' => 'Cambiar la posición de la columna', + 'Switch to the project overview' => 'Cambiar a la vista general del proyecto', + 'User filters' => 'Filtros de usuario', + 'Category filters' => 'Filtros de categorÃas', + 'Upload a file' => 'Cargar un archivo', + 'View file' => 'Ver archivo', + 'Last activity' => 'Última actividad', + 'Change subtask position' => 'Cambiar la posición de la subtarea', 'This value must be greater than %d' => 'Este valor no debe de ser más grande que %d', - // 'Another swimlane with the same name exists in the project' => '', - // 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => '', - // 'Actions duplicated successfully.' => '', - // 'Unable to duplicate actions.' => '', - // 'Add a new action' => '', - // 'Import from another project' => '', - // 'There is no action at the moment.' => '', - // 'Import actions from another project' => '', - // 'There is no available project.' => '', - // 'Local File' => '', - // 'Configuration' => '', - // 'PHP version:' => '', - // 'PHP SAPI:' => '', - // 'OS version:' => '', - // 'Database version:' => '', - // 'Browser:' => '', - // 'Task view' => '', - // 'Edit task' => '', - // 'Edit description' => '', - // 'New internal link' => '', - // 'Display list of keyboard shortcuts' => '', - // 'Menu' => '', - // 'Set start date' => '', - // 'Avatar' => '', - // 'Upload my avatar image' => '', - // 'Remove my image' => '', - // 'The OAuth2 state parameter is invalid' => '', - // 'User not found.' => '', - // 'Search in activity stream' => '', - // 'My activities' => '', - // 'Activity until yesterday' => '', - // 'Activity until today' => '', - // 'Search by creator: ' => '', - // 'Search by creation date: ' => '', - // 'Search by task status: ' => '', - // 'Search by task title: ' => '', - // 'Activity stream search' => '', - // 'Projects where "%s" is manager' => '', - // 'Projects where "%s" is member' => '', - // 'Open tasks assigned to "%s"' => '', - // 'Closed tasks assigned to "%s"' => '', - // 'Assign automatically a color based on a priority' => '', - // 'Overdue tasks for the project(s) "%s"' => '', - // 'Upload files' => '', - // 'Installed Plugins' => '', - // 'Plugin Directory' => '', - // 'Plugin installed successfully.' => '', - // 'Plugin updated successfully.' => '', - // 'Plugin removed successfully.' => '', - // 'Subtask converted to task successfully.' => '', - // 'Unable to convert the subtask.' => '', - // 'Unable to extract plugin archive.' => '', - // 'Plugin not found.' => '', - // 'You don\'t have the permission to remove this plugin.' => '', - // 'Unable to download plugin archive.' => '', - // 'Unable to write temporary file for plugin.' => '', - // 'Unable to open plugin archive.' => '', - // 'There is no file in the plugin archive.' => '', - // 'Create tasks in bulk' => '', - // 'Your Kanboard instance is not configured to install plugins from the user interface.' => '', - // 'There is no plugin available.' => '', - // 'Install' => '', + 'Another swimlane with the same name exists in the project' => 'En el proyecto existe otro carril con el mismo nombre', + 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Ejemplo: http://example.kanboard.net/ (utilizado para generar URLs absolutas)', + 'Actions duplicated successfully.' => 'Acciones duplicadas correctamente.', + 'Unable to duplicate actions.' => 'No se pueden duplicar acciones.', + 'Add a new action' => 'Añadir una acción nueva', + 'Import from another project' => 'Importar desde otro proyecto', + 'There is no action at the moment.' => 'No hay acción en este momento.', + 'Import actions from another project' => 'Importar acciones de otro proyecto', + 'There is no available project.' => 'No hay proyecto disponible.', + 'Local File' => 'Archivo local', + 'Configuration' => 'Configuración', + 'PHP version:' => 'Versión de PHP:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'Versión del sistema operativo:', + 'Database version:' => 'Versión de base de datos:', + 'Browser:' => 'Navegador:', + 'Task view' => 'Vista de la tarea', + 'Edit task' => 'Editar tarea', + 'Edit description' => 'Editar Descripción', + 'New internal link' => 'Nuevo enlace interno', + 'Display list of keyboard shortcuts' => 'Mostrar lista de atajos de teclado', + 'Menu' => 'Menú', + 'Set start date' => 'Establecer fecha de inicio', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'Subir mi imagen de avatar', + 'Remove my image' => 'Eliminar mi imagen', + 'The OAuth2 state parameter is invalid' => 'El parámetro de estado OAuth2 no es válido', + 'User not found.' => 'Usuario no encontrado.', + 'Search in activity stream' => 'Buscar en el flujo de actividades', + 'My activities' => 'Mis actividades', + 'Activity until yesterday' => 'Actividad hasta ayer', + 'Activity until today' => 'Actividad hasta hoy', + 'Search by creator: ' => 'Búsqueda por creador: ', + 'Search by creation date: ' => 'Búsqueda por fecha de creación: ', + 'Search by task status: ' => 'Búsqueda por estado de la tarea: ', + 'Search by task title: ' => 'Buscar por tÃtulo de tarea: ', + 'Activity stream search' => 'Búsqueda de flujo de actividad', + 'Projects where "%s" is manager' => 'Proyectos donde "%s" es gerente', + 'Projects where "%s" is member' => 'Proyectos donde "%s" es miembro', + 'Open tasks assigned to "%s"' => 'Tareas abiertas asignadas a "%s"', + 'Closed tasks assigned to "%s"' => 'Tareas cerradas asignadas a "%s"', + 'Assign automatically a color based on a priority' => 'Asignar automáticamente un color basado en una prioridad', + 'Overdue tasks for the project(s) "%s"' => 'Tareas vencidas para el (los) proyecto(s) "%s"', + 'Upload files' => 'Subir archivos', + 'Installed Plugins' => 'Plugins instalados', + 'Plugin Directory' => 'Directorio de Plugins', + 'Plugin installed successfully.' => 'Plugin instalado correctamente.', + 'Plugin updated successfully.' => 'Plugin actualizado correctamente.', + 'Plugin removed successfully.' => 'Plugin eliminado correctamente.', + 'Subtask converted to task successfully.' => 'Subtarea convertida a la tarea con éxito.', + 'Unable to convert the subtask.' => 'No se puede convertir la subtarea.', + 'Unable to extract plugin archive.' => 'No se puede extraer archivo plugin.', + 'Plugin not found.' => 'No se ha encontrado el plugin.', + 'You don\'t have the permission to remove this plugin.' => 'No tienes permiso para eliminar este plugin.', + 'Unable to download plugin archive.' => 'No es posible descargar archivos plugin.', + 'Unable to write temporary file for plugin.' => 'No se puede escribir el archivo temporal para el plugin.', + 'Unable to open plugin archive.' => 'No se puede abrir el archivo de plugin', + 'There is no file in the plugin archive.' => 'No hay ningún archivo en el archivo de plugins.', + 'Create tasks in bulk' => 'Crear tareas en lote', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Tu instancia de Kanboard no está configurada para instalar complementos desde la interfaz de usuario.', + 'There is no plugin available.' => 'No hay un plugin disponible.', + 'Install' => 'Instalar', 'Update' => 'Actualizar', - // 'Up to date' => '', - // 'Not available' => '', - // 'Remove plugin' => '', - // 'Do you really want to remove this plugin: "%s"?' => '', - // 'Uninstall' => '', - // 'Listing' => '', - // 'Metadata' => '', - // 'Manage projects' => '', - // 'Convert to task' => '', - // 'Convert sub-task to task' => '', - // 'Do you really want to convert this sub-task to a task?' => '', - // 'My task title' => '', - // 'Enter one task by line.' => '', - // 'Number of failed login:' => '', - // 'Account locked until:' => '', - // 'Email settings' => '', - // 'Email sender address' => '', - // 'Email transport' => '', - // 'Webhook token' => '', - // 'Project tags management' => '', - // 'Tag created successfully.' => '', - // 'Unable to create this tag.' => '', - // 'Tag updated successfully.' => '', - // 'Unable to update this tag.' => '', - // 'Tag removed successfully.' => '', - // 'Unable to remove this tag.' => '', - // 'Global tags management' => '', - // 'Tags' => '', - // 'Tags management' => '', - // 'Add new tag' => '', - // 'Edit a tag' => '', - // 'Project tags' => '', - // 'There is no specific tag for this project at the moment.' => '', - // 'Tag' => '', - // 'Remove a tag' => '', - // 'Do you really want to remove this tag: "%s"?' => '', - // 'Global tags' => '', - // 'There is no global tag at the moment.' => '', - // 'This field cannot be empty' => '', - // 'Close a task when there is no activity in an specific column' => '', - // '%s removed a subtask for the task #%d' => '', - // '%s removed a comment on the task #%d' => '', - // 'Comment removed on task #%d' => '', - // 'Subtask removed on task #%d' => '', - // 'Hide tasks in this column in the dashboard' => '', - // '%s removed a comment on the task %s' => '', - // '%s removed a subtask for the task %s' => '', - // 'Comment removed' => '', - // 'Subtask removed' => '', - // '%s set a new internal link for the task #%d' => '', - // '%s removed an internal link for the task #%d' => '', - // 'A new internal link for the task #%d have been defined' => '', - // 'Internal link removed for the task #%d' => '', - // '%s set a new internal link for the task %s' => '', - // '%s removed an internal link for the task %s' => '', - // 'Automatically set the due date on task creation' => '', - // 'Move the task to another column when closed' => '', - // 'Move the task to another column when not moved during a given period' => '', - // 'Dashboard for %s' => '', - // 'Tasks overview for %s' => '', - // 'Subtasks overview for %s' => '', - // 'Projects overview for %s' => '', - // 'Activity stream for %s' => '', - // 'Calendar for %s' => '', - // 'Notifications for %s' => '', - // 'Assign a color when the task is moved to a specific swimlane' => '', - // 'Assign a priority when the task is moved to a specific swimlane' => '', - // 'User unlocked successfully.' => '', - // 'Unable to unlock the user.' => '', - // 'Move a task to another swimlane' => '', - // 'Creator Name' => '', - // 'Time spent and estimated' => '', - // 'Move position' => '', - // 'Move task to another position on the board' => '', - // 'Insert before this task' => '', - // 'Insert after this task' => '', - // 'Unlock this user' => '', - // 'Custom Project Roles' => '', - // 'Add a new custom role' => '', - // 'Restrictions for the role "%s"' => '', - // 'Add a new project restriction' => '', - // 'Add a new drag and drop restriction' => '', - // 'Add a new column restriction' => '', - // 'Edit this role' => '', - // 'Remove this role' => '', - // 'There is no restriction for this role.' => '', - // 'Only moving task between those columns is permitted' => '', - // 'Close a task in a specific column when not moved during a given period' => '', - // 'Edit columns' => '', - // 'The column restriction has been created successfully.' => '', - // 'Unable to create this column restriction.' => '', - // 'Column restriction removed successfully.' => '', - // 'Unable to remove this restriction.' => '', - // 'Your custom project role has been created successfully.' => '', - // 'Unable to create custom project role.' => '', - // 'Your custom project role has been updated successfully.' => '', - // 'Unable to update custom project role.' => '', - // 'Custom project role removed successfully.' => '', - // 'Unable to remove this project role.' => '', - // 'The project restriction has been created successfully.' => '', - // 'Unable to create this project restriction.' => '', - // 'Project restriction removed successfully.' => '', - // 'You cannot create tasks in this column.' => '', - // 'Task creation is permitted for this column' => '', - // 'Closing or opening a task is permitted for this column' => '', - // 'Task creation is blocked for this column' => '', - // 'Closing or opening a task is blocked for this column' => '', - // 'Task creation is not permitted' => '', - // 'Closing or opening a task is not permitted' => '', - // 'New drag and drop restriction for the role "%s"' => '', - // 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '', - // 'Remove a column restriction' => '', - // 'Do you really want to remove this column restriction: "%s" to "%s"?' => '', - // 'New column restriction for the role "%s"' => '', - // 'Rule' => '', - // 'Do you really want to remove this column restriction?' => '', - // 'Custom roles' => '', - // 'New custom project role' => '', - // 'Edit custom project role' => '', - // 'Remove a custom role' => '', - // 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '', - // 'There is no custom role for this project.' => '', - // 'New project restriction for the role "%s"' => '', - // 'Restriction' => '', - // 'Remove a project restriction' => '', - // 'Do you really want to remove this project restriction: "%s"?' => '', - // 'Duplicate to multiple projects' => '', - // 'This field is required' => '', - // 'Moving a task is not permitted' => '', - // 'This value must be in the range %d to %d' => '', - // 'You are not allowed to move this task.' => '', - // 'API User Access' => '', + 'Up to date' => 'Hasta la fecha', + 'Not available' => 'No disponible', + 'Remove plugin' => 'Eliminar plugin', + 'Do you really want to remove this plugin: "%s"?' => '¿Realmente desea eliminar este plugin: "%s"?', + 'Uninstall' => 'Desinstalar', + 'Listing' => 'Listado', + 'Metadata' => 'Metadata', + 'Manage projects' => 'Gestionar proyectos', + 'Convert to task' => 'Convertir en tarea', + 'Convert sub-task to task' => 'Convertir subtarea en tarea', + 'Do you really want to convert this sub-task to a task?' => '¿Realmente desea convertir esta subtarea a una tarea?', + 'My task title' => 'TÃtulo de mi tarea', + 'Enter one task by line.' => 'Ingrese una tarea por lÃnea.', + 'Number of failed login:' => 'Número de inicio de sesión fallido:', + 'Account locked until:' => 'Cuenta bloqueada hasta:', + 'Email settings' => 'Ajustes de correo electrónico', + 'Email sender address' => 'Dirección del remitente del correo electrónico', + 'Email transport' => 'Transporte por correo electrónico', + 'Webhook token' => 'Token Webhook', + 'Project tags management' => 'Gestión de etiquetas de proyecto', + 'Tag created successfully.' => 'Etiqueta creada correctamente.', + 'Unable to create this tag.' => 'No se puede crear esta etiqueta.', + 'Tag updated successfully.' => 'Etiqueta actualizada correctamente.', + 'Unable to update this tag.' => 'No se puede actualizar esta etiqueta.', + 'Tag removed successfully.' => 'Etiqueta eliminada correctamente.', + 'Unable to remove this tag.' => 'No se ha podido eliminar esta etiqueta.', + 'Global tags management' => 'Gestión de etiquetas globales', + 'Tags' => 'Etiquetas', + 'Tags management' => 'Gestión de etiquetas', + 'Add new tag' => 'Añadir nueva etiqueta', + 'Edit a tag' => 'Modificar una etiqueta', + 'Project tags' => 'Etiquetas del proyecto', + 'There is no specific tag for this project at the moment.' => 'No hay una etiqueta especÃfica para este proyecto en este momento.', + 'Tag' => 'Etiqueta', + 'Remove a tag' => 'Eliminar una etiqueta', + 'Do you really want to remove this tag: "%s"?' => '¿Desea realmente eliminar esta etiqueta: "%s"?', + 'Global tags' => 'Etiquetas globales', + 'There is no global tag at the moment.' => 'No hay una etiqueta global en este momento.', + 'This field cannot be empty' => 'Este campo no puede estar vacÃo', + 'Close a task when there is no activity in an specific column' => 'Cierra una tarea cuando no hay actividad en una columna especÃfica', + '%s removed a subtask for the task #%d' => '%s eliminó una subtarea para la tarea %d', + '%s removed a comment on the task #%d' => '%s eliminó un comentario para la tarea %d', + 'Comment removed on task #%d' => 'Comentario eliminado en la tarea #%d', + 'Subtask removed on task #%d' => 'Subtarea eliminada en la tarea #%d', + 'Hide tasks in this column in the dashboard' => 'Ocultar tareas en esta columna en el tablero', + '%s removed a comment on the task %s' => '%s eliminó un comentario para la tarea %s', + '%s removed a subtask for the task %s' => '%s eliminó una subtarea para la tarea %s', + 'Comment removed' => 'Comentario eliminado', + 'Subtask removed' => 'Sub-tarea eliminada', + '%s set a new internal link for the task #%d' => '%s estableció un nuevo enlace interno para la tarea %d', + '%s removed an internal link for the task #%d' => '%s eliminó un nuevo enlace interno para la tarea %d', + 'A new internal link for the task #%d have been defined' => 'Se ha definido un nuevo enlace interno para la tarea #%d', + 'Internal link removed for the task #%d' => 'Enlace interno eliminado para la tarea #%d', + '%s set a new internal link for the task %s' => '%s estableció un nuevo enlace interno para la tarea %s', + '%s removed an internal link for the task %s' => '%s eliminó un enlace interno para la tarea %s', + 'Automatically set the due date on task creation' => 'Establecer automáticamente la fecha de vencimiento en la creación de tareas', + 'Move the task to another column when closed' => 'Mover la tarea a otra columna cuando esté cerrada', + 'Move the task to another column when not moved during a given period' => 'Mover la tarea a otra columna cuando no se mueve durante un perÃodo determinado', + 'Dashboard for %s' => 'Tablero para %s', + 'Tasks overview for %s' => 'Visión general de tareas para %s', + 'Subtasks overview for %s' => 'Visión general de subtareas para %s', + 'Projects overview for %s' => 'Visión general de proyectos para %s', + 'Activity stream for %s' => 'Flujo de actividad para %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Asignar un color cuando la tarea se mueve a un carril especÃfico', + 'Assign a priority when the task is moved to a specific swimlane' => 'Asignar una prioridad cuando la tarea se mueve a un carril especÃfico', + 'User unlocked successfully.' => 'Usuario desbloqueado correctamente.', + 'Unable to unlock the user.' => 'No se puede desbloquear el usuario.', + 'Move a task to another swimlane' => 'Mueve una tarea a otro carril', + 'Creator Name' => 'Nombre del Creador', + 'Time spent and estimated' => 'Tiempo empleado y estimado', + 'Move position' => 'Mover la posición', + 'Move task to another position on the board' => 'Mover la tarea a otra posición en el tablero', + 'Insert before this task' => 'Insertar antes de esta tarea', + 'Insert after this task' => 'Insertar después de esta tarea', + 'Unlock this user' => 'Desbloquear este usuario', + 'Custom Project Roles' => 'Roles de proyecto personalizados', + 'Add a new custom role' => 'Agregar un nuevo rol personalizado', + 'Restrictions for the role "%s"' => 'Restricciones para el rol "%s"', + 'Add a new project restriction' => 'Añadir una nueva restricción de proyecto', + 'Add a new drag and drop restriction' => 'Añadir una nueva restricción de arrastrar y soltar', + 'Add a new column restriction' => 'Agregar una nueva restricción de columna', + 'Edit this role' => 'Editar este rol', + 'Remove this role' => 'Eliminar este rol', + 'There is no restriction for this role.' => 'No hay restricción para este rol.', + 'Only moving task between those columns is permitted' => 'Sólo se permite el movimiento de una tarea entre esas columnas', + 'Close a task in a specific column when not moved during a given period' => 'Cierra una tarea en una columna especÃfica cuando no se mueve durante un perÃodo determinado', + 'Edit columns' => 'Editar columnas', + 'The column restriction has been created successfully.' => 'La restricción de columna se ha creado correctamente.', + 'Unable to create this column restriction.' => 'No se puede crear esta restricción de columna.', + 'Column restriction removed successfully.' => 'Se ha eliminado correctamente la restricción de columna.', + 'Unable to remove this restriction.' => 'No se puede eliminar esta restricción.', + 'Your custom project role has been created successfully.' => 'El rol de proyecto personalizado se ha creado correctamente.', + 'Unable to create custom project role.' => 'No se puede crear un rol de proyecto personalizado.', + 'Your custom project role has been updated successfully.' => 'Su rol de proyecto personalizado se ha actualizado correctamente.', + 'Unable to update custom project role.' => 'No se puede actualizar el rol de proyecto personalizado.', + 'Custom project role removed successfully.' => 'El rol de proyecto personalizado se eliminó correctamente.', + 'Unable to remove this project role.' => 'No se pudo eliminar esta función del proyecto.', + 'The project restriction has been created successfully.' => 'La restricción del proyecto se ha creado con éxito.', + 'Unable to create this project restriction.' => 'No se puede crear esta restricción de proyecto.', + 'Project restriction removed successfully.' => 'Se ha eliminado correctamente la restricción de proyecto.', + 'You cannot create tasks in this column.' => 'No puede crear tareas en esta columna.', + 'Task creation is permitted for this column' => 'La creación de tareas está permitida para esta columna', + 'Closing or opening a task is permitted for this column' => 'El cierre o apertura de una tarea está permitido para esta columna', + 'Task creation is blocked for this column' => 'La creación de tareas está bloqueada para esta columna', + 'Closing or opening a task is blocked for this column' => 'El cierre o apertura de una tarea está bloqueado para esta columna', + 'Task creation is not permitted' => 'No se permite la creación de tareas', + 'Closing or opening a task is not permitted' => 'No se permite cerrar ni abrir una tarea', + 'New drag and drop restriction for the role "%s"' => 'Nueva restricción de arrastrar y soltar para el rol "%s"', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Las personas pertenecientes a este rol sólo podrán mover las tareas entre la columna de origen y la de destino.', + 'Remove a column restriction' => 'Eliminar una restricción de columna', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => '¿Realmente desea eliminar esta restricción de columna: "%s" a "%s"?', + 'New column restriction for the role "%s"' => 'Nueva restricción de columna para el rol "%s"', + 'Rule' => 'Regla', + 'Do you really want to remove this column restriction?' => '¿Realmente desea eliminar esta restricción de columna?', + 'Custom roles' => 'Roles personalizados', + 'New custom project role' => 'Nuevo rol de proyecto personalizado', + 'Edit custom project role' => 'Editar un rol de proyecto personalizado', + 'Remove a custom role' => 'Eliminar un rol personalizado', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '¿Realmente desea eliminar este rol personalizado: "%s"? Todas las personas asignadas a este rol se convertirán en miembros del proyecto.', + 'There is no custom role for this project.' => 'No hay ningun rol personalizado para este proyecto.', + 'New project restriction for the role "%s"' => 'Nueva restricción de proyecto para el rol "%s"', + 'Restriction' => 'Restricción', + 'Remove a project restriction' => 'Eliminar una restricción de proyecto', + 'Do you really want to remove this project restriction: "%s"?' => '¿Realmente desea eliminar esta restricción de proyecto: "%s"?', + 'Duplicate to multiple projects' => 'Duplicar en varios proyectos', + 'This field is required' => 'Este campo es requerido', + 'Moving a task is not permitted' => 'No se permite mover una tarea', + 'This value must be in the range %d to %d' => 'Este valor debe estar en el rango %d a %d', + 'You are not allowed to move this task.' => 'No se le permite mover esta tarea.', + 'API User Access' => 'Acceso de usuario de API', 'Preview' => 'Previsualizar', 'Write' => 'Escribir', 'Write your text in Markdown' => 'Redacta el texto en Markdown', - // 'New External Task: %s' => '', - // 'No personal API access token registered.' => '', - // 'Your personal API access token is "%s"' => '', - // 'Remove your token' => '', - // 'Generate a new token' => '', - // 'Showing %d-%d of %d' => '', - // 'Outgoing Emails' => '', - // 'Add or change currency rate' => '', - // 'Reference currency: %s' => '', - // 'Add custom filters' => '', - // 'Export' => '', - // 'Add link label' => '', - // 'Incompatible Plugins' => '', - // 'Compatibility' => '', - // 'Permissions and ownership' => '', - // 'Priorities' => '', - // 'Close this window' => '', - // 'Unable to upload this file.' => '', - // 'Import tasks' => '', - // 'Choose a project' => '', - // 'Profile' => '', - // 'Application role' => '', - // '%d invitations were sent.' => '', - // '%d invitation was sent.' => '', - // 'Unable to create this user.' => '', - // 'Kanboard Invitation' => '', - // 'Visible on dashboard' => '', - // 'Created at:' => '', - // 'Updated at:' => '', - // 'There is no custom filter.' => '', - // 'New User' => '', - // 'Authentication' => '', - // 'If checked, this user will use a third-party system for authentication.' => '', - // 'The password is necessary only for local users.' => '', - // 'You have been invited to register on Kanboard.' => '', - // 'Click here to join your team' => '', - // 'Invite people' => '', - // 'Emails' => '', - // 'Enter one email address by line.' => '', - // 'Add these people to this project' => '', - // 'Add this person to this project' => '', - // 'Sign-up' => '', - // 'Credentials' => '', + 'No personal API access token registered.' => 'No se ha registrado ningún token de acceso a API personal.', + 'Your personal API access token is "%s"' => 'Su token de acceso a la API personal es "%s"', + 'Remove your token' => 'Elimine su token', + 'Generate a new token' => 'Generar un nuevo token', + 'Showing %d-%d of %d' => 'Mostrando %d-%d de %d', + 'Outgoing Emails' => 'Correo electrónico saliente', + 'Add or change currency rate' => 'Añadir o cambiar el tipo de cambio', + 'Reference currency: %s' => 'Divisa de referencia: %s', + 'Add custom filters' => 'Añadir filtros personalizados', + 'Export' => 'Exportar', + 'Add link label' => 'Añadir etiqueta de enlace', + 'Incompatible Plugins' => 'Plugins incompatibles', + 'Compatibility' => 'Compatibilidad', + 'Permissions and ownership' => 'Permisos y propiedad', + 'Priorities' => 'Prioridades', + 'Close this window' => 'Cierra esta ventana', + 'Unable to upload this file.' => 'No se puede subir este archivo.', + 'Import tasks' => 'Importar tareas', + 'Choose a project' => 'Elija un proyecto', + 'Profile' => 'Perfil', + 'Application role' => 'Rol de la aplicación', + '%d invitations were sent.' => '%d invitaciones enviadas.', + '%d invitation was sent.' => '%d invitación fue enviada.', + 'Unable to create this user.' => 'No se puede crear este usuario.', + 'Kanboard Invitation' => 'Invitación de Kanboard', + 'Visible on dashboard' => 'Visible en el tablero', + 'Created at:' => 'Creado en:', + 'Updated at:' => 'Actualizado en:', + 'There is no custom filter.' => 'No hay un filtro personalizado.', + 'New User' => 'Nuevo usuario', + 'Authentication' => 'Autenticación', + 'If checked, this user will use a third-party system for authentication.' => 'Si está marcado, este usuario utilizará un sistema de terceros para la autenticación.', + 'The password is necessary only for local users.' => 'La contraseña sólo es necesaria para los usuarios locales.', + 'You have been invited to register on Kanboard.' => 'Te han invitado a registrarte en Kanboard.', + 'Click here to join your team' => 'Haz clic aquà para unirte a tu equipo', + 'Invite people' => 'Invitar personas', + 'Emails' => 'Emails', + 'Enter one email address by line.' => 'Ingrese una dirección de correo electrónico por lÃnea.', + 'Add these people to this project' => 'Añadir a estas personas a este proyecto', + 'Add this person to this project' => 'Añadir a esta persona a este proyecto', + 'Sign-up' => 'RegÃstrate', + 'Credentials' => 'Credenciales', 'New user' => 'Añadir un usuario', - // 'This username is already taken' => '', - // 'A link to reset your password has been sent by email.' => '', - // 'Your profile must have a valid email address.' => '', - // 'Unfortunately, we are unable to reset your password. Did you entered a valid username? Do you have an email address in your profile?' => '', - // 'TRL - Turkish Lira' => '', - // 'The project email is optional and could be used by several plugins.' => '', - // 'The project email must be unique across all projects' => '', - // 'The email configuration has been disabled by the administrator.' => '', - // 'Close this project' => '', - // 'Open this project' => '', - // 'Close a project' => '', - // 'Do you really want to close this project: "%s"?' => '', - // 'Reopen a project' => '', - // 'Do you really want to reopen this project: "%s"?' => '', - // 'This project is open' => '', - // 'This project is closed' => '', - // 'Unable to upload files, check the permissions of your data folder.' => '', - // 'Another category with the same name exists in this project' => '', - // 'Comment sent by email successfully.' => '', - // 'Sent by email to [%s](mailto:%s) (%s)' => '', - // 'Unable to read uploaded file.' => '', - // 'Database uploaded successfully.' => '', - // 'Task sent by email successfully.' => '', - // 'There is no category in this project.' => '', - // 'Send by email' => '', - // 'Create and send a comment by email' => '', - // 'Subject' => '', - // 'Upload the database' => '', - // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', - // 'Database file' => '', - // 'Upload' => '', - // 'Remove this user from group' => '', - // 'Your project must have at least one active swimlane.' => '', - // 'Project: %s' => '', - // 'Automatic action not found: "%s"' => '', - // '%d projects' => '', - // '%d project' => '', - // 'There is no project.' => '', - // 'Sort' => '', - // 'Project ID' => '', - // 'Project name' => '', - // 'Public' => '', - // 'Private' => '', - // '%d tasks' => '', - // '%d task' => '', - // 'Task ID' => '', - // 'Assign automatically a color when due date is expired' => '', - // 'Total score in this column across all swimlanes' => '', - // 'HRK - Kuna' => '', + 'This username is already taken' => 'Este nombre de usuario ya está en uso', + 'A link to reset your password has been sent by email.' => 'Se ha enviado por correo electrónico un enlace para restablecer su contraseña.', + 'Your profile must have a valid email address.' => 'Su perfil debe tener una dirección de email válido.', + 'Unfortunately, we are unable to reset your password. Did you entered a valid username? Do you have an email address in your profile?' => 'Desafortunadamente, no nos es posible reiniciar su contraseña. ¿Usted introdujo un usuario válido?, ¿Usted tiene una dirección de email válida en su perfil?', + 'TRL - Turkish Lira' => 'TRL - Lira Turca', + 'The project email is optional and could be used by several plugins.' => 'El email del proyecto es opcional y podrÃa ser usado por varios plugins', + 'The project email must be unique across all projects' => 'El email del proyecto debe ser único entre todos los proyectos', + 'The email configuration has been disabled by the administrator.' => 'La configuración de email se ha deshabilitado por el administrador', + 'Close this project' => 'Cerrar este proyecto', + 'Open this project' => 'Abrir este proyecto', + 'Close a project' => 'Cerrar un proyecto', + 'Do you really want to close this project: "%s"?' => '¿Realmente quiere cerrar este proyecto: "%s"?', + 'Reopen a project' => 'Reabrir un proyecto', + 'Do you really want to reopen this project: "%s"?' => '¿Realmente quiere reabrir este proyecto: "%s"?', + 'This project is open' => 'Este proyecto está abierdo', + 'This project is closed' => 'Este proyecto está cerrado', + 'Unable to upload files, check the permissions of your data folder.' => 'No se pueden cargar archivos, verifique los permisos de su carpeta de datos (data)', + 'Another category with the same name exists in this project' => 'Ya existe otra categorÃa con el mismo nombre en este proyecto', + 'Comment sent by email successfully.' => 'Comentario enviado exitosamente por email', + 'Sent by email to [%s](mailto:%s) (%s)' => 'Enviado por email a [%s](mailto:%s) (%s)', + 'Unable to read uploaded file.' => 'No se pudo leer el archivo cargado', + 'Database uploaded successfully.' => 'Base de datos cargada exitosamente', + 'Task sent by email successfully.' => 'Tarea enviada exitosamente por email', + 'There is no category in this project.' => 'No hay categorÃas en este proyecto', + 'Send by email' => 'Enviar por email', + 'Create and send a comment by email' => 'Crear y enviar un comentario por email', + 'Subject' => 'Asunto', + 'Upload the database' => 'Cargar la base de datos', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'PodrÃa cargar la base de datos Sqlite descargada previamente (formato Gzip)', + 'Database file' => 'Archivo de Base de Datos', + 'Upload' => 'Cargar', + 'Your project must have at least one active swimlane.' => 'Su proyecto debe tener al menos un carril activo', + 'Project: %s' => 'Proyecto: %s', + 'Automatic action not found: "%s"' => 'No se encontró la acción automática: "%s"', + '%d projects' => '%d proyectos', + '%d project' => '%d proyecto', + 'There is no project.' => 'No hay proyecto', + 'Sort' => 'Ordenar', + 'Project ID' => 'ID Proyecto', + 'Project name' => 'Nombre del Proyecto', + 'Public' => 'Público', + 'Private' => 'Privado', + '%d tasks' => '%d tareas', + '%d task' => '%d tarea', + 'Task ID' => 'ID Tarea', + 'Assign automatically a color when due date is expired' => 'Asignar un color automáticamente cuando la fecha de vencimiento haya expirado', + 'Total score in this column across all swimlanes' => 'Puntaje Total en esta columna para todos los carriles', + 'HRK - Kuna' => 'HRK - Kuna', + 'ARS - Argentine Peso' => 'ARS - Peso Argentino', + 'COP - Colombian Peso' => 'COP - Peso Colombiano', + '%d groups' => '%d grupos', + '%d group' => '%d grupo', + 'Group ID' => 'ID Grupo', + 'External ID' => 'ID Externa', + '%d users' => '%d usuarios', + '%d user' => '%d usuario', + 'Hide subtasks' => 'Ocultar subtareas', + 'Show subtasks' => 'Mostrar subtareas', + 'Authentication Parameters' => 'Parámetros de Autenticación', + 'API Access' => 'Acceso API', + 'No users found.' => 'No se encontraron usuarios', + 'User ID' => 'ID del Usuario', + 'Notifications are activated' => 'Las notificaciones están activadas', + 'Notifications are disabled' => 'Las notificaciones están deshabilitadas', + 'User disabled' => 'Usuario deshabilitado', + '%d notifications' => '%d notificaciones', + '%d notification' => '%d notificación', + 'There is no external integration installed.' => 'No se ha instalado ninguna integración externa.', + 'You are not allowed to update tasks assigned to someone else.' => 'No se le permite actualizar tareas asignadas a alguien más.', + 'You are not allowed to change the assignee.' => 'No se le permite cambiar la persona asignada.', + 'Task suppression is not permitted' => 'No está permitido la eliminación de tarea', + 'Changing assignee is not permitted' => 'No está permitido cambiar la persona asignada', + 'Update only assigned tasks is permitted' => 'Está permitido actualizar solo las tareas asignadas', + 'Only for tasks assigned to the current user' => 'Solo para las tareas asignadas al usuario actual', + 'My projects' => 'Mis proyectos', + 'Your are not member of any project.' => 'No eres miembro de ningún proyecto.', + 'My subtasks' => 'Mis subtareas', + '%d subtasks' => '%d subtareas', + '%d subtask' => '%d subtarea', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Mover tareas entre esas columnas solo está permitido para las tareas asignadas al usuario actual.', + '[DUPLICATE]' => '[DUPLICAR]', + 'DKK - Danish Krona' => 'DKK - Corona Danesa', + 'Remove user from group' => 'Eliminar usuario del grupo', + 'Assign the task to its creator' => 'Asignar la tarea a su creador', + 'This task was sent by email to "%s" with subject "%s".' => 'Esta tarea fue enviada por correo a "%s" con el asunto "%s".', + 'Predefined Email Subjects' => 'Asuntos de correo predefinidos', + 'Write one subject by line.' => 'Escriba un asunto por lÃnea.', + // 'Create another link' => '', + // 'BRL - Brazilian Real' => '', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/fi_FI/translations.php b/app/Locale/fi_FI/translations.php index ee7165c8..3c4915a6 100644 --- a/app/Locale/fi_FI/translations.php +++ b/app/Locale/fi_FI/translations.php @@ -39,7 +39,6 @@ return array( 'Administrator' => 'Ylläpitäjä', 'Sign in' => 'Kirjaudu sisään', 'Users' => 'Käyttäjät', - 'No user' => 'Ei käyttäjää', 'Forbidden' => 'Estetty', 'Access Forbidden' => 'Pääsy estetty', 'Edit user' => 'Muokkaa käyttäjää', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'Alitehtävä päivitettiin onnistuneesti.', 'Unable to update your sub-task.' => 'Alitehtävän päivitys epäonnistui.', 'Unable to create your sub-task.' => 'Alitehtävän luonti epäonnistui.', - 'Sub-task added successfully.' => 'Alitehtävä luotiin onnistuneesti.', 'Maximum size: ' => 'Maksimikoko: ', 'Display another project' => 'Näytä toinen projekti', 'Created by %s' => 'Luonut: %s', @@ -396,22 +394,17 @@ return array( 'Activity stream' => 'Toiminta', 'Dashboard' => 'Työpöytä', 'Confirmation' => 'Vahvistus', - 'Allow everybody to access to this project' => 'Anna kaikille käyttöoikeus tähän projektiin', - 'Everybody have access to this project.' => 'Kaikilla on käyttöoikeus projektiin.', // 'Webhooks' => '', // 'API' => '', // 'Create a comment from an external provider' => '', 'Project management' => 'Projektin hallinta', - 'My projects' => 'Minun projektini', 'Columns' => 'Sarakkeet', 'Task' => 'Tehtävät', - 'Your are not member of any project.' => 'Et ole minkään projektin jäsen.', 'Percentage' => 'Prosentti', 'Number of tasks' => 'Tehtävien määrä', 'Task distribution' => 'Tehtävien jakauma', 'Analytics' => 'Analytiikka', 'Subtask' => 'Alitehtävä', - 'My subtasks' => 'Minun alitehtäväni', // 'User repartition' => '', 'Clone this project' => 'Kahdenna projekti', 'Column removed successfully.' => 'Sarake poistettu onnstuneesti.', @@ -459,7 +452,6 @@ return array( // 'Language:' => '', // 'Timezone:' => '', // 'All columns' => '', - // 'Calendar' => '', // 'Next' => '', // '#%d' => '', // 'All swimlanes' => '', @@ -557,7 +549,6 @@ return array( // 'Unable to add this currency rate.' => '', // 'Webhook URL' => '', // '%s removed the assignee of the task %s' => '', - // 'Enable Gravatar images' => '', // 'Information' => '', // 'Check two factor authentication code' => '', // 'The two factor authentication code is not valid.' => '', @@ -611,14 +602,7 @@ return array( // 'When task is moved from first column' => '', // 'When task is moved to last column' => '', // 'Year(s)' => '', - // 'Calendar settings' => '', - // 'Project calendar view' => '', // 'Project settings' => '', - // 'Show subtasks based on the time tracking' => '', - // 'Show tasks based on the creation date' => '', - // 'Show tasks based on the start date' => '', - // 'Subtasks time tracking' => '', - // 'User calendar view' => '', // 'Automatically update the start date' => '', // 'iCal feed' => '', // 'Preferences' => '', @@ -637,7 +621,6 @@ return array( // 'Notification' => '', // '%s moved the task #%d to the first swimlane' => '', // 'Swimlane' => '', - // 'Gravatar' => '', // '%s moved the task %s to the first swimlane' => '', // '%s moved the task %s to the swimlane "%s"' => '', // 'This report contains all subtasks information for the given date range.' => '', @@ -674,7 +657,6 @@ return array( // 'Stop timer' => '', // 'Start timer' => '', // 'My activity stream' => '', - // 'My calendar' => '', // 'Search tasks' => '', // 'Reset filters' => '', // 'My tasks due tomorrow' => '', @@ -688,7 +670,6 @@ return array( // 'Overview' => '', // 'Board/Calendar/List view' => '', // 'Switch to the board view' => '', - // 'Switch to the calendar view' => '', // 'Switch to the list view' => '', // 'Go to the search/filter box' => '', // 'There is no activity yet.' => '', @@ -743,15 +724,8 @@ return array( // 'License:' => '', // 'License' => '', // 'Enter the text below' => '', - // 'Sort by position' => '', - // 'Sort by date' => '', - // 'Add task' => '', // 'Start date:' => '', // 'Due date:' => '', - // 'There is no start date or due date for this task.' => '', - // 'Moving or resizing a task will change the start and due date of the task.' => '', - // 'There is no task in your project.' => '', - // 'Gantt chart' => '', // 'People who are project managers' => '', // 'People who are project members' => '', // 'NOK - Norwegian Krone' => '', @@ -763,22 +737,15 @@ return array( // 'Members' => '', // 'Shared project' => '', // 'Project managers' => '', - // 'Gantt chart for all projects' => '', // 'Projects list' => '', - // 'Gantt chart for this project' => '', - // 'Project board' => '', // 'End date:' => '', - // 'There is no start date or end date for this project.' => '', - // 'Projects Gantt chart' => '', // 'Change task color when using a specific task link' => '', // 'Task link creation or modification' => '', // 'Milestone' => '', // 'Documentation: %s' => '', - // 'Switch to the Gantt chart view' => '', // 'Reset the search/filter box' => '', // 'Documentation' => '', // 'Table of contents' => '', - // 'Gantt' => '', // 'Author' => '', // 'Version' => '', // 'Plugins' => '', @@ -889,7 +856,6 @@ return array( // 'There is no user available.' => '', // 'Do you really want to remove the user "%s" from the group "%s"?' => '', // 'There is no group.' => '', - // 'External Id' => '', // 'Add group member' => '', // 'Do you really want to remove this group: "%s"?' => '', // 'There is no user in this group.' => '', @@ -955,7 +921,6 @@ return array( // 'Default priority' => '', // 'Lowest priority' => '', // 'Highest priority' => '', - // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Close a task when there is no activity' => '', // 'Duration in days' => '', // 'Send email when there is no activity on a task' => '', @@ -1169,8 +1134,6 @@ return array( // 'Subtasks overview for %s' => '', // 'Projects overview for %s' => '', // 'Activity stream for %s' => '', - // 'Calendar for %s' => '', - // 'Notifications for %s' => '', // 'Assign a color when the task is moved to a specific swimlane' => '', // 'Assign a priority when the task is moved to a specific swimlane' => '', // 'User unlocked successfully.' => '', @@ -1241,7 +1204,6 @@ return array( // 'Preview' => '', // 'Write' => '', // 'Write your text in Markdown' => '', - // 'New External Task: %s' => '', // 'No personal API access token registered.' => '', // 'Your personal API access token is "%s"' => '', // 'Remove your token' => '', @@ -1316,7 +1278,6 @@ return array( // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', // 'Database file' => '', // 'Upload' => '', - // 'Remove this user from group' => '', // 'Your project must have at least one active swimlane.' => '', // 'Project: %s' => '', // 'Automatic action not found: "%s"' => '', @@ -1334,4 +1295,75 @@ return array( // 'Assign automatically a color when due date is expired' => '', // 'Total score in this column across all swimlanes' => '', // 'HRK - Kuna' => '', + // 'ARS - Argentine Peso' => '', + // 'COP - Colombian Peso' => '', + // '%d groups' => '', + // '%d group' => '', + // 'Group ID' => '', + // 'External ID' => '', + // '%d users' => '', + // '%d user' => '', + // 'Hide subtasks' => '', + // 'Show subtasks' => '', + // 'Authentication Parameters' => '', + // 'API Access' => '', + // 'No users found.' => '', + // 'User ID' => '', + // 'Notifications are activated' => '', + // 'Notifications are disabled' => '', + // 'User disabled' => '', + // '%d notifications' => '', + // '%d notification' => '', + // 'There is no external integration installed.' => '', + // 'You are not allowed to update tasks assigned to someone else.' => '', + // 'You are not allowed to change the assignee.' => '', + // 'Task suppression is not permitted' => '', + // 'Changing assignee is not permitted' => '', + // 'Update only assigned tasks is permitted' => '', + // 'Only for tasks assigned to the current user' => '', + // 'My projects' => '', + // 'Your are not member of any project.' => '', + // 'My subtasks' => '', + // '%d subtasks' => '', + // '%d subtask' => '', + // 'Only moving task between those columns is permitted for tasks assigned to the current user' => '', + // '[DUPLICATE]' => '', + // 'DKK - Danish Krona' => '', + // 'Remove user from group' => '', + // 'Assign the task to its creator' => '', + // 'This task was sent by email to "%s" with subject "%s".' => '', + // 'Predefined Email Subjects' => '', + // 'Write one subject by line.' => '', + // 'Create another link' => '', + // 'BRL - Brazilian Real' => '', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/fr_FR/translations.php b/app/Locale/fr_FR/translations.php index 4a34d7f3..c86aa18e 100644 --- a/app/Locale/fr_FR/translations.php +++ b/app/Locale/fr_FR/translations.php @@ -28,18 +28,17 @@ return array( 'Amber' => 'Ambre', 'Save' => 'Enregistrer', 'Login' => 'Connexion', - 'Official website:' => 'Site web officiel :', + 'Official website:' => 'Site web officiel :', 'Unassigned' => 'Non assigné', 'View this task' => 'Voir cette tâche', 'Remove user' => 'Supprimer un utilisateur', - 'Do you really want to remove this user: "%s"?' => 'Voulez-vous vraiment supprimer cet utilisateur : « %s » ?', + 'Do you really want to remove this user: "%s"?' => 'Voulez-vous vraiment supprimer cet utilisateur : « %s » ?', 'All users' => 'Tous les utilisateurs', 'Username' => 'Identifiant', 'Password' => 'Mot de passe', 'Administrator' => 'Administrateur', 'Sign in' => 'Connexion', 'Users' => 'Utilisateurs', - 'No user' => 'Aucun utilisateur', 'Forbidden' => 'Accès interdit', 'Access Forbidden' => 'Accès interdit', 'Edit user' => 'Modifier un utilisateur', @@ -60,25 +59,25 @@ return array( 'Disable' => 'Désactiver', 'Enable' => 'Activer', 'New project' => 'Nouveau projet', - 'Do you really want to remove this project: "%s"?' => 'Voulez-vous vraiment supprimer ce projet : « %s » ?', + 'Do you really want to remove this project: "%s"?' => 'Voulez-vous vraiment supprimer ce projet : « %s » ?', 'Remove project' => 'Supprimer le projet', - 'Edit the board for "%s"' => 'Modifier le tableau pour « %s »', + 'Edit the board for "%s"' => 'Modifier le tableau pour « %s »', 'Add a new column' => 'Ajouter une nouvelle colonne', 'Title' => 'Titre', 'Assigned to %s' => 'Assigné à %s', 'Remove a column' => 'Supprimer une colonne', 'Unable to remove this column.' => 'Impossible de supprimer cette colonne.', - 'Do you really want to remove this column: "%s"?' => 'Voulez vraiment supprimer cette colonne : « %s » ?', + 'Do you really want to remove this column: "%s"?' => 'Voulez vraiment supprimer cette colonne : « %s » ?', 'Settings' => 'Préférences', 'Application settings' => 'Paramètres de l\'application', 'Language' => 'Langue', - 'Webhook token:' => 'Jeton de securité pour les webhooks :', - 'API token:' => 'Jeton de securité pour l\'API :', + 'Webhook token:' => 'Jeton de sécurité pour les webhooks :', + 'API token:' => 'Jeton de sécurité pour l\'API :', 'Database size:' => 'Taille de la base de données :', 'Download the database' => 'Télécharger la base de données', 'Optimize the database' => 'Optimiser la base de données', '(VACUUM command)' => '(Commande VACUUM)', - '(Gzip compressed Sqlite file)' => '(Fichier Sqlite compressé en Gzip)', + '(Gzip compressed Sqlite file)' => '(Fichier SQLite compressé en Gzip)', 'Close a task' => 'Fermer une tâche', 'Column' => 'Colonne', 'Color' => 'Couleur', @@ -86,7 +85,7 @@ return array( 'Create another task' => 'Créer une autre tâche', 'New task' => 'Nouvelle tâche', 'Open a task' => 'Ouvrir une tâche', - 'Do you really want to open this task: "%s"?' => 'Voulez-vous vraiment ouvrir cette tâche : « %s » ?', + 'Do you really want to open this task: "%s"?' => 'Voulez-vous vraiment ouvrir cette tâche : « %s » ?', 'Back to the board' => 'Retour au tableau', 'There is nobody assigned' => 'Il n\'y a personne d\'assigné à cette tâche', 'Column on the board:' => 'Colonne sur le tableau : ', @@ -102,7 +101,7 @@ return array( 'The username must be unique' => 'Le nom d\'utilisateur doit être unique', 'The user id is required' => 'L\'id de l\'utilisateur est obligatoire', 'Passwords don\'t match' => 'Les mots de passe ne correspondent pas', - 'The confirmation is required' => 'Le confirmation est requise', + 'The confirmation is required' => 'La confirmation est requise', 'The project is required' => 'Le projet est obligatoire', 'The id is required' => 'L\'identifiant est obligatoire', 'The project id is required' => 'L\'identifiant du projet est obligatoire', @@ -110,7 +109,7 @@ return array( 'The title is required' => 'Le titre est obligatoire', 'Settings saved successfully.' => 'Paramètres sauvegardés avec succès.', 'Unable to save your settings.' => 'Impossible de sauvegarder vos réglages.', - 'Database optimization done.' => 'Optmisation de la base de données terminée.', + 'Database optimization done.' => 'Optimisation de la base de données terminée.', 'Your project have been created successfully.' => 'Votre projet a été créé avec succès.', 'Unable to create your project.' => 'Impossible de créer un projet.', 'Project updated successfully.' => 'Votre projet a été mis à jour avec succès.', @@ -139,11 +138,11 @@ return array( 'Backlog' => 'En attente', 'Work in progress' => 'En cours', 'Done' => 'Terminé', - 'Application version:' => 'Version de l\'application :', + 'Application version:' => 'Version de l\'application :', 'Id' => 'Id.', 'Public link' => 'Lien public', 'Timezone' => 'Fuseau horaire', - 'Sorry, I didn\'t find this information in my database!' => 'Désolé, je n\'ai pas trouvé cette information dans ma base de données !', + 'Sorry, I didn\'t find this information in my database!' => 'Désolé, je n\'ai pas trouvé cette information dans ma base de données !', 'Page not found' => 'Page introuvable', 'Complexity' => 'Complexité', 'Task limit' => 'Tâches Max.', @@ -161,15 +160,15 @@ return array( 'Remove an action' => 'Supprimer une action', 'Unable to remove this action.' => 'Impossible de supprimer cette action', 'Action removed successfully.' => 'Action supprimée avec succès.', - 'Automatic actions for the project "%s"' => 'Actions automatisées pour le projet « %s »', + 'Automatic actions for the project "%s"' => 'Actions automatisées pour le projet « %s »', 'Add an action' => 'Ajouter une action', - 'Event name' => 'Nom de l\'événement', + 'Event name' => 'Nom de l\'évènement', 'Action' => 'Action', - 'Event' => 'Événement', - 'When the selected event occurs execute the corresponding action.' => 'Lorsque l\'événement sélectionné se déclenche, exécuter l\'action correspondante.', + 'Event' => 'Évènement', + 'When the selected event occurs execute the corresponding action.' => 'Lorsque l\'évènement sélectionné se déclenche, exécuter l\'action correspondante.', 'Next step' => 'Étape suivante', 'Define action parameters' => 'Définition des paramètres de l\'action', - 'Do you really want to remove this action: "%s"?' => 'Voulez-vous vraiment supprimer cette action « %s » ?', + 'Do you really want to remove this action: "%s"?' => 'Voulez-vous vraiment supprimer cette action « %s » ?', 'Remove an automatic action' => 'Supprimer une action automatisée', 'Assign the task to a specific user' => 'Assigner la tâche à un utilisateur spécifique', 'Assign the task to the person who does the action' => 'Assigner la tâche à la personne qui fait l\'action', @@ -188,8 +187,8 @@ return array( 'Remove a comment' => 'Supprimer un commentaire', 'Comment removed successfully.' => 'Commentaire supprimé avec succès.', 'Unable to remove this comment.' => 'Impossible de supprimer ce commentaire.', - 'Do you really want to remove this comment?' => 'Voulez-vous vraiment supprimer ce commentaire ?', - 'Current password for the user "%s"' => 'Mot de passe actuel pour l\'utilisateur « %s »', + 'Do you really want to remove this comment?' => 'Voulez-vous vraiment supprimer ce commentaire ?', + 'Current password for the user "%s"' => 'Mot de passe actuel pour l\'utilisateur « %s »', 'The current password is required' => 'Le mot de passe actuel est obligatoire', 'Wrong password' => 'Mot de passe invalide', 'Unknown' => 'Inconnu', @@ -221,12 +220,12 @@ return array( 'Task removed successfully.' => 'Tâche supprimée avec succès.', 'Unable to remove this task.' => 'Impossible de supprimer cette tâche.', 'Remove a task' => 'Supprimer une tâche', - 'Do you really want to remove this task: "%s"?' => 'Voulez-vous vraiment supprimer cette tâche « %s » ?', + 'Do you really want to remove this task: "%s"?' => 'Voulez-vous vraiment supprimer cette tâche « %s » ?', 'Assign automatically a color based on a category' => 'Assigner automatiquement une couleur par rapport à une catégorie définie', 'Assign automatically a category based on a color' => 'Assigner automatiquement une catégorie par rapport à une couleur définie', 'Task creation or modification' => 'Création ou modification d\'une tâche', 'Category' => 'Catégorie', - 'Category:' => 'Catégorie :', + 'Category:' => 'Catégorie :', 'Categories' => 'Catégories', 'Your category have been created successfully.' => 'Votre catégorie a été créée avec succès.', 'This category has been updated successfully.' => 'Cette catégorie a été mise à jour avec succès.', @@ -234,10 +233,10 @@ return array( 'Remove a category' => 'Supprimer une catégorie', 'Category removed successfully.' => 'Catégorie supprimée avec succès.', 'Unable to remove this category.' => 'Impossible de supprimer cette catégorie.', - 'Category modification for the project "%s"' => 'Modification d\'une catégorie pour le projet « %s »', + 'Category modification for the project "%s"' => 'Modification d\'une catégorie pour le projet « %s »', 'Category Name' => 'Nom de la catégorie', 'Add a new category' => 'Ajouter une nouvelle catégorie', - 'Do you really want to remove this category: "%s"?' => 'Voulez-vous vraiment supprimer cette catégorie « %s » ?', + 'Do you really want to remove this category: "%s"?' => 'Voulez-vous vraiment supprimer cette catégorie « %s » ?', 'All categories' => 'Toutes les catégories', 'No category' => 'Aucune catégorie', 'The name is required' => 'Le nom est requis', @@ -245,16 +244,16 @@ return array( 'Unable to remove this file.' => 'Impossible de supprimer ce fichier.', 'File removed successfully.' => 'Fichier supprimé avec succès.', 'Attach a document' => 'Joindre un document', - 'Do you really want to remove this file: "%s"?' => 'Voulez-vous vraiment supprimer ce fichier « %s » ?', + 'Do you really want to remove this file: "%s"?' => 'Voulez-vous vraiment supprimer ce fichier « %s » ?', 'Attachments' => 'Pièces-jointes', 'Edit the task' => 'Modifier la tâche', 'Add a comment' => 'Ajouter un commentaire', 'Edit a comment' => 'Modifier un commentaire', 'Summary' => 'Résumé', 'Time tracking' => 'Suivi du temps', - 'Estimate:' => 'Estimation :', + 'Estimate:' => 'Estimation :', 'Spent:' => 'Passé :', - 'Do you really want to remove this sub-task?' => 'Voulez-vous vraiment supprimer cette sous-tâche ?', + 'Do you really want to remove this sub-task?' => 'Voulez-vous vraiment supprimer cette sous-tâche ?', 'Remaining:' => 'Restant :', 'hours' => 'heures', 'spent' => 'passé', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'Sous-tâche mise à jour avec succès.', 'Unable to update your sub-task.' => 'Impossible de mettre à jour votre sous-tâche.', 'Unable to create your sub-task.' => 'Impossible de créer votre sous-tâche.', - 'Sub-task added successfully.' => 'Sous-tâche ajoutée avec succès.', 'Maximum size: ' => 'Taille maximum : ', 'Display another project' => 'Afficher un autre projet', 'Created by %s' => 'Créé par %s', @@ -288,22 +286,22 @@ return array( 'Clone' => 'Clone', 'Project cloned successfully.' => 'Projet cloné avec succès.', 'Unable to clone this project.' => 'Impossible de cloner ce projet.', - 'Enable email notifications' => 'Activer les notifications par emails', - 'Task position:' => 'Position de la tâche :', + 'Enable email notifications' => 'Activer les notifications par email', + 'Task position:' => 'Position de la tâche :', 'The task #%d have been opened.' => 'La tâche #%d a été ouverte.', 'The task #%d have been closed.' => 'La tâche #%d a été fermée.', 'Sub-task updated' => 'Sous-tâche mise à jour', - 'Title:' => 'Titre :', - 'Status:' => 'État :', - 'Assignee:' => 'Assigné :', - 'Time tracking:' => 'Gestion du temps :', + 'Title:' => 'Titre :', + 'Status:' => 'État :', + 'Assignee:' => 'Assigné :', + 'Time tracking:' => 'Gestion du temps :', 'New sub-task' => 'Nouvelle sous-tâche', - 'New attachment added "%s"' => 'Nouvelle pièce-jointe ajoutée « %s »', - 'New comment posted by %s' => 'Nouveau commentaire ajouté par « %s »', + 'New attachment added "%s"' => 'Nouvelle pièce-jointe ajoutée « %s »', + 'New comment posted by %s' => 'Nouveau commentaire ajouté par « %s »', 'New comment' => 'Nouveau commentaire', 'Comment updated' => 'Commentaire mis à jour', 'New subtask' => 'Nouvelle sous-tâche', - 'I want to receive notifications only for those projects:' => 'Je souhaite reçevoir les notifications uniquement pour les projets sélectionnés :', + 'I want to receive notifications only for those projects:' => 'Je souhaite reçevoir les notifications uniquement pour les projets sélectionnés :', 'view the task on Kanboard' => 'voir la tâche sur Kanboard', 'Public access' => 'Accès public', 'Disable public access' => 'Désactiver l\'accès public', @@ -311,7 +309,7 @@ return array( 'Public access disabled' => 'Accès public désactivé', 'Move the task to another project' => 'Déplacer la tâche vers un autre projet', 'Move to another project' => 'Déplacer vers un autre projet', - 'Do you really want to duplicate this task?' => 'Voulez-vous vraiment dupliquer cette tâche ?', + 'Do you really want to duplicate this task?' => 'Voulez-vous vraiment dupliquer cette tâche ?', 'Duplicate a task' => 'Dupliquer une tâche', 'External accounts' => 'Comptes externes', 'Account type' => 'Type de compte', @@ -319,12 +317,12 @@ return array( 'Remote' => 'Distant', 'Enabled' => 'Activé', 'Disabled' => 'Désactivé', - 'Login:' => 'Nom d\'utilisateur :', - 'Full Name:' => 'Nom :', - 'Email:' => 'Email :', - 'Notifications:' => 'Notifications :', + 'Login:' => 'Nom d\'utilisateur :', + 'Full Name:' => 'Nom :', + 'Email:' => 'Email :', + 'Notifications:' => 'Notifications :', 'Notifications' => 'Notifications', - 'Account type:' => 'Type de compte :', + 'Account type:' => 'Type de compte :', 'Edit profile' => 'Modifier le profil', 'Change password' => 'Changer le mot de passe', 'Password modification' => 'Changement de mot de passe', @@ -336,14 +334,14 @@ return array( 'Change category' => 'Changer de catégorie', '%s updated the task %s' => '%s a mis à jour la tâche %s', '%s opened the task %s' => '%s a ouvert la tâche %s', - '%s moved the task %s to the position #%d in the column "%s"' => '%s a déplacé la tâche %s à la position n°%d dans la colonne « %s »', - '%s moved the task %s to the column "%s"' => '%s a déplacé la tâche %s dans la colonne « %s »', + '%s moved the task %s to the position #%d in the column "%s"' => '%s a déplacé la tâche %s à la position n°%d dans la colonne « %s »', + '%s moved the task %s to the column "%s"' => '%s a déplacé la tâche %s dans la colonne « %s »', '%s created the task %s' => '%s a créé la tâche %s', '%s closed the task %s' => '%s a fermé la tâche %s', '%s created a subtask for the task %s' => '%s a créé une sous-tâche pour la tâche %s', '%s updated a subtask for the task %s' => '%s a mis à jour une sous-tâche appartenant à la tâche %s', - 'Assigned to %s with an estimate of %s/%sh' => 'Assigné à %s avec un estimé de %s/%sh', - 'Not assigned, estimate of %sh' => 'Personne assigné, estimé de %sh', + 'Assigned to %s with an estimate of %s/%sh' => 'Assigné à %s avec un estimé de %s/%s h', + 'Not assigned, estimate of %sh' => 'Personne assigné, estimé de %s h', '%s updated a comment on the task %s' => '%s a mis à jour un commentaire appartenant à la tâche %s', '%s commented the task %s' => '%s a ajouté un commentaire sur la tâche %s', '%s\'s activity' => 'Activité du projet %s', @@ -357,13 +355,13 @@ return array( '%s closed the task #%d' => '%s a fermé la tâche n°%d', '%s opened the task #%d' => '%s a ouvert la tâche n°%d', 'Activity' => 'Activité', - 'Default values are "%s"' => 'Les valeurs par défaut sont « %s »', + 'Default values are "%s"' => 'Les valeurs par défaut sont « %s »', 'Default columns for new projects (Comma-separated)' => 'Colonnes par défaut pour les nouveaux projets (séparation par des virgules)', 'Task assignee change' => 'Modification de la personne assignée à une tâche', '%s changed the assignee of the task #%d to %s' => '%s a changé la personne assignée à la tâche nËš%d pour %s', '%s changed the assignee of the task %s to %s' => '%s a changé la personne assignée à la tâche %s pour %s', - 'New password for the user "%s"' => 'Nouveau mot de passe pour l\'utilisateur « %s »', - 'Choose an event' => 'Choisir un événement', + 'New password for the user "%s"' => 'Nouveau mot de passe pour l\'utilisateur « %s »', + 'Choose an event' => 'Choisir un évènement', 'Create a task from an external provider' => 'Créer une tâche depuis un fournisseur externe', 'Change the assignee based on an external username' => 'Changer l\'assigné en fonction d\'un utilisateur externe', 'Change the category based on an external label' => 'Changer la catégorie en fonction d\'un libellé externe', @@ -371,13 +369,13 @@ return array( 'Label' => 'Libellé', 'Database' => 'Base de données', 'About' => 'À propos', - 'Database driver:' => 'Type de base de données :', + 'Database driver:' => 'Type de base de données :', 'Board settings' => 'Paramètres du tableau', 'Webhook settings' => 'Paramètres pour les webhooks', 'Reset token' => 'Regénérer le jeton de sécurité', - 'API endpoint:' => 'URL de l\'API :', - 'Refresh interval for private board' => 'Intervalle pour rafraîchir un tableau privé', - 'Refresh interval for public board' => 'Intervalle pour rafraîchir un tableau public', + 'API endpoint:' => 'URL de l\'API :', + 'Refresh interval for private board' => 'Intervalle pour rafraichir un tableau privé', + 'Refresh interval for public board' => 'Intervalle pour rafraichir un tableau public', 'Task highlight period' => 'Durée pour mettre une tâche en évidence', 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Durée en seconde pour considérer une tâche comme récemment modifiée (0 pour désactiver, 2 jours par défaut)', 'Frequency in second (60 seconds by default)' => 'Fréquence en seconde (60 secondes par défaut)', @@ -385,7 +383,7 @@ return array( 'Application URL' => 'URL de l\'application', 'Token regenerated.' => 'Jeton de sécurité regénéré.', 'Date format' => 'Format des dates', - 'ISO format is always accepted, example: "%s" and "%s"' => 'Le format ISO est toujours accepté, exemple : « %s » et « %s »', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Le format ISO est toujours accepté, exemple : « %s » et « %s »', 'New private project' => 'Nouveau projet privé', 'This project is private' => 'Ce projet est privé', 'Add' => 'Ajouter', @@ -396,22 +394,17 @@ return array( 'Activity stream' => 'Flux d\'activité', 'Dashboard' => 'Tableau de bord', 'Confirmation' => 'Confirmation', - 'Allow everybody to access to this project' => 'Autoriser tout le monde à accéder à ce projet', - 'Everybody have access to this project.' => 'Tout le monde a accès à ce projet.', 'Webhooks' => 'Webhooks', 'API' => 'API', 'Create a comment from an external provider' => 'Créer un commentaire depuis un fournisseur externe', 'Project management' => 'Gestion des projets', - 'My projects' => 'Mes projets', 'Columns' => 'Colonnes', 'Task' => 'Tâche', - 'Your are not member of any project.' => 'Vous n\'êtes membre d\'aucun projet.', 'Percentage' => 'Pourcentage', 'Number of tasks' => 'Nombre de tâches', 'Task distribution' => 'Répartition des tâches', 'Analytics' => 'Analytique', 'Subtask' => 'Sous-tâche', - 'My subtasks' => 'Mes sous-tâches', 'User repartition' => 'Répartition des utilisateurs', 'Clone this project' => 'Cloner ce projet', 'Column removed successfully.' => 'Colonne supprimée avec succès.', @@ -439,14 +432,14 @@ return array( 'Do you really want to remove this swimlane: "%s"?' => 'Voulez-vous vraiment supprimer cette swimlane : « %s » ?', 'Inactive swimlanes' => 'Swimlanes inactives', 'Remove a swimlane' => 'Supprimer une swimlane', - 'Swimlane modification for the project "%s"' => 'Modification d\'une swimlane pour le projet « %s »', + 'Swimlane modification for the project "%s"' => 'Modification d\'une swimlane pour le projet « %s »', 'Swimlane removed successfully.' => 'Swimlane supprimée avec succès.', 'Swimlanes' => 'Swimlanes', 'Swimlane updated successfully.' => 'Swimlane mise à jour avec succès.', 'Unable to remove this swimlane.' => 'Impossible de supprimer cette swimlane.', 'Unable to update this swimlane.' => 'Impossible de mettre à jour cette swimlane.', 'Your swimlane have been created successfully.' => 'Votre swimlane a été créée avec succès.', - 'Example: "Bug, Feature Request, Improvement"' => 'Exemple: « Incident, Demande de fonctionnalité, Amélioration »', + 'Example: "Bug, Feature Request, Improvement"' => 'Exemple: « Incident, Demande de fonctionnalité, Amélioration »', 'Default categories for new projects (Comma-separated)' => 'Catégories par défaut pour les nouveaux projets (séparation par des virgules)', 'Integrations' => 'Intégrations', 'Integration with third-party services' => 'Intégration avec des services externes', @@ -456,10 +449,9 @@ return array( 'Task Title' => 'Titre de la tâche', 'Untitled' => 'Sans nom', 'Application default' => 'Valeur par défaut de l\'application', - 'Language:' => 'Langue :', - 'Timezone:' => 'Fuseau horaire :', + 'Language:' => 'Langue :', + 'Timezone:' => 'Fuseau horaire :', 'All columns' => 'Toutes les colonnes', - 'Calendar' => 'Agenda', 'Next' => 'Suivant', '#%d' => 'nËš%d', 'All swimlanes' => 'Toutes les swimlanes', @@ -467,8 +459,8 @@ return array( 'Moved to column %s' => 'Tâche déplacée à la colonne %s', 'User dashboard' => 'Tableau de bord de l\'utilisateur', 'Allow only one subtask in progress at the same time for a user' => 'Autoriser une seule sous-tâche en progrès en même temps pour un utilisateur', - 'Edit column "%s"' => 'Modifier la colonne « %s »', - 'Select the new status of the subtask: "%s"' => 'Selectionnez le nouveau statut de la sous-tâche : « %s »', + 'Edit column "%s"' => 'Modifier la colonne « %s »', + 'Select the new status of the subtask: "%s"' => 'Sélectionnez le nouveau statut de la sous-tâche : « %s »', 'Subtask timesheet' => 'Feuille de temps des sous-tâches', 'There is nothing to show.' => 'Il n\'y a rien à montrer.', 'Time Tracking' => 'Feuille de temps', @@ -479,7 +471,7 @@ return array( 'End' => 'Fin', 'Task age in days' => 'Âge de la tâche en jours', 'Days in this column' => 'Jours dans cette colonne', - '%dd' => '%dj', + '%dd' => '%d j', 'Add a new link' => 'Ajouter un nouveau lien', 'Do you really want to remove this link: "%s"?' => 'Voulez-vous vraiment supprimer ce lien : « %s » ?', 'Do you really want to remove this link with task #%d?' => 'Voulez-vous vraiment supprimer ce lien avec la tâche n°%d ?', @@ -510,12 +502,12 @@ return array( 'fixes' => 'corrige', 'is fixed by' => 'est corrigée par', 'This task' => 'Cette tâche', - '<1h' => '<1h', - '%dh' => '%dh', + '<1h' => '< 1 h', + '%dh' => '%d h', 'Expand tasks' => 'Déplier les tâches', 'Collapse tasks' => 'Replier les tâches', 'Expand/collapse tasks' => 'Plier/déplier les tâches', - 'Close dialog box' => 'Fermer une boîte de dialogue', + 'Close dialog box' => 'Fermer une boite de dialogue', 'Submit a form' => 'Enregistrer un formulaire', 'Board view' => 'Page du tableau', 'Keyboard shortcuts' => 'Raccourcis clavier', @@ -529,7 +521,7 @@ return array( 'AUD - Australian Dollar' => 'AUD - Dollar australien', 'CAD - Canadian Dollar' => 'CAD - Dollar canadien', 'CHF - Swiss Francs' => 'CHF - Franc suisse', - 'Custom Stylesheet' => 'Feuille de style personalisée', + 'Custom Stylesheet' => 'Feuille de style personnalisée', 'download' => 'télécharger', 'EUR - Euro' => 'EUR - Euro', 'GBP - British Pound' => 'GBP - Livre sterling', @@ -557,16 +549,15 @@ return array( 'Unable to add this currency rate.' => 'Impossible d\'ajouter ce taux de change', 'Webhook URL' => 'URL du webhook', '%s removed the assignee of the task %s' => '%s a enlevé la personne assignée à la tâche %s', - 'Enable Gravatar images' => 'Activer les images Gravatar', 'Information' => 'Informations', 'Check two factor authentication code' => 'Vérification du code pour l\'authentification à deux-facteurs', 'The two factor authentication code is not valid.' => 'Le code pour l\'authentification à deux-facteurs n\'est pas valide.', 'The two factor authentication code is valid.' => 'Le code pour l\'authentification à deux-facteurs est valide.', 'Code' => 'Code', 'Two factor authentication' => 'Authentification à deux-facteurs', - 'This QR code contains the key URI: ' => 'Ce code QR contient l\'url de la clé : ', + 'This QR code contains the key URI: ' => 'Ce code QR contient l\'URL de la clé : ', 'Check my code' => 'Vérifier mon code', - 'Secret key: ' => 'Clé secrète : ', + 'Secret key: ' => 'Clé secrète : ', 'Test your device' => 'Testez votre appareil', 'Assign a color when the task is moved to a specific column' => 'Assigner une couleur lorsque la tâche est déplacée dans une colonne spécifique', '%s via Kanboard' => '%s via Kanboard', @@ -581,13 +572,13 @@ return array( 'Disable two factor authentication' => 'Désactiver l\'authentification à deux facteurs', 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Voulez-vous vraiment désactiver l\'authentification à deux facteurs pour cet utilisateur : « %s » ?', 'Edit link' => 'Modifier un lien', - 'Start to type task title...' => 'Entrez le titre de la tâche...', + 'Start to type task title...' => 'Entrez le titre de la tâche…', 'A task cannot be linked to itself' => 'Une tâche ne peut être liée à elle-même', 'The exact same link already exists' => 'Un lien identique existe déjà ', 'Recurrent task is scheduled to be generated' => 'La tâche récurrente est programmée pour être créée', 'Score' => 'Complexité', 'The identifier must be unique' => 'L\'identifiant doit être unique', - 'This linked task id doesn\'t exists' => 'L\'identifiant de la task liée n\'existe pas', + 'This linked task id doesn\'t exists' => 'L\'identifiant de la tâche liée n\'existe pas', 'This value must be alphanumeric' => 'Cette valeur doit être alphanumérique', 'Edit recurrence' => 'Modifier la récurrence', 'Generate recurrent task' => 'Générer une tâche récurrente', @@ -597,7 +588,7 @@ return array( 'Base date to calculate new due date' => 'Date à utiliser pour calculer la nouvelle date d\'échéance', 'Action date' => 'Date de l\'action', 'Base date to calculate new due date: ' => 'Date utilisée pour calculer la nouvelle date d\'échéance : ', - 'This task has created this child task: ' => 'Cette tâche a créée la tâche enfant : ', + 'This task has created this child task: ' => 'Cette tâche a créé la tâche enfant : ', 'Day(s)' => 'Jour(s)', 'Existing due date' => 'Date d\'échéance existante', 'Factor to calculate new due date: ' => 'Facteur pour calculer la nouvelle date d\'échéance : ', @@ -611,14 +602,7 @@ return array( 'When task is moved from first column' => 'Lorsque la tâche est déplacée en dehors de la première colonne', 'When task is moved to last column' => 'Lorsque la tâche est déplacée dans la dernière colonne', 'Year(s)' => 'Année(s)', - 'Calendar settings' => 'Paramètres du calendrier', - 'Project calendar view' => 'Vue en mode projet du calendrier', 'Project settings' => 'Paramètres du projet', - 'Show subtasks based on the time tracking' => 'Afficher les sous-tâches basé sur le suivi du temps', - 'Show tasks based on the creation date' => 'Afficher les tâches en fonction de la date de création', - 'Show tasks based on the start date' => 'Afficher les tâches en fonction de la date de début', - 'Subtasks time tracking' => 'Suivi du temps par rapport aux sous-tâches', - 'User calendar view' => 'Vue en mode utilisateur du calendrier', 'Automatically update the start date' => 'Mettre à jour automatiquement la date de début', 'iCal feed' => 'Abonnement iCal', 'Preferences' => 'Préférences', @@ -627,7 +611,7 @@ return array( 'Two factor authentication enabled' => 'Authentification à deux facteurs activée', 'Unable to update this user.' => 'Impossible de mettre à jour cet utilisateur.', 'There is no user management for private projects.' => 'Il n\'y a pas de gestion d\'utilisateurs pour les projets privés.', - 'User that will receive the email' => 'Utilisateur qui va reçevoir l\'email', + 'User that will receive the email' => 'Utilisateur qui va recevoir l\'email', 'Email subject' => 'Sujet de l\'email', 'Date' => 'Date', 'Add a comment log when moving the task between columns' => 'Ajouter un commentaire d\'information lorsque une tâche est déplacée dans une autre colonne', @@ -637,31 +621,30 @@ return array( 'Notification' => 'Notification', '%s moved the task #%d to the first swimlane' => '%s a déplacé la tâche n°%d dans la première swimlane', 'Swimlane' => 'Swimlane', - 'Gravatar' => 'Gravatar', '%s moved the task %s to the first swimlane' => '%s a déplacé la tâche %s dans la première swimlane', '%s moved the task %s to the swimlane "%s"' => '%s a déplacé la tâche %s dans la swimlane « %s »', 'This report contains all subtasks information for the given date range.' => 'Ce rapport contient les informations de toutes les sous-tâches pour la période sélectionnée.', 'This report contains all tasks information for the given date range.' => 'Ce rapport contient les informations de toutes les tâches pour la période sélectionnée.', - 'Project activities for %s' => 'Activité des projets pour « %s »', + 'Project activities for %s' => 'Activité des projets pour « %s »', 'view the board on Kanboard' => 'voir le tableau sur Kanboard', 'The task have been moved to the first swimlane' => 'La tâche a été déplacée dans la première swimlane', - 'The task have been moved to another swimlane:' => 'La tâche a été déplacée dans une autre swimlane :', - 'New title: %s' => 'Nouveau titre : %s', + 'The task have been moved to another swimlane:' => 'La tâche a été déplacée dans une autre swimlane :', + 'New title: %s' => 'Nouveau titre : %s', 'The task is not assigned anymore' => 'La tâche n\'est plus assignée maintenant', - 'New assignee: %s' => 'Nouvel assigné : %s', + 'New assignee: %s' => 'Nouvel assigné : %s', 'There is no category now' => 'Il n\'y a plus de catégorie maintenant', - 'New category: %s' => 'Nouvelle catégorie : %s', - 'New color: %s' => 'Nouvelle couleur : %s', - 'New complexity: %d' => 'Nouvelle complexité : %d', + 'New category: %s' => 'Nouvelle catégorie : %s', + 'New color: %s' => 'Nouvelle couleur : %s', + 'New complexity: %d' => 'Nouvelle complexité : %d', 'The due date have been removed' => 'La date d\'échéance a été enlevée', 'There is no description anymore' => 'Il n\'y a plus de description maintenant', 'Recurrence settings have been modified' => 'Les réglages de la récurrence ont été modifiés', - 'Time spent changed: %sh' => 'Le temps passé a été changé : %sh', - 'Time estimated changed: %sh' => 'Le temps estimé a été changé : %sh', + 'Time spent changed: %sh' => 'Le temps passé a été changé : %s h', + 'Time estimated changed: %sh' => 'Le temps estimé a été changé : %s h', 'The field "%s" have been updated' => 'Le champ « %s » a été mis à jour', 'The description has been modified:' => 'La description a été modifiée', 'Do you really want to close the task "%s" as well as all subtasks?' => 'Voulez-vous vraiment fermer la tâche « %s » ainsi que toutes ses sous-tâches ?', - 'I want to receive notifications for:' => 'Je veux reçevoir les notifications pour :', + 'I want to receive notifications for:' => 'Je veux recevoir les notifications pour :', 'All tasks' => 'Toutes les Tâches', 'Only for tasks assigned to me' => 'Seulement les tâches qui me sont assignées', 'Only for tasks created by me' => 'Seulement les tâches que j\'ai créées', @@ -669,12 +652,11 @@ return array( '%%Y-%%m-%%d' => '%%d/%%m/%%Y', 'Total for all columns' => 'Total pour toutes les colonnes', 'You need at least 2 days of data to show the chart.' => 'Vous avez besoin d\'au minimum 2 jours de données pour afficher le graphique.', - '<15m' => '<15m', - '<30m' => '<30m', + '<15m' => '< 15 min', + '<30m' => '< 30 min', 'Stop timer' => 'Stopper le chrono', 'Start timer' => 'Démarrer le chrono', 'My activity stream' => 'Mon flux d\'activité', - 'My calendar' => 'Mon agenda', 'Search tasks' => 'Rechercher des tâches', 'Reset filters' => 'Réinitialiser les filtres', 'My tasks due tomorrow' => 'Mes tâches qui arrivent à échéance demain', @@ -688,36 +670,35 @@ return array( 'Overview' => 'Vue d\'ensemble', 'Board/Calendar/List view' => 'Vue Tableau/Calendrier/Liste', 'Switch to the board view' => 'Basculer vers le tableau', - 'Switch to the calendar view' => 'Basculer vers le calendrier', 'Switch to the list view' => 'Basculer vers la vue en liste', 'Go to the search/filter box' => 'Aller au champ de recherche', 'There is no activity yet.' => 'Il n\'y a pas encore d\'activité.', 'No tasks found.' => 'Aucune tâche trouvée.', - 'Keyboard shortcut: "%s"' => 'Raccourci clavier : « %s »', + 'Keyboard shortcut: "%s"' => 'Raccourci clavier : « %s »', 'List' => 'Liste', 'Filter' => 'Filtre', 'Advanced search' => 'Recherche avancée', - 'Example of query: ' => 'Exemple de requête : ', - 'Search by project: ' => 'Rechercher par projet : ', - 'Search by column: ' => 'Rechercher par colonne : ', - 'Search by assignee: ' => 'Rechercher par assigné : ', - 'Search by color: ' => 'Rechercher par couleur : ', - 'Search by category: ' => 'Rechercher par catégorie : ', - 'Search by description: ' => 'Rechercher par description : ', - 'Search by due date: ' => 'Rechercher par date d\'échéance : ', + 'Example of query: ' => 'Exemple de requête : ', + 'Search by project: ' => 'Rechercher par projet : ', + 'Search by column: ' => 'Rechercher par colonne : ', + 'Search by assignee: ' => 'Rechercher par assigné : ', + 'Search by color: ' => 'Rechercher par couleur : ', + 'Search by category: ' => 'Rechercher par catégorie : ', + 'Search by description: ' => 'Rechercher par description : ', + 'Search by due date: ' => 'Rechercher par date d\'échéance : ', 'Average time spent into each column' => 'Temps moyen passé dans chaque colonne', 'Average time spent' => 'Temps moyen passé', 'This chart show the average time spent into each column for the last %d tasks.' => 'Ce graphique montre le temps passé moyen dans chaque colonne pour les %d dernières tâches.', 'Average Lead and Cycle time' => 'Durée moyenne du lead et cycle time', - 'Average lead time: ' => 'Lead time moyen : ', - 'Average cycle time: ' => 'Cycle time moyen : ', + 'Average lead time: ' => 'Lead time moyen : ', + 'Average cycle time: ' => 'Cycle time moyen : ', 'Cycle Time' => 'Cycle time', 'Lead Time' => 'Lead time', 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Ce graphique montre la durée moyenne du lead et cycle time pour les %d dernières tâches.', 'Average time into each column' => 'Temps moyen dans chaque colonne', 'Lead and cycle time' => 'Lead et cycle time', - 'Lead time: ' => 'Lead time : ', - 'Cycle time: ' => 'Temps de cycle : ', + 'Lead time: ' => 'Lead time : ', + 'Cycle time: ' => 'Temps de cycle : ', 'Time spent into each column' => 'Temps passé dans chaque colonne', 'The lead time is the duration between the task creation and the completion.' => 'Le lead time est la durée entre la création de la tâche et sa complétion.', 'The cycle time is the duration between the start date and the completion.' => 'Le cycle time est la durée entre la date de début et la complétion.', @@ -725,33 +706,26 @@ return array( 'Set automatically the start date' => 'Définir automatiquement la date de début', 'Edit Authentication' => 'Modifier l\'authentification', 'Remote user' => 'Utilisateur distant', - 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Les utilisateurs distants ne stockent pas leur mot de passe dans la base de données de Kanboard, exemples : comptes LDAP, Github ou Google.', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Les utilisateurs distants ne stockent pas leur mot de passe dans la base de données de Kanboard, exemples : comptes LDAP, GitHub ou Google.', 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Si vous cochez la case « Interdire le formulaire d\'authentification », les identifiants entrés dans le formulaire d\'authentification seront ignorés.', 'Default task color' => 'Couleur par défaut des tâches', 'This feature does not work with all browsers.' => 'Cette fonctionnalité n\'est pas compatible avec tous les navigateurs', 'There is no destination project available.' => 'Il n\'y a pas de projet de destination disponible.', 'Trigger automatically subtask time tracking' => 'Déclencher automatiquement le suivi du temps pour les sous-tâches', 'Include closed tasks in the cumulative flow diagram' => 'Inclure les tâches fermées dans le diagramme de flux cumulé', - 'Current swimlane: %s' => 'Swimlane actuelle : %s', - 'Current column: %s' => 'Colonne actuelle : %s', - 'Current category: %s' => 'Catégorie actuelle : %s', + 'Current swimlane: %s' => 'Swimlane actuelle : %s', + 'Current column: %s' => 'Colonne actuelle : %s', + 'Current category: %s' => 'Catégorie actuelle : %s', 'no category' => 'aucune catégorie', - 'Current assignee: %s' => 'Assigné actuel : %s', + 'Current assignee: %s' => 'Assigné actuel : %s', 'not assigned' => 'non assigné', - 'Author:' => 'Auteur :', + 'Author:' => 'Auteur :', 'contributors' => 'contributeurs', - 'License:' => 'Licence :', + 'License:' => 'Licence :', 'License' => 'Licence', 'Enter the text below' => 'Entrez le texte ci-dessous', - 'Sort by position' => 'Trier par position', - 'Sort by date' => 'Trier par date', - 'Add task' => 'Ajouter une tâche', 'Start date:' => 'Date de début :', 'Due date:' => 'Date d\'échéance :', - 'There is no start date or due date for this task.' => 'Il n\'y a pas de date de début ou de date de fin pour cette tâche.', - 'Moving or resizing a task will change the start and due date of the task.' => 'Déplacer ou redimensionner une tâche va changer la date de début et la date de fin de la tâche.', - 'There is no task in your project.' => 'Il n\'y a aucune tâche dans votre projet.', - 'Gantt chart' => 'Diagramme de Gantt', 'People who are project managers' => 'Personnes qui sont gestionnaires de projet', 'People who are project members' => 'Personnes qui sont membres de projet', 'NOK - Norwegian Krone' => 'NOK - Couronne norvégienne', @@ -763,42 +737,35 @@ return array( 'Members' => 'Membres', 'Shared project' => 'Projet partagé', 'Project managers' => 'Gestionnaires de projet', - 'Gantt chart for all projects' => 'Diagramme de Gantt pour tous les projets', 'Projects list' => 'Liste des projets', - 'Gantt chart for this project' => 'Diagramme de Gantt pour ce projet', - 'Project board' => 'Tableau du projet', 'End date:' => 'Date de fin :', - 'There is no start date or end date for this project.' => 'Il n\'y a pas de date de début ou de date de fin pour ce projet.', - 'Projects Gantt chart' => 'Diagramme de Gantt des projets', 'Change task color when using a specific task link' => 'Changer la couleur de la tâche lorsqu\'un lien spécifique est utilisé', 'Task link creation or modification' => 'Création ou modification d\'un lien sur une tâche', 'Milestone' => 'Étape importante', - 'Documentation: %s' => 'Documentation : %s', - 'Switch to the Gantt chart view' => 'Passer à la vue en diagramme de Gantt', + 'Documentation: %s' => 'Documentation : %s', 'Reset the search/filter box' => 'Réinitialiser le champ de recherche', 'Documentation' => 'Documentation', 'Table of contents' => 'Table des matières', - 'Gantt' => 'Gantt', 'Author' => 'Auteur', 'Version' => 'Version', 'Plugins' => 'Extensions', 'There is no plugin loaded.' => 'Il n\'y a aucune extension chargée.', 'My notifications' => 'Mes notifications', - 'Custom filters' => 'Filtres personalisés', - 'Your custom filter have been created successfully.' => 'Votre filter personalisé a été créé avec succès.', - 'Unable to create your custom filter.' => 'Impossible de créer votre filter personalisé.', - 'Custom filter removed successfully.' => 'Filtre personalisé supprimé avec succès.', - 'Unable to remove this custom filter.' => 'Impossible de supprimer ce filter personalisé.', - 'Edit custom filter' => 'Modification d\'un filtre personalisé', - 'Your custom filter have been updated successfully.' => 'Votre filtre personalisé a été mis à jour avec succès.', - 'Unable to update custom filter.' => 'Impossible de mettre à jour votre filtre personalisé.', + 'Custom filters' => 'Filtres personnalisés', + 'Your custom filter have been created successfully.' => 'Votre filtre personnalisé a été créé avec succès.', + 'Unable to create your custom filter.' => 'Impossible de créer votre filtre personnalisé.', + 'Custom filter removed successfully.' => 'Filtre personnalisé supprimé avec succès.', + 'Unable to remove this custom filter.' => 'Impossible de supprimer ce filtre personnalisé.', + 'Edit custom filter' => 'Modification d\'un filtre personnalisé', + 'Your custom filter have been updated successfully.' => 'Votre filtre personnalisé a été mis à jour avec succès.', + 'Unable to update custom filter.' => 'Impossible de mettre à jour votre filtre personnalisé.', 'Web' => 'Web', - 'New attachment on task #%d: %s' => 'Nouveau fichier joint sur la tâche n°%d : %s', + 'New attachment on task #%d: %s' => 'Nouveau fichier joint sur la tâche n°%d : %s', 'New comment on task #%d' => 'Nouveau commentaire sur la tâche n°%d', 'Comment updated on task #%d' => 'Commentaire mis à jour sur la tâche n°%d', 'New subtask on task #%d' => 'Nouvelle sous-tâche sur la tâche n°%d', 'Subtask updated on task #%d' => 'Sous-tâche mise à jour sur la tâche n°%d', - 'New task #%d: %s' => 'Nouvelle tâche n°%d : %s', + 'New task #%d: %s' => 'Nouvelle tâche n°%d : %s', 'Task updated #%d' => 'Tâche n°%d mise à jour', 'Task #%d closed' => 'Tâche n°%d fermée', 'Task #%d opened' => 'Tâche n°%d ouverte', @@ -819,7 +786,7 @@ return array( 'Shared' => 'Partagé', 'Owner' => 'Propriétaire', 'Unread notifications' => 'Notifications non lus', - 'Notification methods:' => 'Méthodes de notifications :', + 'Notification methods:' => 'Méthodes de notifications :', 'Unable to read your file' => 'Impossible de lire votre fichier', '%d task(s) have been imported successfully.' => '%d tâche(s) ont été importées avec succès.', 'Nothing have been imported!' => 'Rien n\'a été importé', @@ -830,15 +797,15 @@ return array( 'Tab' => 'Tabulation', 'Vertical bar' => 'Barre verticale', 'Double Quote' => 'Guillemet double', - 'Single Quote' => 'Guillement simple', + 'Single Quote' => 'Guillemet simple', '%s attached a file to the task #%d' => '%s a attaché un fichier sur la tâche n°%d', 'There is no column or swimlane activated in your project!' => 'Il n\'y a aucune colonne ou swimlane dans votre projet !', - 'Append filter (instead of replacement)' => 'Ajouter le filtre au lieu de le remplaçer', - 'Append/Replace' => 'Ajouter/Remplaçer', + 'Append filter (instead of replacement)' => 'Ajouter le filtre au lieu de le remplacer', + 'Append/Replace' => 'Ajouter/Remplacer', 'Append' => 'Ajouter', - 'Replace' => 'Remplaçer', + 'Replace' => 'Remplacer', 'Import' => 'Importation', - 'Change sorting' => 'changer l\'ordre', + 'Change sorting' => 'Changer l\'ordre', 'Tasks Importation' => 'Importation des tâches', 'Delimiter' => 'Délimiteur', 'Enclosure' => 'Caractère d\'encadrement', @@ -848,7 +815,7 @@ return array( 'Your file must be encoded in UTF-8' => 'Votre fichier doit être encodé en UTF-8', 'The first row must be the header' => 'La première ligne doit être le titre des colonnes', 'Duplicates are not verified for you' => 'Les doublons ne sont pas vérifiés pour vous', - 'The due date must use the ISO format: YYYY-MM-DD' => 'La date d\'échéance doit utiliser le format ISO : AAAA-MM-JJ', + 'The due date must use the ISO format: YYYY-MM-DD' => 'La date d\'échéance doit utiliser le format ISO : AAAA-MM-JJ', 'Download CSV template' => 'Télécharger le modèle CSV', 'No external integration registered.' => 'Aucune intégration externe enregistrée.', 'Duplicates are not imported' => 'Les doublons ne sont pas importés', @@ -864,14 +831,14 @@ return array( 'Members of %s' => 'Membres de %s', 'New group' => 'Nouveau groupe', 'Group created successfully.' => 'Groupe créé avec succès.', - 'Unable to create your group.' => 'Impossible de créé votre groupe.', + 'Unable to create your group.' => 'Impossible de créer votre groupe.', 'Edit group' => 'Modifier le groupe', 'Group updated successfully.' => 'Groupe mis à jour avec succès.', 'Unable to update your group.' => 'Impossible de mettre à jour votre groupe.', - 'Add group member to "%s"' => 'Ajouter un membre au groupe « %s »', + 'Add group member to "%s"' => 'Ajouter un membre au groupe « %s »', 'Group member added successfully.' => 'Membre ajouté avec succès au groupe.', 'Unable to add group member.' => 'Impossible d\'ajouter ce membre au groupe.', - 'Remove user from group "%s"' => 'Supprimer un utilisateur d\'un groupe « %s »', + 'Remove user from group "%s"' => 'Supprimer un utilisateur d\'un groupe « %s »', 'User removed successfully from this group.' => 'Utilisateur supprimé avec succès de ce groupe.', 'Unable to remove this user from the group.' => 'Impossible de supprimer cet utilisateur du groupe.', 'Remove group' => 'Supprimer le groupe', @@ -883,67 +850,66 @@ return array( 'Project Member' => 'Membre du projet', 'Project Viewer' => 'Visualiseur de projet', 'Your account is locked for %d minutes' => 'Votre compte est verrouillé pour %d minutes', - 'Invalid captcha' => 'Captcha invalid', + 'Invalid captcha' => 'Captcha invalide', 'The name must be unique' => 'Le nom doit être unique', 'View all groups' => 'Voir tous les groupes', 'There is no user available.' => 'Il n\'y a aucun utilisateur disponible', - 'Do you really want to remove the user "%s" from the group "%s"?' => 'Voulez-vous vraiment supprimer l\'utilisateur « %s » du groupe « %s » ?', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Voulez-vous vraiment supprimer l\'utilisateur « %s » du groupe « %s » ?', 'There is no group.' => 'Il n\'y a aucun groupe.', - 'External Id' => 'Identifiant externe', 'Add group member' => 'Ajouter un membre au groupe', - 'Do you really want to remove this group: "%s"?' => 'Voulez-vous vraiment supprimer ce groupe : « %s » ?', + 'Do you really want to remove this group: "%s"?' => 'Voulez-vous vraiment supprimer ce groupe : « %s » ?', 'There is no user in this group.' => 'Il n\'y a aucun utilisateur dans ce groupe', 'Permissions' => 'Permissions', 'Allowed Users' => 'Utilisateurs autorisés', 'No user have been allowed specifically.' => 'Aucun utilisateur a été autorisé spécifiquement.', 'Role' => 'Rôle', - 'Enter user name...' => 'Entrez le nom de l\'utilisateur...', + 'Enter user name...' => 'Entrez le nom de l\'utilisateur…', 'Allowed Groups' => 'Groupes autorisés', 'No group have been allowed specifically.' => 'Aucun groupe a été autorisé spécifiquement.', 'Group' => 'Groupe', 'Group Name' => 'Nom du groupe', - 'Enter group name...' => 'Entrez le nom du groupe...', - 'Role:' => 'Rôle :', + 'Enter group name...' => 'Entrez le nom du groupe…', + 'Role:' => 'Rôle :', 'Project members' => 'Membres du projet', '%s mentioned you in the task #%d' => '%s vous a mentionné dans la tâche n°%d', '%s mentioned you in a comment on the task #%d' => '%s vous a mentionné dans un commentaire de la tâche n°%d', 'You were mentioned in the task #%d' => 'Vous avez été mentionné dans la tâche n°%d', 'You were mentioned in a comment on the task #%d' => 'Vous avez été mentionné dans un commentaire de la tâche n°%d', - 'Estimated hours: ' => 'Heures estimées : ', - 'Actual hours: ' => 'Heures actuelles : ', + 'Estimated hours: ' => 'Heures estimées : ', + 'Actual hours: ' => 'Heures actuelles : ', 'Hours Spent' => 'Heures passées', 'Hours Estimated' => 'Heures estimées', 'Estimated Time' => 'Temps estimé', 'Actual Time' => 'Temps actuel', - 'Estimated vs actual time' => 'Temps estimé vs actuel', + 'Estimated vs actual time' => 'Temps estimé vs réel', 'RUB - Russian Ruble' => 'RUB - Rouble russe', 'Assign the task to the person who does the action when the column is changed' => 'Assigner la tâche à la personne qui fait l\'action lorsque la colonne est changée', 'Close a task in a specific column' => 'Fermer une tâche dans une colonne specifique', 'Time-based One-time Password Algorithm' => 'Mot de passe à usage unique basé sur le temps', - 'Two-Factor Provider: ' => 'Fournisseur d\'authentification à deux facteurs : ', + 'Two-Factor Provider: ' => 'Fournisseur d\'authentification à deux facteurs : ', 'Disable two-factor authentication' => 'Désactiver l\'authentification à deux-facteurs', 'Enable two-factor authentication' => 'Activer l\'authentification à deux-facteurs', 'There is no integration registered at the moment.' => 'Il n\'y a aucune intégration enregistrée pour le moment.', 'Password Reset for Kanboard' => 'Réinitialisation du mot de passe pour Kanboard', - 'Forgot password?' => 'Mot de passe oublié ?', - 'Enable "Forget Password"' => 'Activer la fonctionnalité « Mot de passe oublié »', + 'Forgot password?' => 'Mot de passe oublié ?', + 'Enable "Forget Password"' => 'Activer la fonctionnalité « Mot de passe oublié »', 'Password Reset' => 'Réinitialisation du mot de passe', 'New password' => 'Nouveau mot de passe', 'Change Password' => 'Changer de mot de passe', - 'To reset your password click on this link:' => 'Pour réinitialiser votre mot de passe cliquer sur ce lien :', - 'Last Password Reset' => 'Dernières réinitialisation de mot de passe', + 'To reset your password click on this link:' => 'Pour réinitialiser votre mot de passe cliquer sur ce lien :', + 'Last Password Reset' => 'Dernières réinitialisations de mot de passe', 'The password has never been reinitialized.' => 'Le mot de passe n\'a jamais été réinitialisé.', 'Creation' => 'Création', 'Expiration' => 'Expiration', 'Password reset history' => 'Historique de la réinitialisation du mot de passe', - 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Toutes les tâches de la colonne « %s » et de la swimlane « %s » ont été fermées avec succès.', - 'Do you really want to close all tasks of this column?' => 'Voulez-vous vraiment fermer toutes les tâches de cette colonne ?', - '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d tâche(s) dans la colonne « %s » et la swimlane « %s » seront fermées.', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Toutes les tâches de la colonne « %s » et de la swimlane « %s » ont été fermées avec succès.', + 'Do you really want to close all tasks of this column?' => 'Voulez-vous vraiment fermer toutes les tâches de cette colonne ?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d tâche(s) dans la colonne « %s » et la swimlane « %s » seront fermées.', 'Close all tasks of this column' => 'Fermer toutes les tâches de cette colonne', 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Aucun plugin n\'a enregistré une méthode de notification de projet. Vous pouvez toujours configurer les notifications individuelles dans votre profil utilisateur.', 'My dashboard' => 'Mon tableau de bord', 'My profile' => 'Mon profil', - 'Project owner: ' => 'Responsable du projet : ', + 'Project owner: ' => 'Responsable du projet : ', 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'L\'identifiant du projet est optionnel et doit être alphanumérique, example: MONPROJET.', 'Project owner' => 'Responsable du projet', 'Private projects do not have users and groups management.' => 'Les projets privés n\'ont pas de gestion d\'utilisateurs et de groupes.', @@ -955,7 +921,6 @@ return array( 'Default priority' => 'Priorité par défaut', 'Lowest priority' => 'Priorité basse', 'Highest priority' => 'Priorité haute', - 'If you put zero to the low and high priority, this feature will be disabled.' => 'Si vous mettez zéro pour la priorité basse et haute, cette fonctionnalité sera désactivée.', 'Close a task when there is no activity' => 'Fermer une tâche sans activité', 'Duration in days' => 'Durée en jours', 'Send email when there is no activity on a task' => 'Envoyer un email lorsqu\'il n\'y a pas d\'activité sur une tâche', @@ -974,7 +939,7 @@ return array( 'Add a new external link' => 'Ajouter un nouveau lien externe', 'Edit external link' => 'Modifier un lien externe', 'External link' => 'Lien externe', - 'Copy and paste your link here...' => 'Copier-coller vôtre lien ici...', + 'Copy and paste your link here...' => 'Copier-coller votre lien ici…', 'URL' => 'URL', 'Internal links' => 'Liens internes', 'Assign to me' => 'Assigner à moi', @@ -986,48 +951,48 @@ return array( 'Create from another project' => 'Créer depuis un autre projet', 'open' => 'ouvert', 'closed' => 'fermé', - 'Priority:' => 'Priorité :', - 'Reference:' => 'Référence :', - 'Complexity:' => 'Complexité :', - 'Swimlane:' => 'Swimlane :', - 'Column:' => 'Colonne :', - 'Position:' => 'Position :', - 'Creator:' => 'Créateur :', - 'Time estimated:' => 'Temps estimé :', + 'Priority:' => 'Priorité :', + 'Reference:' => 'Référence :', + 'Complexity:' => 'Complexité :', + 'Swimlane:' => 'Swimlane :', + 'Column:' => 'Colonne :', + 'Position:' => 'Position :', + 'Creator:' => 'Créateur :', + 'Time estimated:' => 'Temps estimé :', '%s hours' => '%s heures', - 'Time spent:' => 'Temps passé :', - 'Created:' => 'Créé le :', - 'Modified:' => 'Modifié le :', - 'Completed:' => 'Terminé le :', - 'Started:' => 'Commençé le :', - 'Moved:' => 'Déplacé le : ', + 'Time spent:' => 'Temps passé :', + 'Created:' => 'Créé le :', + 'Modified:' => 'Modifié le :', + 'Completed:' => 'Terminé le :', + 'Started:' => 'Commencé le :', + 'Moved:' => 'Déplacé le : ', 'Task #%d' => 'Tâche n°%d', 'Time format' => 'Format de l\'heure', - 'Start date: ' => 'Date de début : ', - 'End date: ' => 'Date de fin : ', - 'New due date: ' => 'Nouvelle date d\'échéance : ', - 'Start date changed: ' => 'Date de début modifiée : ', + 'Start date: ' => 'Date de début : ', + 'End date: ' => 'Date de fin : ', + 'New due date: ' => 'Nouvelle date d\'échéance : ', + 'Start date changed: ' => 'Date de début modifiée : ', 'Disable private projects' => 'Désactiver les projets privés', - 'Do you really want to remove this custom filter: "%s"?' => 'Voulez-vous vraiment supprimer ce filtre personnalisé : « %s » ?', + 'Do you really want to remove this custom filter: "%s"?' => 'Voulez-vous vraiment supprimer ce filtre personnalisé : « %s » ?', 'Remove a custom filter' => 'Supprimer un filtre personnalisé', 'User activated successfully.' => 'Utilisateur activé avec succès.', 'Unable to enable this user.' => 'Impossible d\'activer cet utilisateur.', 'User disabled successfully.' => 'Utilisateur désactivé avec succès.', 'Unable to disable this user.' => 'Impossible de désactiver cet utilisateur.', - 'All files have been uploaded successfully.' => 'Tous les fichiers ont été uploadés avec succès.', - 'The maximum allowed file size is %sB.' => 'La taille maximale autorisée pour les fichiers est de %so.', + 'All files have been uploaded successfully.' => 'Tous les fichiers ont été envoyés avec succès.', + 'The maximum allowed file size is %sB.' => 'La taille maximale autorisée pour les fichiers est de %s o.', 'Drag and drop your files here' => 'Glissez-déposez vos fichiers ici', 'choose files' => 'choisissez des fichiers', 'View profile' => 'Voir le profil', 'Two Factor' => 'Deux-Facteurs', 'Disable user' => 'Désactiver l\'utilisateur', - 'Do you really want to disable this user: "%s"?' => 'Voulez-vous vraiment désactiver cet utilisateur : « %s » ?', + 'Do you really want to disable this user: "%s"?' => 'Voulez-vous vraiment désactiver cet utilisateur : « %s » ?', 'Enable user' => 'Activer un utilisateur', - 'Do you really want to enable this user: "%s"?' => 'Voulez-vous vraiment activer cet utilisateur : « %s » ?', + 'Do you really want to enable this user: "%s"?' => 'Voulez-vous vraiment activer cet utilisateur : « %s » ?', 'Download' => 'Télécharger', - 'Uploaded: %s' => 'Uploadé : %s', - 'Size: %s' => 'Taille : %s', - 'Uploaded by %s' => 'Uploadé par %s', + 'Uploaded: %s' => 'Envoyé : %s', + 'Size: %s' => 'Taille : %s', + 'Uploaded by %s' => 'Envoyé par %s', 'Filename' => 'Nom du fichier', 'Size' => 'Taille', 'Column created successfully.' => 'La colonne a été créée avec succès.', @@ -1044,7 +1009,7 @@ return array( 'Change subtask position' => 'Changer la position de la sous-tâche', 'This value must be greater than %d' => 'Cette valeur doit être plus grande que %d', 'Another swimlane with the same name exists in the project' => 'Une autre swimlane existe avec le même nom dans le projet', - 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Exemple : http://exemple.kanboard.net/ (utilisé pour générer les URLs absolues)', + 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Exemple : http://exemple.kanboard.net/ (utilisé pour générer les URL absolues)', 'Actions duplicated successfully.' => 'Actions dupliquées avec succès.', 'Unable to duplicate actions.' => 'Impossible de dupliquer les actions.', 'Add a new action' => 'Ajouter une nouvelle action', @@ -1054,11 +1019,11 @@ return array( 'There is no available project.' => 'Il n\'y a pas de projet disponible.', 'Local File' => 'Fichier local', 'Configuration' => 'Configuration', - 'PHP version:' => 'Version de PHP :', - 'PHP SAPI:' => 'PHP SAPI :', - 'OS version:' => 'Version du système d\'exploitation :', - 'Database version:' => 'Version de la base de donnée :', - 'Browser:' => 'Navigateur web :', + 'PHP version:' => 'Version de PHP :', + 'PHP SAPI:' => 'PHP SAPI :', + 'OS version:' => 'Version du système d\'exploitation :', + 'Database version:' => 'Version de la base de donnée :', + 'Browser:' => 'Navigateur web :', 'Task view' => 'Vue détaillée d\'une tâche', 'Edit task' => 'Modifier la tâche', 'Edit description' => 'Modifier la description', @@ -1069,24 +1034,24 @@ return array( 'Avatar' => 'Avatar', 'Upload my avatar image' => 'Uploader mon image d\'avatar', 'Remove my image' => 'Supprimer mon image', - 'The OAuth2 state parameter is invalid' => 'Le paramètre "state" de OAuth2 est invalide', + 'The OAuth2 state parameter is invalid' => 'Le paramètre « state » de OAuth2 est invalide', 'User not found.' => 'Utilisateur introuvable.', 'Search in activity stream' => 'Chercher dans le flux d\'activité', 'My activities' => 'Mes activités', 'Activity until yesterday' => 'Activités jusqu\'à hier', 'Activity until today' => 'Activités jusqu\'à aujourd\'hui', - 'Search by creator: ' => 'Rechercher par créateur : ', - 'Search by creation date: ' => 'Rechercher par date de création : ', - 'Search by task status: ' => 'Rechercher par le statut des tâches : ', - 'Search by task title: ' => 'Rechercher par le titre des tâches : ', + 'Search by creator: ' => 'Rechercher par créateur : ', + 'Search by creation date: ' => 'Rechercher par date de création : ', + 'Search by task status: ' => 'Rechercher par le statut des tâches : ', + 'Search by task title: ' => 'Rechercher par le titre des tâches : ', 'Activity stream search' => 'Recherche dans le flux d\'activité', - 'Projects where "%s" is manager' => 'Projets où « %s » est gestionnaire', - 'Projects where "%s" is member' => 'Projets où « %s » est membre du projet', - 'Open tasks assigned to "%s"' => 'Tâches ouvertes assignées à « %s »', - 'Closed tasks assigned to "%s"' => 'Tâches fermées assignées à « %s »', + 'Projects where "%s" is manager' => 'Projets où « %s » est gestionnaire', + 'Projects where "%s" is member' => 'Projets où « %s » est membre du projet', + 'Open tasks assigned to "%s"' => 'Tâches ouvertes assignées à « %s »', + 'Closed tasks assigned to "%s"' => 'Tâches fermées assignées à « %s »', 'Assign automatically a color based on a priority' => 'Assigner automatiquement une couleur par rapport à une priorité', - 'Overdue tasks for the project(s) "%s"' => 'Tâches en retard pour le projet(s) « %s »', - 'Upload files' => 'Uploader les fichiers', + 'Overdue tasks for the project(s) "%s"' => 'Tâches en retard pour le projet(s) « %s »', + 'Upload files' => 'Envoyer les fichiers', 'Installed Plugins' => 'Extensions installées', 'Plugin Directory' => 'Liste des extensions', 'Plugin installed successfully.' => 'Extension installée avec succès.', @@ -1102,32 +1067,32 @@ return array( 'Unable to open plugin archive.' => 'Impossible d\'ouvrir l\'archive du plugin.', 'There is no file in the plugin archive.' => 'Il n\'y a aucun fichier dans l\'archive du plugin.', 'Create tasks in bulk' => 'Créer plusieurs tâches en même temps', - 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Votre instance de Kanboard n\'est pas configurée pour installer des extension depuis l\'interface utilisateur.', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Votre instance de Kanboard n\'est pas configurée pour installer des extensions depuis l\'interface utilisateur.', 'There is no plugin available.' => 'Il n\'a aucune extension disponible.', 'Install' => 'Installer', 'Update' => 'Mettre à jour', 'Up to date' => 'À jour', 'Not available' => 'Non disponible', 'Remove plugin' => 'Supprimer l\'extension', - 'Do you really want to remove this plugin: "%s"?' => 'Voulez-vous vraiment supprimer cette extension : « %s » ?', + 'Do you really want to remove this plugin: "%s"?' => 'Voulez-vous vraiment supprimer cette extension : « %s » ?', 'Uninstall' => 'Désinstaller', 'Listing' => 'Listing', 'Metadata' => 'Metadonnées', 'Manage projects' => 'Gérer les projets', 'Convert to task' => 'Convertir en tâche', 'Convert sub-task to task' => 'Convertir une sous-tâche en tâche', - 'Do you really want to convert this sub-task to a task?' => 'Voulez-vous vraiment convertir cette sous-tâche en tâche ?', + 'Do you really want to convert this sub-task to a task?' => 'Voulez-vous vraiment convertir cette sous-tâche en tâche ?', 'My task title' => 'Mon titre pour la tâche', 'Enter one task by line.' => 'Entrez une tâche par ligne.', - 'Number of failed login:' => 'Nombre de connexion échouées :', - 'Account locked until:' => 'Compte bloqué jusqu\'au :', + 'Number of failed login:' => 'Nombre de connexions échouées :', + 'Account locked until:' => 'Compte bloqué jusqu\'au :', 'Email settings' => 'Paramètres des emails', 'Email sender address' => 'Adresse email de l\'expéditeur', 'Email transport' => 'Transport des emails', 'Webhook token' => 'Jeton de sécurité des webhooks', 'Project tags management' => 'Gestion des libellés pour le projet', 'Tag created successfully.' => 'Libellé créé avec succès.', - 'Unable to create this tag.' => 'Imposssible de créer ce libellé.', + 'Unable to create this tag.' => 'Impossible de créer ce libellé.', 'Tag updated successfully.' => 'Libellé mis à jour avec succès.', 'Unable to update this tag.' => 'Impossible de mettre à jour ce libellé.', 'Tag removed successfully.' => 'Libellé supprimé avec succès.', @@ -1141,7 +1106,7 @@ return array( 'There is no specific tag for this project at the moment.' => 'Il n\'y a aucun libellé spécifique pour ce projet pour le moment.', 'Tag' => 'Libellé', 'Remove a tag' => 'Supprimer un libellé', - 'Do you really want to remove this tag: "%s"?' => 'Voulez-vous vraiment supprimer ce libellé : « %s » ?', + 'Do you really want to remove this tag: "%s"?' => 'Voulez-vous vraiment supprimer ce libellé : « %s » ?', 'Global tags' => 'Libellés globaux', 'There is no global tag at the moment.' => 'Il n\'y a aucun libellé global pour le moment.', 'This field cannot be empty' => 'Ce champ ne peut être vide', @@ -1169,23 +1134,21 @@ return array( 'Subtasks overview for %s' => 'Aperçu des sous-tâches pour %s', 'Projects overview for %s' => 'Aperçu des projets pour %s', 'Activity stream for %s' => 'Flux d\'activité pour %s', - 'Calendar for %s' => 'Calendrier pour %s', - 'Notifications for %s' => 'Notifications pour %s', - 'Assign a color when the task is moved to a specific swimlane' => 'Assigner une couleur lorsque une tâche est déplaçée dans une swimlane spécifique', - 'Assign a priority when the task is moved to a specific swimlane' => 'Assigner une priorité lorsque une tâche est déplaçée dans une swimlane spécifique', + 'Assign a color when the task is moved to a specific swimlane' => 'Assigner une couleur lorsque une tâche est déplacée dans une swimlane spécifique', + 'Assign a priority when the task is moved to a specific swimlane' => 'Assigner une priorité lorsque une tâche est déplacée dans une swimlane spécifique', 'User unlocked successfully.' => 'Utilisateur débloqué avec succès.', 'Unable to unlock the user.' => 'Impossible de débloquer cet utilisateur.', - 'Move a task to another swimlane' => 'Déplaçer une tâche dans une autre swimlane', + 'Move a task to another swimlane' => 'Déplacer une tâche dans une autre swimlane', 'Creator Name' => 'Nom du créateur', 'Time spent and estimated' => 'Temps passé et estimé', 'Move position' => 'Changer la position', - 'Move task to another position on the board' => 'Déplaçer la tâche vers une autre position sur le tableau', + 'Move task to another position on the board' => 'Déplacer la tâche vers une autre position sur le tableau', 'Insert before this task' => 'Insérer avant cette tâche', 'Insert after this task' => 'Insérer après cette tâche', 'Unlock this user' => 'Débloquer cet utilisateur', 'Custom Project Roles' => 'Rôles personnalisés du projet', 'Add a new custom role' => 'Ajouter un nouveau rôle personnalisé', - 'Restrictions for the role "%s"' => 'Restrictions pour le rôle « %s »', + 'Restrictions for the role "%s"' => 'Restrictions pour le rôle « %s »', 'Add a new project restriction' => 'Ajouter une nouvelle restriction pour ce projet', 'Add a new drag and drop restriction' => 'Ajouter une nouvelle restriction pour le glisser-déposer', 'Add a new column restriction' => 'Ajouter une nouvelle restriction basé sur les colonnes', @@ -1213,43 +1176,42 @@ return array( 'Closing or opening a task is permitted for this column' => 'Fermer ou ouvrir une tâche est permis pour cette colonne', 'Task creation is blocked for this column' => 'La création de tâche est bloquée pour cette colonne', 'Closing or opening a task is blocked for this column' => 'Fermer ou ouvrir une tâche est interdit pour cette colonne', - 'Task creation is not permitted' => 'La création de tâche ne pas permise', + 'Task creation is not permitted' => 'La création de tâche n\'est pas permise', 'Closing or opening a task is not permitted' => 'Fermer ou ouvrir une tâche n\'est pas autorisé', - 'New drag and drop restriction for the role "%s"' => 'Nouvelle restriction de glisser-déposer pour le rôle « %s »', - 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Les gens qui font partis de ce rôle pourrons seulement déplaçer des tâches entre la colonne source et de destination.', + 'New drag and drop restriction for the role "%s"' => 'Nouvelle restriction de glisser-déposer pour le rôle « %s »', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Les gens qui font partie de ce rôle pourront seulement déplacer des tâches entre la colonne source et de destination.', 'Remove a column restriction' => 'Supprimer une restriction de colonne', - 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Voulez-vous vraiment supprimer cette restriction de colonne : « %s » vers « %s » ?', - 'New column restriction for the role "%s"' => 'Nouvelle restriction de colonne pour le rôle « %s »', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Voulez-vous vraiment supprimer cette restriction de colonne : « %s » vers « %s » ?', + 'New column restriction for the role "%s"' => 'Nouvelle restriction de colonne pour le rôle « %s »', 'Rule' => 'Règle', - 'Do you really want to remove this column restriction?' => 'Voulez-vous vraiment supprimer cette restriction de colonne ?', + 'Do you really want to remove this column restriction?' => 'Voulez-vous vraiment supprimer cette restriction de colonne ?', 'Custom roles' => 'Rôles personnalisés', 'New custom project role' => 'Nouveau rôle de projet personnalisé', 'Edit custom project role' => 'Modifier un rôle de projet personnalisé', 'Remove a custom role' => 'Supprimer un rôle personnalisé', - 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Voulez-vous vraiment supprimer ce rôle personnalisé « %s » ? Tous les gens assignés à ce rôle deviendrons membre du projet.', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Voulez-vous vraiment supprimer ce rôle personnalisé « %s » ? Tous les gens assignés à ce rôle deviendront membre du projet.', 'There is no custom role for this project.' => 'Il n\'y a aucun rôle personnalisé pour ce projet.', - 'New project restriction for the role "%s"' => 'Nouvelle restriction de projet pour le rôle « %s »', + 'New project restriction for the role "%s"' => 'Nouvelle restriction de projet pour le rôle « %s »', 'Restriction' => 'Restriction', - 'Remove a project restriction' => 'Supprimer une restriction de project', - 'Do you really want to remove this project restriction: "%s"?' => 'Voulez-vous vraiment supprimer cette restriction de projet : « %s » ?', + 'Remove a project restriction' => 'Supprimer une restriction de projet', + 'Do you really want to remove this project restriction: "%s"?' => 'Voulez-vous vraiment supprimer cette restriction de projet : « %s » ?', 'Duplicate to multiple projects' => 'Dupliquer vers plusieurs projets', 'This field is required' => 'Ce champ est requis', - 'Moving a task is not permitted' => 'Déplaçer une tâche n\'est pas autorisé', + 'Moving a task is not permitted' => 'Déplacer une tâche n\'est pas autorisé', 'This value must be in the range %d to %d' => 'Cette valeur doit être définie entre %d et %d', 'You are not allowed to move this task.' => 'Vous n\'êtes pas autorisé à déplacer cette tâche.', 'API User Access' => 'Accès utilisateur de l\'API', 'Preview' => 'Aperçu', 'Write' => 'Écrire', 'Write your text in Markdown' => 'Écrivez votre texte en Markdown', - 'New External Task: %s' => 'Nouvelle tâche externe : %s', 'No personal API access token registered.' => 'Aucun jeton d\'accès personnel à l\'API enregistré.', - 'Your personal API access token is "%s"' => 'Votre jeton d\'accès personnel à l\'API est « %s »', + 'Your personal API access token is "%s"' => 'Votre jeton d\'accès personnel à l\'API est « %s »', 'Remove your token' => 'Supprimer votre jeton', 'Generate a new token' => 'Générer un nouveau jeton', 'Showing %d-%d of %d' => 'Éléments %d à %d sur %d', 'Outgoing Emails' => 'Emails sortants', 'Add or change currency rate' => 'Ajouter ou changer le taux de change', - 'Reference currency: %s' => 'Monnaie de référence : %s', + 'Reference currency: %s' => 'Monnaie de référence : %s', 'Add custom filters' => 'Ajouter un filtre personnalisé', 'Export' => 'Exporter', 'Add link label' => 'Ajouter un libellé de lien', @@ -1268,8 +1230,8 @@ return array( 'Unable to create this user.' => 'Impossible de créer cet utilisateur.', 'Kanboard Invitation' => 'Invitation pour Kanboard', 'Visible on dashboard' => 'Visible sur le tableau de bord', - 'Created at:' => 'Créé le :', - 'Updated at:' => 'Mis à jour le :', + 'Created at:' => 'Créé le :', + 'Updated at:' => 'Mis à jour le :', 'There is no custom filter.' => 'Il n\'y a aucun filtre personnalisé.', 'New User' => 'Nouvel utilisateur', 'Authentication' => 'Authentification', @@ -1288,7 +1250,7 @@ return array( 'This username is already taken' => 'Ce nom d\'utilisateur est déjà pris', 'A link to reset your password has been sent by email.' => 'Un lien pour réinitialiser votre mot de passe a été envoyé par email.', 'Your profile must have a valid email address.' => 'Votre profil doit avoir une adresse e-mail valide.', - 'Unfortunately, we are unable to reset your password. Did you entered a valid username? Do you have an email address in your profile?' => 'Malheureusement, nous ne pouvons pas réinitialiser votre mot de passe. Avez-vous saisi un nom d\'utilisateur valide ? Avez-vous une adresse e-mail dans votre profil ?', + 'Unfortunately, we are unable to reset your password. Did you entered a valid username? Do you have an email address in your profile?' => 'Malheureusement, nous ne pouvons pas réinitialiser votre mot de passe. Avez-vous saisi un nom d\'utilisateur valide ? Avez-vous une adresse e-mail dans votre profil ?', 'TRL - Turkish Lira' => 'TRL - Livre turque', 'The project email is optional and could be used by several plugins.' => 'L\'adresse email d\'un projet est optionnel et pourrait être utilisé par plusieurs extensions.', 'The project email must be unique across all projects' => 'L\'adresse email d\'un projet doit être unique pour tous les projets', @@ -1296,9 +1258,9 @@ return array( 'Close this project' => 'Fermer ce projet', 'Open this project' => 'Ouvrir ce projet', 'Close a project' => 'Fermer un projet', - 'Do you really want to close this project: "%s"?' => 'Voulez-vous vraiment fermer ce projet : « %s » ?', - 'Reopen a project' => 'Réouvrir un projet', - 'Do you really want to reopen this project: "%s"?' => 'Voulez-vous vraiment réouvrir ce projet : « %s » ?', + 'Do you really want to close this project: "%s"?' => 'Voulez-vous vraiment fermer ce projet : « %s » ?', + 'Reopen a project' => 'Rouvrir un projet', + 'Do you really want to reopen this project: "%s"?' => 'Voulez-vous vraiment rouvrir ce projet : « %s » ?', 'This project is open' => 'Ce projet est actif', 'This project is closed' => 'Ce projet est fermé', 'Unable to upload files, check the permissions of your data folder.' => 'Impossible de transférer le ou les fichiers, vérifiez les permissions du répertoire de données.', @@ -1313,13 +1275,12 @@ return array( 'Create and send a comment by email' => 'Créer et envoyer un commentaire par email', 'Subject' => 'Sujet', 'Upload the database' => 'Téléverser la base de données', - 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Vous pouvez téléverser la base de données Sqlite précédemment téléchargée au format Gzip.', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Vous pouvez téléverser la base de données SQLite précédemment téléchargée au format Gzip.', 'Database file' => 'Fichier de la base de données', 'Upload' => 'Téléverser', - 'Remove this user from group' => 'Enlever cet utilisateur du groupe', 'Your project must have at least one active swimlane.' => 'Votre projet doit avoir au moins une swimlane active.', - 'Project: %s' => 'Projet : %s', - 'Automatic action not found: "%s"' => 'Action automatique introuvable : « %s »', + 'Project: %s' => 'Projet : %s', + 'Automatic action not found: "%s"' => 'Action automatique introuvable : « %s »', '%d projects' => '%d projets', '%d project' => '%d projet', 'There is no project.' => 'Il n\'y a pas de projet.', @@ -1334,4 +1295,75 @@ return array( 'Assign automatically a color when due date is expired' => 'Assigner automatiquement une couleur lorsque la date d\'échéance expire', 'Total score in this column across all swimlanes' => 'Score total dans cette colonne à travers toutes les swimlanes', 'HRK - Kuna' => 'HRK - Kuna croate', + 'ARS - Argentine Peso' => 'ARS - Peso argentin', + 'COP - Colombian Peso' => 'COP - Peso colombien', + '%d groups' => '%d groupes', + '%d group' => '%d groupe', + 'Group ID' => 'ID du groupe', + 'External ID' => 'Identifiant externe', + '%d users' => '%d utilisateurs', + '%d user' => '%d utilisateur', + 'Hide subtasks' => 'Cacher les sous-tâches', + 'Show subtasks' => 'Montrer les sous-tâches', + 'Authentication Parameters' => 'Paramètres d\'authentification', + 'API Access' => 'Accès à l\'API', + 'No users found.' => 'Aucun utilisateur trouvé.', + 'User ID' => 'ID de l\'utilisateur', + 'Notifications are activated' => 'Notifications activées', + 'Notifications are disabled' => 'Notifications désactivées', + 'User disabled' => 'Utilisateur désactivé', + '%d notifications' => '%d notifications', + '%d notification' => '%d notification', + 'There is no external integration installed.' => 'Il n\'y a aucune intégration externe installée.', + 'You are not allowed to update tasks assigned to someone else.' => 'Vous n\'êtes pas autorisé à mettre à jour une tâche assignée à quelqu\'un d\'autre.', + 'You are not allowed to change the assignee.' => 'Vous n\'êtes pas autorisé à changer l\'assigné', + 'Task suppression is not permitted' => 'La suppression des tâches n\'est pas autorisée', + 'Changing assignee is not permitted' => 'Changement d\'assigné non autorisé', + 'Update only assigned tasks is permitted' => 'Seulement la mise à jour des tâches assignées est autorisé', + 'Only for tasks assigned to the current user' => 'Seulement pour les tâches assignées à l\'utilisateur courant', + 'My projects' => 'Mes projets', + 'Your are not member of any project.' => 'Vous n\'êtes membre d\'aucun projet', + 'My subtasks' => 'Mes sous-tâches', + '%d subtasks' => '%d sous-tâches', + '%d subtask' => '%d sous-tâche', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Seulement le déplacement des tâches entre ces colonnes est autorisé pour l\'utilisateur courant', + '[DUPLICATE]' => '[COPIE]', + 'DKK - Danish Krona' => 'DKK - Couronne danoise', + 'Remove user from group' => 'Supprimer cet utilisateur du groupe', + 'Assign the task to its creator' => 'Assigner une tâche à son créateur', + 'This task was sent by email to "%s" with subject "%s".' => 'Cette tâche a été envoyée par courrier électronique à « %s » avec le sujet « %s ».', + 'Predefined Email Subjects' => 'Sujets de courrier électronique prédéfinis', + 'Write one subject by line.' => 'Écrivez un sujet par ligne.', + 'Create another link' => 'Créer un autre lien', + 'BRL - Brazilian Real' => 'BRL - Real brésilien', + 'Add a new Kanboard task' => 'Ajouter une nouvelle tâche Kanboard', + 'Subtask not started' => 'Sous-tâche pas encore commencé', + 'Subtask currently in progress' => 'Sous-tâche actuellement en progrès', + 'Subtask completed' => 'Sous-tâche terminée', + 'Subtask added successfully.' => 'Sous-tâche ajoutée avec succès.', + '%d subtasks added successfully.' => '%d sous-tâches ajoutées avec succès.', + 'Enter one subtask by line.' => 'Entrez une sous-tâche par ligne.', + 'Predefined Contents' => 'Contenus prédéfini', + 'Predefined contents' => 'Contenus prédéfini', + 'Predefined Task Description' => 'Modèles de description de tâches', + 'Do you really want to remove this template? "%s"' => 'Voulez-vous vraiment supprimer ce modèle ? « %s »', + 'Add predefined task description' => 'Ajouter un modèle de description de tâche', + 'Predefined Task Descriptions' => 'Modèles de description de tâches', + 'Template created successfully.' => 'Modèle créé avec succès.', + 'Unable to create this template.' => 'Impossible de créer ce modèle.', + 'Template updated successfully.' => 'Modèle mis à jour avec succès.', + 'Unable to update this template.' => 'Impossible de mettre à jour ce modèle.', + 'Template removed successfully.' => 'Modèle supprimé avec succès.', + 'Unable to remove this template.' => 'Impossible de supprimer ce modèle.', + 'Template for the task description' => 'Modèle pour la description des tâches', + 'The start date is greater than the end date' => 'La date de début est plus grande que la date d\'échéance', + 'Tags must be separated by a comma' => 'Les labels doivent être séparé par une virgule', + 'Only the task title is required' => 'Seulement le titre est obligatoire', + 'Creator Username' => 'Identifiant du créateur', + 'Color Name' => 'Nom de la couleur', + 'Column Name' => 'Nom de la colonne', + 'Swimlane Name' => 'Nom de la swimlane', + 'Time Estimated' => 'Durée estimée', + 'Time Spent' => 'Temps passé', + 'External Link' => 'Lien externe', ); diff --git a/app/Locale/hr_HR/translations.php b/app/Locale/hr_HR/translations.php index 28474da6..b57dc3b9 100644 --- a/app/Locale/hr_HR/translations.php +++ b/app/Locale/hr_HR/translations.php @@ -37,9 +37,8 @@ return array( 'Username' => 'Korisnik', 'Password' => 'Lozinka', 'Administrator' => 'Administrator', - 'Sign in' => 'Odjava', + 'Sign in' => 'Prijava', 'Users' => 'Korisnik', - 'No user' => 'Ne', 'Forbidden' => 'Zabranjeno', 'Access Forbidden' => 'Zabranjen prostup', 'Edit user' => 'Promjeni korisnika', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'Pod-zadatak je uspjeÅ¡no dopunjen.', 'Unable to update your sub-task.' => 'Nije moguće dopuniti ovaj pod-zadatak.', 'Unable to create your sub-task.' => 'Nije moguće krerati ovaj pod-zadatak.', - 'Sub-task added successfully.' => 'Pod-zadatak uspjeÅ¡no dodan', 'Maximum size: ' => 'Maksimalna veliÄina: ', 'Display another project' => 'Prikaži drugi projekt', 'Created by %s' => 'Kreirao %s', @@ -341,7 +339,7 @@ return array( '%s created the task %s' => '%s je kreirao zadatak %s', '%s closed the task %s' => '%s je zatvorio zadatak %s', '%s created a subtask for the task %s' => '%s je kreirao pod-zadatak zadatka %s', - '%s updated a subtask for the task %s' => '%s je dopunjen pod-zadatak zadatka %s', + '%s updated a subtask for the task %s' => '%s je dopunio pod-zadatak zadatka %s', 'Assigned to %s with an estimate of %s/%sh' => 'Dodijeljen korisniku %s uz procjenu vremena %s/%sh', 'Not assigned, estimate of %sh' => 'Nije dodijeljen, procijenjeno vrijeme %sh', '%s updated a comment on the task %s' => '%s je dopunio komentar zadatka %s', @@ -396,22 +394,17 @@ return array( 'Activity stream' => 'Popis aktivnosti', 'Dashboard' => 'Nadzorna ploÄa', 'Confirmation' => 'Potvrda', - 'Allow everybody to access to this project' => 'Dozvoli svima pristup projektu', - 'Everybody have access to this project.' => 'Svima je dozvoljen pristup.', // 'Webhooks' => '', // 'API' => '', // 'Create a comment from an external provider' => '', 'Project management' => 'UreÄ‘ivanje projekata', - 'My projects' => 'Moji projekti', 'Columns' => 'Stupci', 'Task' => 'Zadaci', - 'Your are not member of any project.' => 'Nisi Älan niti jednog projekta', 'Percentage' => 'Postotak', 'Number of tasks' => 'Broj zadataka', 'Task distribution' => 'Podjela zadataka', 'Analytics' => 'Analiza', 'Subtask' => 'Pod-zadatak', - 'My subtasks' => 'Moji pod-zadaci', 'User repartition' => 'Zaduženja korisnika', 'Clone this project' => 'Kopiraj projekt', 'Column removed successfully.' => 'Stupac je uspjeÅ¡no maknut.', @@ -459,7 +452,6 @@ return array( 'Language:' => 'Jezik:', 'Timezone:' => 'Vremenska zona:', 'All columns' => 'Svi stupci', - 'Calendar' => 'Kalendar', 'Next' => 'Idući', // '#%d' => '', 'All swimlanes' => 'Sve staze', @@ -543,10 +535,10 @@ return array( // 'Move the task to another column when assigned to a user' => '', // 'Move the task to another column when assignee is cleared' => '', // 'Source column' => '', - // 'Transitions' => '', + 'Transitions' => 'Pomicanja', // 'Executer' => '', - // 'Time spent in the column' => '', - // 'Task transitions' => '', + 'Time spent in the column' => 'Vrijeme provedeno u stupcu', + 'Task transitions' => 'Pomicanja zadatka', // 'Task transitions export' => '', // 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => '', // 'Currency rates' => '', @@ -557,7 +549,6 @@ return array( // 'Unable to add this currency rate.' => '', // 'Webhook URL' => '', // '%s removed the assignee of the task %s' => '', - 'Enable Gravatar images' => 'Omogući Gravatar slike', // 'Information' => '', // 'Check two factor authentication code' => '', // 'The two factor authentication code is not valid.' => '', @@ -569,7 +560,7 @@ return array( // 'Secret key: ' => '', // 'Test your device' => '', // 'Assign a color when the task is moved to a specific column' => '', - // '%s via Kanboard' => '', + '%s via Kanboard' => '%s pomoću Kanboard', // 'Burndown chart' => '', // 'This chart show the task complexity over the time (Work Remaining).' => '', // 'Screenshot taken %s' => '', @@ -589,7 +580,7 @@ return array( // 'The identifier must be unique' => '', // 'This linked task id doesn\'t exists' => '', // 'This value must be alphanumeric' => '', - // 'Edit recurrence' => '', + 'Edit recurrence' => 'Promjena ponavljanja', // 'Generate recurrent task' => '', // 'Trigger to generate recurrent task' => '', // 'Factor to calculate new due date' => '', @@ -611,14 +602,7 @@ return array( // 'When task is moved from first column' => '', // 'When task is moved to last column' => '', // 'Year(s)' => '', - 'Calendar settings' => 'Postavke kalendara', - 'Project calendar view' => 'Projektni kalendar', 'Project settings' => 'Postavke projekta', - 'Show subtasks based on the time tracking' => 'Prikaz pod-zadataka po evidenciji vremena', - 'Show tasks based on the creation date' => 'Prikaz zadataka po datumu kreiranja', - 'Show tasks based on the start date' => 'Prikaz zadataka po datumu poÄetka', - 'Subtasks time tracking' => 'Evidencija vremena pod-zadataka', - 'User calendar view' => 'KorisniÄki kalendar', // 'Automatically update the start date' => '', // 'iCal feed' => '', 'Preferences' => 'Postavke', @@ -636,14 +620,13 @@ return array( // 'Reopen a task' => '', // 'Notification' => '', // '%s moved the task #%d to the first swimlane' => '', - // 'Swimlane' => '', - // 'Gravatar' => '', + 'Swimlane' => 'Staza', // '%s moved the task %s to the first swimlane' => '', // '%s moved the task %s to the swimlane "%s"' => '', // 'This report contains all subtasks information for the given date range.' => '', // 'This report contains all tasks information for the given date range.' => '', // 'Project activities for %s' => '', - // 'view the board on Kanboard' => '', + 'view the board on Kanboard' => 'Pregledaj ploÄu', // 'The task have been moved to the first swimlane' => '', // 'The task have been moved to another swimlane:' => '', // 'New title: %s' => '', @@ -674,7 +657,6 @@ return array( // 'Stop timer' => '', // 'Start timer' => '', 'My activity stream' => 'Moje aktivnosti', - 'My calendar' => 'Moj kalendar', 'Search tasks' => 'Pretraživanje zadataka', 'Reset filters' => 'ObriÅ¡i filtere', 'My tasks due tomorrow' => 'Moji zadaci sa rokom sutra', @@ -688,7 +670,6 @@ return array( 'Overview' => 'Pregled', // 'Board/Calendar/List view' => '', // 'Switch to the board view' => '', - // 'Switch to the calendar view' => '', // 'Switch to the list view' => '', // 'Go to the search/filter box' => '', // 'There is no activity yet.' => '', @@ -737,21 +718,14 @@ return array( // 'Current category: %s' => '', // 'no category' => '', // 'Current assignee: %s' => '', - // 'not assigned' => '', + 'not assigned' => 'nije dodjeljeno', // 'Author:' => '', // 'contributors' => '', // 'License:' => '', // 'License' => '', // 'Enter the text below' => '', - // 'Sort by position' => '', - // 'Sort by date' => '', - // 'Add task' => '', // 'Start date:' => '', - // 'Due date:' => '', - // 'There is no start date or due date for this task.' => '', - // 'Moving or resizing a task will change the start and due date of the task.' => '', - // 'There is no task in your project.' => '', - // 'Gantt chart' => '', + 'Due date:' => 'Rok:', // 'People who are project managers' => '', // 'People who are project members' => '', // 'NOK - Norwegian Krone' => '', @@ -763,22 +737,15 @@ return array( 'Members' => 'ÄŒlanovi', // 'Shared project' => '', // 'Project managers' => '', - // 'Gantt chart for all projects' => '', // 'Projects list' => '', - // 'Gantt chart for this project' => '', - // 'Project board' => '', // 'End date:' => '', - // 'There is no start date or end date for this project.' => '', - // 'Projects Gantt chart' => '', // 'Change task color when using a specific task link' => '', // 'Task link creation or modification' => '', // 'Milestone' => '', // 'Documentation: %s' => '', - // 'Switch to the Gantt chart view' => '', // 'Reset the search/filter box' => '', 'Documentation' => 'Dokumentacija', // 'Table of contents' => '', - // 'Gantt' => '', // 'Author' => '', // 'Version' => '', // 'Plugins' => '', @@ -804,7 +771,7 @@ return array( // 'Task #%d opened' => '', // 'Column changed for task #%d' => '', // 'New position for task #%d' => '', - // 'Swimlane changed for task #%d' => '', + 'Swimlane changed for task #%d' => 'Staza izmjenjena za zadatak #%d', // 'Assignee changed on task #%d' => '', // '%d overdue tasks' => '', // 'Task #%d is overdue' => '', @@ -854,7 +821,7 @@ return array( // 'Duplicates are not imported' => '', // 'Usernames must be lowercase and unique' => '', // 'Passwords will be encrypted if present' => '', - // '%s attached a new file to the task %s' => '', + '%s attached a new file to the task %s' => '%s je dodao datoteku za zadatak %s', // 'Link type' => '', // 'Assign automatically a category based on a link' => '', // 'BAM - Konvertible Mark' => '', @@ -889,7 +856,6 @@ return array( // 'There is no user available.' => '', // 'Do you really want to remove the user "%s" from the group "%s"?' => '', // 'There is no group.' => '', - // 'External Id' => '', // 'Add group member' => '', // 'Do you really want to remove this group: "%s"?' => '', // 'There is no user in this group.' => '', @@ -907,8 +873,8 @@ return array( // 'Project members' => '', // '%s mentioned you in the task #%d' => '', // '%s mentioned you in a comment on the task #%d' => '', - // 'You were mentioned in the task #%d' => '', - // 'You were mentioned in a comment on the task #%d' => '', + 'You were mentioned in the task #%d' => 'Spomenuti ste u zadatku #%d', + 'You were mentioned in a comment on the task #%d' => 'Spomenuti ste u komntaru od zadatka #%d', // 'Estimated hours: ' => '', // 'Actual hours: ' => '', // 'Hours Spent' => '', @@ -925,7 +891,7 @@ return array( // 'Enable two-factor authentication' => '', // 'There is no integration registered at the moment.' => '', // 'Password Reset for Kanboard' => '', - // 'Forgot password?' => '', + 'Forgot password?' => 'Zaboravili ste lozinku?', 'Enable "Forget Password"' => 'Omogući opciju "Zaboravljena lozinka"', // 'Password Reset' => '', // 'New password' => '', @@ -955,7 +921,6 @@ return array( 'Default priority' => 'Inicijalni prioritet', // 'Lowest priority' => '', // 'Highest priority' => '', - // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Close a task when there is no activity' => '', // 'Duration in days' => '', // 'Send email when there is no activity on a task' => '', @@ -967,7 +932,7 @@ return array( // 'Title not found' => '', // 'Web Link' => '', // 'External links' => '', - // 'Add external link' => '', + 'Add external link' => 'Dodaj vanjski link', // 'Type' => '', // 'Dependency' => '', // 'Add internal link' => '', @@ -977,8 +942,8 @@ return array( // 'Copy and paste your link here...' => '', // 'URL' => '', // 'Internal links' => '', - // 'Assign to me' => '', - // 'Me' => '', + 'Assign to me' => 'Dodjeli meni', + 'Me' => 'Ja', // 'Do not duplicate anything' => '', 'Projects management' => 'UreÄ‘ivanje projekata', // 'Users management' => '', @@ -987,9 +952,9 @@ return array( 'open' => 'otvoren', 'closed' => 'zatvoren', 'Priority:' => 'Prioritet:', - // 'Reference:' => '', - // 'Complexity:' => '', - // 'Swimlane:' => '', + 'Reference:' => 'Referenca:', + 'Complexity:' => 'Kompleksnost:', + 'Swimlane:' => 'Staza:', 'Column:' => 'Stupac:', 'Position:' => 'Pozicija:', 'Creator:' => 'Napravio:', @@ -1146,13 +1111,13 @@ return array( 'There is no global tag at the moment.' => 'Trenutno nema globalno definiranih oznaka.', // 'This field cannot be empty' => '', // 'Close a task when there is no activity in an specific column' => '', - // '%s removed a subtask for the task #%d' => '', + '%s removed a subtask for the task #%d' => '%s je uklonio pod-zadatak od zadataka #%d', // '%s removed a comment on the task #%d' => '', // 'Comment removed on task #%d' => '', // 'Subtask removed on task #%d' => '', // 'Hide tasks in this column in the dashboard' => '', // '%s removed a comment on the task %s' => '', - // '%s removed a subtask for the task %s' => '', + '%s removed a subtask for the task %s' => '%s je uklonio pod-zadatak od zadataka #%s', // 'Comment removed' => '', // 'Subtask removed' => '', // '%s set a new internal link for the task #%d' => '', @@ -1166,11 +1131,9 @@ return array( // 'Move the task to another column when not moved during a given period' => '', 'Dashboard for %s' => 'Nadzorna ploÄa za %s', 'Tasks overview for %s' => 'Pregled zadataka za %s', - // 'Subtasks overview for %s' => '', - // 'Projects overview for %s' => '', + 'Subtasks overview for %s' => 'Pregled podzadataka za %s', + 'Projects overview for %s' => 'Pregled projekata za %s', // 'Activity stream for %s' => '', - // 'Calendar for %s' => '', - // 'Notifications for %s' => '', // 'Assign a color when the task is moved to a specific swimlane' => '', // 'Assign a priority when the task is moved to a specific swimlane' => '', // 'User unlocked successfully.' => '', @@ -1241,7 +1204,6 @@ return array( // 'Preview' => '', // 'Write' => '', // 'Write your text in Markdown' => '', - // 'New External Task: %s' => '', 'No personal API access token registered.' => 'Nije zabilježen niti jedan pristupni token.', // 'Your personal API access token is "%s"' => '', // 'Remove your token' => '', @@ -1309,19 +1271,18 @@ return array( // 'Database uploaded successfully.' => '', // 'Task sent by email successfully.' => '', // 'There is no category in this project.' => '', - // 'Send by email' => '', + 'Send by email' => 'PoÅ¡alji emailom', // 'Create and send a comment by email' => '', // 'Subject' => '', // 'Upload the database' => '', // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', // 'Database file' => '', // 'Upload' => '', - // 'Remove this user from group' => '', // 'Your project must have at least one active swimlane.' => '', // 'Project: %s' => '', // 'Automatic action not found: "%s"' => '', - // '%d projects' => '', - // '%d project' => '', + '%d projects' => '%d projekta', + '%d project' => '%d projekt', // 'There is no project.' => '', // 'Sort' => '', // 'Project ID' => '', @@ -1334,4 +1295,75 @@ return array( // 'Assign automatically a color when due date is expired' => '', // 'Total score in this column across all swimlanes' => '', // 'HRK - Kuna' => '', + // 'ARS - Argentine Peso' => '', + // 'COP - Colombian Peso' => '', + // '%d groups' => '', + // '%d group' => '', + // 'Group ID' => '', + // 'External ID' => '', + // '%d users' => '', + // '%d user' => '', + // 'Hide subtasks' => '', + // 'Show subtasks' => '', + // 'Authentication Parameters' => '', + // 'API Access' => '', + // 'No users found.' => '', + // 'User ID' => '', + // 'Notifications are activated' => '', + // 'Notifications are disabled' => '', + // 'User disabled' => '', + // '%d notifications' => '', + // '%d notification' => '', + // 'There is no external integration installed.' => '', + // 'You are not allowed to update tasks assigned to someone else.' => '', + // 'You are not allowed to change the assignee.' => '', + // 'Task suppression is not permitted' => '', + // 'Changing assignee is not permitted' => '', + // 'Update only assigned tasks is permitted' => '', + // 'Only for tasks assigned to the current user' => '', + 'My projects' => 'Moji projekti', + // 'Your are not member of any project.' => '', + 'My subtasks' => 'Moji podzadaci', + // '%d subtasks' => '', + // '%d subtask' => '', + // 'Only moving task between those columns is permitted for tasks assigned to the current user' => '', + // '[DUPLICATE]' => '', + // 'DKK - Danish Krona' => '', + // 'Remove user from group' => '', + // 'Assign the task to its creator' => '', + // 'This task was sent by email to "%s" with subject "%s".' => '', + // 'Predefined Email Subjects' => '', + // 'Write one subject by line.' => '', + // 'Create another link' => '', + // 'BRL - Brazilian Real' => '', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/hu_HU/translations.php b/app/Locale/hu_HU/translations.php index 2d02f07e..eac0761e 100644 --- a/app/Locale/hu_HU/translations.php +++ b/app/Locale/hu_HU/translations.php @@ -5,44 +5,43 @@ return array( 'number.thousands_separator' => ' ', 'None' => 'Nincs', 'Edit' => 'Szerkesztés', - 'Remove' => 'Törlés', + 'Remove' => 'EltávolÃtás', 'Yes' => 'Igen', 'No' => 'Nem', - 'cancel' => 'Mégsem', + 'cancel' => 'mégse', 'or' => 'vagy', 'Yellow' => 'Sárga', 'Blue' => 'Kék', 'Green' => 'Zöld', 'Purple' => 'Lila', 'Red' => 'Piros', - 'Orange' => 'Narancs', + 'Orange' => 'Narancssárga', 'Grey' => 'Szürke', 'Brown' => 'Barna', 'Deep Orange' => 'Sötét narancs', 'Dark Grey' => 'Sötét szürke', 'Pink' => 'RózsaszÃn', - 'Teal' => 'Pávakék', + 'Teal' => 'Kékeszöld', 'Cyan' => 'Ciánkék', - 'Lime' => 'Lime', + 'Lime' => 'Citrus', 'Light Green' => 'Világos zöld', 'Amber' => 'Borostyán', 'Save' => 'Mentés', 'Login' => 'Bejelentkezés', - 'Official website:' => 'Hivatalos honlap:', + 'Official website:' => 'Hivatalos weboldal:', 'Unassigned' => 'Nincs felelÅ‘s', 'View this task' => 'Feladat megtekintése', - 'Remove user' => 'Felhasználó törlése', - 'Do you really want to remove this user: "%s"?' => 'Valóban törölni akarja ezt a felhasználót: "%s"?', - 'All users' => 'Minden felhasználó', + 'Remove user' => 'Felhasználó eltávolÃtása', + 'Do you really want to remove this user: "%s"?' => 'Valóban el szeretné távolÃtani ezt a felhasználót: „%sâ€?', + 'All users' => 'Összes felhasználó', 'Username' => 'Felhasználónév', 'Password' => 'Jelszó', - 'Administrator' => 'Rendszergazda', - 'Sign in' => 'Jelentkezzen be', + 'Administrator' => 'Adminisztrátor', + 'Sign in' => 'Bejelentkezés', 'Users' => 'Felhasználók', - 'No user' => 'Nincs felhasználó', - 'Forbidden' => 'tiltott', + 'Forbidden' => 'Tiltott', 'Access Forbidden' => 'Hozzáférés megtagadva', - 'Edit user' => 'Felhasználó módosÃtása', + 'Edit user' => 'Felhasználó szerkesztése', 'Logout' => 'Kilépés', 'Bad username or password' => 'Rossz felhasználónév vagy jelszó', 'Edit project' => 'Projekt szerkesztése', @@ -51,159 +50,159 @@ return array( 'No project' => 'Nincs projekt', 'Project' => 'Projekt', 'Status' => 'Ãllapot', - 'Tasks' => 'Feladat', + 'Tasks' => 'Feladatok', 'Board' => 'Tábla', 'Actions' => 'Műveletek', 'Inactive' => 'InaktÃv', 'Active' => 'AktÃv', - 'Unable to update this board.' => 'Nem lehet frissÃteni a táblát.', + 'Unable to update this board.' => 'Nem lehet frissÃteni ezt a táblát.', 'Disable' => 'Letiltás', 'Enable' => 'Engedélyezés', 'New project' => 'Új projekt', - 'Do you really want to remove this project: "%s"?' => 'Valóban törölni akarja ezt a projektet: "%s"?', - 'Remove project' => 'Projekt törlése', - 'Edit the board for "%s"' => 'Tábla szerkesztése: "%s"', - 'Add a new column' => 'Új oszlop', + 'Do you really want to remove this project: "%s"?' => 'Valóban el szeretné távolÃtani ezt a projektet: „%sâ€?', + 'Remove project' => 'Projekt eltávolÃtása', + 'Edit the board for "%s"' => 'Tábla szerkesztése: „%sâ€', + 'Add a new column' => 'Új oszlop hozzáadása', 'Title' => 'CÃm', 'Assigned to %s' => 'FelelÅ‘s: %s', - 'Remove a column' => 'Oszlop törlése', - 'Unable to remove this column.' => 'Az oszlop törlése nem lehetséges.', - 'Do you really want to remove this column: "%s"?' => 'Valóban törölni akarja ezt az oszlopot: "%s"?', + 'Remove a column' => 'Oszlop eltávolÃtása', + 'Unable to remove this column.' => 'Nem lehet eltávolÃtani ezt az oszlopot.', + 'Do you really want to remove this column: "%s"?' => 'Valóban el szeretné távolÃtani ezt az oszlopot: „%sâ€?', 'Settings' => 'BeállÃtások', - 'Application settings' => 'Alkalmazás beállÃtások', + 'Application settings' => 'Alkalmazás beállÃtásai', 'Language' => 'Nyelv', - 'Webhook token:' => 'Webhook token:', + 'Webhook token:' => 'Webhurok token:', 'API token:' => 'API token:', - 'Database size:' => 'Adatbázis méret:', + 'Database size:' => 'Adatbázis mérete:', 'Download the database' => 'Adatbázis letöltése', 'Optimize the database' => 'Adatbázis optimalizálása', '(VACUUM command)' => '(VACUUM parancs)', - '(Gzip compressed Sqlite file)' => '(Gzip tömörÃtett SQLite fájl)', + '(Gzip compressed Sqlite file)' => '(Gzip-pel tömörÃtett SQLite fájl)', 'Close a task' => 'Feladat lezárása', 'Column' => 'Oszlop', 'Color' => 'SzÃn', 'Assignee' => 'FelelÅ‘s', - 'Create another task' => 'Új feladat létrehozása', + 'Create another task' => 'Másik feladat létrehozása', 'New task' => 'Új feladat', - 'Open a task' => 'Feladat felnyitás', - 'Do you really want to open this task: "%s"?' => 'Valóban meg akarja nyitni ezt a feladatot: "%s"?', + 'Open a task' => 'Feladat megnyitása', + 'Do you really want to open this task: "%s"?' => 'Valóban meg szeretné nyitni ezt a feladatot: „%sâ€?', 'Back to the board' => 'Vissza a táblához', - 'There is nobody assigned' => 'Nincs felelÅ‘s', - 'Column on the board:' => 'Tábla oszlopa: ', + 'There is nobody assigned' => 'Nincs senki se hozzárendelve', + 'Column on the board:' => 'Oszlop a táblán:', 'Close this task' => 'Feladat lezárása', - 'Open this task' => 'Feladat felnyitása', - 'There is no description.' => 'Nincs elérhetÅ‘ leÃrás.', + 'Open this task' => 'Feladat megnyitása', + 'There is no description.' => 'Nincs leÃrás.', 'Add a new task' => 'Új feladat hozzáadása', - 'The username is required' => 'Felhasználói név szükséges', - 'The maximum length is %d characters' => 'A maximális hossz %d karakter', - 'The minimum length is %d characters' => 'A minimális hossza %d karakter', - 'The password is required' => 'Jelszó szükséges', + 'The username is required' => 'A felhasználónév kötelezÅ‘', + 'The maximum length is %d characters' => 'A legnagyobb hossz %d karakter', + 'The minimum length is %d characters' => 'A legkisebb hossz %d karakter', + 'The password is required' => 'A jelszó kötelezÅ‘', 'This value must be an integer' => 'Ez az érték csak egész szám lehet', - 'The username must be unique' => 'A felhasználó nevének egyedinek kell lennie', - 'The user id is required' => 'A felhasználói azonosÃtót meg kell adni', + 'The username must be unique' => 'A felhasználónévnek egyedinek kell lennie', + 'The user id is required' => 'A felhasználó-azonosÃtó kötelezÅ‘', 'Passwords don\'t match' => 'A jelszavak nem egyeznek', - 'The confirmation is required' => 'MegerÅ‘sÃtés szükséges', - 'The project is required' => 'A projektet meg kell adni', - 'The id is required' => 'Az ID-t (azonosÃtót) meg kell adni', - 'The project id is required' => 'A projekt ID-t (azonosÃtót) meg kell adni', - 'The project name is required' => 'A projekt nevét meg kell adni', - 'The title is required' => 'A cÃmet meg kell adni', + 'The confirmation is required' => 'A megerÅ‘sÃtés kötelezÅ‘', + 'The project is required' => 'A projekt kötelezÅ‘', + 'The id is required' => 'Az azonosÃtó kötelezÅ‘', + 'The project id is required' => 'A projekt-azonosÃtó kötelezÅ‘', + 'The project name is required' => 'A projektnév kötelezÅ‘', + 'The title is required' => 'A cÃm kötelezÅ‘', 'Settings saved successfully.' => 'A beállÃtások sikeresen mentve.', - 'Unable to save your settings.' => 'A beállÃtások mentése sikertelen.', - 'Database optimization done.' => 'Adatbázis optimalizálás kész.', - 'Your project have been created successfully.' => 'Projekt sikeresen létrehozva', - 'Unable to create your project.' => 'Projekt létrehozása sikertelen.', - 'Project updated successfully.' => 'Projekt sikeresen frissÃtve.', - 'Unable to update this project.' => 'Projekt frissÃtése sikertelen.', - 'Unable to remove this project.' => 'Projekt törlése sikertelen.', - 'Project removed successfully.' => 'Projekt sikeresen törölve.', - 'Project activated successfully.' => 'Projekt sikeresen aktiválva.', - 'Unable to activate this project.' => 'Projekt aktiválása sikertelen.', - 'Project disabled successfully.' => 'Projekt sikeresen letiltva.', - 'Unable to disable this project.' => 'Projekt letiltása sikertelen.', - 'Unable to open this task.' => 'A feladat felnyitása sikertelen.', - 'Task opened successfully.' => 'Feladat sikeresen megnyitva .', - 'Unable to close this task.' => 'A feladat lezárása sikertelen.', - 'Task closed successfully.' => 'Feladat sikeresen lezárva.', - 'Unable to update your task.' => 'Feladat frissÃtése sikertelen.', - 'Task updated successfully.' => 'Feladat sikeresen frissÃtve.', - 'Unable to create your task.' => 'Feladat létrehozása sikertelen.', - 'Task created successfully.' => 'Feladat sikeresen létrehozva.', - 'User created successfully.' => 'Felhasználó létrehozva.', - 'Unable to create your user.' => 'Felhasználó létrehozása sikertelen.', - 'User updated successfully.' => 'Felhasználó sikeresen frissÃtve.', - 'User removed successfully.' => 'Felhasználó sikeresen törölve.', - 'Unable to remove this user.' => 'Felhasználó törlése sikertelen.', - 'Board updated successfully.' => 'Tábla sikeresen frissÃtve.', - 'Ready' => 'ElÅ‘készÃtés', - 'Backlog' => 'Napló', + 'Unable to save your settings.' => 'Nem lehet elmenteni a beállÃtásokat.', + 'Database optimization done.' => 'Az adatbázis optimalizálása kész.', + 'Your project have been created successfully.' => 'A projekt sikeresen létrehozva.', + 'Unable to create your project.' => 'Nem lehet létrehozni a projektet.', + 'Project updated successfully.' => 'A projekt sikeresen frissÃtve.', + 'Unable to update this project.' => 'Nem lehet frissÃteni ezt a projektet.', + 'Unable to remove this project.' => 'Nem lehet eltávolÃtani ezt a projektet.', + 'Project removed successfully.' => 'A projekt sikeresen eltávolÃtva.', + 'Project activated successfully.' => 'A projekt sikeresen aktiválva.', + 'Unable to activate this project.' => 'Nem lehet aktiválni ezt a projektet.', + 'Project disabled successfully.' => 'A projekt sikeresen letiltva.', + 'Unable to disable this project.' => 'Nem lehet letiltani ezt a projektet.', + 'Unable to open this task.' => 'Nem lehet megnyitni ezt a feladatot.', + 'Task opened successfully.' => 'A feladat sikeresen megnyitva.', + 'Unable to close this task.' => 'Nem lehet lezárni ezt a feladatot.', + 'Task closed successfully.' => 'A feladat sikeresen lezárva.', + 'Unable to update your task.' => 'Nem lehet frissÃteni a feladatot.', + 'Task updated successfully.' => 'A feladat sikeresen frissÃtve.', + 'Unable to create your task.' => 'Nem lehet létrehozni a feladatot.', + 'Task created successfully.' => 'A feladat sikeresen létrehozva.', + 'User created successfully.' => 'A felhasználó sikeresen létrehozva.', + 'Unable to create your user.' => 'Nem lehet létrehozni a felhasználót.', + 'User updated successfully.' => 'A felhasználó sikeresen frissÃtve.', + 'User removed successfully.' => 'A felhasználó sikeresen eltávolÃtva.', + 'Unable to remove this user.' => 'Nem lehet eltávolÃtani ezt a felhasználót.', + 'Board updated successfully.' => 'A tábla sikeresen frissÃtve.', + 'Ready' => 'Felkészülés', + 'Backlog' => 'ElintézendÅ‘', 'Work in progress' => 'Folyamatban', 'Done' => 'Kész', - 'Application version:' => 'Alkalmazás verzió:', - 'Id' => 'ID', + 'Application version:' => 'Alkalmazás verziója:', + 'Id' => 'AzonosÃtó', 'Public link' => 'Nyilvános hivatkozás', 'Timezone' => 'IdÅ‘zóna', - 'Sorry, I didn\'t find this information in my database!' => 'Ez az információ nem található az adatbázisban!', + 'Sorry, I didn\'t find this information in my database!' => 'Elnézést, ez az információ nem található az adatbázisban!', 'Page not found' => 'Az oldal nem található', 'Complexity' => 'Bonyolultság', - 'Task limit' => 'Maximális számú feladat', + 'Task limit' => 'Feladatkorlát', 'Task count' => 'Feladatok száma', 'User' => 'Felhasználó', 'Comments' => 'Hozzászólások', - 'Comment is required' => 'A hozzászólás mezÅ‘ kötelezÅ‘', - 'Comment added successfully.' => 'Hozzászólás sikeresen elküldve.', - 'Unable to create your comment.' => 'Hozzászólás létrehozása nem lehetséges.', + 'Comment is required' => 'A hozzászólás kötelezÅ‘', + 'Comment added successfully.' => 'A hozzászólás sikeresen hozzáadva.', + 'Unable to create your comment.' => 'Nem lehet létrehozni a hozzászólást.', 'Due Date' => 'HatáridÅ‘', 'Invalid date' => 'Érvénytelen dátum', - 'Automatic actions' => 'Automatikus intézkedések', - 'Your automatic action have been created successfully.' => 'Az automatikus intézkedés sikeresen elkészült.', - 'Unable to create your automatic action.' => 'Automatikus intézkedés létrehozása nem lehetséges.', - 'Remove an action' => 'Intézkedés törlése', - 'Unable to remove this action.' => 'Intézkedés törlése nem lehetséges.', - 'Action removed successfully.' => 'Intézkedés sikeresen törölve.', - 'Automatic actions for the project "%s"' => 'Automatikus intézkedések a projektben: "%s"', - 'Add an action' => 'Intézkedés létrehozása', + 'Automatic actions' => 'Automatikus műveletek', + 'Your automatic action have been created successfully.' => 'Az automatikus művelet sikeresen létrehozva.', + 'Unable to create your automatic action.' => 'Nem lehet létrehozni az automatikus műveletet.', + 'Remove an action' => 'Művelet eltávolÃtása', + 'Unable to remove this action.' => 'Nem lehet eltávolÃtani ezt a műveletet.', + 'Action removed successfully.' => 'A művelet sikeresen eltávolÃtva.', + 'Automatic actions for the project "%s"' => 'Automatikus műveletek a projekthez: „%sâ€', + 'Add an action' => 'Művelet hozzáadása', 'Event name' => 'Esemény neve', - 'Action' => 'Intézkedés', + 'Action' => 'Művelet', 'Event' => 'Esemény', - 'When the selected event occurs execute the corresponding action.' => 'Ha a kiválasztott esemény bekövetkezik, hajtsa végre a megfelelÅ‘ intézkedéseket.', + 'When the selected event occurs execute the corresponding action.' => 'Ha a kiválasztott esemény bekövetkezik, hajtsa végre a megfelelÅ‘ műveletet.', 'Next step' => 'KövetkezÅ‘ lépés', - 'Define action parameters' => 'Határozza meg az intézkedés paramétereit', - 'Do you really want to remove this action: "%s"?' => 'Valóban törölni akarja ezt az intézkedést: "%s"?', - 'Remove an automatic action' => 'Automatikus intézkedés törlése', - 'Assign the task to a specific user' => 'Feladat kiosztása megadott felhasználónak', - 'Assign the task to the person who does the action' => 'Feladat kiosztása az intézkedÅ‘ személynek', - 'Duplicate the task to another project' => 'Feladat másolása másik projektbe', - 'Move a task to another column' => 'Feladat mozgatása másik oszlopba', + 'Define action parameters' => 'Művelet paramétereinek meghatározása', + 'Do you really want to remove this action: "%s"?' => 'Valóban el szeretné távolÃtani ezt a műveletet: „%sâ€?', + 'Remove an automatic action' => 'Automatikus művelet eltávolÃtása', + 'Assign the task to a specific user' => 'Feladat kiosztása egy adott felhasználónak', + 'Assign the task to the person who does the action' => 'Feladat kiosztása a műveletet elvégzÅ‘ személynek', + 'Duplicate the task to another project' => 'Feladat másolása egy másik projektbe', + 'Move a task to another column' => 'Feladat áthelyezése egy másik oszlopba', 'Task modification' => 'Feladat módosÃtása', 'Task creation' => 'Feladat létrehozása', 'Closing a task' => 'Feladat lezárása', - 'Assign a color to a specific user' => 'SzÃn hozzárendelése a felhasználóhoz', + 'Assign a color to a specific user' => 'SzÃn hozzárendelése egy adott felhasználóhoz', 'Position' => 'PozÃció', - 'Duplicate to another project' => 'Másolás másik projektbe', + 'Duplicate to another project' => 'Másolás egy másik projektbe', 'Duplicate' => 'Másolás', - 'Link' => 'hivatkozás', - 'Comment updated successfully.' => 'Megjegyzés sikeresen frissÃtve.', - 'Unable to update your comment.' => 'Megjegyzés frissÃtése sikertelen.', - 'Remove a comment' => 'Megjegyzés törlése', - 'Comment removed successfully.' => 'Megjegyzés sikeresen törölve.', - 'Unable to remove this comment.' => 'Megjegyzés törölése nem lehetséges.', - 'Do you really want to remove this comment?' => 'Valóban törölni szeretné ezt a megjegyzést?', - 'Current password for the user "%s"' => 'Felhasználó jelenlegi jelszava: "%s"', - 'The current password is required' => 'A jelenlegi jelszót meg kell adni', + 'Link' => 'Hivatkozás', + 'Comment updated successfully.' => 'A megjegyzés sikeresen frissÃtve.', + 'Unable to update your comment.' => 'Nem lehet frissÃteni a megjegyzést.', + 'Remove a comment' => 'Megjegyzés eltávolÃtása', + 'Comment removed successfully.' => 'A megjegyzés sikeresen eltávolÃtva.', + 'Unable to remove this comment.' => 'Nem lehet eltávolÃtani a megjegyzést.', + 'Do you really want to remove this comment?' => 'Valóban el szeretné távolÃtani ezt a megjegyzést?', + 'Current password for the user "%s"' => '„%s†felhasználó jelenlegi jelszava', + 'The current password is required' => 'A jelenlegi jelszó kötelezÅ‘', 'Wrong password' => 'Hibás jelszó', 'Unknown' => 'Ismeretlen', 'Last logins' => 'Legutóbbi bejelentkezések', 'Login date' => 'Bejelentkezés dátuma', - 'Authentication method' => 'AzonosÃtási módszer', + 'Authentication method' => 'HitelesÃtési módszer', 'IP address' => 'IP-cÃm', - 'User agent' => 'User Agent', - 'Persistent connections' => 'Tartós (perzisztens) kapcsolatok', - 'No session.' => 'Nincs session.', - 'Expiration date' => 'Lejárati dátum', - 'Remember Me' => 'Emlékezz rám', + 'User agent' => 'Felhasználói ügynök', + 'Persistent connections' => 'Tartós kapcsolatok', + 'No session.' => 'Nincs munkamenet.', + 'Expiration date' => 'Lejárat dátuma', + 'Remember Me' => 'Emlékezzen rám', 'Creation date' => 'Létrehozás dátuma', - 'Everybody' => 'Minden felhasználó', + 'Everybody' => 'Mindenki', 'Open' => 'Nyitott', 'Closed' => 'Lezárt', 'Search' => 'Keresés', @@ -213,97 +212,96 @@ return array( '%d comments' => '%d megjegyzés', '%d comment' => '%d megjegyzés', 'Email address invalid' => 'Érvénytelen e-mail cÃm', - 'Your external account is not linked anymore to your profile.' => 'Az ön külsÅ‘ számlája és az ön profilja közötti kapcsolat megszűnt.', - 'Unable to unlink your external account.' => 'Nem sikerült megszüntetni a kapcsolatot az ön külsÅ‘ számlájával.', - 'External authentication failed' => 'A külsÅ‘ jelszó ellenÅ‘rzés nem sikerült', - 'Your external account is linked to your profile successfully.' => 'Az ön külsÅ‘ számlája sikeresen össze lett kapcsolva az ön profiljával.', + 'Your external account is not linked anymore to your profile.' => 'A külsÅ‘ fiókja többé nincs hozzákapcsolva a profiljához.', + 'Unable to unlink your external account.' => 'Nem lehet megszüntetni a kapcsolatot a külsÅ‘ fiókjával.', + 'External authentication failed' => 'A külsÅ‘ hitelesÃtés sikertelen', + 'Your external account is linked to your profile successfully.' => 'A külsÅ‘ fiókja sikeresen össze lett kapcsolva a profiljával.', 'Email' => 'E-mail', - 'Task removed successfully.' => 'Feladat sikeresen törölve.', - 'Unable to remove this task.' => 'A feladatot nem lehet törölni.', - 'Remove a task' => 'Feladat törlése', - 'Do you really want to remove this task: "%s"?' => 'Valóban törölni akarja ezt a feladatot: "%s"?', - 'Assign automatically a color based on a category' => 'SzÃn hozzárendelése automatikusan kategória alapján', - 'Assign automatically a category based on a color' => 'Kategória hozzárendelése automatikusan szÃn alapján', + 'Task removed successfully.' => 'A feladat sikeresen eltávolÃtva.', + 'Unable to remove this task.' => 'Nem lehet eltávolÃtani a feladatot.', + 'Remove a task' => 'Feladat eltávolÃtása', + 'Do you really want to remove this task: "%s"?' => 'Valóban el szeretné távolÃtani ezt a feladatot: „%sâ€?', + 'Assign automatically a color based on a category' => 'SzÃn automatikus hozzárendelése egy kategória alapján', + 'Assign automatically a category based on a color' => 'Kategória automatikus hozzárendelése egy szÃn alapján', 'Task creation or modification' => 'Feladat létrehozása vagy módosÃtása', 'Category' => 'Kategória', 'Category:' => 'Kategória:', 'Categories' => 'Kategóriák', - 'Your category have been created successfully.' => 'Kategória sikeresen létrehozva.', - 'This category has been updated successfully.' => 'Kategória sikeresen frissÃtve.', - 'Unable to update this category.' => 'Kategória frissÃtése nem lehetséges.', - 'Remove a category' => 'Kategória törlése', - 'Category removed successfully.' => 'Kategória törlése megtörtént.', - 'Unable to remove this category.' => 'A kategória törlése nem lehetséges.', - 'Category modification for the project "%s"' => 'Kategória módosÃtása a projektben "%s"', + 'Your category have been created successfully.' => 'A kategória sikeresen létrehozva.', + 'This category has been updated successfully.' => 'A kategória sikeresen frissÃtve.', + 'Unable to update this category.' => 'Nem lehet frissÃteni ezt a kategóriát.', + 'Remove a category' => 'Kategória eltávolÃtása', + 'Category removed successfully.' => 'A kategória sikeresen eltávolÃtva.', + 'Unable to remove this category.' => 'Nem lehet eltávolÃtani ezt a kategóriát.', + 'Category modification for the project "%s"' => 'Kategória módosÃtása a projektnél: „%sâ€', 'Category Name' => 'Kategória neve', - 'Add a new category' => 'Új kategória', - 'Do you really want to remove this category: "%s"?' => 'Valóban törölni akarja ezt a kategóriát: "%s"?', - 'All categories' => 'Minden kategória', + 'Add a new category' => 'Új kategória hozzáadása', + 'Do you really want to remove this category: "%s"?' => 'Valóban el szeretné távolÃtani ezt a kategóriát: „%sâ€?', + 'All categories' => 'Összes kategória', 'No category' => 'Nincs kategória', - 'The name is required' => 'A név megadása kötelezÅ‘', - 'Remove a file' => 'Fájl törlése', - 'Unable to remove this file.' => 'Fájl törlése nem lehetséges.', - 'File removed successfully.' => 'Fájl sikeresen törölve.', - 'Attach a document' => 'Fájl csatolása', - 'Do you really want to remove this file: "%s"?' => 'Valóban törölni akarja ezt a fájlt: "%s"?', + 'The name is required' => 'A név kötelezÅ‘', + 'Remove a file' => 'Fájl eltávolÃtása', + 'Unable to remove this file.' => 'Nem lehet eltávolÃtani ezt a fájlt.', + 'File removed successfully.' => 'A fájl sikeresen eltávolÃtva.', + 'Attach a document' => 'Dokumentum csatolása', + 'Do you really want to remove this file: "%s"?' => 'Valóban el szeretné távolÃtani ezt a fájlt: „%sâ€?', 'Attachments' => 'Mellékletek', - 'Edit the task' => 'Feladat módosÃtása', - 'Add a comment' => 'Új megjegyzés', + 'Edit the task' => 'Feladat szerkesztése', + 'Add a comment' => 'Megjegyzés hozzáadása', 'Edit a comment' => 'Megjegyzés szerkesztése', 'Summary' => 'Összegzés', 'Time tracking' => 'IdÅ‘ követés', 'Estimate:' => 'Becsült:', 'Spent:' => 'Eltöltött:', - 'Do you really want to remove this sub-task?' => 'Valóban törölni akarja ezt a részfeladatot?', + 'Do you really want to remove this sub-task?' => 'Valóban el szeretné távolÃtani ezt a részfeladatot?', 'Remaining:' => 'HátralévÅ‘:', 'hours' => 'óra', 'spent' => 'eltöltött', 'estimated' => 'becsült', 'Sub-Tasks' => 'Részfeladatok', - 'Add a sub-task' => 'Részfeladat létrehozása', + 'Add a sub-task' => 'Részfeladat hozzáadása', 'Original estimate' => 'Eredeti idÅ‘becslés', 'Create another sub-task' => 'További részfeladat létrehozása', 'Time spent' => 'Eltöltött idÅ‘', 'Edit a sub-task' => 'Részfeladat szerkesztése', - 'Remove a sub-task' => 'Részfeladat törlése', - 'The time must be a numeric value' => 'IdÅ‘ csak számérték lehet', + 'Remove a sub-task' => 'Részfeladat eltávolÃtása', + 'The time must be a numeric value' => 'Az idÅ‘ csak számérték lehet', 'Todo' => 'TeendÅ‘', 'In progress' => 'Folyamatban', - 'Sub-task removed successfully.' => 'Részfeladat sikeresen törölve.', - 'Unable to remove this sub-task.' => 'Részfeladat törlése nem lehetséges.', - 'Sub-task updated successfully.' => 'Részfeladat sikeresen frissÃtve.', - 'Unable to update your sub-task.' => 'Részfeladat frissÃtése nem lehetséges.', - 'Unable to create your sub-task.' => 'Részfeladat létrehozása nem lehetséges.', - 'Sub-task added successfully.' => 'Részfeladat sikeresen létrehozva.', - 'Maximum size: ' => 'Maximális méret: ', + 'Sub-task removed successfully.' => 'A részfeladat sikeresen eltávolÃtva.', + 'Unable to remove this sub-task.' => 'Nem lehet eltávolÃtani a részfeladatot.', + 'Sub-task updated successfully.' => 'A részfeladat sikeresen frissÃtve.', + 'Unable to update your sub-task.' => 'Nem lehet frissÃteni a részfeladatot.', + 'Unable to create your sub-task.' => 'Nem lehet létrehozni a részfeladatot.', + 'Maximum size: ' => 'Legnagyobb méret: ', 'Display another project' => 'Másik projekt megjelenÃtése', - 'Created by %s' => 'KészÃtette: %s', + 'Created by %s' => 'Létrehozta: %s', 'Tasks Export' => 'Feladatok exportálása', 'Start Date' => 'Kezdés dátuma', - 'Execute' => 'Végrehajt', - 'Task Id' => 'Feladat ID', - 'Creator' => 'KészÃtette', + 'Execute' => 'Végrehajtás', + 'Task Id' => 'FeladatazonosÃtó', + 'Creator' => 'Létrehozó', 'Modification date' => 'MódosÃtás dátuma', - 'Completion date' => 'Befejezés határideje', - 'Clone' => 'Másolat', - 'Project cloned successfully.' => 'A projekt sikeresen másolva.', - 'Unable to clone this project.' => 'A projekt másolása sikertelen.', + 'Completion date' => 'Befejezés dátuma', + 'Clone' => 'Klónozás', + 'Project cloned successfully.' => 'A projekt sikeresen lemásolva.', + 'Unable to clone this project.' => 'Nem lehet lemásolni a projektet.', 'Enable email notifications' => 'E-mail értesÃtések engedélyezése', 'Task position:' => 'Feladat helye:', - 'The task #%d have been opened.' => 'Feladat #%d megnyitva.', - 'The task #%d have been closed.' => 'Feladat #%d lezárva.', + 'The task #%d have been opened.' => '#%d. feladat megnyitva.', + 'The task #%d have been closed.' => '#%d. feladat lezárva.', 'Sub-task updated' => 'Részfeladat frissÃtve', 'Title:' => 'CÃm', 'Status:' => 'Ãllapot:', 'Assignee:' => 'FelelÅ‘s:', 'Time tracking:' => 'IdÅ‘ követés:', 'New sub-task' => 'Új részfeladat', - 'New attachment added "%s"' => 'Új melléklet "%s" hozzáadva.', - 'New comment posted by %s' => 'Új megjegyzés %s', + 'New attachment added "%s"' => 'Új melléklet hozzáadva: „%sâ€.', + 'New comment posted by %s' => 'Új megjegyzést adott hozzá: %s', 'New comment' => 'Új megjegyzés', 'Comment updated' => 'Megjegyzés frissÃtve', 'New subtask' => 'Új részfeladat', - 'I want to receive notifications only for those projects:' => 'Csak ezekrÅ‘l a projektekrÅ‘l kérek értesÃtést:', + 'I want to receive notifications only for those projects:' => 'Csak ezekrÅ‘l a projektekrÅ‘l szeretnék értesÃtést kapni:', 'view the task on Kanboard' => 'feladat megtekintése a Kanboardon', 'Public access' => 'Nyilvános hozzáférés', 'Disable public access' => 'Nyilvános hozzáférés letiltása', @@ -311,7 +309,7 @@ return array( 'Public access disabled' => 'Nyilvános hozzáférés letiltva', 'Move the task to another project' => 'Feladat áthelyezése másik projektbe', 'Move to another project' => 'Ãthelyezés másik projektbe', - 'Do you really want to duplicate this task?' => 'Tényleg szeretné megkettÅ‘zni ezt a feladatot?', + 'Do you really want to duplicate this task?' => 'Valóban le szeretné másolni ezt a feladatot?', 'Duplicate a task' => 'Feladat másolása', 'External accounts' => 'KülsÅ‘ fiókok', 'Account type' => 'Fiók tÃpusa', @@ -320,383 +318,366 @@ return array( 'Enabled' => 'Engedélyezve', 'Disabled' => 'Letiltva', 'Login:' => 'Felhasználónév:', - 'Full Name:' => 'Név:', + 'Full Name:' => 'Teljes név:', 'Email:' => 'E-mail:', 'Notifications:' => 'ÉrtesÃtések:', 'Notifications' => 'ÉrtesÃtések', 'Account type:' => 'Fiók tÃpusa:', 'Edit profile' => 'Profil szerkesztése', - 'Change password' => 'Jelszó módosÃtása', + 'Change password' => 'Jelszó megváltoztatása', 'Password modification' => 'Jelszó módosÃtása', - 'External authentications' => 'KülsÅ‘ azonosÃtás', - 'Never connected.' => 'Sosem csatlakozva.', - 'No external authentication enabled.' => 'A külsÅ‘ azonosÃtás nincs engedélyezve.', + 'External authentications' => 'KülsÅ‘ hitelesÃtések', + 'Never connected.' => 'Sosem csatlakoztatott.', + 'No external authentication enabled.' => 'Nincs külsÅ‘ hitelesÃtés engedélyezve.', 'Password modified successfully.' => 'A jelszó sikeresen módosÃtva.', - 'Unable to change the password.' => 'A jelszó módosÃtása sikertelen.', - 'Change category' => 'Kategória módosÃtása', - '%s updated the task %s' => '%s frissÃtette a feladatot %s', - '%s opened the task %s' => '%s megnyitott a feladatot %s', - '%s moved the task %s to the position #%d in the column "%s"' => '%s átmozgatta a feladatot %s #%d pozÃcióba a "%s" oszlopban', - '%s moved the task %s to the column "%s"' => '%s átmozgatta a feladatot %s "%s" oszlopba', - '%s created the task %s' => '%s létrehozta a feladatot %s', - '%s closed the task %s' => '%s lezárta a feladatot %s', - '%s created a subtask for the task %s' => '%s létrehozott egy részfeladat a feladathoz %s', - '%s updated a subtask for the task %s' => '%s frissÃtett egy részfeladatot a feladathoz %s', - 'Assigned to %s with an estimate of %s/%sh' => '%s-nek kiosztva %s/%s óra becsült idÅ‘ mellett', + 'Unable to change the password.' => 'Nem lehet megváltoztatni a jelszót.', + 'Change category' => 'Kategória megváltoztatása', + '%s updated the task %s' => '%s frissÃtette a(z) %s feladatot', + '%s opened the task %s' => '%s megnyitott a(z) %s feladatot', + '%s moved the task %s to the position #%d in the column "%s"' => '%s áthelyezte a(z) %s feladatot a(z) #%d. pozÃcióba a(z) „%s†oszlopban', + '%s moved the task %s to the column "%s"' => '%s áthelyezte a(z) %s feladatot a(z) „%s†oszlopba', + '%s created the task %s' => '%s létrehozta a(z) %s feladatot', + '%s closed the task %s' => '%s lezárta a(z) %s feladatot', + '%s created a subtask for the task %s' => '%s létrehozott egy részfeladat a(z) %s feladathoz', + '%s updated a subtask for the task %s' => '%s frissÃtett egy részfeladatot a(z) %s feladatnál', + 'Assigned to %s with an estimate of %s/%sh' => 'Hozzárendelve a(z) %s feladathoz %s/%s óra becsült idÅ‘vel', 'Not assigned, estimate of %sh' => 'Nincs kiosztva, becsült idÅ‘: %s óra', - '%s updated a comment on the task %s' => '%s frissÃtette a megjegyzését a feladatban %s', - '%s commented the task %s' => '%s megjegyzést fűzött a feladathoz %s', - '%s\'s activity' => 'Tevékenységek: %s', - 'RSS feed' => 'RSS feed', - '%s updated a comment on the task #%d' => '%s frissÃtett egy megjegyzést a feladatban #%d', - '%s commented on the task #%d' => '%s megjegyzést tett a feladathoz #%d', - '%s updated a subtask for the task #%d' => '%s frissÃtett egy részfeladatot a feladatban #%d', - '%s created a subtask for the task #%d' => '%s létrehozott egy részfeladatot a feladatban #%d', - '%s updated the task #%d' => '%s frissÃtette a feladatot #%d', - '%s created the task #%d' => '%s létrehozta a feladatot #%d', - '%s closed the task #%d' => '%s lezárta a feladatot #%d', - '%s opened the task #%d' => '%s megnyitotta a feladatot #%d', - 'Activity' => 'Tevékenységek', - 'Default values are "%s"' => 'Az alapértelmezett értékek: %s', - 'Default columns for new projects (Comma-separated)' => 'Alapértelmezett oszlopok az új projektekben (vesszÅ‘vel elválasztva)', - 'Task assignee change' => 'FelelÅ‘s módosÃtása', - '%s changed the assignee of the task #%d to %s' => '%s a felelÅ‘st módosÃtotta #%d %s', - '%s changed the assignee of the task %s to %s' => '%s a felelÅ‘st %s módosÃtotta: %s', - 'New password for the user "%s"' => 'Felhasználó új jelszava: %s', - 'Choose an event' => 'Válasszon eseményt', - 'Create a task from an external provider' => 'Feladat létrehozása külsÅ‘s számára', - 'Change the assignee based on an external username' => 'FelelÅ‘s módosÃtása külsÅ‘ felhasználónév alapján', - 'Change the category based on an external label' => 'Kategória módosÃtása külsÅ‘ cÃmke alapján', + '%s updated a comment on the task %s' => '%s frissÃtett egy megjegyzését a(z) %s feladatban', + '%s commented the task %s' => '%s megjegyzést Ãrt a(z) %s feladathoz', + '%s\'s activity' => '%s tevékenységei', + 'RSS feed' => 'RSS hÃrforrás', + '%s updated a comment on the task #%d' => '%s frissÃtett egy megjegyzést a(z) #%d. feladatban', + '%s commented on the task #%d' => '%s megjegyzést Ãrt a(z) #%d. feladathoz', + '%s updated a subtask for the task #%d' => '%s frissÃtett egy részfeladatot a(z) #%d. feladatnál', + '%s created a subtask for the task #%d' => '%s létrehozott egy részfeladatot a(z) #%d. feladatnál', + '%s updated the task #%d' => '%s frissÃtette a(z) #%d. feladatot', + '%s created the task #%d' => '%s létrehozta a(z) #%d. feladatot', + '%s closed the task #%d' => '%s lezárta a(z) #%d feladatot', + '%s opened the task #%d' => '%s megnyitotta a(z) #%d. feladatot', + 'Activity' => 'Tevékenység', + 'Default values are "%s"' => 'Az alapértelmezett értékek: „%sâ€', + 'Default columns for new projects (Comma-separated)' => 'Alapértelmezett oszlopok az új projekteknél (vesszÅ‘vel elválasztva)', + 'Task assignee change' => 'A feladat felelÅ‘sének megváltoztatása', + '%s changed the assignee of the task #%d to %s' => '%s megváltoztatta a(z) #%d. feladat felelÅ‘sét erre: %s', + '%s changed the assignee of the task %s to %s' => '%s megváltoztatta a(z) %s feladat felelÅ‘sét erre: %s', + 'New password for the user "%s"' => '„%s†felhasználó új jelszava', + 'Choose an event' => 'Esemény választása', + 'Create a task from an external provider' => 'Feladat létrehozása egy külsÅ‘s szolgáltatóból', + 'Change the assignee based on an external username' => 'FelelÅ‘s megváltoztatása egy külsÅ‘ felhasználónév alapján', + 'Change the category based on an external label' => 'Kategória megváltoztatása egy külsÅ‘ cÃmke alapján', 'Reference' => 'Hivatkozás', 'Label' => 'CÃmke', 'Database' => 'Adatbázis', - 'About' => 'Kanboard információ', - 'Database driver:' => 'Adatbázis motor:', - 'Board settings' => 'Tábla beállÃtások', - 'Webhook settings' => 'Webhook beállÃtások', - 'Reset token' => 'Token újragenerálása', + 'About' => 'Névjegy', + 'Database driver:' => 'Adatbázis-meghajtó:', + 'Board settings' => 'Tábla beállÃtásai', + 'Webhook settings' => 'Webhurok beállÃtásai', + 'Reset token' => 'Token visszaállÃtása', 'API endpoint:' => 'API végpont:', - 'Refresh interval for private board' => 'Privát táblák frissÃtési intervalluma', - 'Refresh interval for public board' => 'Nyilvános táblák frissÃtési intervalluma', - 'Task highlight period' => 'Feladat kiemelés idÅ‘tartama', - 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Mennyi ideig tekintendÅ‘ egy feladat "mostanában" módosÃtottnak (másodpercben) (0: funkció letiltva, alapértelmezés szerint 2 nap)', + 'Refresh interval for private board' => 'FrissÃtési idÅ‘köz a személyes táblánál', + 'Refresh interval for public board' => 'FrissÃtési idÅ‘köz a nyilvános táblánál', + 'Task highlight period' => 'Feladat kiemelésének idÅ‘tartama', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Az idÅ‘szak (másodpercben), amÃg a feladatot legutóbb módosÃtottnak kell tekinteni (0: letiltás, alapértelmezetten 2 nap)', 'Frequency in second (60 seconds by default)' => 'Gyakoriság másodpercben (alapértelmezetten 60 másodperc)', - 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Gyakoriság másodpercben (0 funkció letiltva, alapértelmezetten 10 másodperc)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Gyakoriság másodpercben (0: a funkció letiltása, alapértelmezetten 10 másodperc)', 'Application URL' => 'Alkalmazás URL', - 'Token regenerated.' => 'Token újragenerálva.', - 'Date format' => 'Dátum formátum', - 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO formátum mindig elfogadott, pl: "%s" és "%s"', - 'New private project' => 'Új privát projekt', - 'This project is private' => 'Ez egy privát projekt', + 'Token regenerated.' => 'A token újra létrehozva.', + 'Date format' => 'Dátumformátum', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Az ISO formátum mindig elfogadott, például „%s†és „%sâ€', + 'New private project' => 'Új személyes projekt', + 'This project is private' => 'Ez a projekt személyes', 'Add' => 'Hozzáadás', 'Start date' => 'Kezdés dátuma', 'Time estimated' => 'Becsült idÅ‘tartam', - 'There is nothing assigned to you.' => 'Nincs kiosztott feladat.', - 'My tasks' => 'Feladataim', - 'Activity stream' => 'Legutóbbi tevékenységek', + 'There is nothing assigned to you.' => 'Nincs semmi sem Önhöz rendelve.', + 'My tasks' => 'Saját feladatok', + 'Activity stream' => 'Tevékenységfolyam', 'Dashboard' => 'VezérlÅ‘pult', 'Confirmation' => 'MegerÅ‘sÃtés', - 'Allow everybody to access to this project' => 'A projekt elérése mindenkinek engedélyezett', - 'Everybody have access to this project.' => 'Mindenki elérheti a projektet', - 'Webhooks' => 'Webhook', + 'Webhooks' => 'Webhurkok', 'API' => 'API', - 'Create a comment from an external provider' => 'Megjegyzés létrehozása külsÅ‘ felhasználótól', - 'Project management' => 'Projekt menedzsment', - 'My projects' => 'Projektjeim', + 'Create a comment from an external provider' => 'Megjegyzés létrehozása egy külsÅ‘ szolgáltatótól', + 'Project management' => 'Projektmenedzsment', 'Columns' => 'Oszlopok', 'Task' => 'Feladat', - 'Your are not member of any project.' => 'Ön nem tagja projektnek.', 'Percentage' => 'Százalék', - 'Number of tasks' => 'A feladatok száma', + 'Number of tasks' => 'Feladatok száma', 'Task distribution' => 'Feladatelosztás', - 'Analytics' => 'Analitika', + 'Analytics' => 'Elemzések', 'Subtask' => 'Részfeladat', - 'My subtasks' => 'Részfeladataim', - 'User repartition' => 'Felhasználó újrafelosztás', + 'User repartition' => 'Felhasználónkénti megoszlás', 'Clone this project' => 'Projekt másolása', - 'Column removed successfully.' => 'Oszlop sikeresen törölve.', - 'Not enough data to show the graph.' => 'Nincs elég adat a grafikonhoz.', + 'Column removed successfully.' => 'Az oszlop sikeresen eltávolÃtva.', + 'Not enough data to show the graph.' => 'Nincs elég adat a grafikon megjelenÃtéséhez.', 'Previous' => 'ElÅ‘zÅ‘', - 'The id must be an integer' => 'Az ID csak egész szám lehet', - 'The project id must be an integer' => 'A projekt ID csak egész szám lehet', + 'The id must be an integer' => 'Az azonosÃtó csak egész szám lehet', + 'The project id must be an integer' => 'A projektazonosÃtó csak egész szám lehet', 'The status must be an integer' => 'Az állapot csak egész szám lehet', - 'The subtask id is required' => 'A részfeladat ID-t meg kell adni', - 'The subtask id must be an integer' => 'A részfeladat ID csak egész szám lehet', - 'The task id is required' => 'A feladat ID-t meg kell adni', - 'The task id must be an integer' => 'A feladat ID csak egész szám lehet', - 'The user id must be an integer' => 'A felhasználói ID csak egész szám lehet', - 'This value is required' => 'Ez a mezÅ‘ kötelezÅ‘', - 'This value must be numeric' => 'Ez a mezÅ‘ csak szám lehet', - 'Unable to create this task.' => 'A feladat nem hozható létre,', - 'Cumulative flow diagram' => 'KumulatÃv folyamatábra', + 'The subtask id is required' => 'A részfeladat-azonosÃtó kötelezÅ‘', + 'The subtask id must be an integer' => 'A részfeladat-azonosÃtó csak egész szám lehet', + 'The task id is required' => 'A feladatazonosÃtó kötelezÅ‘', + 'The task id must be an integer' => 'A feladatazonosÃtó csak egész szám lehet', + 'The user id must be an integer' => 'A felhasználó-azonosÃtó csak egész szám lehet', + 'This value is required' => 'Ez az érték kötelezÅ‘', + 'This value must be numeric' => 'Ez az érték csak szám lehet', + 'Unable to create this task.' => 'Nem lehet létrehozni a feladatot.', + 'Cumulative flow diagram' => 'Halmozott folyamatdiagram', 'Daily project summary' => 'Napi projektösszefoglaló', 'Daily project summary export' => 'Napi projektösszefoglaló exportálása', 'Exports' => 'Exportálások', - 'This export contains the number of tasks per column grouped per day.' => 'Ez az export tartalmazza a feladatok számát oszloponként összesÃtve, napokra lebontva.', + 'This export contains the number of tasks per column grouped per day.' => 'Ez az export tartalmazza a feladatok számát oszloponként csoportosÃtva, napokra lebontva.', 'Active swimlanes' => 'AktÃv sávok', - 'Add a new swimlane' => 'Új sáv', - 'Default swimlane' => 'Alapértelmezett folyamat', - 'Do you really want to remove this swimlane: "%s"?' => 'Valóban törölni akarja ezt a sávot: %s ?', + 'Add a new swimlane' => 'Új sáv hozzáadása', + 'Default swimlane' => 'Alapértelmezett sáv', + 'Do you really want to remove this swimlane: "%s"?' => 'Valóban el szeretné távolÃtani ezt a sávot: „%sâ€?', 'Inactive swimlanes' => 'InaktÃv sávok', - 'Remove a swimlane' => 'Sáv törlés', - 'Swimlane modification for the project "%s"' => '%s projekt sávjainak módosÃtása', - 'Swimlane removed successfully.' => 'Sáv sikeresen törölve.', + 'Remove a swimlane' => 'Sáv eltávolÃtása', + 'Swimlane modification for the project "%s"' => 'SávmódosÃtás a(z) „%s†projektnél', + 'Swimlane removed successfully.' => 'A sáv sikeresen eltávolÃtva.', 'Swimlanes' => 'Sávok', - 'Swimlane updated successfully.' => 'Sáv sikeresen frissÃtve', - 'Unable to remove this swimlane.' => 'A sáv törlése sikertelen.', - 'Unable to update this swimlane.' => 'A sáv frissÃtése sikertelen.', + 'Swimlane updated successfully.' => 'A sáv sikeresen frissÃtve.', + 'Unable to remove this swimlane.' => 'Nem lehet eltávolÃtani ezt a sávot.', + 'Unable to update this swimlane.' => 'Nem lehet frissÃteni ezt a sávot.', 'Your swimlane have been created successfully.' => 'A sáv sikeresen létrehozva.', - 'Example: "Bug, Feature Request, Improvement"' => 'Például: Hiba, Új funkció, Fejlesztés', - 'Default categories for new projects (Comma-separated)' => 'Alapértelmezett kategóriák az új projektekben (VesszÅ‘vel elválasztva)', - 'Integrations' => 'Integráció', - 'Integration with third-party services' => 'Integráció harmadik féllel', - 'Subtask Id' => 'Részfeladat id', + 'Example: "Bug, Feature Request, Improvement"' => 'Például: Hiba, Funkciókérés, Fejlesztés', + 'Default categories for new projects (Comma-separated)' => 'Alapértelmezett kategóriák az új projekteknél (vesszÅ‘vel elválasztva)', + 'Integrations' => 'Integrációk', + 'Integration with third-party services' => 'Integráció harmadik féltÅ‘l származó szolgáltatásokkal', + 'Subtask Id' => 'Részfeladat-azonosÃtó', 'Subtasks' => 'Részfeladatok', - 'Subtasks Export' => 'Részfeladat exportálás', + 'Subtasks Export' => 'Részfeladat exportálása', 'Task Title' => 'Feladat cÃme', 'Untitled' => 'Névtelen', - 'Application default' => 'Alkalmazás alapértelmezett', + 'Application default' => 'Alkalmazás alapértelmezettje', 'Language:' => 'Nyelv:', 'Timezone:' => 'IdÅ‘zóna:', - 'All columns' => 'Minden oszlop', - 'Calendar' => 'Naptár', + 'All columns' => 'Összes oszlop', 'Next' => 'KövetkezÅ‘', '#%d' => '#%d', - 'All swimlanes' => 'Minden sáv', - 'All colors' => 'Minden szÃn', + 'All swimlanes' => 'Összes sáv', + 'All colors' => 'Összes szÃn', 'Moved to column %s' => '%s oszlopba áthelyezve', - 'User dashboard' => 'Felhasználói vezérlÅ‘pult', - 'Allow only one subtask in progress at the same time for a user' => 'Egyszerre csak egy folyamatban levÅ‘ részfeladat engedélyezése a felhasználóknak', - 'Edit column "%s"' => 'Oszlop szerkesztés: %s', - 'Select the new status of the subtask: "%s"' => 'Részfeladat állapot változtatás: %s', - 'Subtask timesheet' => 'Részfeladat idÅ‘vonal', - 'There is nothing to show.' => 'Nincs megjelenÃtendÅ‘ adat.', + 'User dashboard' => 'Felhasználó vezérlÅ‘pultja', + 'Allow only one subtask in progress at the same time for a user' => 'Egyszerre csak egy folyamatban levÅ‘ részfeladat engedélyezése egy felhasználónak', + 'Edit column "%s"' => '„%s†oszlop szerkesztése', + 'Select the new status of the subtask: "%s"' => 'A részfeladat új állapotának kiválasztása: „%sâ€', + 'Subtask timesheet' => 'Részfeladat idÅ‘beosztása', + 'There is nothing to show.' => 'Nincs mit megjelenÃteni.', 'Time Tracking' => 'IdÅ‘ követés', 'You already have one subtask in progress' => 'Már van egy folyamatban levÅ‘ részfeladata', 'Which parts of the project do you want to duplicate?' => 'A projekt mely részeit szeretné másolni?', - 'Disallow login form' => 'A login ablak letiltása', + 'Disallow login form' => 'Bejelentkezési űrlap letiltása', 'Start' => 'Kezdet', 'End' => 'Vég', 'Task age in days' => 'Feladat életkora napokban', 'Days in this column' => 'Napok ebben az oszlopban', - '%dd' => '%dd', + '%dd' => '%dn', 'Add a new link' => 'Új hivatkozás hozzáadása', - 'Do you really want to remove this link: "%s"?' => 'Biztos törölni akarja a hivatkozást: "%s"?', - 'Do you really want to remove this link with task #%d?' => 'Biztos törölni akarja a(z) #%d. feladatra mutató hivatkozást?', - 'Field required' => 'KötelezÅ‘ mezÅ‘', - 'Link added successfully.' => 'Hivatkozás sikeresen létrehozva.', - 'Link updated successfully.' => 'Hivatkozás sikeresen frissÃtve.', - 'Link removed successfully.' => 'Hivatkozás sikeresen törölve.', + 'Do you really want to remove this link: "%s"?' => 'Valóban el szeretné távolÃtani ezt a hivatkozást: „%sâ€?', + 'Do you really want to remove this link with task #%d?' => 'Valóban el szeretné távolÃtani a(z) #%d. feladatra mutató hivatkozást?', + 'Field required' => 'A mezÅ‘ kötelezÅ‘', + 'Link added successfully.' => 'A hivatkozás sikeresen hozzáadva.', + 'Link updated successfully.' => 'A hivatkozás sikeresen frissÃtve.', + 'Link removed successfully.' => 'A hivatkozás sikeresen eltávolÃtva.', 'Link labels' => 'Hivatkozás cÃmkék', - 'Link modification' => 'Hivatkozás módosÃtás', + 'Link modification' => 'Hivatkozás módosÃtása', 'Links' => 'Hivatkozások', 'Opposite label' => 'EllenkezÅ‘ cÃmke', - 'Remove a link' => 'Hivatkozás törlése', + 'Remove a link' => 'Hivatkozás eltávolÃtása', 'The labels must be different' => 'A cÃmkék nem lehetnek azonosak', 'There is no link.' => 'Nincs hivatkozás.', 'This label must be unique' => 'A cÃmkének egyedinek kell lennie.', - 'Unable to create your link.' => 'Hivatkozás létrehozása sikertelen.', - 'Unable to update your link.' => 'Hivatkozás frissÃtése sikertelen.', - 'Unable to remove this link.' => 'Hivatkozás törlése sikertelen.', - 'relates to' => 'hozzá tartozik:', - 'blocks' => 'letiltva:', - 'is blocked by' => 'letitoltta:', + 'Unable to create your link.' => 'Nem lehet létrehozni a hivatkozást.', + 'Unable to update your link.' => 'Nem lehet frissÃteni a hivatkozást.', + 'Unable to remove this link.' => 'Nem lehet eltávolÃtani a hivatkozást.', + 'relates to' => 'ehhez tartozik:', + 'blocks' => 'blokkolja:', + 'is blocked by' => 'blokkolva van:', 'duplicates' => 'eredeti:', - 'is duplicated by' => 'másolat:', - 'is a child of' => 'szülÅ‘je:', - 'is a parent of' => 'gyermeke:', + 'is duplicated by' => 'másolata:', + 'is a child of' => 'gyermeke:', + 'is a parent of' => 'szülÅ‘je:', 'targets milestone' => 'megcélzott mérföldkÅ‘:', - 'is a milestone of' => 'ehhez a mérföldkÅ‘höz tartozik:', + 'is a milestone of' => 'mérföldköve:', 'fixes' => 'javÃtás:', 'is fixed by' => 'javÃtotta:', 'This task' => 'Ez a feladat', '<1h' => '<1ó', '%dh' => '%dó', - 'Expand tasks' => 'Feladatok lenyitása', + 'Expand tasks' => 'Feladatok kinyitása', 'Collapse tasks' => 'Feladatok összecsukása', - 'Expand/collapse tasks' => 'Feladatok lenyitása/összecsukása', - 'Close dialog box' => 'Ablak bezárása', - 'Submit a form' => 'Űrlap beküldése', + 'Expand/collapse tasks' => 'Feladatok kinyitása/összecsukása', + 'Close dialog box' => 'Párbeszédablak bezárása', + 'Submit a form' => 'Űrlap elküldése', 'Board view' => 'Tábla nézet', - 'Keyboard shortcuts' => 'Billentyű kombinációk', - 'Open board switcher' => 'Tábla választó lenyitása', + 'Keyboard shortcuts' => 'Gyorsbillentyűk', + 'Open board switcher' => 'Táblaválasztó megnyitása', 'Application' => 'Alkalmazás', - 'Compact view' => 'Kompakt nézet', + 'Compact view' => 'Tömör nézet', 'Horizontal scrolling' => 'VÃzszintes görgetés', - 'Compact/wide view' => 'Kompakt/széles nézet', + 'Compact/wide view' => 'Tömör/széles nézet', 'Currency' => 'Pénznem', - 'Private project' => 'Privát projekt', - 'AUD - Australian Dollar' => 'AUD - Ausztrál dollár', - 'CAD - Canadian Dollar' => 'CAD - Kanadai dollár', - 'CHF - Swiss Francs' => 'CHF - Svájci frank', + 'Private project' => 'Személyes projekt', + 'AUD - Australian Dollar' => 'AUD - ausztrál dollár', + 'CAD - Canadian Dollar' => 'CAD - kanadai dollár', + 'CHF - Swiss Francs' => 'CHF - svájci frank', 'Custom Stylesheet' => 'Egyéni stÃluslap', 'download' => 'letöltés', - 'EUR - Euro' => 'EUR - Euro', - 'GBP - British Pound' => 'GBP - Angol font', - 'INR - Indian Rupee' => 'INR - Indiai rúpia', - 'JPY - Japanese Yen' => 'JPY - Japán Yen', - 'NZD - New Zealand Dollar' => 'NZD - Új-Zélandi dollár', - 'RSD - Serbian dinar' => 'RSD - Szerb dÃnár', - // 'CNY - Chinese Yuan' => '', - 'USD - US Dollar' => 'USD - Amerikai dollár', - 'Destination column' => 'Cél oszlop', - 'Move the task to another column when assigned to a user' => 'Feladat másik oszlopba helyezése felhasználóhoz rendélés után', - 'Move the task to another column when assignee is cleared' => 'Feladat másik oszlopba helyezése felhasználóhoz rendélés törlésekor', - 'Source column' => 'Forrás oszlop', - 'Transitions' => 'Ãllapot-átmenetek', + 'EUR - Euro' => 'EUR - euro', + 'GBP - British Pound' => 'GBP - angol font', + 'INR - Indian Rupee' => 'INR - indiai rúpia', + 'JPY - Japanese Yen' => 'JPY - japán jen', + 'NZD - New Zealand Dollar' => 'NZD - új-zélandi dollár', + 'RSD - Serbian dinar' => 'RSD - szerb dinár', + 'CNY - Chinese Yuan' => 'CNY - kÃnai jüan', + 'USD - US Dollar' => 'USD - amerikai dollár', + 'Destination column' => 'Céloszlop', + 'Move the task to another column when assigned to a user' => 'Feladat áthelyezése másik oszlopba, amikor egy felhasználóhoz rendelik', + 'Move the task to another column when assignee is cleared' => 'Feladat áthelyezése másik oszlopba, amikor a felelÅ‘st törlik', + 'Source column' => 'Forrásoszlop', + 'Transitions' => 'Ãtmenetek', 'Executer' => 'Végrehajtó', - 'Time spent in the column' => 'Az oszlopban töltött idÅ‘', - 'Task transitions' => 'Feladat állapot-átmenetek', - 'Task transitions export' => 'Feladat állapot-átmenetek exportálása', - 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Ez a riport az összes feladatra vonatkozóan tartalmazza az oszlop mozgatásokat. Szerepel benne a dátum, a felhasználó neve, és az egyes állapot-átemenetekkel eltöltött idÅ‘.', - 'Currency rates' => 'Ãrfolyamok', + 'Time spent in the column' => 'Az oszlopban eltöltött idÅ‘', + 'Task transitions' => 'Feladatátmenetek', + 'Task transitions export' => 'Feladatátmenetek exportálása', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Ez a jelentés az egyes feladatok összes oszlopáthelyezését tartalmazza dátummal, felhasználóval és az egyes átmenetekben eltöltött idÅ‘vel.', + 'Currency rates' => 'Devizaárfolyamok', 'Rate' => 'Ãrfolyam', - 'Change reference currency' => 'A bázis pénznem megváltoztatása', + 'Change reference currency' => 'Bázis pénznem megváltoztatása', 'Reference currency' => 'Bázis pénznem', - 'The currency rate have been added successfully.' => 'Az átváltási árfolyammal történÅ‘ bÅ‘vÃtés sikerült', - 'Unable to add this currency rate.' => 'Nem sikerült az átváltási árfolyam felvétele', - 'Webhook URL' => 'Webhook URL', - '%s removed the assignee of the task %s' => '%s eltávolÃtotta a %s feladathoz rendelt személyt', - 'Enable Gravatar images' => 'Gravatár képek engedélyezése', + 'The currency rate have been added successfully.' => 'A devizaárfolyam sikeresen hozzáadva.', + 'Unable to add this currency rate.' => 'Nem lehet hozzáadni ezt a devizaárfolyamot.', + 'Webhook URL' => 'Webhurok URL', + '%s removed the assignee of the task %s' => '%s eltávolÃtotta a(z) %s feladat felelÅ‘sét', 'Information' => 'Információ', - 'Check two factor authentication code' => 'Két fázisú beléptetÅ‘ kód ellenÅ‘rzése', - 'The two factor authentication code is not valid.' => 'A két fázisú beléptetÅ‘ kód érvénytelen', - 'The two factor authentication code is valid.' => 'A két fázisú beléptetÅ‘ kód érvényes', + 'Check two factor authentication code' => 'A kétlépcsÅ‘s hitelesÃtés kódjának ellenÅ‘rzése', + 'The two factor authentication code is not valid.' => 'A kétlépcsÅ‘s hitelesÃtés kódja nem érvényes.', + 'The two factor authentication code is valid.' => 'A kétlépcsÅ‘s hitelesÃtés kódja érvényes.', 'Code' => 'Kód', - 'Two factor authentication' => 'Két fázisú beléptetés', - 'This QR code contains the key URI: ' => 'Ez a QR kód a következÅ‘ kulcs URI-t tartalmazza: ', + 'Two factor authentication' => 'KétlépcsÅ‘s hitelesÃtés', + 'This QR code contains the key URI: ' => 'Ez a QR-kód tartalmazza a kulcs URI-t: ', 'Check my code' => 'A kódom ellenÅ‘rzése', 'Secret key: ' => 'Titkos kulcs', 'Test your device' => 'Az eszköz ellenÅ‘rzése', - 'Assign a color when the task is moved to a specific column' => 'SzÃn hozzárendelése, ha a feladatot egy adott oszlopba mozgatták', - '%s via Kanboard' => '%s a Kanboard-on keresztül', - // 'Burndown chart' => '', + 'Assign a color when the task is moved to a specific column' => 'SzÃn hozzárendelése, ha a feladatot egy adott oszlopba helyezték át', + '%s via Kanboard' => '%s a Kanboardon keresztül', + 'Burndown chart' => 'Burndown diagram', 'This chart show the task complexity over the time (Work Remaining).' => 'Ez a diagram a feladat idÅ‘beli bonyolultságát ábrázolja (mennyi munka van hátra)', - 'Screenshot taken %s' => 'A képernyÅ‘mentés megtörtént, %s', - 'Add a screenshot' => 'KépernyÅ‘mentés hozzáadása', - 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Végezzen képernyÅ‘mentést, majd a CTRL+V vagy ⌘+V megnyomásával másolja be ide.', - 'Screenshot uploaded successfully.' => 'A képernyÅ‘mentés feltöltése sikeresen megtörtént.', - 'SEK - Swedish Krona' => 'SEK - Svéd korona', + 'Screenshot taken %s' => 'KépernyÅ‘kép készült: %s', + 'Add a screenshot' => 'KépernyÅ‘kép hozzáadása', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'KészÃtsen képernyÅ‘képet, majd a CTRL+V vagy ⌘+V megnyomásával illessze be ide.', + 'Screenshot uploaded successfully.' => 'A képernyÅ‘kép sikeresen feltöltése.', + 'SEK - Swedish Krona' => 'SEK - svéd korona', 'Identifier' => 'AzonosÃtó', - 'Disable two factor authentication' => 'A kétfázisú beléptetés letiltása', - 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Valóban le akarja tiltani a kétfázisú beléptetést ennél a felhasználónál: "%s"?', + 'Disable two factor authentication' => 'A kétlépcsÅ‘s hitelesÃtés letiltása', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Valóban le szeretné tiltani a kétlépcsÅ‘s hitelesÃtést ennél a felhasználónál: „%sâ€?', 'Edit link' => 'Hivatkozás szerkesztése', - 'Start to type task title...' => 'Kezdje el begépelni a feladat cÃmét... ', + 'Start to type task title...' => 'Kezdje el gépelni a feladat cÃmét…', 'A task cannot be linked to itself' => 'Egy feladatot nem lehet önmagához kapcsolni', 'The exact same link already exists' => 'Már létezik pontosan ugyanez a hivatkozás', 'Recurrent task is scheduled to be generated' => 'Az ismétlÅ‘dÅ‘ feladat előállÃtása ütemezve lett', 'Score' => 'Pontszám', - 'The identifier must be unique' => 'Egyedi azonosÃtó szükséges', - 'This linked task id doesn\'t exists' => 'Ez a hivatkozott feladat nem létezik', - 'This value must be alphanumeric' => 'Alfanumerikus érték szükséges', + 'The identifier must be unique' => 'Az azonosÃtónak egyedinek kell lennie', + 'This linked task id doesn\'t exists' => 'Ez a hivatkozott feladatazonosÃtó nem létezik', + 'This value must be alphanumeric' => 'Ez az érték csak betűket és számokat tartalmazhat', 'Edit recurrence' => 'IsmétlÅ‘dés szerkesztése', 'Generate recurrent task' => 'IsmétlÅ‘dÅ‘ feladat előállÃtása', - 'Trigger to generate recurrent task' => 'Az ismétlÅ‘dÅ‘ feladatot előállÃtó trigger', - 'Factor to calculate new due date' => 'Az új határidÅ‘ kiszámÃtásához használt tényezÅ‘', - 'Timeframe to calculate new due date' => 'Az új határidÅ‘ kiszámÃtásához használt idÅ‘ablak', - 'Base date to calculate new due date' => 'A határidÅ‘ kiszámÃtásához használt kezdÅ‘ dátum', - 'Action date' => 'Intézkedés dátuma', - 'Base date to calculate new due date: ' => 'Az új határidÅ‘ kiszámÃtásához használt kezdÅ‘ dátum: ', - 'This task has created this child task: ' => 'Ez a feladat a következÅ‘ leszármazott feladatot hozta létre: ', - 'Day(s)' => 'Nap(ok)', - 'Existing due date' => 'A jelenlegi határidÅ‘', - 'Factor to calculate new due date: ' => 'Az új határidÅ‘ kiszámÃtásához használt tényezÅ‘: ', - 'Month(s)' => 'Hónap(ok)', + 'Trigger to generate recurrent task' => 'Aktiváló az ismétlÅ‘dÅ‘ feladat előállÃtásához', + 'Factor to calculate new due date' => 'TényezÅ‘ az új határidÅ‘ kiszámÃtásához', + 'Timeframe to calculate new due date' => 'IdÅ‘ablak az új határidÅ‘ kiszámÃtásához', + 'Base date to calculate new due date' => 'Alapdátum az új határidÅ‘ kiszámÃtásához', + 'Action date' => 'Tevékenység dátuma', + 'Base date to calculate new due date: ' => 'Alapdátum az új határidÅ‘ kiszámÃtásához: ', + 'This task has created this child task: ' => 'Ez a feladat ezt a gyermekfeladatot hozta létre: ', + 'Day(s)' => 'Nap', + 'Existing due date' => 'MeglévÅ‘ határidÅ‘', + 'Factor to calculate new due date: ' => 'TényezÅ‘ az új határidÅ‘ kiszámÃtásához: ', + 'Month(s)' => 'Hónap', 'Recurrence' => 'IsmétlÅ‘dés', 'This task has been created by: ' => 'Ezt a feladatot a következÅ‘ személy hozta létre: ', - 'Recurrent task has been generated:' => 'IsmétlÅ‘dÅ‘ feladat lett létrehozva: ', - 'Timeframe to calculate new due date: ' => 'Az új határidÅ‘ kiszámÃtásához használt idÅ‘ablak: ', - 'Trigger to generate recurrent task: ' => 'Az ismétlÅ‘dÅ‘ feladatot előállÃtó trigger: ', - 'When task is closed' => 'Mikor a feladat be lett zárva', - 'When task is moved from first column' => 'Mikor a feladat az elsÅ‘ oszlopból el lett mozgatva', - 'When task is moved to last column' => 'Mikor a feladat az utolsó oszlopba lett elmozgatva', - 'Year(s)' => 'Év(ek)', - 'Calendar settings' => 'Naptár beállÃtások', - 'Project calendar view' => 'A projekt megjelenÃtése naptári formában', - 'Project settings' => 'Projekt beállÃtások', - 'Show subtasks based on the time tracking' => 'A részfeladatok megjelenÃtése az idÅ‘ nyomkövetés alapján', - 'Show tasks based on the creation date' => 'A feladatok megjelenÃtése a létrehozás dátuma alapján', - 'Show tasks based on the start date' => 'A feladatok megjelenÃtése a kezdÅ‘ dátum alapján', - 'Subtasks time tracking' => 'A részfeladatok idejének megjelenÃtése', - 'User calendar view' => 'A felhasználó naptárának megjelenÃtése', - 'Automatically update the start date' => 'A kezdÅ‘ dátum automatikus módosÃtása', - // 'iCal feed' => '', - 'Preferences' => 'Preferenciák', + 'Recurrent task has been generated:' => 'IsmétlÅ‘dÅ‘ feladat lett előállÃtva: ', + 'Timeframe to calculate new due date: ' => 'IdÅ‘ablak az új határidÅ‘ kiszámÃtásához: ', + 'Trigger to generate recurrent task: ' => 'Aktiváló az ismétlÅ‘dÅ‘ feladat előállÃtásához: ', + 'When task is closed' => 'Amikor a feladatok lezárták', + 'When task is moved from first column' => 'Amikor a feladatot áthelyezték az elsÅ‘ oszlopból', + 'When task is moved to last column' => 'Amikor a feladatot áthelyezték az utolsó oszlopba', + 'Year(s)' => 'Év', + 'Project settings' => 'Projekt beállÃtásai', + 'Automatically update the start date' => 'A kezdési dátum automatikus frissÃtése', + 'iCal feed' => 'iCal hÃrforrás', + 'Preferences' => 'BeállÃtások', 'Security' => 'Biztonság', - 'Two factor authentication disabled' => 'A két fázisú beléptetés tiltva van', - 'Two factor authentication enabled' => 'A két fázisú beléptetés engedélyezve van', - 'Unable to update this user.' => 'A felhasználó {adatainak} módosÃtása nem sikerült.', - 'There is no user management for private projects.' => 'A privát projektek esetén nincs felhasználó kezelés', - 'User that will receive the email' => 'Az email-t a következÅ‘ felhaszáló fogja megkapni', - 'Email subject' => 'Email tárgy', + 'Two factor authentication disabled' => 'A kétlépcsÅ‘s hitelesÃtés letiltva', + 'Two factor authentication enabled' => 'A kétlépcsÅ‘s hitelesÃtés engedélyezve', + 'Unable to update this user.' => 'Nem lehet frissÃteni a felhasználót.', + 'There is no user management for private projects.' => 'Nincs felhasználókezelés a személyes projekteknél.', + 'User that will receive the email' => 'A felhasználó, aki megkapja az e-mailt', + 'Email subject' => 'E-mail tárgya', 'Date' => 'Dátum', - 'Add a comment log when moving the task between columns' => 'Egy napló megjegyzés létrehozása a feladat oszlopok közötti mozgatásakor', - 'Move the task to another column when the category is changed' => 'A feladat átmozgatása egy másik oszlopba, ha megváltozik a kategória', - 'Send a task by email to someone' => 'Email-en egy feladat küldése valakinek', - 'Reopen a task' => 'Egy feladat újbóli megnyitása', + 'Add a comment log when moving the task between columns' => 'Megjegyzésnapló hozzáadása, ha a feladatot az oszlopok között mozgatják', + 'Move the task to another column when the category is changed' => 'A feladat áthelyezése egy másik oszlopba, ha megváltozik a kategória', + 'Send a task by email to someone' => 'Feladat elküldése e-mailben valakinek', + 'Reopen a task' => 'Feladat ismételt megnyitása', 'Notification' => 'ÉrtesÃtés', - '%s moved the task #%d to the first swimlane' => '%s a #%d feladatot az elsÅ‘ sávba mozgatta', + '%s moved the task #%d to the first swimlane' => '%s áthelyezte a(z) #%d. feladatot az elsÅ‘ sávba', 'Swimlane' => 'Sáv', - 'Gravatar' => 'Gravatár', - '%s moved the task %s to the first swimlane' => '%s a %s feladatot az elsÅ‘ sávba mozgatta', - '%s moved the task %s to the swimlane "%s"' => '%s a %s feladatot a "%s" sávba mozgatta', - 'This report contains all subtasks information for the given date range.' => 'Ez a riport az adott dátumtartományra vonatkozón az összes részfeladatot tartalmazza', - 'This report contains all tasks information for the given date range.' => 'Ez a riport az adott dátumtartományra vonatkozóan az összes feladatot tartalmazza', - 'Project activities for %s' => '%s projekt tevékenységei', - 'view the board on Kanboard' => 'a tábla megjelenÃtése a Kanboard-on', - 'The task have been moved to the first swimlane' => 'A feladat ez elsÅ‘ sávba lett elmozgatva', - 'The task have been moved to another swimlane:' => 'A feladat egy másik sávba lett elmozgatva', + '%s moved the task %s to the first swimlane' => '%s áthelyezte a(z) %s feladatot az elsÅ‘ sávba', + '%s moved the task %s to the swimlane "%s"' => '%s áthelyezte a(z) %s feladatot a(z) „%s†sávba', + 'This report contains all subtasks information for the given date range.' => 'Ez a jelentés az összes részfeladat-információt tartalmazza az adott dátumtartományban', + 'This report contains all tasks information for the given date range.' => 'Ez a jelentés az összes feladatinformációt tartalmazza az adott dátumtartományban', + 'Project activities for %s' => '%s projekttevékenységei', + 'view the board on Kanboard' => 'a tábla megjelenÃtése a Kanboardon', + 'The task have been moved to the first swimlane' => 'A feladat át lett helyezve az elsÅ‘ sávba', + 'The task have been moved to another swimlane:' => 'A feladat át lett helyezve egy másik sávba:', 'New title: %s' => 'Új cÃm: %s', - 'The task is not assigned anymore' => 'A feladatnak már nincs felelÅ‘se', - 'New assignee: %s' => 'Az új felelÅ‘s: %s', + 'The task is not assigned anymore' => 'A feladatnak többé már nincs felelÅ‘se', + 'New assignee: %s' => 'Új felelÅ‘s: %s', 'There is no category now' => 'Jelenleg nincs kategória', - 'New category: %s' => 'Az új kategória: %s', - 'New color: %s' => 'Az új szÃn: %s', - 'New complexity: %d' => 'Az új bonyolultság: %d', - 'The due date have been removed' => 'A határidÅ‘ törölve lett', + 'New category: %s' => 'Új kategória: %s', + 'New color: %s' => 'Új szÃn: %s', + 'New complexity: %d' => 'Új bonyolultság: %d', + 'The due date have been removed' => 'A határidÅ‘ eltávolÃtásra került', 'There is no description anymore' => 'Többé már nincs leÃrás', - 'Recurrence settings have been modified' => 'Az ismétlÅ‘dés beállÃtásai módosultak', - 'Time spent changed: %sh' => 'Módosult a ráfordÃtott idÅ‘: %s óra', - 'Time estimated changed: %sh' => 'Módosult az idÅ‘becslés: %s óra', - 'The field "%s" have been updated' => 'A "%s" mezÅ‘ módosÃtva lett', + 'Recurrence settings have been modified' => 'Az ismétlÅ‘dés beállÃtásai módosÃtva lettek', + 'Time spent changed: %sh' => 'Az eltöltött idÅ‘ megváltozott: %s óra', + 'Time estimated changed: %sh' => 'A becsült idÅ‘ megváltozott: %s óra', + 'The field "%s" have been updated' => 'A(z) „%s†mezÅ‘ frissÃtve lett', 'The description has been modified:' => 'A leÃrás módosÃtva lett:', - 'Do you really want to close the task "%s" as well as all subtasks?' => 'Valóban be akarja zárni a "%s" feladatot, valamint a hozzá tartozó részfeladatokat?', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Valóban le szeretné zárni a(z) „%s†feladatot, valamint az összes részfeladatot?', 'I want to receive notifications for:' => 'ÉrtesÃtéseket szeretnék kapni a következÅ‘krÅ‘l:', - 'All tasks' => 'Az összes feladat', + 'All tasks' => 'Összes feladat', 'Only for tasks assigned to me' => 'Csak a hozzám rendelt feladatok', 'Only for tasks created by me' => 'Csak az általam létrehozott feladatok', - 'Only for tasks created by me and assigned to me' => 'Csak az általam lérehozott és a hozzám rendelt feladatok', + 'Only for tasks created by me and assigned to me' => 'Csak az általam létrehozott és a hozzám rendelt feladatok', '%%Y-%%m-%%d' => '%%Y-%%m-%%d', 'Total for all columns' => 'Az összes oszlop összege', - 'You need at least 2 days of data to show the chart.' => 'Legalább 2 nap adatára van szükség az ábra megjelenÃtéséshez', - '<15m' => '15p', - '<30m' => '30p', + 'You need at least 2 days of data to show the chart.' => 'Legalább 2 nap adatára van szükség a diagram megjelenÃtéséhez.', + '<15m' => '<15p', + '<30m' => '<30p', 'Stop timer' => 'IdÅ‘mérÅ‘ leállÃtása', 'Start timer' => 'IdÅ‘mérÅ‘ elindÃtása', - 'My activity stream' => 'Tevékenységem', - 'My calendar' => 'Naptáram', - 'Search tasks' => 'Feladatok közötti keresés', - 'Reset filters' => 'SzűrÅ‘ alaphelyzetbe állÃtás', + 'My activity stream' => 'Saját tevékenységfolyamom', + 'Search tasks' => 'Feladatok keresése', + 'Reset filters' => 'SzűrÅ‘k visszaállÃtása', 'My tasks due tomorrow' => 'Holnapi határidejű feladataim', 'Tasks due today' => 'Mai határidejű feladatok', 'Tasks due tomorrow' => 'Holnapi határidejű feladatok', 'Tasks due yesterday' => 'Tegnapi határidejű feladatok', 'Closed tasks' => 'Lezárt feladatok', 'Open tasks' => 'Nyitott feladatok', - 'Not assigned' => 'Nincs felelÅ‘s', - 'View advanced search syntax' => 'Részletes keresés megjelenÃtése', + 'Not assigned' => 'Nincs hozzárendelve', + 'View advanced search syntax' => 'Speciális keresés szintaxis megtekintése', 'Overview' => 'Ãttekintés', 'Board/Calendar/List view' => 'Tábla/Naptár/Lista nézet', - 'Switch to the board view' => 'Ãtkapcsolás tábla nézetbe', - 'Switch to the calendar view' => 'Ãtkapcsolás naptár nézetbe', - 'Switch to the list view' => 'Ãtkapcsolás lista nézetbe', + 'Switch to the board view' => 'Ãtváltás tábla nézetre', + 'Switch to the list view' => 'Ãtváltás lista nézetre', 'Go to the search/filter box' => 'Ugrás a keresés/szűrés dobozhoz', - 'There is no activity yet.' => 'Még nincs tevékenység', - 'No tasks found.' => 'Nincs feladat.', - 'Keyboard shortcut: "%s"' => 'Billentyű parancsok: "%s"', + 'There is no activity yet.' => 'Még nincs tevékenység.', + 'No tasks found.' => 'Nem található feladat.', + 'Keyboard shortcut: "%s"' => 'Gyorsbillentyű: „%sâ€', 'List' => 'Lista', 'Filter' => 'SzűrÅ‘', - 'Advanced search' => 'Keresés haladóknak', + 'Advanced search' => 'Speciális keresés', 'Example of query: ' => 'Lekérdezési példa: ', 'Search by project: ' => 'Keresés projekt alapján: ', 'Search by column: ' => 'Keresés oszlop alapján: ', @@ -705,267 +686,251 @@ return array( 'Search by category: ' => 'Keresés kategória alapján: ', 'Search by description: ' => 'Keresés leÃrás alapján: ', 'Search by due date: ' => 'Keresés határidÅ‘ alapján: ', - 'Average time spent into each column' => 'Az egyes oszlopokban töltött átlagos idÅ‘', - 'Average time spent' => 'Az eltöltött átlagos idÅ‘', - 'This chart show the average time spent into each column for the last %d tasks.' => 'Ez az ábra az utolsó %d feladatra vonatkozóan mutatja az egyes oszlopkban eltöltött átlagos idÅ‘t.', - 'Average Lead and Cycle time' => 'Ãtlagos átfutási idÅ‘ és ciklusidÅ‘', + 'Average time spent into each column' => 'Ãtlagosan eltöltött idÅ‘ az egyes oszlopokban', + 'Average time spent' => 'Ãtlagosan eltöltött idÅ‘', + 'This chart show the average time spent into each column for the last %d tasks.' => 'Ez a diagram az egyes oszlopokban átlagosan eltöltött idÅ‘t jelenÃti meg az utolsó %d feladatnál.', + 'Average Lead and Cycle time' => 'Ãtlagos átfutási és ciklusidÅ‘', 'Average lead time: ' => 'Ãtlagos átfutási idÅ‘: ', 'Average cycle time: ' => 'Ãtlagos ciklusidÅ‘: ', 'Cycle Time' => 'CiklusidÅ‘', 'Lead Time' => 'Ãtfutási idÅ‘', - 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Ez az ábra az utolsó %d feladatra vonatkozóan mutatja az átlagos átfutási idÅ‘t és ciklusidÅ‘t az idÅ‘ függvényében.', - 'Average time into each column' => 'Az egyes oszlopokban töltött átlagos idÅ‘', + 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Ez a diagram az átlagos átfutási és ciklusidÅ‘t jelenÃti meg az utolsó %d feladatnál az idÅ‘ függvényében.', + 'Average time into each column' => 'Ãtlagos idÅ‘ az egyes oszlopokban', 'Lead and cycle time' => 'Ãtfutási és ciklusidÅ‘', 'Lead time: ' => 'Ãtfutási idÅ‘: ', 'Cycle time: ' => 'CiklusidÅ‘: ', - 'Time spent into each column' => 'Az egyes oszlopokban töltött idÅ‘', - 'The lead time is the duration between the task creation and the completion.' => 'Az átfutási idÅ‘ a feladat létrehozása és befejezése között eltelt idÅ‘.', - 'The cycle time is the duration between the start date and the completion.' => 'A ciklusidÅ‘ a feladat elkezdése és befejezése közötti eltelt idÅ‘.', - 'If the task is not closed the current time is used instead of the completion date.' => 'Ha a feladat még nincs lezárva, akkor befejezés ideje helyett az aktuális idÅ‘ lesz használva.', + 'Time spent into each column' => 'Az egyes oszlopokban eltöltött idÅ‘', + 'The lead time is the duration between the task creation and the completion.' => 'Az átfutási idÅ‘ a feladat létrehozása és befejezése közötti idÅ‘tartam.', + 'The cycle time is the duration between the start date and the completion.' => 'A ciklusidÅ‘ a kezdési dátum és a befejezés közötti idÅ‘tartam.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Ha a feladat nincs lezárva, akkor az aktuális idÅ‘ lesz használva a befejezés dátuma helyett.', 'Set automatically the start date' => 'A kezdési idÅ‘ automatikus beállÃtása', - 'Edit Authentication' => 'A beléptetés szerkesztése', + 'Edit Authentication' => 'HitelesÃtés szerkesztése', 'Remote user' => 'Távoli felhasználó', - 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'A távoli felhasználók jelszava nem a Kanboard adatbázisban van tárolva. Példák: LDAP, Google és GitHub számlák.', - 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Ha bekattintja a "Bejelentkezési ablak tiltása" jelölÅ‘négyzetet, akkor a login ablakban megadott jelszó nem lesz figyelembe véve.', - 'Default task color' => 'A feladathoz rendelt alapszÃn', - 'This feature does not work with all browsers.' => 'Ez a jellemzÅ‘ nem minden böngészÅ‘ben működik.', - 'There is no destination project available.' => 'Nincs ilyen cél projekt.', - 'Trigger automatically subtask time tracking' => 'A részfeladatok idÅ‘felhasználas-követésének automatikus indÃtása', - 'Include closed tasks in the cumulative flow diagram' => 'A lezárt feladatok szerepeltetáse az összesÃtett folyamatábrán', - 'Current swimlane: %s' => 'Aktuális sáv: %s', - 'Current column: %s' => 'Aktuális oszlop: %s', - 'Current category: %s' => 'Aktuális kategória: %s', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'A távoli felhasználók jelszava nem a Kanboard adatbázisban van tárolva. Példák: LDAP, Google és GitHub fiókok.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Ha bejelöli a „Bejelentkezési űrlap letiltása†jelölÅ‘négyzetet, akkor a bejelentkezési űrlapon megadott hitelesÃtési adatok figyelmen kÃvül lesznek hagyva.', + 'Default task color' => 'Alapértelmezett feladatszÃn', + 'This feature does not work with all browsers.' => 'Ez a funkció nem működik minden böngészÅ‘ben.', + 'There is no destination project available.' => 'Nem érhetÅ‘ el célprojekt.', + 'Trigger automatically subtask time tracking' => 'A részfeladatok idÅ‘követésének automatikus aktiválása', + 'Include closed tasks in the cumulative flow diagram' => 'A lezárt feladatok is legyenek benne a halmozott folyamdiagramban', + 'Current swimlane: %s' => 'Jelenlegi sáv: %s', + 'Current column: %s' => 'Jelenlegi oszlop: %s', + 'Current category: %s' => 'Jelenlegi kategória: %s', 'no category' => 'nincs kategória', - 'Current assignee: %s' => 'Aktuális felelÅ‘s: %s', + 'Current assignee: %s' => 'Jelenlegi felelÅ‘s: %s', 'not assigned' => 'nincs kijelölt felelÅ‘s', 'Author:' => 'SzerzÅ‘:', - 'contributors' => 'bedolgozók', - 'License:' => 'Engedély:', - 'License' => 'Engedély', - 'Enter the text below' => 'Adja be a lenti szöveget', - 'Sort by position' => 'Rendezés hely szerint', - 'Sort by date' => 'Rendezés idÅ‘ szerint', - 'Add task' => 'Feladat hozzáadása', - 'Start date:' => 'Kezdés ideje: ', + 'contributors' => 'közreműködÅ‘k', + 'License:' => 'Licenc:', + 'License' => 'Licenc', + 'Enter the text below' => 'Adja meg a lenti szöveget', + 'Start date:' => 'Kezdés dátuma: ', 'Due date:' => 'HatáridÅ‘: ', - 'There is no start date or due date for this task.' => 'Ehhez a feladathoz nem adtak meg kezdési idÅ‘t vagy határidÅ‘t.', - 'Moving or resizing a task will change the start and due date of the task.' => 'A feladat elmozgatása vagy méretének megváltoztatása meg fogja változtatni a feladat kezdési idejét és határidejét.', - 'There is no task in your project.' => 'Az ön projektjében nincsenek feladatok.', - 'Gantt chart' => 'Gantt diagram', - 'People who are project managers' => 'Projekt vezetÅ‘k', - 'People who are project members' => 'Projekt tagok', - 'NOK - Norwegian Krone' => 'NOK - Norvég korona', - 'Show this column' => 'Oszlop mutatása', + 'People who are project managers' => 'Emberek, akik projektvezetÅ‘k', + 'People who are project members' => 'Emberek, akik projekttagok', + 'NOK - Norwegian Krone' => 'NOK - norvég korona', + 'Show this column' => 'Oszlop megjelenÃtése', 'Hide this column' => 'Oszlop elrejtése', - 'open file' => 'fájl megnyitás', - 'End date' => 'Végdátum', + 'open file' => 'fájl megnyitása', + 'End date' => 'Befejezési dátum', 'Users overview' => 'Felhasználók áttekintése', 'Members' => 'Tagok', - 'Shared project' => 'Közös projekt', + 'Shared project' => 'Megosztott projekt', 'Project managers' => 'ProjektvezetÅ‘k', - 'Gantt chart for all projects' => 'Gantt diagram az összes projektre vonatkozóan', - 'Projects list' => 'Projekt lista', - 'Gantt chart for this project' => 'Gantt diagram erre a projektre vonatkozóan', - 'Project board' => 'Projekt tábla', - 'End date:' => 'Befejezés dátuma: ', - 'There is no start date or end date for this project.' => 'Ennél a projektnél nem adták meg a kezdés és a befejezés dátumát.', - 'Projects Gantt chart' => 'A projektek Gantt diagramja', - 'Change task color when using a specific task link' => 'Az egyes specifikus feladat hivatkozások használatakor a feladat szÃnének megváltoztatása', - 'Task link creation or modification' => 'Feladat hivatkozás létrehozása vagy módosÃtása', + 'Projects list' => 'Projektek listája', + 'End date:' => 'Befejezési dátum: ', + 'Change task color when using a specific task link' => 'FeladatszÃn megváltoztatása, ha egy adott feladathivatkozást használnak', + 'Task link creation or modification' => 'Feladathivatkozás létrehozása vagy módosÃtása', 'Milestone' => 'MérföldkÅ‘', 'Documentation: %s' => 'Dokumentáció: %s', - 'Switch to the Gantt chart view' => 'Ãtkapcsolás a Gantt diagram nézetre', - 'Reset the search/filter box' => 'A keresés/szűrés doboz alaphelyzetbe állÃtása', + 'Reset the search/filter box' => 'A keresés/szűrés doboz visszaállÃtása', 'Documentation' => 'Dokumentáció', 'Table of contents' => 'Tartalomjegyzék', - 'Gantt' => 'Gantt', 'Author' => 'SzerzÅ‘', 'Version' => 'Verzió', - 'Plugins' => 'Plugin-ek', - 'There is no plugin loaded.' => 'Nincs betöltött plugin.', - 'My notifications' => 'EmlékeztetÅ‘im', - 'Custom filters' => 'Egyedi szűrÅ‘k', - 'Your custom filter have been created successfully.' => 'Az ön egyedi szűrÅ‘je sikeresen létrejött.', - 'Unable to create your custom filter.' => 'Nem sikerült létrehozni az ön egyedi szűrÅ‘jét.', - 'Custom filter removed successfully.' => 'Az egyedi szűrÅ‘ sikeresen törölve lett.', - 'Unable to remove this custom filter.' => 'Nem sikerült egy egyedi szűrÅ‘ törlése.', - 'Edit custom filter' => 'Egyedi szűrÅ‘ szerkesztése', - 'Your custom filter have been updated successfully.' => 'Az ön egyedi szűrÅ‘je sikeresen módosult.', - 'Unable to update custom filter.' => 'Nem sikerült az egyedi szűrÅ‘ módosÃtása', - 'Web' => 'Web (háló)', - 'New attachment on task #%d: %s' => 'A #%d számú feladatnak új melléklete van: %s', - 'New comment on task #%d' => 'A #%d számú feladatnak új megjegyzése van', - 'Comment updated on task #%d' => 'A #%d szűmú feladathoz tartozó megjegyzés módosult', - 'New subtask on task #%d' => 'A #%d számú feladatnak új részfeladata van', - 'Subtask updated on task #%d' => 'A #%d számú feladat részfeladata módosult', + 'Plugins' => 'BÅ‘vÃtmények', + 'There is no plugin loaded.' => 'Nincsenek betöltött bÅ‘vÃtmények.', + 'My notifications' => 'Saját emlékeztetÅ‘k', + 'Custom filters' => 'Egyéni szűrÅ‘k', + 'Your custom filter have been created successfully.' => 'Az egyéni szűrÅ‘je sikeresen létrehozva.', + 'Unable to create your custom filter.' => 'Nem lehet létrehozni az egyéni szűrÅ‘jét.', + 'Custom filter removed successfully.' => 'Az egyéni szűrÅ‘ sikeresen eltávolÃtva.', + 'Unable to remove this custom filter.' => 'Nem lehet eltávolÃtani az egyéni szűrÅ‘t.', + 'Edit custom filter' => 'Egyéni szűrÅ‘ szerkesztése', + 'Your custom filter have been updated successfully.' => 'Az egyéni szűrÅ‘je sikeresen frissÃtve.', + 'Unable to update custom filter.' => 'Nem lehet frissÃteni az egyéni szűrÅ‘t.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Új melléklet a(z) #%d. feladatnál: %s', + 'New comment on task #%d' => 'Új megjegyzés a(z) #%d. feladatnál', + 'Comment updated on task #%d' => 'A megjegyzés frissÃtve lett a(z) #%d. feladatnál', + 'New subtask on task #%d' => 'Új részfeladat a(z) #%d. feladatnál', + 'Subtask updated on task #%d' => 'A részfeladat frissÃtve lett a(z) #%d. feladatnál', 'New task #%d: %s' => 'Új #%d számú feladat: %s', - 'Task updated #%d' => 'A #%d számú feladat módosult', - 'Task #%d closed' => 'A #%d számú feladat le lett zárva', - 'Task #%d opened' => 'A #%d számú feladat meg lett nyitva', - 'Column changed for task #%d' => 'A #%d számú feladat oszlopa módosult', - 'New position for task #%d' => 'A #%d számú feladat új helyre került', - 'Swimlane changed for task #%d' => 'A #%d számú feladat új sávba került', - 'Assignee changed on task #%d' => 'A #%d számú feladat felelÅ‘se megváltozott', - '%d overdue tasks' => '%d db feladatnál van határidÅ‘ túllépés', - 'Task #%d is overdue' => 'A #%d számú feladat határideje lejárt', - 'No notification.' => 'Nincs új emlékeztetÅ‘.', + 'Task updated #%d' => 'A(z) #%d. feladat frissült', + 'Task #%d closed' => 'A(z) #%d. feladat le lett zárva', + 'Task #%d opened' => 'A(z) #%d. feladat meg lett nyitva', + 'Column changed for task #%d' => 'Az oszlop megváltozott a(z) #%d. feladatnál', + 'New position for task #%d' => 'Új pozÃció a(z) #%d. feladatnál', + 'Swimlane changed for task #%d' => 'A sáv megváltozott a(z) #%d. feladatnál', + 'Assignee changed on task #%d' => 'A felelÅ‘s megváltozott a(z) #%d. feladatnál', + '%d overdue tasks' => '%d lejárt feladat', + 'Task #%d is overdue' => 'A(z) #%d. feladat határideje lejárt', + 'No notification.' => 'Nincs értesÃtés.', 'Mark all as read' => 'Az összes megjelölése olvasottként', 'Mark as read' => 'Megjelölés olvasottként', 'Total number of tasks in this column across all swimlanes' => 'Az ebben az oszlopban, az összes sávban lévÅ‘ feladatok száma', 'Collapse swimlane' => 'Sáv összecsukása', - 'Expand swimlane' => 'Sáv lenyitása', + 'Expand swimlane' => 'Sáv kinyitása', 'Add a new filter' => 'Új szűrÅ‘ hozzáadása', - 'Share with all project members' => 'Megosztás minden projekt taggal', + 'Share with all project members' => 'Megosztás az összes projekttaggal', 'Shared' => 'Megosztva', 'Owner' => 'Tulajdonos', 'Unread notifications' => 'Olvasatlan értesÃtések', 'Notification methods:' => 'ÉrtesÃtési módszerek:', - 'Unable to read your file' => 'A fájl nem olvasható', - '%d task(s) have been imported successfully.' => '%d feladat sikeresen feldolgozva.', - 'Nothing have been imported!' => 'Nem történt beolvasás!', - 'Import users from CSV file' => 'Felhasználók importálása CSV fájlból', + 'Unable to read your file' => 'Nem lehet beolvasni a fájlt', + '%d task(s) have been imported successfully.' => '%d feladat sikeresen importálva.', + 'Nothing have been imported!' => 'Semmi sem lett importálva!', + 'Import users from CSV file' => 'Felhasználók importálása CSV-fájlból', '%d user(s) have been imported successfully.' => '%d felhasználó sikeresen importálva.', - // 'Comma' => '', - // 'Semi-colon' => '', - // 'Tab' => '', - // 'Vertical bar' => '', - // 'Double Quote' => '', - // 'Single Quote' => '', - '%s attached a file to the task #%d' => '%s hozzákapcsolt a #%d feladathoz egy fájlt', - 'There is no column or swimlane activated in your project!' => 'Az ön projektjében nincs aktÃv oszlop vagy sáv!', - 'Append filter (instead of replacement)' => 'SzűrÅ‘ hozzáfűzése (a helyettesÃtés helyett)', - 'Append/Replace' => 'Hozzáfűzés/HelyettesÃtés', - 'Append' => 'Hozzáfűz', - 'Replace' => 'HelyettesÃt', - 'Import' => 'Importál', - 'Change sorting' => 'rendezési sorrend megváltoztatása', - 'Tasks Importation' => 'Feladat importálás', - // 'Delimiter' => '', - // 'Enclosure' => '', - 'CSV File' => 'CSV fájl', + 'Comma' => 'VesszÅ‘', + 'Semi-colon' => 'PontosvesszÅ‘', + 'Tab' => 'Tabulátor', + 'Vertical bar' => 'FüggÅ‘leges vonal', + 'Double Quote' => 'IdézÅ‘jel', + 'Single Quote' => 'Aposztróf', + '%s attached a file to the task #%d' => '%s mellékelt egy fájlt a(z) #%d. feladathoz', + 'There is no column or swimlane activated in your project!' => 'Nincs aktivált oszlop vagy sáv a projektjében!', + 'Append filter (instead of replacement)' => 'SzűrÅ‘ hozzáfűzése (kicserélés helyett)', + 'Append/Replace' => 'Hozzáfűzés/csere', + 'Append' => 'Hozzáfűzés', + 'Replace' => 'Csere', + 'Import' => 'Importálás', + 'Change sorting' => 'Rendezés megváltoztatása', + 'Tasks Importation' => 'Feladat importálása', + 'Delimiter' => 'Elválasztó', + 'Enclosure' => 'Határoló', + 'CSV File' => 'CSV-fájl', 'Instructions' => 'UtasÃtások', - 'Your file must use the predefined CSV format' => 'A fájlnak az elÅ‘re definiált CSV formátumot kell használnia', - 'Your file must be encoded in UTF-8' => 'A fájlnak UTF-8 karakterkódolást kell használnia', - 'The first row must be the header' => 'Az elsÅ‘ sor kötelezÅ‘en a fejléc', - 'Duplicates are not verified for you' => 'Az ismétlÅ‘dések nincsenek ellenÅ‘rizve.', - 'The due date must use the ISO format: YYYY-MM-DD' => 'A határidÅ‘t ISO formátumban kell megadni: YYYY-MM-DD', - 'Download CSV template' => 'A CSV minta (template) letöltése', + 'Your file must use the predefined CSV format' => 'A fájlnak az elÅ‘re meghatározott CSV-formátumot kell használnia', + 'Your file must be encoded in UTF-8' => 'A fájlnak UTF-8 kódolásúnak kell lennie', + 'The first row must be the header' => 'Az elsÅ‘ sornak fejlécnek kell lennie', + 'Duplicates are not verified for you' => 'Az ismétlÅ‘dések nincsenek ellenÅ‘rizve', + 'The due date must use the ISO format: YYYY-MM-DD' => 'A határidÅ‘nek ISO formátumot kell használnia: YYYY-MM-DD', + 'Download CSV template' => 'CSV-sablon letöltése', 'No external integration registered.' => 'Nincs külsÅ‘ integráció regisztrálva.', - 'Duplicates are not imported' => 'Az ismétlÅ‘dÅ‘ értékek nem lesznek beimportálva', - 'Usernames must be lowercase and unique' => 'A felhasználói nevekre kötelezÅ‘, hogy kisbetűsek és egyediek legyenek', - 'Passwords will be encrypted if present' => 'A jelszavak titkosÃtva lesznek', - '%s attached a new file to the task %s' => '%s a %s feladathoz egy új fájlt kapcsolt hozzá', - 'Link type' => 'Hivatkozás tÃpus', - 'Assign automatically a category based on a link' => 'A hivatkozás alapján kategória automatikus hozzárendelése', - 'BAM - Konvertible Mark' => 'BAM - Konvertibilis márka', - 'Assignee Username' => 'A felelÅ‘s felhasználói neve', - 'Assignee Name' => 'A felelÅ‘s neve', + 'Duplicates are not imported' => 'Az ismétlÅ‘dések nincsenek importálva', + 'Usernames must be lowercase and unique' => 'A felhasználóneveknek kisbetűsnek és egyedinek kell lenniük', + 'Passwords will be encrypted if present' => 'A jelszavak titkosÃtva lesznek, ha meg vannak adva', + '%s attached a new file to the task %s' => '%s mellékelt egy új fájlt a(z) %s feladathoz', + 'Link type' => 'Hivatkozás tÃpusa', + 'Assign automatically a category based on a link' => 'Kategória automatikus hozzárendelése egy hivatkozás alapján', + 'BAM - Konvertible Mark' => 'BAM - konvertibilis márka', + 'Assignee Username' => 'FelelÅ‘s felhasználóneve', + 'Assignee Name' => 'FelelÅ‘s neve', 'Groups' => 'Csoportok', - 'Members of %s' => 'A %s tagjai', + 'Members of %s' => '%s tagjai', 'New group' => 'Új csoport', - 'Group created successfully.' => 'A csoport sikeresen létrejött.', - 'Unable to create your group.' => 'Nem sikerült a csoport létrehozása.', + 'Group created successfully.' => 'A csoport sikeresen létrehozva.', + 'Unable to create your group.' => 'Nem lehet létrehozni a csoportot.', 'Edit group' => 'Csoport szerkesztése', - 'Group updated successfully.' => 'A csoport módosÃtása sikeresen megtörtént', - 'Unable to update your group.' => 'Nem sikerült a csoport módosÃtása', - 'Add group member to "%s"' => 'Csoport tag hozzáadáasa "%s"-hez', - 'Group member added successfully.' => 'Tag hozzáadás sikeresen megtörtént', - 'Unable to add group member.' => 'Nem sikerült a tagot hozzáadni a csoporthoz.', - 'Remove user from group "%s"' => 'Felhasználó eltávolÃtása a "%s" csoportból', + 'Group updated successfully.' => 'A csoport sikeresen frissÃtve.', + 'Unable to update your group.' => 'Nem lehet frissÃteni a csoportot.', + 'Add group member to "%s"' => 'Csoporttag hozzáadása ehhez: „%sâ€', + 'Group member added successfully.' => 'A csoporttag sikeresen hozzáadva.', + 'Unable to add group member.' => 'Nem lehet hozzáadni a csoporttagot.', + 'Remove user from group "%s"' => 'Felhasználó eltávolÃtása a(z) „%s†csoportból', 'User removed successfully from this group.' => 'A felhasználó sikeresen el lett távolÃtva a csoportból.', - 'Unable to remove this user from the group.' => 'Nem sikerült a felhasználó eltávolÃtása a csoportból', - 'Remove group' => 'Csoport törlése', - 'Group removed successfully.' => 'A csoport törlése sikeresen megtörtént.', - 'Unable to remove this group.' => 'Nem sikerült a csoport törlése.', - 'Project Permissions' => 'Projekt engedélyek', + 'Unable to remove this user from the group.' => 'Nem lehet eltávolÃtani a felhasználót a csoportból.', + 'Remove group' => 'Csoport eltávolÃtása', + 'Group removed successfully.' => 'A csoport sikeresen eltávolÃtva.', + 'Unable to remove this group.' => 'Nem lehet eltávolÃtani a csoportot.', + 'Project Permissions' => 'Projektjogosultságok', 'Manager' => 'VezetÅ‘', - 'Project Manager' => 'Projekt vezetÅ‘', - 'Project Member' => 'Projekt tag', - 'Project Viewer' => 'Projekt megtekintés', - 'Your account is locked for %d minutes' => 'Az ön számlája %d percre zárolva lett.', + 'Project Manager' => 'ProjektvezetÅ‘', + 'Project Member' => 'Projekttag', + 'Project Viewer' => 'ProjektmegtekintÅ‘', + 'Your account is locked for %d minutes' => 'A fiókja zárolva lett %d percre', 'Invalid captcha' => 'Érvénytelen captcha', 'The name must be unique' => 'A névnek egyedinek kell lennie', 'View all groups' => 'Az összes csoport megtekintése', - 'There is no user available.' => 'Nincs ilyen felhasználó.', - 'Do you really want to remove the user "%s" from the group "%s"?' => 'Valóban el kÃvánja távolÃtani a "%s" felhasználót a "%s" csoportból?', - 'There is no group.' => 'Nincs ilyen csoport.', - 'External Id' => 'KülsÅ‘ azonosÃtó', + 'There is no user available.' => 'Nincs elérhetÅ‘ felhasználó.', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Valóban el szeretné távolÃtani „%s†felhasználót a(z) „%s†csoportból?', + 'There is no group.' => 'Nincs csoport.', 'Add group member' => 'Csoporttag hozzáadása', - 'Do you really want to remove this group: "%s"?' => 'Valóban törölni kÃvánja ezt a csoportot: "%s"?', - 'There is no user in this group.' => 'Nincs egy felhasználó sem ebben a csoportban.', - 'Permissions' => 'Engedélyek', - 'Allowed Users' => 'Megengedett felhasználók', - 'No user have been allowed specifically.' => 'Egyedileg semelyik felhasználó sem lett engedélyezve.', - 'Role' => 'Szerepkör', - 'Enter user name...' => 'Adja be a felhasználó nevét...', - 'Allowed Groups' => 'Megengedett csoportok', - 'No group have been allowed specifically.' => 'Egyedileg semelyik csoport sem lett engedélyezve.', + 'Do you really want to remove this group: "%s"?' => 'Valóban el szeretné távolÃtani ezt a csoportot: „%sâ€?', + 'There is no user in this group.' => 'Nincs felhasználó ebben a csoportban.', + 'Permissions' => 'Jogosultságok', + 'Allowed Users' => 'Engedélyezett felhasználók', + 'No user have been allowed specifically.' => 'Egyedileg egyetlen felhasználó sem lett engedélyezve.', + 'Role' => 'Szerep', + 'Enter user name...' => 'Adja meg a felhasználó nevét…', + 'Allowed Groups' => 'Engedélyezett csoportok', + 'No group have been allowed specifically.' => 'Egyedileg egyetlen csoport sem lett engedélyezve.', 'Group' => 'Csoport', - 'Group Name' => 'Csoport név', - 'Enter group name...' => 'Adja meg a csoport nevét...', - 'Role:' => 'Szerepkör:', - 'Project members' => 'Projekt tagok', - '%s mentioned you in the task #%d' => '%s megemlÃtette önt a #%d feladatban', - '%s mentioned you in a comment on the task #%d' => '%s megemlÃtette önt a #%d feladathoz fűzött megjegyzésben', - 'You were mentioned in the task #%d' => 'Ön meg lett emlÃtve a #%d feladatban', - 'You were mentioned in a comment on the task #%d' => 'Ön meg lett emlÃtve a #%d feladathoz fűzött megjegyzésben', + 'Group Name' => 'Csoport neve', + 'Enter group name...' => 'Adja meg a csoport nevét…', + 'Role:' => 'Szerep:', + 'Project members' => 'Projekttagok', + '%s mentioned you in the task #%d' => '%s megemlÃtette Önt a(z) #%d. feladatban', + '%s mentioned you in a comment on the task #%d' => '%s megemlÃtette Önt a(z) #%d. feladathoz fűzött megjegyzésben', + 'You were mentioned in the task #%d' => 'MegemlÃtették Önt a(z) #%d. feladatban', + 'You were mentioned in a comment on the task #%d' => 'MegemlÃtették Önt a(z) #%d. feladathoz fűzött megjegyzésben', 'Estimated hours: ' => 'Becsült órák: ', 'Actual hours: ' => 'Tényleges órák: ', - 'Hours Spent' => 'RáfordÃtás órákban', - 'Hours Estimated' => 'Becsült idÅ‘ órákban', + 'Hours Spent' => 'Eltöltött órák', + 'Hours Estimated' => 'Becsült órák', 'Estimated Time' => 'Becsült idÅ‘', 'Actual Time' => 'Tényleges idÅ‘', - 'Estimated vs actual time' => 'Becsült idÅ‘ ill. tényleges idÅ‘', - 'RUB - Russian Ruble' => 'RUB - Orosz rubel', - 'Assign the task to the person who does the action when the column is changed' => 'A feladat hozzárendelése ahhoz a személyhez, aki az oszlop megváltoztatásakor intézkedik', - 'Close a task in a specific column' => 'A megadott oszlopban lévÅ‘ feladat lezárása', - 'Time-based One-time Password Algorithm' => 'IdÅ‘ alapú, egyszer használható jelszó algoritmus', - 'Two-Factor Provider: ' => 'Két tényezÅ‘s bejelentkezés szolgáltatója', - 'Disable two-factor authentication' => 'Két tényezÅ‘s bejelentkezés tiltása', - 'Enable two-factor authentication' => 'Két tényezÅ‘s bejelentkezés engedélyezése', - 'There is no integration registered at the moment.' => 'Jelenleg nincs regisztrált integráció.', - 'Password Reset for Kanboard' => 'Jelszó alaphelyzetbe állÃtása', - 'Forgot password?' => 'Elfelejtett jelszó?', - 'Enable "Forget Password"' => '"Elfelejtett jelszó" engedélyezése', - 'Password Reset' => 'Jelszó alaphelyzetbe állÃása', + 'Estimated vs actual time' => 'Becsült ↔ tényleges idÅ‘', + 'RUB - Russian Ruble' => 'RUB - orosz rubel', + 'Assign the task to the person who does the action when the column is changed' => 'A feladat hozzárendelése ahhoz a személyhez, aki elvégzi a műveletet az oszlop megváltoztatásakor', + 'Close a task in a specific column' => 'Feladat lezárása egy adott oszlopban', + 'Time-based One-time Password Algorithm' => 'IdÅ‘alapú, egyszer használható jelszó algoritmus', + 'Two-Factor Provider: ' => 'KétlépcsÅ‘s hitelesÃtés szolgáltatója: ', + 'Disable two-factor authentication' => 'KétlépcsÅ‘s hitelesÃtés letiltása', + 'Enable two-factor authentication' => 'KétlépcsÅ‘s hitelesÃtés engedélyezése', + 'There is no integration registered at the moment.' => 'Jelenleg nincs integráció regisztrálva.', + 'Password Reset for Kanboard' => 'Kanboard jelszó visszaállÃtása', + 'Forgot password?' => 'Elfelejtette a jelszavát?', + 'Enable "Forget Password"' => '„Elfelejtett jelszó†engedélyezése', + 'Password Reset' => 'Jelszó visszaállÃtása', 'New password' => 'Új jelszó', 'Change Password' => 'Jelszó megváltoztatása', - 'To reset your password click on this link:' => 'A jelszó megváltoztatásáshoz kattintson erre a hivatkozásra', + 'To reset your password click on this link:' => 'A jelszó visszaállÃtásához kattintson erre a hivatkozásra:', 'Last Password Reset' => 'Jelszó alaphelyzetbe állÃtás utoljára ekkor', - 'The password has never been reinitialized.' => 'A jelszó soha sem lett alaphelyzetbe állÃtva.', + 'The password has never been reinitialized.' => 'A jelszó soha sem volt visszaállÃtva.', 'Creation' => 'Létrehozás', 'Expiration' => 'Lejárat', - 'Password reset history' => 'A jelszó alaphelyzetbe állÃtás története', - 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'A "%s" oszlophoz és a "%s" sávhoz tartozó összes feladat sikeresen le lett zárva.', - 'Do you really want to close all tasks of this column?' => 'Valóban le kÃvánja zárni az oszlophoz tartozó összes feladatot?', - '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d feladat lesz lezárva, ezek a "%s" oszlopban és a "%s" sávban vannak.', - 'Close all tasks of this column' => 'Az oszlophoz tartozó összes feladat lezárása', - 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Egy plugin sem regisztrált projekt értesÃtési módszert. Egyedi értesÃtések még mindig beállÃthatók a felhasználói profilban.', - 'My dashboard' => 'Az én dashboard-om', - 'My profile' => 'Az én profilom', - 'Project owner: ' => 'A projekt tulajdonosa: ', - 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'A projekt azonosÃtó opcionális, és kötelezÅ‘en alfanumerikus karakterekbÅ‘l áll, pl: MYPROJECT.', - 'Project owner' => 'Projekt tulajdonos', - 'Private projects do not have users and groups management.' => 'A privát projektekhez nem tartozik felhasználó kezelés és csoport kezelés.', - 'There is no project member.' => 'A projektnek nincs tagja.', + 'Password reset history' => 'Jelszó visszaállÃtásának elÅ‘zményei ', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'A(z) „%s†oszlop és a(z) „%s†sáv összes feladata sikeresen lezárva.', + 'Do you really want to close all tasks of this column?' => 'Valóban le szeretné zárni az oszlop összes feladatát?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d feladat lesz lezárva a(z) „%s†oszlopban és a(z) „%s†sávban.', + 'Close all tasks of this column' => 'Az oszlop összes feladatának lezárása', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Egyetlen bÅ‘vÃtmény sem regisztrált projekt értesÃtési módszert. Egyedi értesÃtéseket továbbra is beállÃthat a felhasználói profiljában.', + 'My dashboard' => 'Saját vezérlÅ‘pult', + 'My profile' => 'Saját profil', + 'Project owner: ' => 'Projekttulajdonos: ', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'A projektazonosÃtó elhagyható, és csak betűk és számok lehetnek, például: MYPROJECT.', + 'Project owner' => 'Projekttulajdonos', + 'Private projects do not have users and groups management.' => 'A személyes projekteknek nincs felhasználó- és csoportkezelése.', + 'There is no project member.' => 'Nincs projekttag.', 'Priority' => 'Prioritás', - 'Task priority' => 'Feladat prioritás', + 'Task priority' => 'Feladat prioritása', 'General' => 'Ãltalános', 'Dates' => 'Dátumok', - 'Default priority' => 'Alap prioritás', + 'Default priority' => 'Alapértelmezett prioritás', 'Lowest priority' => 'Legalacsonyabb prioritás', 'Highest priority' => 'Legmagasabb prioritás', - 'If you put zero to the low and high priority, this feature will be disabled.' => 'Ha nullát ad meg a legalacsonyabb és legmagasabb prioritásként, akkor ez a jellemzÅ‘ letiltásra kerül.', - 'Close a task when there is no activity' => 'A feladat lezárása, ha nincs tevékenység', - 'Duration in days' => 'Hossz napokban', - 'Send email when there is no activity on a task' => 'email küldése, ha egy feladathoz nincs tevékenység', - 'Unable to fetch link information.' => 'A hivatkozás adatainak sikertelen beolvasása', - 'Daily background job for tasks' => 'A feladatokhoz tartozó napi háttér munkák', + 'Close a task when there is no activity' => 'Feladat lezárása, ha nincs tevékenység', + 'Duration in days' => 'IdÅ‘tartam napokban', + 'Send email when there is no activity on a task' => 'E-mail küldése, ha nincs tevékenység egy feladatban', + 'Unable to fetch link information.' => 'Nem lehet lekérni a hivatkozás információit.', + 'Daily background job for tasks' => 'Napi háttérmunka a feladatoknál', 'Auto' => 'Automatikus', 'Related' => 'Kapcsolódó', - 'Attachment' => 'Csatolmány', - 'Title not found' => 'Nincs ilyen cÃm', - 'Web Link' => 'Web hivatkozás', + 'Attachment' => 'Melléklet', + 'Title not found' => 'A cÃm nem található', + 'Web Link' => 'Webes hivatkozás', 'External links' => 'KülsÅ‘ hivatkozások', 'Add external link' => 'KülsÅ‘ hivatkozás hozzáadása', 'Type' => 'TÃpus', @@ -974,364 +939,431 @@ return array( 'Add a new external link' => 'Új külsÅ‘ hivatkozás hozzáadása', 'Edit external link' => 'KülsÅ‘ hivatkozás szerkesztése', 'External link' => 'KülsÅ‘ hivatkozás', - 'Copy and paste your link here...' => 'Másold be a hivatkozást ide...', + 'Copy and paste your link here...' => 'Másolja ki és illessze be a hivatkozást ide…', 'URL' => 'URL', 'Internal links' => 'BelsÅ‘ hivatkozások', - 'Assign to me' => 'Rendeld hozzám', + 'Assign to me' => 'Rendelje hozzám', 'Me' => 'Hozzám', - 'Do not duplicate anything' => 'Semmit se duplázz meg', - 'Projects management' => 'Projekt kezelés', + 'Do not duplicate anything' => 'Ne kettÅ‘zzön semmit sem', + 'Projects management' => 'Projektek kezelése', 'Users management' => 'Felhasználók kezelése', 'Groups management' => 'Csoportok kezelése', 'Create from another project' => 'Létrehozás egy másik projektbÅ‘l', - 'open' => 'nyitva', - 'closed' => 'zárva', + 'open' => 'nyitott', + 'closed' => 'lezárt', 'Priority:' => 'Prioritás:', 'Reference:' => 'Hivatkozás:', - 'Complexity:' => 'Komplexitás:', + 'Complexity:' => 'Bonyolultság:', 'Swimlane:' => 'Sáv:', 'Column:' => 'Oszlop:', 'Position:' => 'PozÃció:', 'Creator:' => 'Létrehozó:', 'Time estimated:' => 'Becsült idÅ‘:', '%s hours' => '%s óra', - 'Time spent:' => 'Eltelt idÅ‘:', + 'Time spent:' => 'Eltöltött idÅ‘:', 'Created:' => 'Létrehozva:', 'Modified:' => 'MódosÃtva:', 'Completed:' => 'Elkészült:', 'Started:' => 'Elindult:', - 'Moved:' => 'Elmozgatva:', + 'Moved:' => 'Ãthelyezve:', 'Task #%d' => '#%d. feladat', - 'Time format' => 'IdÅ‘ formátum', - 'Start date: ' => 'KezdÅ‘ datum: ', - 'End date: ' => 'Vég dátum: ', + 'Time format' => 'IdÅ‘formátum', + 'Start date: ' => 'Kezdési dátum: ', + 'End date: ' => 'Befejezési dátum: ', 'New due date: ' => 'Új határidÅ‘: ', - 'Start date changed: ' => 'KezdÅ‘ dátum megváltozott: ', - 'Disable private projects' => 'Privát projektek tiltása', - 'Do you really want to remove this custom filter: "%s"?' => 'Valóban el kÃvánja távolÃtani ezt az egyedi szűrÅ‘t: "%s"?', - 'Remove a custom filter' => 'Egyedi szűrÅ‘ eltávolÃtása', - 'User activated successfully.' => 'A felhasználó sikeresen aktiválva lett.', - 'Unable to enable this user.' => 'Nem sikerült a felhasználó engedélyezése.', - 'User disabled successfully.' => 'A felhaszáló sikeresen le lett tiltva.', - 'Unable to disable this user.' => 'Nem sikerült a felhasználó letiltása.', - 'All files have been uploaded successfully.' => 'Az összes fájl sikeresen feltöltÅ‘dött.', - 'The maximum allowed file size is %sB.' => 'A fájl max. megengedett mérete %s bájt', - 'Drag and drop your files here' => 'Fogdd-és-vidd módszerrel dobja ide a fájlt', - 'choose files' => 'válasszon fájlt', - 'View profile' => 'Profil megtekintés', - 'Two Factor' => 'Két tényezÅ‘s', - 'Disable user' => 'Felhasználó tiltása', - 'Do you really want to disable this user: "%s"?' => 'Valóban le akarja tiltani ezt a felhasználót: "%s"?', + 'Start date changed: ' => 'A kezdési dátum megváltozott: ', + 'Disable private projects' => 'Személyes projektek letiltása', + 'Do you really want to remove this custom filter: "%s"?' => 'Valóban el szeretné távolÃtani ezt az egyéni szűrÅ‘t: „%sâ€?', + 'Remove a custom filter' => 'Egyéni szűrÅ‘ eltávolÃtása', + 'User activated successfully.' => 'A felhasználó sikeresen aktiválva.', + 'Unable to enable this user.' => 'Nem lehet engedélyezni a felhasználót.', + 'User disabled successfully.' => 'A felhasználó sikeresen letiltva.', + 'Unable to disable this user.' => 'Nem lehet letiltani a felhasználót.', + 'All files have been uploaded successfully.' => 'Az összes fájl sikeresen feltöltve.', + 'The maximum allowed file size is %sB.' => 'A legnagyobb megengedett fájlméret %s bájt.', + 'Drag and drop your files here' => 'Fogd-és-vidd módszerrel húzza ide a fájlokat', + 'choose files' => 'fájlok kiválasztása', + 'View profile' => 'Profil megtekintése', + 'Two Factor' => 'KétlépcsÅ‘s', + 'Disable user' => 'Felhasználó letiltása', + 'Do you really want to disable this user: "%s"?' => 'Valóban le szeretné tiltani ezt a felhasználót: „%sâ€?', 'Enable user' => 'Felhasználó engedélyezése', - 'Do you really want to enable this user: "%s"?' => 'Valóban engedélyezni akarja ezt a felhasználót: "%s"?', + 'Do you really want to enable this user: "%s"?' => 'Valóban engedélyezni szeretné ezt a felhasználót: „%sâ€?', 'Download' => 'Letöltés', 'Uploaded: %s' => 'Feltöltve: %s', - 'Size: %s' => 'méret %s', - 'Uploaded by %s' => 'Feltöltve %s által', + 'Size: %s' => 'Méret: %s', + 'Uploaded by %s' => 'Feltöltötte: %s', 'Filename' => 'Fájlnév', 'Size' => 'Méret', - 'Column created successfully.' => 'Az oszlop sikeresen létrejött', - 'Another column with the same name exists in the project' => 'A projektben már létezik egy ugyanilyen nevű oszlop', - 'Default filters' => 'Alap szűrÅ‘k', - 'Your board doesn\'t have any columns!' => 'A táblának nincsenek oszlopai!', + 'Column created successfully.' => 'Az oszlop sikeresen létrehozva.', + 'Another column with the same name exists in the project' => 'Létezik egy ugyanilyen nevű oszlop a projektben', + 'Default filters' => 'Alapértelmezett szűrÅ‘k', + 'Your board doesn\'t have any columns!' => 'A táblának nincs egyetlen oszlopa sem!', 'Change column position' => 'Oszlop helyzetének megváltoztatása', - 'Switch to the project overview' => 'Projektek áttekintése', - 'User filters' => 'Felhasználók szűrése', - 'Category filters' => 'Kategóriák szűrése', + 'Switch to the project overview' => 'Ãtváltás a projekt áttekintÅ‘re', + 'User filters' => 'Felhasználók szűrÅ‘i', + 'Category filters' => 'Kategóriák szűrÅ‘i', 'Upload a file' => 'Fájl feltöltése', 'View file' => 'Fájl megtekintése', - 'Last activity' => 'Utolsó aktivitás', + 'Last activity' => 'Utolsó tevékenység', 'Change subtask position' => 'Részfeladat helyzetének megváltoztatása', - 'This value must be greater than %d' => 'Ennek az értéknek nagyobbnak kell lennie, mint %d', - 'Another swimlane with the same name exists in the project' => 'A projektben létezik egy ugyanilyen nevű másik sáv', - 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Példa: http://example.kanboard.net/ (ez abszolút URL-ek előállÃtására használható)', - 'Actions duplicated successfully.' => 'A tevékenység sikeresen meg lett duplázva.', - 'Unable to duplicate actions.' => 'A tevékenység megduplázása nem sikerült.', - 'Add a new action' => 'Új tevékenység létrehozása', + 'This value must be greater than %d' => 'Ennek az értéknek nagyobbnak kell lennie mint %d', + 'Another swimlane with the same name exists in the project' => 'Létezik egy ugyanilyen nevű sáv a projektben', + 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Példa: http://example.kanboard.net/ (abszolút URL-ek előállÃtásához használható)', + 'Actions duplicated successfully.' => 'A műveletek sikeresen megkettÅ‘zve.', + 'Unable to duplicate actions.' => 'Nem lehet megkettÅ‘zni a műveleteket.', + 'Add a new action' => 'Új művelet hozzáadása', 'Import from another project' => 'Importálás egy másik projektbÅ‘l', - 'There is no action at the moment.' => 'Jelenleg nincs egy tevkenység sem.', - 'Import actions from another project' => 'Tevékenységek importálása egy másik projektbÅ‘l', - 'There is no available project.' => 'Nincs rendelkezésre álló projekt.', + 'There is no action at the moment.' => 'Jelenleg nincs egyetlen művelet sem.', + 'Import actions from another project' => 'Műveletek importálása egy másik projektbÅ‘l', + 'There is no available project.' => 'Nincs elérhetÅ‘ projekt.', 'Local File' => 'Helyi fájl', 'Configuration' => 'Konfiguráció', - 'PHP version:' => 'PHP verzió:', + 'PHP version:' => 'PHP verziója:', 'PHP SAPI:' => 'PHP SAPI:', - 'OS version:' => 'Operációs rendszer verzió:', - 'Database version:' => 'Adatbázis verzió:', + 'OS version:' => 'Operációs rendszer verziója:', + 'Database version:' => 'Adatbázis verziója:', 'Browser:' => 'BöngészÅ‘:', 'Task view' => 'Feladat nézet', 'Edit task' => 'Feladat szerkesztése', 'Edit description' => 'LeÃrás szerkesztése', 'New internal link' => 'Új belsÅ‘ hivatkozás', - 'Display list of keyboard shortcuts' => 'Billentyű parancsok megjelenÃtése', + 'Display list of keyboard shortcuts' => 'Gyorsbillentyűk listájának megjelenÃtése', 'Menu' => 'Menü', - 'Set start date' => 'KezdÅ‘ idÅ‘pont beállÃtása', + 'Set start date' => 'Kezdési dátum beállÃtása', 'Avatar' => 'Avatár', - 'Upload my avatar image' => 'Kép feltöltése', - 'Remove my image' => 'Kép törése', - 'The OAuth2 state parameter is invalid' => 'Az OAuth2 állapot paraméter érvénytelen', - 'User not found.' => 'Nincs ilyen felhasználó.', - 'Search in activity stream' => 'Keresés a tevékenységek között', - 'My activities' => 'Tevékenységeim', + 'Upload my avatar image' => 'Saját avatárkép feltöltése', + 'Remove my image' => 'Saját kép eltávolÃtása', + 'The OAuth2 state parameter is invalid' => 'Az OAuth2 állapotparaméter érvénytelen', + 'User not found.' => 'A felhasználó nem található.', + 'Search in activity stream' => 'Keresés a tevékenységfolyamban', + 'My activities' => 'Saját tevékenységek', 'Activity until yesterday' => 'Tevékenységek tegnapig', 'Activity until today' => 'Tevékenységek a mai napig', 'Search by creator: ' => 'Keresés a létrehozó szerint: ', - 'Search by creation date: ' => 'Keresés a létrehozás ideje szerint: ', + 'Search by creation date: ' => 'Keresés a létrehozás dátuma szerint: ', 'Search by task status: ' => 'Keresés a feladat állapota szerint: ', 'Search by task title: ' => 'Keresés a feladat cÃme szerint: ', - 'Activity stream search' => 'Tevékenységi láncban történÅ‘ keresés', - 'Projects where "%s" is manager' => 'Azok a projektek, amelyekben "%s" vezetÅ‘', - 'Projects where "%s" is member' => 'Azok a projektek, amelyekben "%s" tag', - 'Open tasks assigned to "%s"' => '"%s" -hez rendelt nyitott feladatok', - 'Closed tasks assigned to "%s"' => '"%s" -hez rendelt lezárt feladatok', - 'Assign automatically a color based on a priority' => 'A prioritás alapján szÃn automatikus hozzárendelése', - 'Overdue tasks for the project(s) "%s"' => 'A "%s" projekt(ek)hez tartozó, határidÅ‘t túllépÅ‘ feladatok', + 'Activity stream search' => 'Tevékenységfolyam keresés', + 'Projects where "%s" is manager' => 'Azok a projektek, ahol „%s†vezetÅ‘', + 'Projects where "%s" is member' => 'Azok a projektek, ahol „%s†tag', + 'Open tasks assigned to "%s"' => '„%s†felhasználóhoz rendelt nyitott feladatok', + 'Closed tasks assigned to "%s"' => '„%s†felhasználóhoz rendelt lezárt feladatok', + 'Assign automatically a color based on a priority' => 'SzÃn automatikus hozzárendelése prioritás alapján', + 'Overdue tasks for the project(s) "%s"' => 'Lejárt feladatok a(z) „%s†projektnél', 'Upload files' => 'Fájlok feltöltése', - 'Installed Plugins' => 'Installált plugin-ek', - 'Plugin Directory' => 'Plugin könyvtár', - 'Plugin installed successfully.' => 'A plugin installálása sikerült.', - 'Plugin updated successfully.' => 'A plugin módosÃtása sikerült.', - 'Plugin removed successfully.' => 'A plugin törlése sikerült.', - 'Subtask converted to task successfully.' => 'Az alfeladat feladattá történÅ‘ átalakÃtása sikerült.', - 'Unable to convert the subtask.' => 'Az alfeladat átalakÃtása nem sikerült.', - 'Unable to extract plugin archive.' => 'A plugin archÃvum kibontása nem sikerült.', - 'Plugin not found.' => 'A plugin nem található.', - 'You don\'t have the permission to remove this plugin.' => 'Nincs joga ennek a plugin-nek az eltávolÃtásához.', - 'Unable to download plugin archive.' => 'A plugin archÃvum letöltése nem sikerült.', - 'Unable to write temporary file for plugin.' => 'A plugin-hez nem sikerült egy átmeneti fájl létrehozása.', - 'Unable to open plugin archive.' => 'Nem sikerült a plugin archÃvum megnyitása.', - 'There is no file in the plugin archive.' => 'A plugin archÃvumban nincs egyetelen egy fájl sem.', + 'Installed Plugins' => 'TelepÃtett bÅ‘vÃtmények', + 'Plugin Directory' => 'BÅ‘vÃtmény könyvtár', + 'Plugin installed successfully.' => 'A bÅ‘vÃtmény sikeresen telepÃtve.', + 'Plugin updated successfully.' => 'A bÅ‘vÃtmény sikeresen frissÃtve.', + 'Plugin removed successfully.' => 'A bÅ‘vÃtmény sikeresen eltávolÃtva.', + 'Subtask converted to task successfully.' => 'A részfeladat sikeresen átalakÃtva feladattá.', + 'Unable to convert the subtask.' => 'Nem lehet átalakÃtani a részfeladatot.', + 'Unable to extract plugin archive.' => 'Nem lehet kibontani a bÅ‘vÃtmény archÃvumot.', + 'Plugin not found.' => 'A bÅ‘vÃtmény nem található.', + 'You don\'t have the permission to remove this plugin.' => 'Nincs jogosultsága eltávolÃtani ezt a bÅ‘vÃtményt.', + 'Unable to download plugin archive.' => 'Nem lehet letölteni a bÅ‘vÃtmény archÃvumot.', + 'Unable to write temporary file for plugin.' => 'Nem lehet Ãrni az átmeneti fájlt a bÅ‘vÃtményhez.', + 'Unable to open plugin archive.' => 'Nem lehet megnyitni a bÅ‘vÃtmény archÃvumot.', + 'There is no file in the plugin archive.' => 'Nincs fájl a bÅ‘vÃtmény archÃvumban.', 'Create tasks in bulk' => 'Feladatok tömeges létrehozása', - 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Az Ön Kanboard példánya úgy lett konfigurálva, hogy ne lehessen plugin-eket installálni a felhasználói felületrÅ‘l.', - 'There is no plugin available.' => 'Nem áll plugin rendelkezésre.', - 'Install' => 'Installálás', - 'Update' => 'MódosÃtás', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'A Kanboard példánya úgy lett beállÃtva, hogy ne lehessen bÅ‘vÃtményeket telepÃteni a felhasználói felületrÅ‘l.', + 'There is no plugin available.' => 'Nincs elérhetÅ‘ bÅ‘vÃtmény.', + 'Install' => 'TelepÃtés', + 'Update' => 'FrissÃtés', 'Up to date' => 'Naprakész', - 'Not available' => 'Nem áll rendelkezésre', - 'Remove plugin' => 'Plugin eltávolÃtása', - 'Do you really want to remove this plugin: "%s"?' => 'Valóban el kÃvánja távolÃtani ezt a plugin-t: "%s"?', - // 'Uninstall' => '', + 'Not available' => 'Nem érhetÅ‘ el', + 'Remove plugin' => 'BÅ‘vÃtmény eltávolÃtása', + 'Do you really want to remove this plugin: "%s"?' => 'Valóban el szeretné távolÃtani ezt a bÅ‘vÃtményt: „%sâ€?', + 'Uninstall' => 'EltávolÃtás', 'Listing' => 'Listázás', 'Metadata' => 'Metaadat', 'Manage projects' => 'Projektek kezelése', - 'Convert to task' => 'Feladattá történÅ‘ átalakÃtás', - 'Convert sub-task to task' => 'Alfeladat feladattá történÅ‘ átalakÃtása', - 'Do you really want to convert this sub-task to a task?' => 'Valóban feladattá akarja átalakÃtani ezt az alfeladatot?', - 'My task title' => 'A feladat cÃme', - 'Enter one task by line.' => 'Minden sorban egy feladatot adjon meg.', - 'Number of failed login:' => 'A sikertelen bejelentkezések száma:', - 'Account locked until:' => 'A számla zárolva a következÅ‘ idÅ‘pontig:', - // 'Email settings' => '', - // 'Email sender address' => '', - // 'Email transport' => '', - // 'Webhook token' => '', - // 'Project tags management' => '', - // 'Tag created successfully.' => '', - // 'Unable to create this tag.' => '', - // 'Tag updated successfully.' => '', - // 'Unable to update this tag.' => '', - // 'Tag removed successfully.' => '', - // 'Unable to remove this tag.' => '', - // 'Global tags management' => '', - // 'Tags' => '', - // 'Tags management' => '', - // 'Add new tag' => '', - // 'Edit a tag' => '', - // 'Project tags' => '', - // 'There is no specific tag for this project at the moment.' => '', - // 'Tag' => '', - // 'Remove a tag' => '', - // 'Do you really want to remove this tag: "%s"?' => '', - // 'Global tags' => '', - // 'There is no global tag at the moment.' => '', - // 'This field cannot be empty' => '', - // 'Close a task when there is no activity in an specific column' => '', - // '%s removed a subtask for the task #%d' => '', - // '%s removed a comment on the task #%d' => '', - // 'Comment removed on task #%d' => '', - // 'Subtask removed on task #%d' => '', - // 'Hide tasks in this column in the dashboard' => '', - // '%s removed a comment on the task %s' => '', - // '%s removed a subtask for the task %s' => '', - // 'Comment removed' => '', - // 'Subtask removed' => '', - // '%s set a new internal link for the task #%d' => '', - // '%s removed an internal link for the task #%d' => '', - // 'A new internal link for the task #%d have been defined' => '', - // 'Internal link removed for the task #%d' => '', - // '%s set a new internal link for the task %s' => '', - // '%s removed an internal link for the task %s' => '', - // 'Automatically set the due date on task creation' => '', - // 'Move the task to another column when closed' => '', - // 'Move the task to another column when not moved during a given period' => '', - // 'Dashboard for %s' => '', - // 'Tasks overview for %s' => '', - // 'Subtasks overview for %s' => '', - // 'Projects overview for %s' => '', - // 'Activity stream for %s' => '', - // 'Calendar for %s' => '', - // 'Notifications for %s' => '', - // 'Assign a color when the task is moved to a specific swimlane' => '', - // 'Assign a priority when the task is moved to a specific swimlane' => '', - // 'User unlocked successfully.' => '', - // 'Unable to unlock the user.' => '', - // 'Move a task to another swimlane' => '', - // 'Creator Name' => '', - // 'Time spent and estimated' => '', - // 'Move position' => '', - // 'Move task to another position on the board' => '', - // 'Insert before this task' => '', - // 'Insert after this task' => '', - // 'Unlock this user' => '', - // 'Custom Project Roles' => '', - // 'Add a new custom role' => '', - // 'Restrictions for the role "%s"' => '', - // 'Add a new project restriction' => '', - // 'Add a new drag and drop restriction' => '', - // 'Add a new column restriction' => '', - // 'Edit this role' => '', - // 'Remove this role' => '', - // 'There is no restriction for this role.' => '', - // 'Only moving task between those columns is permitted' => '', - // 'Close a task in a specific column when not moved during a given period' => '', - // 'Edit columns' => '', - // 'The column restriction has been created successfully.' => '', - // 'Unable to create this column restriction.' => '', - // 'Column restriction removed successfully.' => '', - // 'Unable to remove this restriction.' => '', - // 'Your custom project role has been created successfully.' => '', - // 'Unable to create custom project role.' => '', - // 'Your custom project role has been updated successfully.' => '', - // 'Unable to update custom project role.' => '', - // 'Custom project role removed successfully.' => '', - // 'Unable to remove this project role.' => '', - // 'The project restriction has been created successfully.' => '', - // 'Unable to create this project restriction.' => '', - // 'Project restriction removed successfully.' => '', - // 'You cannot create tasks in this column.' => '', - // 'Task creation is permitted for this column' => '', - // 'Closing or opening a task is permitted for this column' => '', - // 'Task creation is blocked for this column' => '', - // 'Closing or opening a task is blocked for this column' => '', - // 'Task creation is not permitted' => '', - // 'Closing or opening a task is not permitted' => '', - // 'New drag and drop restriction for the role "%s"' => '', - // 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '', - // 'Remove a column restriction' => '', - // 'Do you really want to remove this column restriction: "%s" to "%s"?' => '', - // 'New column restriction for the role "%s"' => '', - // 'Rule' => '', - // 'Do you really want to remove this column restriction?' => '', - // 'Custom roles' => '', - // 'New custom project role' => '', - // 'Edit custom project role' => '', - // 'Remove a custom role' => '', - // 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '', - // 'There is no custom role for this project.' => '', - // 'New project restriction for the role "%s"' => '', - // 'Restriction' => '', - // 'Remove a project restriction' => '', - // 'Do you really want to remove this project restriction: "%s"?' => '', - // 'Duplicate to multiple projects' => '', - // 'This field is required' => '', - // 'Moving a task is not permitted' => '', - // 'This value must be in the range %d to %d' => '', - // 'You are not allowed to move this task.' => '', - // 'API User Access' => '', - // 'Preview' => '', - // 'Write' => '', - // 'Write your text in Markdown' => '', - // 'New External Task: %s' => '', - // 'No personal API access token registered.' => '', - // 'Your personal API access token is "%s"' => '', - // 'Remove your token' => '', - // 'Generate a new token' => '', - // 'Showing %d-%d of %d' => '', - // 'Outgoing Emails' => '', - // 'Add or change currency rate' => '', - // 'Reference currency: %s' => '', - // 'Add custom filters' => '', - // 'Export' => '', - // 'Add link label' => '', - // 'Incompatible Plugins' => '', - // 'Compatibility' => '', - // 'Permissions and ownership' => '', - // 'Priorities' => '', - // 'Close this window' => '', - // 'Unable to upload this file.' => '', - // 'Import tasks' => '', - // 'Choose a project' => '', - // 'Profile' => '', - // 'Application role' => '', - // '%d invitations were sent.' => '', - // '%d invitation was sent.' => '', - // 'Unable to create this user.' => '', - // 'Kanboard Invitation' => '', - // 'Visible on dashboard' => '', - // 'Created at:' => '', - // 'Updated at:' => '', - // 'There is no custom filter.' => '', - // 'New User' => '', - // 'Authentication' => '', - // 'If checked, this user will use a third-party system for authentication.' => '', - // 'The password is necessary only for local users.' => '', - // 'You have been invited to register on Kanboard.' => '', - // 'Click here to join your team' => '', - // 'Invite people' => '', - // 'Emails' => '', - // 'Enter one email address by line.' => '', - // 'Add these people to this project' => '', - // 'Add this person to this project' => '', - // 'Sign-up' => '', - // 'Credentials' => '', - // 'New user' => '', - // 'This username is already taken' => '', - // 'A link to reset your password has been sent by email.' => '', - // 'Your profile must have a valid email address.' => '', - // 'Unfortunately, we are unable to reset your password. Did you entered a valid username? Do you have an email address in your profile?' => '', - // 'TRL - Turkish Lira' => '', - // 'The project email is optional and could be used by several plugins.' => '', - // 'The project email must be unique across all projects' => '', - // 'The email configuration has been disabled by the administrator.' => '', - // 'Close this project' => '', - // 'Open this project' => '', - // 'Close a project' => '', - // 'Do you really want to close this project: "%s"?' => '', - // 'Reopen a project' => '', - // 'Do you really want to reopen this project: "%s"?' => '', - // 'This project is open' => '', - // 'This project is closed' => '', - // 'Unable to upload files, check the permissions of your data folder.' => '', - // 'Another category with the same name exists in this project' => '', - // 'Comment sent by email successfully.' => '', - // 'Sent by email to [%s](mailto:%s) (%s)' => '', - // 'Unable to read uploaded file.' => '', - // 'Database uploaded successfully.' => '', - // 'Task sent by email successfully.' => '', - // 'There is no category in this project.' => '', - // 'Send by email' => '', - // 'Create and send a comment by email' => '', - // 'Subject' => '', - // 'Upload the database' => '', - // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', - // 'Database file' => '', - // 'Upload' => '', - // 'Remove this user from group' => '', - // 'Your project must have at least one active swimlane.' => '', - // 'Project: %s' => '', - // 'Automatic action not found: "%s"' => '', - // '%d projects' => '', - // '%d project' => '', - // 'There is no project.' => '', - // 'Sort' => '', - // 'Project ID' => '', - // 'Project name' => '', - // 'Public' => '', - // 'Private' => '', - // '%d tasks' => '', - // '%d task' => '', - // 'Task ID' => '', - // 'Assign automatically a color when due date is expired' => '', - // 'Total score in this column across all swimlanes' => '', - // 'HRK - Kuna' => '', + 'Convert to task' => 'ÃtalakÃtás feladattá', + 'Convert sub-task to task' => 'Részfeladat átalakÃtása feladattá', + 'Do you really want to convert this sub-task to a task?' => 'Valóban át szeretné alakÃtani ezt a részfeladatot feladattá?', + 'My task title' => 'Saját feladatcÃm', + 'Enter one task by line.' => 'Adjon meg soronként egy feladatot.', + 'Number of failed login:' => 'Sikertelen bejelentkezések száma:', + 'Account locked until:' => 'A fiók zárolva eddig:', + 'Email settings' => 'E-mail beállÃtások', + 'Email sender address' => 'E-mail küldÅ‘cÃme', + 'Email transport' => 'E-mail átviteli módja', + 'Webhook token' => 'Webhurok token', + 'Project tags management' => 'ProjektcÃmkék kezelése', + 'Tag created successfully.' => 'A cÃmke sikeresen létrehozva.', + 'Unable to create this tag.' => 'Nem lehet létrehozni a cÃmkét.', + 'Tag updated successfully.' => 'A cÃmke sikeresen frissÃtve.', + 'Unable to update this tag.' => 'Nem lehet frissÃteni a cÃmkét.', + 'Tag removed successfully.' => 'A cÃmke sikeresen eltávolÃtva.', + 'Unable to remove this tag.' => 'Nem lehet eltávolÃtani a cÃmkét.', + 'Global tags management' => 'Globális cÃmkék kezelése', + 'Tags' => 'CÃmkék', + 'Tags management' => 'CÃmkék kezelése', + 'Add new tag' => 'Új cÃmke hozzáadása', + 'Edit a tag' => 'CÃmke szerkesztése', + 'Project tags' => 'ProjektcÃmkék', + 'There is no specific tag for this project at the moment.' => 'Jelenleg nincs cÃmke megadva ehhez a projekthez.', + 'Tag' => 'CÃmke', + 'Remove a tag' => 'CÃmke eltávolÃtása', + 'Do you really want to remove this tag: "%s"?' => 'Valóban el szeretné távolÃtani ezt a cÃmkét: „%sâ€?', + 'Global tags' => 'Globális cÃmkék', + 'There is no global tag at the moment.' => 'Jelenleg nincs globális cÃmke.', + 'This field cannot be empty' => 'Ez a mezÅ‘ nem lehet üres', + 'Close a task when there is no activity in an specific column' => 'Feladat lezárása, ha nincs tevékenység egy adott oszlopban', + '%s removed a subtask for the task #%d' => '%s eltávolÃtott egy részfeladatot a(z) #%d. feladatból', + '%s removed a comment on the task #%d' => '%s eltávolÃtott egy megjegyzést a(z) #%d. feladatból', + 'Comment removed on task #%d' => 'Megjegyzés eltávolÃtva a(z) #%d. feladatból', + 'Subtask removed on task #%d' => 'Részfeladat eltávolÃtva a(z) #%d. feladatból', + 'Hide tasks in this column in the dashboard' => 'Feladatok elrejtése ebben az oszlopban a vezérlÅ‘pulton', + '%s removed a comment on the task %s' => '%s eltávolÃtott egy megjegyzést a(z) %s feladatból', + '%s removed a subtask for the task %s' => '%s eltávolÃtott egy részfeladatot a(z) %s feladatból', + 'Comment removed' => 'Megjegyzés eltávolÃtva', + 'Subtask removed' => 'Részfeladat eltávolÃtva', + '%s set a new internal link for the task #%d' => '%s beállÃtott egy új belsÅ‘ hivatkozást a(z) #%d. feladathoz', + '%s removed an internal link for the task #%d' => '%s eltávolÃtott egy belsÅ‘ hivatkozást a(z) #%d. feladatból', + 'A new internal link for the task #%d have been defined' => 'Új belsÅ‘ hivatkozás lett meghatározva a(z) #%d. feladathoz', + 'Internal link removed for the task #%d' => 'BelsÅ‘ hivatkozás eltávolÃtva a(z) #%d. feladatból', + '%s set a new internal link for the task %s' => '%s beállÃtott egy új belsÅ‘ hivatkozást a(z) %s feladathoz', + '%s removed an internal link for the task %s' => '%s eltávolÃtott egy belsÅ‘ hivatkozást a(z) %s feladatból', + 'Automatically set the due date on task creation' => 'A határidÅ‘ automatikus beállÃtása a feladat létrehozásakor', + 'Move the task to another column when closed' => 'Feladat áthelyezése egy másik oszlopba, ha lezárják', + 'Move the task to another column when not moved during a given period' => 'Feladat áthelyezése egy másik oszlopba, ha nincs áthelyezve egy adott ideig', + 'Dashboard for %s' => '%s vezérlÅ‘pultja', + 'Tasks overview for %s' => 'Feladatok áttekintése: %s', + 'Subtasks overview for %s' => 'Részfeladatok áttekintése: %s', + 'Projects overview for %s' => 'Projektek áttekintése: %s', + 'Activity stream for %s' => '%s tevékenységfolyama', + 'Assign a color when the task is moved to a specific swimlane' => 'SzÃn hozzárendelése, ha a feladatot áthelyezik egy adott sávba', + 'Assign a priority when the task is moved to a specific swimlane' => 'Prioritás hozzárendelése, ha a feladatot áthelyezik egy adott sávba', + 'User unlocked successfully.' => 'A felhasználó sikeresen feloldva.', + 'Unable to unlock the user.' => 'Nem lehet feloldani a felhasználót.', + 'Move a task to another swimlane' => 'Feladat áthelyezése egy másik sávba', + 'Creator Name' => 'Létrehozó neve', + 'Time spent and estimated' => 'Eltöltött és becsült idÅ‘', + 'Move position' => 'PozÃció áthelyezése', + 'Move task to another position on the board' => 'Feladat áthelyezése egy másik helyre a táblán', + 'Insert before this task' => 'Beszúrás a feladat elé', + 'Insert after this task' => 'Beszúrás a feladat után', + 'Unlock this user' => 'Felhasználó feloldása', + 'Custom Project Roles' => 'Egyéni projektszerepek', + 'Add a new custom role' => 'Új egyéni szerep hozzáadása', + 'Restrictions for the role "%s"' => 'Korlátozások a(z) „%s†szerepnél', + 'Add a new project restriction' => 'Új projektkorlátozás hozzáadása', + 'Add a new drag and drop restriction' => 'Új fogd-és-vidd korlátozás hozzáadása', + 'Add a new column restriction' => 'Új oszlopkorlátozás hozzáadása', + 'Edit this role' => 'Szerep szerkesztése', + 'Remove this role' => 'Szerep eltávolÃtása', + 'There is no restriction for this role.' => 'Nincs korlátozás ennél a szerepnél.', + 'Only moving task between those columns is permitted' => 'Az oszlopok között csak áthelyezés megengedett', + 'Close a task in a specific column when not moved during a given period' => 'Feladat lezárása egy adott oszlopban, ha nem helyezték át egy adott ideig', + 'Edit columns' => 'Oszlopok szerkesztése', + 'The column restriction has been created successfully.' => 'Az oszlopkorlátozás sikeresen létrehozva.', + 'Unable to create this column restriction.' => 'Nem lehet létrehozni az oszlopkorlátozást.', + 'Column restriction removed successfully.' => 'Az oszlopkorlátozás sikeresen eltávolÃtva.', + 'Unable to remove this restriction.' => 'Nem lehet eltávolÃtani a korlátozást.', + 'Your custom project role has been created successfully.' => 'Az egyéni projektszerep sikeresen létrehozva.', + 'Unable to create custom project role.' => 'Nem lehet létrehozni az egyéni projektszerepet.', + 'Your custom project role has been updated successfully.' => 'Az egyéni projektszerep sikeresen frissÃtve.', + 'Unable to update custom project role.' => 'Nem lehet frissÃteni az egyéni projektszerepet.', + 'Custom project role removed successfully.' => 'Az egyéni projektszerep sikeresen eltávolÃtva.', + 'Unable to remove this project role.' => 'Nem lehet eltávolÃtani a projektszerepet.', + 'The project restriction has been created successfully.' => 'A projektkorlátozás sikeresen létrehozva.', + 'Unable to create this project restriction.' => 'Nem lehet létrehozni a projektkorlátozást.', + 'Project restriction removed successfully.' => 'A projektkorlátozás sikeresen eltávolÃtva.', + 'You cannot create tasks in this column.' => 'Nem lehet feladatokat létrehozni ebben az oszlopban.', + 'Task creation is permitted for this column' => 'A feladat létrehozása engedélyezett ennél az oszlopnál', + 'Closing or opening a task is permitted for this column' => 'A feladat lezárása vagy megnyitása engedélyezett ennél az oszlopnál', + 'Task creation is blocked for this column' => 'A feladat létrehozása blokkolva van ennél az oszlopnál', + 'Closing or opening a task is blocked for this column' => 'A feladat lezárása vagy megnyitása blokkolva van ennél az oszlopnál', + 'Task creation is not permitted' => 'A feladat létrehozása nem engedélyezett', + 'Closing or opening a task is not permitted' => 'Feladat lezárása vagy megnyitása nem engedélyezett', + 'New drag and drop restriction for the role "%s"' => 'Új fogd-és-vidd korlátozás a(z) „%s†szerephez', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Az ehhez a szerephez tartozó emberek csak a forrás- és a céloszlopok között helyezhetnek át feladatokat.', + 'Remove a column restriction' => 'Oszlopkorlátozás eltávolÃtása', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Valóban el szeretné távolÃtani ezt az oszlopkorlátozást: „%s†erre: „%sâ€?', + 'New column restriction for the role "%s"' => 'Új oszlopkorlátozás a(z) „%s†szerephez', + 'Rule' => 'Szabály', + 'Do you really want to remove this column restriction?' => 'Valóban el szeretné távolÃtani ezt az oszlopkorlátozást?', + 'Custom roles' => 'Egyéni szerepek', + 'New custom project role' => 'Új egyéni projektszerep', + 'Edit custom project role' => 'Egyéni projektszerep szerkesztése', + 'Remove a custom role' => 'Egyéni szerep eltávolÃtása', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Valóban el szeretné távolÃtani ezt az egyéni szerepet: „%sâ€? A szerephez rendelt összes ember projekttaggá válik.', + 'There is no custom role for this project.' => 'Nincs egyéni szerep ennél a projektnél.', + 'New project restriction for the role "%s"' => 'Új projektkorlátozás a(z) „%s†szerephez', + 'Restriction' => 'Korlátozás', + 'Remove a project restriction' => 'Projektkorlátozás eltávolÃtása', + 'Do you really want to remove this project restriction: "%s"?' => 'Valóban el szeretné távolÃtani ezt a projektkorlátozást: „%sâ€?', + 'Duplicate to multiple projects' => 'Másolás több projektbe', + 'This field is required' => 'Ez a mezÅ‘ kötelezÅ‘', + 'Moving a task is not permitted' => 'A feladat áthelyezése nem engedélyezett', + 'This value must be in the range %d to %d' => 'Ennek az értéknek %d és %d közötti tartományban kell lennie', + 'You are not allowed to move this task.' => 'A feladat áthelyezése nem engedélyezett.', + 'API User Access' => 'API felhasználói hozzáférés', + 'Preview' => 'ElÅ‘nézet', + 'Write' => 'Ãrás', + 'Write your text in Markdown' => 'Ãrja be a szöveget Markdown formátumban', + 'No personal API access token registered.' => 'Nincs személyes API hozzáférési token regisztrálva.', + 'Your personal API access token is "%s"' => 'A személyes API hozzáférési token: „%sâ€', + 'Remove your token' => 'Token eltávolÃtása', + 'Generate a new token' => 'Új token előállÃtása', + 'Showing %d-%d of %d' => '%d-%d / %d megjelenÃtése', + 'Outgoing Emails' => 'KimenÅ‘ e-mailek', + 'Add or change currency rate' => 'Devizaárfolyam hozzáadása vagy megváltoztatása', + 'Reference currency: %s' => 'Bázis pénznem: %s', + 'Add custom filters' => 'Egyéni szűrÅ‘k hozzáadása', + 'Export' => 'Exportálás', + 'Add link label' => 'Hivatkozási cÃmke hozzáadása', + 'Incompatible Plugins' => 'Összeférhetetlen bÅ‘vÃtmények', + 'Compatibility' => 'ÖsszeférhetÅ‘ség', + 'Permissions and ownership' => 'Jogosultságok és tulajdonjog', + 'Priorities' => 'Prioritások', + 'Close this window' => 'Ablak bezárása', + 'Unable to upload this file.' => 'Nem lehet feltölteni a fájlt.', + 'Import tasks' => 'Feladatok importálása', + 'Choose a project' => 'Projekt kiválasztása', + 'Profile' => 'Profil', + 'Application role' => 'Alkalmazás szerep', + '%d invitations were sent.' => '%d meghÃvás elküldve.', + '%d invitation was sent.' => '%d meghÃvás elküldve.', + 'Unable to create this user.' => 'Nem lehet létrehozni a felhasználót.', + 'Kanboard Invitation' => 'Kanboard meghÃvás', + 'Visible on dashboard' => 'Látható a vezérlÅ‘pulton', + 'Created at:' => 'Létrehozva:', + 'Updated at:' => 'FrissÃtve:', + 'There is no custom filter.' => 'Nincs egyéni szűrÅ‘.', + 'New User' => 'Új felhasználó', + 'Authentication' => 'HitelesÃtés', + 'If checked, this user will use a third-party system for authentication.' => 'Ha be van jelölve, akkor ez a felhasználó harmadik fél rendszerét fogja használni a hitelesÃtéshez.', + 'The password is necessary only for local users.' => 'A jelszó csak helyi felhasználóknak szükséges.', + 'You have been invited to register on Kanboard.' => 'MeghÃvták, hogy regisztráljon a Kanboardra.', + 'Click here to join your team' => 'Kattintson ide a csapathoz való csatlakozáshoz', + 'Invite people' => 'Emberek meghÃvása', + 'Emails' => 'E-mailek', + 'Enter one email address by line.' => 'Adjon meg soronként egy e-mail cÃmet.', + 'Add these people to this project' => 'Az emberek hozzáadása a projekthez', + 'Add this person to this project' => 'A személy hozzáadása a projekthez', + 'Sign-up' => 'Regisztráció', + 'Credentials' => 'HitelesÃtési adatok', + 'New user' => 'Új felhasználó', + 'This username is already taken' => 'Ez a felhasználónév már foglalt', + 'A link to reset your password has been sent by email.' => 'A jelszó visszaállÃtásához szükséges hivatkozás elküldésre került e-mailben.', + 'Your profile must have a valid email address.' => 'A profiljának érvényes e-mail cÃmmel kell rendelkeznie.', + 'Unfortunately, we are unable to reset your password. Did you entered a valid username? Do you have an email address in your profile?' => 'Sajnálatos módon nem tudjuk visszaállÃtani a jelszavát. Érvényes felhasználónevet adott meg? Van e-mail cÃm a profiljában?', + 'TRL - Turkish Lira' => 'TRL - török lÃra', + 'The project email is optional and could be used by several plugins.' => 'A projekt e-mail elhagyható, és több bÅ‘vÃtmény is használhatja.', + 'The project email must be unique across all projects' => 'A projekt e-mailnek egyedinek kell lennie a projektek között', + 'The email configuration has been disabled by the administrator.' => 'Az e-mail beállÃtásait letiltotta az adminisztrátor.', + 'Close this project' => 'Projekt bezárása', + 'Open this project' => 'Projekt megnyitása', + 'Close a project' => 'Projekt bezárása', + 'Do you really want to close this project: "%s"?' => 'Valóban le szeretné zárni ezt a projektet: „%sâ€?', + 'Reopen a project' => 'Projekt újranyitása', + 'Do you really want to reopen this project: "%s"?' => 'Valóban újra szeretné nyitni ezt a projektet: „%sâ€?', + 'This project is open' => 'A projekt nyitva van', + 'This project is closed' => 'A projekt le van zárva', + 'Unable to upload files, check the permissions of your data folder.' => 'Nem lehet feltölteni fájlokat. EllenÅ‘rizze az adatkönyvtár jogosultságait.', + 'Another category with the same name exists in this project' => 'Már létezik egy ugyanilyen nevű kategória ebben a projektben', + 'Comment sent by email successfully.' => 'A megjegyzés sikeresen elküldve e-mailben.', + 'Sent by email to [%s](mailto:%s) (%s)' => 'E-mail küldve neki: [%s](mailto:%s) (%s)', + 'Unable to read uploaded file.' => 'Nem lehet beolvasni a feltöltött fájlt.', + 'Database uploaded successfully.' => 'Az adatbázis sikeresen feltöltve.', + 'Task sent by email successfully.' => 'A feladat sikeresen elküldve e-mailben.', + 'There is no category in this project.' => 'Nincs kategória ebben a projektben.', + 'Send by email' => 'Küldés e-mailben', + 'Create and send a comment by email' => 'Megjegyzés létrehozása és küldése e-mailben', + 'Subject' => 'Tárgy', + 'Upload the database' => 'Az adatbázis feltöltése', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Feltöltheti az elÅ‘zÅ‘leg letöltött Sqlite adatbázist (Gzip formátumban).', + 'Database file' => 'Adatbázisfájl', + 'Upload' => 'Feltöltés', + 'Your project must have at least one active swimlane.' => 'A projektnek legalább egy aktÃv sávval kell rendelkeznie.', + 'Project: %s' => 'Projekt: %s', + 'Automatic action not found: "%s"' => 'Az automatikus művelet nem található: „%sâ€', + '%d projects' => '%d projekt', + '%d project' => '%d projekt', + 'There is no project.' => 'Nincs projekt.', + 'Sort' => 'Rendezés', + 'Project ID' => 'ProjektazonosÃtó', + 'Project name' => 'Projektnév', + 'Public' => 'Nyilvános', + 'Private' => 'Személyes', + '%d tasks' => '%d feladat', + '%d task' => '%d feladat', + 'Task ID' => 'FeladatazonosÃtó', + 'Assign automatically a color when due date is expired' => 'SzÃn automatikus hozzárendelése, ha a határidÅ‘ lejárt', + 'Total score in this column across all swimlanes' => 'Összes pontszám ebben az oszlopban minden sávból', + 'HRK - Kuna' => 'HRK - horvát kuna', + 'ARS - Argentine Peso' => 'ARS - argentin peso', + 'COP - Colombian Peso' => 'COP - kolumbiai peso', + '%d groups' => '%d csoport', + '%d group' => '%d csoport', + 'Group ID' => 'CsoportazonosÃtó', + 'External ID' => 'KülsÅ‘ azonosÃtó', + '%d users' => '%d felhasználó', + '%d user' => '%d felhasználó', + 'Hide subtasks' => 'Részfeladatok elrejtése', + 'Show subtasks' => 'Részfeladatok megjelenÃtése', + 'Authentication Parameters' => 'HitelesÃtési paraméterek', + 'API Access' => 'API hozzáférés', + 'No users found.' => 'Nem találhatók felhasználók.', + 'User ID' => 'Felhasználó-azonosÃtó', + 'Notifications are activated' => 'ÉrtesÃtések aktiválva', + 'Notifications are disabled' => 'ÉrtesÃtések letiltva', + 'User disabled' => 'Felhasználó letiltva', + '%d notifications' => '%d értesÃtés', + '%d notification' => '%d értesÃtés', + 'There is no external integration installed.' => 'Nincs külsÅ‘ integráció telepÃtve.', + 'You are not allowed to update tasks assigned to someone else.' => 'Nem engedélyezett a valaki máshoz rendelt feladatok frissÃtése.', + 'You are not allowed to change the assignee.' => 'Nem engedélyezett a felelÅ‘s megváltoztatása.', + 'Task suppression is not permitted' => 'A feladatok eltávolÃtása nem engedélyezett', + 'Changing assignee is not permitted' => 'A felelÅ‘s megváltoztatása nem engedélyezett', + 'Update only assigned tasks is permitted' => 'Csak a hozzárendelt feladatok frissÃtése engedélyezett', + 'Only for tasks assigned to the current user' => 'Csak az aktuális felhasználóhoz rendelt feladatoknál', + 'My projects' => 'Saját projektek', + 'Your are not member of any project.' => 'Ön nem tagja egyetlen projektnek sem.', + 'My subtasks' => 'Saját részfeladatok', + '%d subtasks' => '%d részfeladat', + '%d subtask' => '%d részfeladat', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Az aktuális felhasználóhoz rendelt feladatok csak az engedélyezett oszlopok között helyezhetÅ‘k át', + '[DUPLICATE]' => '[KETTÅZÖTT]', + 'DKK - Danish Krona' => 'DKK - dán korona', + 'Remove user from group' => 'Felhasználó eltávolÃtása a csoportból', + 'Assign the task to its creator' => 'Feladat hozzárendelése a létrehozójához', + 'This task was sent by email to "%s" with subject "%s".' => 'A feladat elküldésre került e-mailben ide: „%sâ€, a következÅ‘ tárggyal: „%sâ€.', + 'Predefined Email Subjects' => 'ElÅ‘re meghatározott e-mail tárgyak', + 'Write one subject by line.' => 'Ãrjon be soronként egy tárgyat.', + 'Create another link' => 'Másik hivatkozás létrehozása', + 'BRL - Brazilian Real' => 'BRL - brazil real', + 'Add a new Kanboard task' => 'Új Kanboard feladat hozzáadása', + 'Subtask not started' => 'A részfeladat nincs elindÃtva', + 'Subtask currently in progress' => 'A részfeladat jelenleg folyamatban van', + 'Subtask completed' => 'A részfeladat befejezÅ‘dött', + 'Subtask added successfully.' => 'A részfeladat sikeresen hozzáadva.', + '%d subtasks added successfully.' => '%d részfeladat sikeresen hozzáadva.', + 'Enter one subtask by line.' => 'Adjon meg soronként egy részfeladatot.', + 'Predefined Contents' => 'ElÅ‘re meghatározott tartalmak', + 'Predefined contents' => 'ElÅ‘re meghatározott tartalmak', + 'Predefined Task Description' => 'ElÅ‘re meghatározott feladatleÃrás', + 'Do you really want to remove this template? "%s"' => 'Valóban el szeretné távolÃtani ezt a sablont: „%sâ€?', + 'Add predefined task description' => 'ElÅ‘re meghatározott feladatleÃrás hozzáadása', + 'Predefined Task Descriptions' => 'ElÅ‘re meghatározott feladatleÃrások', + 'Template created successfully.' => 'A sablon sikeresen létrehozva.', + 'Unable to create this template.' => 'Nem lehet létrehozni a sablont.', + 'Template updated successfully.' => 'A sablon sikeresen frissÃtve.', + 'Unable to update this template.' => 'Nem lehet frissÃteni a sablont.', + 'Template removed successfully.' => 'A sablon sikeresen eltávolÃtva.', + 'Unable to remove this template.' => 'Nem lehet eltávolÃtani a sablont.', + 'Template for the task description' => 'Sablon a feladatleÃráshoz', + 'The start date is greater than the end date' => 'A kezdési dátum nagyobb mint a befejezési dátum', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/id_ID/translations.php b/app/Locale/id_ID/translations.php index ab079f82..6f9f5d02 100644 --- a/app/Locale/id_ID/translations.php +++ b/app/Locale/id_ID/translations.php @@ -39,7 +39,6 @@ return array( 'Administrator' => 'Administrator', 'Sign in' => 'Masuk', 'Users' => 'Pengguna', - 'No user' => 'Tidak ada pengguna', 'Forbidden' => 'Terlarang', 'Access Forbidden' => 'Akses Dilarang', 'Edit user' => 'Edit pengguna', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'Sub-tugas berhasil diperbarui.', 'Unable to update your sub-task.' => 'Tidak dapat memperbarui sub-tugas Anda.', 'Unable to create your sub-task.' => 'Tidak dapat membuat sub-tugas Anda.', - 'Sub-task added successfully.' => 'Sub-tugas berhasil dibuat.', 'Maximum size: ' => 'Ukuran maksimum: ', 'Display another project' => 'Lihat proyek lain', 'Created by %s' => 'Dibuat oleh %s', @@ -396,22 +394,17 @@ return array( 'Activity stream' => 'Arus aktifitas', 'Dashboard' => 'Dasbor', 'Confirmation' => 'Konfirmasi', - 'Allow everybody to access to this project' => 'Izinkan semua orang untuk mengakses proyek ini', - 'Everybody have access to this project.' => 'Semua orang mendapat akses untuk proyek ini.', 'Webhooks' => 'Webhooks', 'API' => 'API', 'Create a comment from an external provider' => 'Buat komentar dari penyedia eksternal', 'Project management' => 'Manajemen proyek', - 'My projects' => 'Proyek saya', 'Columns' => 'Kolom', 'Task' => 'Tugas', - 'Your are not member of any project.' => 'Anda bukan anggota dari proyek apapun.', 'Percentage' => 'Persentasi', 'Number of tasks' => 'Jumlah dari tugas', 'Task distribution' => 'Pembagian tugas', 'Analytics' => 'Analitik', 'Subtask' => 'Sub-tugas', - 'My subtasks' => 'Sub-tugas saya', 'User repartition' => 'Partisi ulang pengguna', 'Clone this project' => 'Klon proyek ini', 'Column removed successfully.' => 'Kolom berhasil dihapus.', @@ -459,7 +452,6 @@ return array( 'Language:' => 'Bahasa:', 'Timezone:' => 'Zona waktu:', 'All columns' => 'Semua kolom', - 'Calendar' => 'Kalender', 'Next' => 'Selanjutnya', '#%d' => '#%d', 'All swimlanes' => 'Semua swimlane', @@ -557,7 +549,6 @@ return array( 'Unable to add this currency rate.' => 'Tidak dapat menambahkan nilai tukar mata uang', 'Webhook URL' => 'URL Webhook', '%s removed the assignee of the task %s' => '%s menghapus penugasan dari tugas %s', - 'Enable Gravatar images' => 'Aktifkan gambar Gravatar', 'Information' => 'Informasi', 'Check two factor authentication code' => 'Cek dua faktor kode otentifikasi', 'The two factor authentication code is not valid.' => 'Kode dua faktor kode otentifikasi tidak sesuai.', @@ -611,14 +602,7 @@ return array( 'When task is moved from first column' => 'Saat tugas dipindahkan dari kolom pertama', 'When task is moved to last column' => 'Saat tugas dipindahkan ke kolom terakhir', 'Year(s)' => 'Tahun', - 'Calendar settings' => 'Pengaturan kalender', - 'Project calendar view' => 'Tampilan kalender proyek', 'Project settings' => 'Pengaturan proyek', - 'Show subtasks based on the time tracking' => 'Tampilkan sub-tugas berdasarkan pelacakan waktu', - 'Show tasks based on the creation date' => 'Tampilkan tugas berdasarkan tanggal pembuatan', - 'Show tasks based on the start date' => 'Tampilkan tugas berdasarkan tanggal mulai', - 'Subtasks time tracking' => 'Pelacakan waktu sub-tugas', - 'User calendar view' => 'Tampilan kalender pengguna', 'Automatically update the start date' => 'Otomatis memperbarui tanggal permulaan', 'iCal feed' => 'Umpan iCal', 'Preferences' => 'Preferensi', @@ -637,7 +621,6 @@ return array( 'Notification' => 'Pemberitahuan', '%s moved the task #%d to the first swimlane' => '%s memindahkan tugas #%d ke swimlane pertama', 'Swimlane' => 'Swimlane', - 'Gravatar' => 'Gravatar', '%s moved the task %s to the first swimlane' => '%s memindahkan tugas %s ke swimlane pertama', '%s moved the task %s to the swimlane "%s"' => '%s memindahkan tugas %s ke swimlane "%s"', 'This report contains all subtasks information for the given date range.' => 'Laporan ini berisi semua informasi sub-tugas untuk rentang tanggal tertentu.', @@ -674,7 +657,6 @@ return array( 'Stop timer' => 'Hentikan timer', 'Start timer' => 'Mulai timer', 'My activity stream' => 'Aliran kegiatan saya', - 'My calendar' => 'Kalender saya', 'Search tasks' => 'Cari tugas', 'Reset filters' => 'Reset saringan', 'My tasks due tomorrow' => 'Tugas saya yang berakhir besok', @@ -688,7 +670,6 @@ return array( 'Overview' => 'Ringkasan', 'Board/Calendar/List view' => 'Tampilan Papan/Kalender/Daftar', 'Switch to the board view' => 'Beralih ke tampilan papan', - 'Switch to the calendar view' => 'Beralih ke tampilan kalender', 'Switch to the list view' => 'Beralih ke tampilan daftar', 'Go to the search/filter box' => 'Pergi ke kotak pencarian/saringan', 'There is no activity yet.' => 'Belum ada aktifitas.', @@ -743,15 +724,8 @@ return array( 'License:' => 'Lisensi:', 'License' => 'Lisensi', 'Enter the text below' => 'Masukkan teks di bawah', - 'Sort by position' => 'Urutkan berdasarkan posisi', - 'Sort by date' => 'Urutkan berdasarkan tanggal', - 'Add task' => 'Tambah tugas', 'Start date:' => 'Tanggal mulai:', 'Due date:' => 'Batas waktu:', - 'There is no start date or due date for this task.' => 'Tidak ada tanggal mulai dan batas waktu untuk tugas ini.', - 'Moving or resizing a task will change the start and due date of the task.' => 'Memindahkan atau mengubah ukuran tugas anda akan mengubah tanggal mulai dan batas waktu dari tugas ini.', - 'There is no task in your project.' => 'Tidak ada tugas didalam proyek anda.', - 'Gantt chart' => 'Grafik Gantt', 'People who are project managers' => 'Orang-orang yang menjadi manajer proyek', 'People who are project members' => 'Orang-orang yang menjadi anggota proyek', 'NOK - Norwegian Krone' => 'NOK - Krone Norwegia', @@ -763,22 +737,15 @@ return array( 'Members' => 'Anggota', 'Shared project' => 'Proyek bersama', 'Project managers' => 'Manajer proyek', - 'Gantt chart for all projects' => 'Grafik Gantt untuk semua proyek', 'Projects list' => 'Daftar proyek', - 'Gantt chart for this project' => 'Grafik Gantt untuk proyek ini', - 'Project board' => 'Papan proyek', 'End date:' => 'Waktu berakhir:', - 'There is no start date or end date for this project.' => 'Tidak ada waktu mulai atau waktu berakhir untuk proyek ini', - 'Projects Gantt chart' => 'Grafik Gantt proyek', 'Change task color when using a specific task link' => 'Ganti warna tugas ketika menggunakan tautan tugas yang spesifik', 'Task link creation or modification' => 'Tautan pembuatan atau modifikasi tugas ', 'Milestone' => 'Milestone', 'Documentation: %s' => 'Dokumentasi: %s', - 'Switch to the Gantt chart view' => 'Beralih ke tampilan grafik Gantt', 'Reset the search/filter box' => 'Reset kotak pencarian/saringan', 'Documentation' => 'Dokumentasi', 'Table of contents' => 'Daftar isi', - 'Gantt' => 'Gantt', 'Author' => 'Penulis', 'Version' => 'Versi', 'Plugins' => 'Plugin', @@ -889,7 +856,6 @@ return array( 'There is no user available.' => 'Tidak ada pengguna yang tersedia', 'Do you really want to remove the user "%s" from the group "%s"?' => 'Anda yakin mau menghapus pengguna "%s" dari grup "%s"?', 'There is no group.' => 'Tidak ada grup', - 'External Id' => 'ID Eksternal', 'Add group member' => 'Tambah anggota grup', 'Do you really want to remove this group: "%s"?' => 'Anda yakin mau menghapus grup ini: "%s"?', 'There is no user in this group.' => 'Tidak ada pengguna dalam grup ini', @@ -955,7 +921,6 @@ return array( 'Default priority' => 'Prioritas default', 'Lowest priority' => 'Prioritas terendah', 'Highest priority' => 'Prioritas tertinggi', - 'If you put zero to the low and high priority, this feature will be disabled.' => 'Jika anda meletakkan nol untuk prioritas rendah dan tinggi, fitur ini akan dinonaktifkan.', 'Close a task when there is no activity' => 'Tutup tugas jika tidak ada aktifitas', 'Duration in days' => 'Durasi dalam hari', 'Send email when there is no activity on a task' => 'Kirim email jika tidak ada aktifitas dalam tugas', @@ -1169,8 +1134,6 @@ return array( 'Subtasks overview for %s' => 'Ringkasan sub-tugas untuk %s', 'Projects overview for %s' => 'Ringkasan proyek untuk %s', 'Activity stream for %s' => 'Arus aktivitas untuk %s', - 'Calendar for %s' => 'Kalender untuk %s', - 'Notifications for %s' => 'Notifikasi untuk %s', 'Assign a color when the task is moved to a specific swimlane' => 'Berikan warna saat tugas dipindahkan ke swimlane tertentu', 'Assign a priority when the task is moved to a specific swimlane' => 'Berikan prioritas saat tugas dipindahkan ke swimlane tertentu', 'User unlocked successfully.' => 'Berhasil membuka blokir pengguna.', @@ -1241,7 +1204,6 @@ return array( 'Preview' => 'Pratinjau', 'Write' => 'Tulis', 'Write your text in Markdown' => 'Tuliskan teks Anda di Markdown', - 'New External Task: %s' => 'Tugas Eksternal Baru: %s', 'No personal API access token registered.' => 'Tidak ada token akses API personal yang terdaftar.', 'Your personal API access token is "%s"' => 'Token akses API personal Anda adalah "%s"', 'Remove your token' => 'Hapus token Anda', @@ -1316,7 +1278,6 @@ return array( // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', // 'Database file' => '', // 'Upload' => '', - // 'Remove this user from group' => '', // 'Your project must have at least one active swimlane.' => '', // 'Project: %s' => '', // 'Automatic action not found: "%s"' => '', @@ -1334,4 +1295,75 @@ return array( // 'Assign automatically a color when due date is expired' => '', // 'Total score in this column across all swimlanes' => '', // 'HRK - Kuna' => '', + // 'ARS - Argentine Peso' => '', + // 'COP - Colombian Peso' => '', + // '%d groups' => '', + // '%d group' => '', + // 'Group ID' => '', + // 'External ID' => '', + // '%d users' => '', + // '%d user' => '', + // 'Hide subtasks' => '', + // 'Show subtasks' => '', + // 'Authentication Parameters' => '', + // 'API Access' => '', + // 'No users found.' => '', + // 'User ID' => '', + // 'Notifications are activated' => '', + // 'Notifications are disabled' => '', + // 'User disabled' => '', + // '%d notifications' => '', + // '%d notification' => '', + // 'There is no external integration installed.' => '', + // 'You are not allowed to update tasks assigned to someone else.' => '', + // 'You are not allowed to change the assignee.' => '', + // 'Task suppression is not permitted' => '', + // 'Changing assignee is not permitted' => '', + // 'Update only assigned tasks is permitted' => '', + // 'Only for tasks assigned to the current user' => '', + // 'My projects' => '', + // 'Your are not member of any project.' => '', + // 'My subtasks' => '', + // '%d subtasks' => '', + // '%d subtask' => '', + // 'Only moving task between those columns is permitted for tasks assigned to the current user' => '', + // '[DUPLICATE]' => '', + // 'DKK - Danish Krona' => '', + // 'Remove user from group' => '', + // 'Assign the task to its creator' => '', + // 'This task was sent by email to "%s" with subject "%s".' => '', + // 'Predefined Email Subjects' => '', + // 'Write one subject by line.' => '', + // 'Create another link' => '', + // 'BRL - Brazilian Real' => '', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/it_IT/translations.php b/app/Locale/it_IT/translations.php index 727d8237..b7e11f75 100644 --- a/app/Locale/it_IT/translations.php +++ b/app/Locale/it_IT/translations.php @@ -39,7 +39,6 @@ return array( 'Administrator' => 'Amministratore', 'Sign in' => 'Accedi', 'Users' => 'Utenti', - 'No user' => 'Nessun utente', 'Forbidden' => 'Vietato', 'Access Forbidden' => 'Accesso vietato', 'Edit user' => 'Modifica un utente', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'Sotto-task aggiornato con successo.', 'Unable to update your sub-task.' => 'Impossibile aggiornare il tuo sotto-task.', 'Unable to create your sub-task.' => 'Impossibile creare il tuo sotto-task.', - 'Sub-task added successfully.' => 'Sotto-task aggiunto con successo.', 'Maximum size: ' => 'Dimensioni massime: ', 'Display another project' => 'Mostra un altro progetto', 'Created by %s' => 'Creato da %s', @@ -396,22 +394,17 @@ return array( 'Activity stream' => 'Flusso attività ', 'Dashboard' => 'Bacheca', 'Confirmation' => 'Conferma', - 'Allow everybody to access to this project' => 'Abilita tutti ad accedere a questo progetto', - 'Everybody have access to this project.' => 'Tutti hanno accesso a questo progetto.', // 'Webhooks' => '', // 'API' => '', 'Create a comment from an external provider' => 'Crea un commit da un provider esterno', 'Project management' => 'Gestione progetti', - 'My projects' => 'I miei progetti', 'Columns' => 'Colonne', 'Task' => 'Task', - 'Your are not member of any project.' => 'Non sei membro di alcun progetto.', 'Percentage' => 'Percentuale', 'Number of tasks' => 'Numero di task', 'Task distribution' => 'Distribuzione dei task', // 'Analytics' => '', 'Subtask' => 'Sotto-task', - 'My subtasks' => 'I miei sotto-task', 'User repartition' => 'Ripartizione per utente', 'Clone this project' => 'Clona questo progetto', 'Column removed successfully.' => 'Colonna rimossa con successo', @@ -459,7 +452,6 @@ return array( 'Language:' => 'Lingua', 'Timezone:' => 'Fuso Orario', 'All columns' => 'Tutte le colonne', - 'Calendar' => 'Calendario', 'Next' => 'Prossimo', // '#%d' => '', 'All swimlanes' => 'Tutte le corsie', @@ -557,7 +549,6 @@ return array( 'Unable to add this currency rate.' => 'Impossibile aggiungere questo tasso di cambio.', 'Webhook URL' => 'URL Webhook', '%s removed the assignee of the task %s' => '%s rimuove l\'assegnatario del task %s', - 'Enable Gravatar images' => 'Abilita immagini Gravatar', 'Information' => 'Informazioni', 'Check two factor authentication code' => 'Controlla il codice di autenticazione "two-factor"', 'The two factor authentication code is not valid.' => 'Il codice di autenticazione "two-factor" non è valido', @@ -611,14 +602,7 @@ return array( 'When task is moved from first column' => 'Quando un task è spostato dalla prima colonna', 'When task is moved to last column' => 'Quando un task è spostato nell\'ultima colonna', 'Year(s)' => 'Anno/i', - 'Calendar settings' => 'Impostazioni del calendario', - 'Project calendar view' => 'Vista di progetto a calendario', 'Project settings' => 'Impostazioni di progetto', - 'Show subtasks based on the time tracking' => 'Mostra i sotto-task in base al time tracking', - 'Show tasks based on the creation date' => 'Mostra i task in base alla data di creazione', - 'Show tasks based on the start date' => 'Mostra i task in base alla data di inizio', - 'Subtasks time tracking' => 'Time tracking per i sotto-task', - 'User calendar view' => 'Vista utente a calendario', 'Automatically update the start date' => 'Aggiorna automaticamente la data di inizio', 'iCal feed' => 'feed iCal', 'Preferences' => 'Preferenze', @@ -637,7 +621,6 @@ return array( 'Notification' => 'Notifica', '%s moved the task #%d to the first swimlane' => '%s ha spostato il task #%d nella prima corsia', 'Swimlane' => 'Corsia', - // 'Gravatar' => '', '%s moved the task %s to the first swimlane' => '%s ha spostato il task %s nella prima corsia', '%s moved the task %s to the swimlane "%s"' => '%s ha spostato il task %s nella corsia %s', 'This report contains all subtasks information for the given date range.' => 'Questo report contiente tutte le informazioni sui sotto-task nell\'arco temporale indicato.', @@ -674,7 +657,6 @@ return array( 'Stop timer' => 'Ferma il timer', 'Start timer' => 'Avvia il timer', 'My activity stream' => 'Il mio flusso attività ', - 'My calendar' => 'Il mio calendario', 'Search tasks' => 'Ricerca task', 'Reset filters' => 'Annulla filtri', 'My tasks due tomorrow' => 'I miei task da completare per domani', @@ -688,7 +670,6 @@ return array( 'Overview' => 'Panoramica', 'Board/Calendar/List view' => 'Vista Bacheca/Calendario/Lista', 'Switch to the board view' => 'Passa alla vista "bacheca"', - 'Switch to the calendar view' => 'Passa alla vista "calendario"', 'Switch to the list view' => 'Passa alla vista "elenco"', 'Go to the search/filter box' => 'Vai alla casella di ricerca/filtro', 'There is no activity yet.' => 'Non è presente ancora nessuna attività .', @@ -743,15 +724,8 @@ return array( 'License:' => 'Licenza:', 'License' => 'Licenza', 'Enter the text below' => 'Inserisci il testo qui sotto', - 'Sort by position' => 'Ordina per posizione', - 'Sort by date' => 'Ordina per data', - 'Add task' => 'Aggiungi task', 'Start date:' => 'Data di inizio:', 'Due date:' => 'Data di completamento:', - 'There is no start date or due date for this task.' => 'Nessuna data di inizio o di scadenza per questo task.', - 'Moving or resizing a task will change the start and due date of the task.' => 'Spostando o ridimensionado un task ne modifca la data di inizio e di scadenza.', - 'There is no task in your project.' => 'Non ci sono task nel tuo progetto.', - 'Gantt chart' => 'Grafici Gantt', 'People who are project managers' => 'Persone che sono manager di progetto', 'People who are project members' => 'Persone che sono membri di progetto', 'NOK - Norwegian Krone' => 'NOK - Corone norvegesi', @@ -763,22 +737,15 @@ return array( 'Members' => 'Membri', 'Shared project' => 'Progetto condiviso', 'Project managers' => 'Manager di progetto', - 'Gantt chart for all projects' => 'Grafico Gantt per tutti i progetti', 'Projects list' => 'Elenco progetti', - 'Gantt chart for this project' => 'Grafico Gantt per questo progetto', - 'Project board' => 'Bacheca del progetto', 'End date:' => 'Data di fine:', - 'There is no start date or end date for this project.' => 'Non è prevista una data di inizio o fine per questo progetto.', - 'Projects Gantt chart' => 'Grafico Gantt dei progetti', 'Change task color when using a specific task link' => 'Cambia colore del task quando si un utilizza una determinata relazione di task', 'Task link creation or modification' => 'Creazione o modifica di relazione di task', // 'Milestone' => '', 'Documentation: %s' => 'Documentazione: %s', - 'Switch to the Gantt chart view' => 'Passa alla vista Grafico Gantt', 'Reset the search/filter box' => 'Resetta la riceca/filtro', 'Documentation' => 'Documentazione', 'Table of contents' => 'Indice dei contenuti', - 'Gantt' => 'Gantt', 'Author' => 'Autore', 'Version' => 'Versione', 'Plugins' => 'Plugin', @@ -889,7 +856,6 @@ return array( 'There is no user available.' => 'Nessun utente disponibile.', 'Do you really want to remove the user "%s" from the group "%s"?' => 'Vuoi davvero rimuovere l\'utente "%s" dal gruppo "%s"?', 'There is no group.' => 'Nessun gruppo presente', - 'External Id' => 'Id esterno', 'Add group member' => 'Aggiungi un membro del gruppo', 'Do you really want to remove this group: "%s"?' => 'Vuoi davvero rimuovere questo gruppo: "%s"?', 'There is no user in this group.' => 'Nessun utente in questo gruppo.', @@ -955,7 +921,6 @@ return array( 'Default priority' => 'Priorità predefinita', 'Lowest priority' => 'Priorità minima', 'Highest priority' => 'Priorità massima', - 'If you put zero to the low and high priority, this feature will be disabled.' => 'Se imposti a zero la priorità massima e minima, questa funzionalità sarà disabilitata.', 'Close a task when there is no activity' => 'Chiudi un task quando non c\'è nessuna attività ', 'Duration in days' => 'Durata in giorni', 'Send email when there is no activity on a task' => 'Invia un\'email quando non c\'è attività sul task', @@ -1169,8 +1134,6 @@ return array( 'Subtasks overview for %s' => 'Panoramica tasks per %s', 'Projects overview for %s' => 'Panoramica progetti per %s', 'Activity stream for %s' => 'Flusso attività per %s', - 'Calendar for %s' => 'Calendario per %s', - 'Notifications for %s' => 'Notifiche per %s', 'Assign a color when the task is moved to a specific swimlane' => 'Assegna un colore quando il task viene spostato su una specifica swimlane', 'Assign a priority when the task is moved to a specific swimlane' => 'Assegna una priorità quando il task viene spostato su una specifica swimlane', 'User unlocked successfully.' => 'Utente sbloccato con successo.', @@ -1241,7 +1204,6 @@ return array( // 'Preview' => '', // 'Write' => '', // 'Write your text in Markdown' => '', - // 'New External Task: %s' => '', // 'No personal API access token registered.' => '', // 'Your personal API access token is "%s"' => '', // 'Remove your token' => '', @@ -1316,7 +1278,6 @@ return array( // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', // 'Database file' => '', // 'Upload' => '', - // 'Remove this user from group' => '', // 'Your project must have at least one active swimlane.' => '', // 'Project: %s' => '', // 'Automatic action not found: "%s"' => '', @@ -1334,4 +1295,75 @@ return array( // 'Assign automatically a color when due date is expired' => '', // 'Total score in this column across all swimlanes' => '', // 'HRK - Kuna' => '', + // 'ARS - Argentine Peso' => '', + // 'COP - Colombian Peso' => '', + // '%d groups' => '', + // '%d group' => '', + // 'Group ID' => '', + // 'External ID' => '', + // '%d users' => '', + // '%d user' => '', + // 'Hide subtasks' => '', + // 'Show subtasks' => '', + // 'Authentication Parameters' => '', + // 'API Access' => '', + // 'No users found.' => '', + // 'User ID' => '', + // 'Notifications are activated' => '', + // 'Notifications are disabled' => '', + // 'User disabled' => '', + // '%d notifications' => '', + // '%d notification' => '', + // 'There is no external integration installed.' => '', + // 'You are not allowed to update tasks assigned to someone else.' => '', + // 'You are not allowed to change the assignee.' => '', + // 'Task suppression is not permitted' => '', + // 'Changing assignee is not permitted' => '', + // 'Update only assigned tasks is permitted' => '', + // 'Only for tasks assigned to the current user' => '', + // 'My projects' => '', + // 'Your are not member of any project.' => '', + // 'My subtasks' => '', + // '%d subtasks' => '', + // '%d subtask' => '', + // 'Only moving task between those columns is permitted for tasks assigned to the current user' => '', + // '[DUPLICATE]' => '', + // 'DKK - Danish Krona' => '', + // 'Remove user from group' => '', + // 'Assign the task to its creator' => '', + // 'This task was sent by email to "%s" with subject "%s".' => '', + // 'Predefined Email Subjects' => '', + // 'Write one subject by line.' => '', + // 'Create another link' => '', + // 'BRL - Brazilian Real' => '', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/ja_JP/translations.php b/app/Locale/ja_JP/translations.php index a4e571eb..69162e2a 100644 --- a/app/Locale/ja_JP/translations.php +++ b/app/Locale/ja_JP/translations.php @@ -1,11 +1,11 @@ <?php return array( - // 'number.decimals_separator' => '', - // 'number.thousands_separator' => '', + 'number.decimals_separator' => 'å°æ•°ç‚¹ã®åŒºåˆ‡ã‚Šè¨˜å·', + 'number.thousands_separator' => 'æ¡ã®åŒºåˆ‡ã‚Šè¨˜å·', 'None' => 'ãªã—', - 'Edit' => '変更', - 'Remove' => '削除ã™ã‚‹', + 'Edit' => '編集', + 'Remove' => '削除', 'Yes' => 'ã¯ã„', 'No' => 'ã„ã„ãˆ', 'cancel' => 'ã‚ャンセル', @@ -17,35 +17,34 @@ return array( 'Red' => 'レッド', 'Orange' => 'オレンジ', 'Grey' => 'グレー', - // 'Brown' => '', - // 'Deep Orange' => '', - // 'Dark Grey' => '', - // 'Pink' => '', - // 'Teal' => '', - // 'Cyan' => '', - // 'Lime' => '', - // 'Light Green' => '', - // 'Amber' => '', + 'Brown' => 'ブラウン', + 'Deep Orange' => 'ディープオレンジ', + 'Dark Grey' => 'ダークグレー', + 'Pink' => 'ピンク', + 'Teal' => 'ティール', + 'Cyan' => 'シアン', + 'Lime' => 'ライム', + 'Light Green' => 'ライトグリーン', + 'Amber' => 'アンãƒãƒ¼', 'Save' => 'ä¿å˜', 'Login' => 'ãƒã‚°ã‚¤ãƒ³', - 'Official website:' => 'å…¬å¼ Web サイト:', + 'Official website:' => 'å…¬å¼ Webサイト:', 'Unassigned' => '担当ãªã—', 'View this task' => 'ã“ã®ã‚¿ã‚¯ã‚¹ã‚’見る', - 'Remove user' => 'ユーザã®å‰Šé™¤', - 'Do you really want to remove this user: "%s"?' => 'ユーザ「%sã€ã‚’本当ã«å‰Šé™¤ã—ã¾ã™ã‹ï¼Ÿ', - 'All users' => 'ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶', - 'Username' => 'ユーザå', + 'Remove user' => 'ユーザーã®å‰Šé™¤', + 'Do you really want to remove this user: "%s"?' => 'ユーザー「%sã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ', + 'All users' => 'ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼', + 'Username' => 'ユーザーå', 'Password' => 'パスワード', - 'Administrator' => '管ç†è€…', + 'Administrator' => 'システム管ç†è€…', 'Sign in' => 'ãƒã‚°ã‚¤ãƒ³', - 'Users' => 'ユーザ', - 'No user' => 'ユーザãŒã„ã¾ã›ã‚“', + 'Users' => 'ユーザー', 'Forbidden' => 'アクセス拒å¦', 'Access Forbidden' => 'ã‚¢ã‚¯ã‚»ã‚¹ãŒæ‹’å¦ã•れã¾ã—ãŸ', - 'Edit user' => 'ユーザを変更ã™ã‚‹', + 'Edit user' => 'ユーザーを編集ã™ã‚‹', 'Logout' => 'ãƒã‚°ã‚¢ã‚¦ãƒˆ', - 'Bad username or password' => 'ユーザåã¾ãŸã¯ãƒ‘スワードãŒé•ã„ã¾ã™ã€‚', - 'Edit project' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚’変更ã™ã‚‹', + 'Bad username or password' => 'ユーザーåã¾ãŸã¯ãƒ‘スワードãŒé•ã„ã¾ã™', + 'Edit project' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚’編集', 'Name' => 'åå‰', 'Projects' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆ', 'No project' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆãŒã‚りã¾ã›ã‚“', @@ -59,48 +58,48 @@ return array( 'Unable to update this board.' => 'ボードを更新ã§ãã¾ã›ã‚“ã§ã—ãŸ', 'Disable' => '無効ã«ã™ã‚‹', 'Enable' => '有効ã«ã™ã‚‹', - 'New project' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚’作る', - 'Do you really want to remove this project: "%s"?' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã€Œ%sã€ã‚’本当ã«å‰Šé™¤ã—ã¾ã™ã‹ï¼Ÿ', + 'New project' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆä½œæˆ', + 'Do you really want to remove this project: "%s"?' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã€Œ%sã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ', 'Remove project' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®å‰Šé™¤', - 'Edit the board for "%s"' => 'ボード「%sã€ã‚’変更ã™ã‚‹', + 'Edit the board for "%s"' => 'ボード「%sã€ã‚’編集', 'Add a new column' => 'カラムã®è¿½åŠ ', 'Title' => 'タイトル', 'Assigned to %s' => '%sãŒæ‹…当', 'Remove a column' => 'カラムã®å‰Šé™¤', - 'Unable to remove this column.' => 'カラムを削除ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚', + 'Unable to remove this column.' => 'カラムを削除ã§ãã¾ã›ã‚“ã§ã—ãŸ', 'Do you really want to remove this column: "%s"?' => 'カラム「%sã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ', 'Settings' => 'è¨å®š', - 'Application settings' => 'アプリケーションã®è¨å®š', + 'Application settings' => 'アプリケーションè¨å®š', 'Language' => '言語', - 'Webhook token:' => 'Webhook トークン:', - 'API token:' => 'API トークン:', - 'Database size:' => 'データベースã®ã‚µã‚¤ã‚º:', + 'Webhook token:' => 'Webhook トークン:', + 'API token:' => 'API トークン:', + 'Database size:' => 'データベースã®ã‚µã‚¤ã‚ºï¼š', 'Download the database' => 'データベースã®ãƒ€ã‚¦ãƒ³ãƒãƒ¼ãƒ‰', 'Optimize the database' => 'ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã®æœ€é©åŒ–', '(VACUUM command)' => '(VACUUM コマンド)', '(Gzip compressed Sqlite file)' => '(GZip コマンドã§åœ§ç¸®ã•れ㟠Sqlite ファイル)', - 'Close a task' => 'タスクをクãƒãƒ¼ã‚¹ã™ã‚‹', + 'Close a task' => 'タスクを完了', 'Column' => 'カラム', 'Color' => '色', 'Assignee' => '担当', - 'Create another task' => 'ç¶šã‘ã¦åˆ¥ã®ã‚¿ã‚¹ã‚¯ã‚’è¿½åŠ ã™ã‚‹', - 'New task' => 'ã‚¿ã‚¹ã‚¯ã‚’è¿½åŠ ã™ã‚‹', - 'Open a task' => 'タスクをオープンã™ã‚‹', - 'Do you really want to open this task: "%s"?' => 'タスク「%sã€ã‚’オープンã—ã¾ã™ã‹ï¼Ÿ', + 'Create another task' => 'ç¶šã‘ã¦åˆ¥ã®ã‚¿ã‚¹ã‚¯ã‚’è¿½åŠ ', + 'New task' => 'ã‚¿ã‚¹ã‚¯ã‚’è¿½åŠ ', + 'Open a task' => 'タスクを開ã', + 'Do you really want to open this task: "%s"?' => 'タスク「%sã€ã‚’作æˆã—ã¾ã™ã‹ï¼Ÿ', 'Back to the board' => 'ãƒœãƒ¼ãƒ‰ã«æˆ»ã‚‹', 'There is nobody assigned' => '担当者ãŒã„ã¾ã›ã‚“', - 'Column on the board:' => 'カラム: ', - 'Close this task' => 'タスクをクãƒãƒ¼ã‚ºã™ã‚‹', - 'Open this task' => 'タスクをオープンã™ã‚‹', + 'Column on the board:' => 'カラム:', + 'Close this task' => 'タスクを完了', + 'Open this task' => 'ã“ã®ã‚¿ã‚¹ã‚¯ã‚’é–‹ã', 'There is no description.' => '説明ãŒã‚りã¾ã›ã‚“', - 'Add a new task' => 'ã‚¿ã‚¹ã‚¯ã‚’è¿½åŠ ã™ã‚‹', - 'The username is required' => 'ユーザåãŒå¿…è¦ã§ã™', + 'Add a new task' => 'ã‚¿ã‚¹ã‚¯ã‚’è¿½åŠ ', + 'The username is required' => 'ユーザーåãŒå¿…è¦ã§ã™', 'The maximum length is %d characters' => '最大 %d æ–‡å—ã§ã™', 'The minimum length is %d characters' => 'æœ€å° %d æ–‡å—å¿…è¦ã§ã™', 'The password is required' => 'パスワードãŒå¿…è¦ã§ã™', 'This value must be an integer' => 'æ•´æ•°ã§å…¥åŠ›ã—ã¦ãã ã•ã„', - 'The username must be unique' => 'ユーザåãŒã™ã§ã«ä½¿ç”¨ã•れã¦ã„ã¾ã™', - 'The user id is required' => 'ユーザ ID ãŒå¿…è¦ã§ã™', + 'The username must be unique' => 'ユーザーåãŒã™ã§ã«ä½¿ç”¨ã•れã¦ã„ã¾ã™', + 'The user id is required' => 'ユーザー ID ãŒå¿…è¦ã§ã™', 'Passwords don\'t match' => 'パスワードãŒä¸€è‡´ã—ã¾ã›ã‚“', 'The confirmation is required' => '確èªç”¨ã®ãƒ‘スワードを入力ã—ã¦ãã ã•ã„', 'The project is required' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆãŒå¿…è¦ã§ã™', @@ -108,38 +107,38 @@ return array( 'The project id is required' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆ ID ãŒå¿…è¦ã§ã™', 'The project name is required' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆåãŒå¿…è¦ã§ã™', 'The title is required' => 'タイトルãŒå¿…è¦ã§ã™', - 'Settings saved successfully.' => 'è¨å®šã‚’ä¿å˜ã—ã¾ã—ãŸã€‚', - 'Unable to save your settings.' => 'è¨å®šã®ä¿å˜ã«å¤±æ•—ã—ã¾ã—ãŸã€‚', - 'Database optimization done.' => 'ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã®æœ€é©åŒ–ãŒçµ‚ã‚りã¾ã—ãŸã€‚', - 'Your project have been created successfully.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚’作æˆã—ã¾ã—ãŸã€‚', - 'Unable to create your project.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®ä½œæˆã«å¤±æ•—ã—ã¾ã—ãŸã€‚', - 'Project updated successfully.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚’æ›´æ–°ã—ã¾ã—ãŸã€‚', - 'Unable to update this project.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸã€‚', - 'Unable to remove this project.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸã€‚', - 'Project removed successfully.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚’削除ã—ã¾ã—ãŸã€‚', - 'Project activated successfully.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚’有効ã«ã—ã¾ã—ãŸã€‚', - 'Unable to activate this project.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®æœ‰åйã«ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚', - 'Project disabled successfully.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚’無効ã«ã—ã¾ã—ãŸã€‚', - 'Unable to disable this project.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®ç„¡åйã«ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚', - 'Unable to open this task.' => 'タスクã®ã‚ªãƒ¼ãƒ—ンã«å¤±æ•—ã—ã¾ã—ãŸã€‚', - 'Task opened successfully.' => 'タスクをオープンã—ã¾ã—ãŸã€‚', - 'Unable to close this task.' => 'タスクã®ã‚¯ãƒãƒ¼ã‚ºã«å¤±æ•—ã—ã¾ã—ãŸã€‚', - 'Task closed successfully.' => 'タスクをクãƒãƒ¼ã‚ºã—ã¾ã—ãŸã€‚', - 'Unable to update your task.' => 'ã‚¿ã‚¹ã‚¯ã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸã€‚', - 'Task updated successfully.' => 'タスクを更新ã—ã¾ã—ãŸã€‚', - 'Unable to create your task.' => 'タスクã®è¿½åŠ ã«å¤±æ•—ã—ã¾ã—ãŸã€‚', - 'Task created successfully.' => 'ã‚¿ã‚¹ã‚¯ã‚’è¿½åŠ ã—ã¾ã—ãŸã€‚', - 'User created successfully.' => 'ãƒ¦ãƒ¼ã‚¶ã‚’è¿½åŠ ã—ã¾ã—ãŸã€‚', - 'Unable to create your user.' => 'ユーザã®è¿½åŠ ã«å¤±æ•—ã—ã¾ã—ãŸã€‚', - 'User updated successfully.' => 'ユーザを更新ã—ã¾ã—ãŸã€‚', - 'User removed successfully.' => 'ユーザを削除ã—ã¾ã—ãŸã€‚', - 'Unable to remove this user.' => 'ユーザã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸã€‚', - 'Board updated successfully.' => 'ボードを更新ã—ã¾ã—ãŸã€‚', + 'Settings saved successfully.' => 'è¨å®šã‚’ä¿å˜ã—ã¾ã—ãŸ', + 'Unable to save your settings.' => 'è¨å®šã®ä¿å˜ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Database optimization done.' => 'ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã®æœ€é©åŒ–ãŒçµ‚ã‚りã¾ã—ãŸ', + 'Your project have been created successfully.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚’作æˆã—ã¾ã—ãŸ', + 'Unable to create your project.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®ä½œæˆã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Project updated successfully.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚’æ›´æ–°ã—ã¾ã—ãŸ', + 'Unable to update this project.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Unable to remove this project.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Project removed successfully.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚’削除ã—ã¾ã—ãŸ', + 'Project activated successfully.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚’有効ã«ã—ã¾ã—ãŸ', + 'Unable to activate this project.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®æœ‰åйã«ã§ãã¾ã›ã‚“ã§ã—ãŸ', + 'Project disabled successfully.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚’無効ã«ã—ã¾ã—ãŸ', + 'Unable to disable this project.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®ç„¡åйã«ã§ãã¾ã›ã‚“ã§ã—ãŸ', + 'Unable to open this task.' => 'タスクã®ä½œæˆã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Task opened successfully.' => 'タスクを作æˆã—ã¾ã—ãŸ', + 'Unable to close this task.' => 'タスクã®å®Œäº†ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Task closed successfully.' => 'タスクを完了ã—ã¾ã—ãŸ', + 'Unable to update your task.' => 'ã‚¿ã‚¹ã‚¯ã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Task updated successfully.' => 'タスクを更新ã—ã¾ã—ãŸ', + 'Unable to create your task.' => 'タスクã®è¿½åŠ ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Task created successfully.' => 'ã‚¿ã‚¹ã‚¯ã‚’è¿½åŠ ã—ã¾ã—ãŸ', + 'User created successfully.' => 'ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’è¿½åŠ ã—ã¾ã—ãŸ', + 'Unable to create your user.' => 'ユーザーã®è¿½åŠ ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'User updated successfully.' => 'ユーザーを更新ã—ã¾ã—ãŸ', + 'User removed successfully.' => 'ユーザーを削除ã—ã¾ã—ãŸ', + 'Unable to remove this user.' => 'ユーザーã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Board updated successfully.' => 'ボードを更新ã—ã¾ã—ãŸ', 'Ready' => 'Ready', 'Backlog' => 'Backlog', 'Work in progress' => 'Work in progress', 'Done' => 'Done', - 'Application version:' => 'アプリケーションã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³:', + 'Application version:' => 'アプリケーションã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ï¼š', 'Id' => 'ID', 'Public link' => '公開アクセス用リンク', 'Timezone' => 'タイムゾーン', @@ -148,48 +147,48 @@ return array( 'Complexity' => '複雑ã•', 'Task limit' => 'タスク数制é™', 'Task count' => 'タスク数', - 'User' => 'ユーザ', + 'User' => 'ユーザー', 'Comments' => 'コメント', 'Comment is required' => 'コメントを入力ã—ã¦ãã ã•ã„', - 'Comment added successfully.' => 'ã‚³ãƒ¡ãƒ³ãƒˆã‚’è¿½åŠ ã—ã¾ã—ãŸã€‚', - 'Unable to create your comment.' => 'コメントã®è¿½åŠ ã«å¤±æ•—ã—ã¾ã—ãŸã€‚', + 'Comment added successfully.' => 'ã‚³ãƒ¡ãƒ³ãƒˆã‚’è¿½åŠ ã—ã¾ã—ãŸ', + 'Unable to create your comment.' => 'コメントã®è¿½åŠ ã«å¤±æ•—ã—ã¾ã—ãŸ', 'Due Date' => '期é™', 'Invalid date' => '日付ãŒç„¡åйã§ã™', - 'Automatic actions' => '自動アクションを管ç†ã™ã‚‹', - 'Your automatic action have been created successfully.' => '自動アクションを作æˆã—ã¾ã—ãŸã€‚', - 'Unable to create your automatic action.' => '自動アクションã®ä½œæˆã«å¤±æ•—ã—ã¾ã—ãŸã€‚', + 'Automatic actions' => '自動アクションã®ç®¡ç†', + 'Your automatic action have been created successfully.' => '自動アクションを作æˆã—ã¾ã—ãŸ', + 'Unable to create your automatic action.' => '自動アクションã®ä½œæˆã«å¤±æ•—ã—ã¾ã—ãŸ', 'Remove an action' => '自動アクションã®å‰Šé™¤', - 'Unable to remove this action.' => '自動アクションã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸã€‚', - 'Action removed successfully.' => '自動アクションã®å‰Šé™¤ã«æˆåŠŸã—ã¾ã—ãŸã€‚', + 'Unable to remove this action.' => '自動アクションã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Action removed successfully.' => '自動アクションã®å‰Šé™¤ã«æˆåŠŸã—ã¾ã—ãŸ', 'Automatic actions for the project "%s"' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã€Œ%sã€ã®è‡ªå‹•アクション', 'Add an action' => '自動アクションã®è¿½åŠ ', 'Event name' => 'イベントå', 'Action' => 'アクション', 'Event' => 'イベント', - 'When the selected event occurs execute the corresponding action.' => 'é¸æŠžã•れãŸã‚¤ãƒ™ãƒ³ãƒˆãŒç™ºç”Ÿã—ãŸæ™‚ã€å¯¾å¿œã™ã‚‹ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’実行ã™ã‚‹ã€‚', + 'When the selected event occurs execute the corresponding action.' => 'é¸æŠžã•れãŸã‚¤ãƒ™ãƒ³ãƒˆãŒç™ºç”Ÿã—ãŸæ™‚ã€å¯¾å¿œã™ã‚‹ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’実行', 'Next step' => '次ã®ã‚¹ãƒ†ãƒƒãƒ—', 'Define action parameters' => 'アクションã®ãƒ‘ラメーター', 'Do you really want to remove this action: "%s"?' => '自動アクション「%sã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ', 'Remove an automatic action' => '自動アクションã®å‰Šé™¤', - 'Assign the task to a specific user' => 'ã‚¿ã‚¹ã‚¯ã®æ‹…当者を割り当ã¦ã‚‹', - 'Assign the task to the person who does the action' => 'アクションを起ã“ã—ãŸãƒ¦ãƒ¼ã‚¶ã‚’担当者ã«ã™ã‚‹', - 'Duplicate the task to another project' => '別ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã«ã‚¿ã‚¹ã‚¯ã‚’複製ã™ã‚‹', - 'Move a task to another column' => 'タスクを別ã®ã‚«ãƒ©ãƒ ã«ç§»å‹•ã™ã‚‹', + 'Assign the task to a specific user' => 'ã‚¿ã‚¹ã‚¯ã®æ‹…当者を割当ã¦ã‚‹', + 'Assign the task to the person who does the action' => 'アクションを起ã“ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’担当者ã«ã™ã‚‹', + 'Duplicate the task to another project' => '別ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã«ã‚¿ã‚¹ã‚¯ã‚’複製', + 'Move a task to another column' => 'タスクを別ã®ã‚«ãƒ©ãƒ ã«ç§»å‹•', 'Task modification' => 'タスクã®å¤‰æ›´', 'Task creation' => 'タスクを作る', - 'Closing a task' => 'タスクをクãƒãƒ¼ã‚ºã™ã‚‹', - 'Assign a color to a specific user' => '色をユーザã«å‰²ã‚Šå½“ã¦ã‚‹', + 'Closing a task' => 'タスクを完了', + 'Assign a color to a specific user' => '色をユーザーã«å‰²å½“ã¦ã‚‹', 'Position' => 'ä½ç½®', - 'Duplicate to another project' => '別ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã«è¤‡è£½ã™ã‚‹', - 'Duplicate' => '複製ã™ã‚‹', + 'Duplicate to another project' => '別プãƒã‚¸ã‚§ã‚¯ãƒˆã«è¤‡è£½', + 'Duplicate' => '複製', 'Link' => 'リンク', - 'Comment updated successfully.' => 'コメントを更新ã—ã¾ã—ãŸã€‚', - 'Unable to update your comment.' => 'ã‚³ãƒ¡ãƒ³ãƒˆã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸã€‚', - 'Remove a comment' => 'コメントを削除ã™ã‚‹', - 'Comment removed successfully.' => 'コメントを削除ã—ã¾ã—ãŸã€‚', - 'Unable to remove this comment.' => 'コメントã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸã€‚', + 'Comment updated successfully.' => 'コメントを更新ã—ã¾ã—ãŸ', + 'Unable to update your comment.' => 'ã‚³ãƒ¡ãƒ³ãƒˆã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Remove a comment' => 'コメントを削除', + 'Comment removed successfully.' => 'コメントを削除ã—ã¾ã—ãŸ', + 'Unable to remove this comment.' => 'コメントã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ', 'Do you really want to remove this comment?' => 'コメントを削除ã—ã¾ã™ã‹ï¼Ÿ', - 'Current password for the user "%s"' => 'ユーザ「%sã€ã®ç¾åœ¨ã®ãƒ‘スワード', + 'Current password for the user "%s"' => 'ユーザー「%sã€ã®ç¾åœ¨ã®ãƒ‘スワード', 'The current password is required' => 'ç¾åœ¨ã®ãƒ‘スワードを入力ã—ã¦ãã ã•ã„', 'Wrong password' => 'パスワードãŒé•ã„ã¾ã™', 'Unknown' => '䏿˜Ž', @@ -197,44 +196,44 @@ return array( 'Login date' => 'ãƒã‚°ã‚¤ãƒ³æ—¥æ™‚', 'Authentication method' => 'èªè¨¼æ–¹æ³•', 'IP address' => 'IP アドレス', - 'User agent' => 'ユーザエージェント', + 'User agent' => 'ユーザーエージェント', 'Persistent connections' => 'æ—¢å˜ã®ã‚³ãƒã‚¯ã‚·ãƒ§ãƒ³', - 'No session.' => 'セッションãªã—。', + 'No session.' => 'セッションãªã—', 'Expiration date' => '有効期é™', - 'Remember Me' => '次回ã‹ã‚‰è‡ªå‹•çš„ã«ãƒã‚°ã‚¤ãƒ³ã™ã‚‹', + 'Remember Me' => '次回ã‹ã‚‰è‡ªå‹•çš„ã«ãƒã‚°ã‚¤ãƒ³', 'Creation date' => 'ä½œæˆæ—¥', 'Everybody' => '全員', - 'Open' => 'オープン', - 'Closed' => 'クãƒãƒ¼ã‚º', + 'Open' => '作æˆ', + 'Closed' => '完了', 'Search' => '検索', - 'Nothing found.' => 'çµæžœãªã—。', + 'Nothing found.' => 'çµæžœãªã—', 'Due date' => '期é™', 'Description' => '説明', '%d comments' => '%d 個ã®ã‚³ãƒ¡ãƒ³ãƒˆ', '%d comment' => '%d 個ã®ã‚³ãƒ¡ãƒ³ãƒˆ', 'Email address invalid' => 'ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ãŒæ£ã—ãã‚りã¾ã›ã‚“', - // 'Your external account is not linked anymore to your profile.' => '', - // 'Unable to unlink your external account.' => '', - // 'External authentication failed' => '', - // 'Your external account is linked to your profile successfully.' => '', + 'Your external account is not linked anymore to your profile.' => 'ã‚ãªãŸã®å¤–部アカウントã¯ãƒ—ãƒãƒ•ィールã«ãƒªãƒ³ã‚¯ã•れã¦ã„ã¾ã›ã‚“', + 'Unable to unlink your external account.' => '外部アカウントã®ãƒªãƒ³ã‚¯ã‚’解除ã§ãã¾ã›ã‚“', + 'External authentication failed' => '外部èªè¨¼ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Your external account is linked to your profile successfully.' => 'ã‚ãªãŸã®å¤–部アカウントã¯ãƒ—ãƒãƒ•ã‚£ãƒ¼ãƒ«ã«æ£å¸¸ã«ãƒªãƒ³ã‚¯ã—ã¦ã„ã¾ã™', 'Email' => 'Email', - 'Task removed successfully.' => 'タスクを削除ã—ã¾ã—ãŸã€‚', - 'Unable to remove this task.' => 'タスクã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸã€‚', + 'Task removed successfully.' => 'タスクを削除ã—ã¾ã—ãŸ', + 'Unable to remove this task.' => 'タスクã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ', 'Remove a task' => 'タスクã®å‰Šé™¤', 'Do you really want to remove this task: "%s"?' => 'タスク「%sã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ', 'Assign automatically a color based on a category' => 'カテゴリã«åŸºã„ã¦è‰²ã‚’変ãˆã‚‹', 'Assign automatically a category based on a color' => '色ã«åŸºã„ã¦ã‚«ãƒ†ã‚´ãƒªã‚’変ãˆã‚‹', 'Task creation or modification' => 'タスクã®ä½œæˆã¾ãŸã¯å¤‰æ›´', 'Category' => 'カテゴリ', - 'Category:' => 'カテゴリ:', + 'Category:' => 'カテゴリ:', 'Categories' => 'カテゴリ', - 'Your category have been created successfully.' => 'カテゴリを作æˆã—ã¾ã—ãŸã€‚', - 'This category has been updated successfully.' => 'カテゴリを更新ã—ã¾ã—ãŸã€‚', - 'Unable to update this category.' => 'ã‚«ãƒ†ã‚´ãƒªã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸã€‚', + 'Your category have been created successfully.' => 'カテゴリを作æˆã—ã¾ã—ãŸ', + 'This category has been updated successfully.' => 'カテゴリを更新ã—ã¾ã—ãŸ', + 'Unable to update this category.' => 'ã‚«ãƒ†ã‚´ãƒªã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸ', 'Remove a category' => 'カテゴリã®å‰Šé™¤', - 'Category removed successfully.' => 'カテゴリを削除ã—ã¾ã—ãŸã€‚', - 'Unable to remove this category.' => 'カテゴリを削除ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚', - 'Category modification for the project "%s"' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã€Œ%sã€ã®ã‚«ãƒ†ã‚´ãƒªã®å¤‰æ›´', + 'Category removed successfully.' => 'カテゴリを削除ã—ã¾ã—ãŸ', + 'Unable to remove this category.' => 'カテゴリを削除ã§ãã¾ã›ã‚“ã§ã—ãŸ', + 'Category modification for the project "%s"' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã€Œ%sã€ã®ã‚«ãƒ†ã‚´ãƒªã®ç·¨é›†', 'Category Name' => 'カテゴリå', 'Add a new category' => 'カテゴリã®è¿½åŠ ', 'Do you really want to remove this category: "%s"?' => 'カテゴリ「%sã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ', @@ -242,40 +241,39 @@ return array( 'No category' => 'カテゴリãªã—', 'The name is required' => 'åå‰ã‚’入力ã—ã¦ãã ã•ã„', 'Remove a file' => 'ファイルã®å‰Šé™¤', - 'Unable to remove this file.' => 'ファイルã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸã€‚', - 'File removed successfully.' => 'ファイルを削除ã—ã¾ã—ãŸã€‚', - 'Attach a document' => 'ドã‚ュメントを添付ã™ã‚‹', + 'Unable to remove this file.' => 'ファイルã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'File removed successfully.' => 'ファイルを削除ã—ã¾ã—ãŸ', + 'Attach a document' => 'ドã‚ュメントを添付', 'Do you really want to remove this file: "%s"?' => 'ファイル「%sã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ', 'Attachments' => '添付', - 'Edit the task' => 'タスクを変更ã™ã‚‹', - 'Add a comment' => 'コメントã®è¿½åŠ ', - 'Edit a comment' => 'コメントを変更ã™ã‚‹', - 'Summary' => '概è¦', - 'Time tracking' => '時間ã®è¨ˆæ¸¬', - 'Estimate:' => '予測:', - 'Spent:' => '経éŽ:', + 'Edit the task' => 'タスクを編集', + 'Add a comment' => 'ã‚³ãƒ¡ãƒ³ãƒˆã‚’è¿½åŠ ', + 'Edit a comment' => 'コメントを編集', + 'Summary' => 'è¦ç´„', + 'Time tracking' => '時間ã®è¿½è·¡', + 'Estimate:' => '見ç©ï¼š', + 'Spent:' => '経éŽï¼š', 'Do you really want to remove this sub-task?' => 'サブタスクを削除ã—ã¾ã™ã‹ï¼Ÿ', - 'Remaining:' => '残り:', + 'Remaining:' => '残:', 'hours' => '時間', 'spent' => '経éŽ', - 'estimated' => '予測', + 'estimated' => '見ç©', 'Sub-Tasks' => 'サブタスク', - 'Add a sub-task' => 'ã‚µãƒ–ã‚¿ã‚¹ã‚¯ã‚’è¿½åŠ ã™ã‚‹', - 'Original estimate' => 'åˆæœŸã®äºˆæ¸¬', - 'Create another sub-task' => 'ç¶šã‘ã¦åˆ¥ã®ã‚µãƒ–ã‚¿ã‚¹ã‚¯ã‚’è¿½åŠ ã™ã‚‹', + 'Add a sub-task' => 'ã‚µãƒ–ã‚¿ã‚¹ã‚¯ã‚’è¿½åŠ ', + 'Original estimate' => 'åˆæœŸã®è¦‹ç©', + 'Create another sub-task' => 'ç¶šã‘ã¦åˆ¥ã®ã‚µãƒ–ã‚¿ã‚¹ã‚¯ã‚’è¿½åŠ ', 'Time spent' => 'çµŒéŽæ™‚é–“', - 'Edit a sub-task' => 'サブタスクを変更ã™ã‚‹', - 'Remove a sub-task' => 'サブタスクを削除ã™ã‚‹', + 'Edit a sub-task' => 'サブタスクを編集', + 'Remove a sub-task' => 'サブタスクを削除', 'The time must be a numeric value' => 'æ™‚é–“ã¯æ•°å—ã§å…¥åŠ›ã—ã¦ãã ã•ã„', 'Todo' => '作æ¥äºˆå®š', 'In progress' => '作æ¥ä¸', - 'Sub-task removed successfully.' => 'サブタスクを削除ã—ã¾ã—ãŸã€‚', - 'Unable to remove this sub-task.' => 'サブタスクã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸã€‚', - 'Sub-task updated successfully.' => 'サブタスクを更新ã—ã¾ã—ãŸã€‚', - 'Unable to update your sub-task.' => 'ã‚µãƒ–ã‚¿ã‚¹ã‚¯ã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸã€‚', - 'Unable to create your sub-task.' => 'サブタスクã®è¿½åŠ ã«å¤±æ•—ã—ã¾ã—ãŸã€‚', - 'Sub-task added successfully.' => 'ã‚µãƒ–ã‚¿ã‚¹ã‚¯ã‚’è¿½åŠ ã—ã¾ã—ãŸã€‚', - 'Maximum size: ' => '最大: ', + 'Sub-task removed successfully.' => 'サブタスクを削除ã—ã¾ã—ãŸ', + 'Unable to remove this sub-task.' => 'サブタスクã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Sub-task updated successfully.' => 'サブタスクを更新ã—ã¾ã—ãŸ', + 'Unable to update your sub-task.' => 'ã‚µãƒ–ã‚¿ã‚¹ã‚¯ã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Unable to create your sub-task.' => 'サブタスクã®è¿½åŠ ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Maximum size: ' => '最大:', 'Display another project' => '別ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã‚’表示', 'Created by %s' => '%s ãŒä½œæˆ', 'Tasks Export' => 'タスクã®å‡ºåŠ›', @@ -283,34 +281,34 @@ return array( 'Execute' => '実行', 'Task Id' => 'タスク ID', 'Creator' => '作æˆè€…', - 'Modification date' => '変更日', + 'Modification date' => 'æ›´æ–°æ—¥', 'Completion date' => '完了日', 'Clone' => '複製', - 'Project cloned successfully.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚’複製ã—ã¾ã—ãŸã€‚', - 'Unable to clone this project.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®è¤‡è£½ã«å¤±æ•—ã—ã¾ã—ãŸã€‚', + 'Project cloned successfully.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚’複製ã—ã¾ã—ãŸ', + 'Unable to clone this project.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®è¤‡è£½ã«å¤±æ•—ã—ã¾ã—ãŸ', 'Enable email notifications' => 'メール通知をè¨å®š', - 'Task position:' => 'タスクã®ä½ç½®:', - 'The task #%d have been opened.' => 'タスク #%d をオープンã—ã¾ã—ãŸã€‚', - 'The task #%d have been closed.' => 'タスク #%d をクãƒãƒ¼ã‚ºã—ã¾ã—ãŸã€‚', + 'Task position:' => 'タスクã®ä½ç½®ï¼š', + 'The task #%d have been opened.' => 'タスク #%d を作æˆã—ã¾ã—ãŸ', + 'The task #%d have been closed.' => 'タスク #%d を完了ã—ã¾ã—ãŸ', 'Sub-task updated' => 'ã‚µãƒ–ã‚¿ã‚¹ã‚¯ã®æ›´æ–°', - 'Title:' => 'タイトル:', - 'Status:' => 'ステータス:', - 'Assignee:' => '担当:', - 'Time tracking:' => '時間計測:', + 'Title:' => 'タイトル:', + 'Status:' => 'ステータス:', + 'Assignee:' => '担当:', + 'Time tracking:' => '時間計測:', 'New sub-task' => 'æ–°ã—ã„サブタスク', 'New attachment added "%s"' => '添付ファイル「%sã€ãŒè¿½åŠ ã•れã¾ã—ãŸ', 'New comment posted by %s' => '「%sã€ã®æ–°ã—ã„コメントãŒè¿½åŠ ã•れã¾ã—ãŸ', 'New comment' => 'æ–°ã—ã„コメント', 'Comment updated' => 'ã‚³ãƒ¡ãƒ³ãƒˆãŒæ›´æ–°ã•れã¾ã—ãŸ', 'New subtask' => 'æ–°ã—ã„サブタスク', - 'I want to receive notifications only for those projects:' => '以下ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã«ã®ã¿é€šçŸ¥ã‚’å—ã‘å–ã‚‹:', + 'I want to receive notifications only for those projects:' => '以下ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã«ã®ã¿é€šçŸ¥ã‚’å—ã‘å–る:', 'view the task on Kanboard' => 'Kanboard ã§ã‚¿ã‚¹ã‚¯ã‚’見る', - 'Public access' => '公開アクセスè¨å®š', + 'Public access' => '公開アクセス', 'Disable public access' => '公開アクセスを無効ã«ã™ã‚‹', 'Enable public access' => '公開アクセスを有効ã«ã™ã‚‹', 'Public access disabled' => '公開アクセスã¯ç„¡åŠ¹åŒ–ã•れã¦ã„ã¾ã™', - 'Move the task to another project' => 'タスクを別プãƒã‚¸ã‚§ã‚¯ãƒˆã«ç§»ã™', - 'Move to another project' => '別プãƒã‚¸ã‚§ã‚¯ãƒˆã«ç§»ã™', + 'Move the task to another project' => 'タスクを別プãƒã‚¸ã‚§ã‚¯ãƒˆã«ç§»å‹•', + 'Move to another project' => '別プãƒã‚¸ã‚§ã‚¯ãƒˆã«ç§»å‹•', 'Do you really want to duplicate this task?' => 'タスクを複製ã—ã¾ã™ã‹ï¼Ÿ', 'Duplicate a task' => 'タスクã®è¤‡è£½', 'External accounts' => '外部アカウント', @@ -319,34 +317,34 @@ return array( 'Remote' => 'リモート', 'Enabled' => '有効', 'Disabled' => '無効', - 'Login:' => 'ユーザå:', - 'Full Name:' => 'åå‰:', - 'Email:' => 'Email:', - 'Notifications:' => '通知:', + 'Login:' => 'ユーザーå:', + 'Full Name:' => 'åå‰ï¼š', + 'Email:' => 'Email:', + 'Notifications:' => '通知:', 'Notifications' => '通知', - 'Account type:' => 'アカウントã®ç¨®é¡ž:', - 'Edit profile' => 'プãƒãƒ•ィールã®å¤‰æ›´', + 'Account type:' => 'アカウントã®ç¨®é¡žï¼š', + 'Edit profile' => 'プãƒãƒ•ィールã®ç·¨é›†', 'Change password' => 'パスワードã®å¤‰æ›´', 'Password modification' => 'パスワードã®å¤‰æ›´', 'External authentications' => '外部èªè¨¼', - 'Never connected.' => '未接続。', - 'No external authentication enabled.' => '外部èªè¨¼ãŒè¨å®šã•れã¦ã„ã¾ã›ã‚“。', - 'Password modified successfully.' => 'パスワードを変更ã—ã¾ã—ãŸã€‚', - 'Unable to change the password.' => 'パスワードãŒå¤‰æ›´ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚', + 'Never connected.' => '未接続', + 'No external authentication enabled.' => '外部èªè¨¼ãŒè¨å®šã•れã¦ã„ã¾ã›ã‚“', + 'Password modified successfully.' => 'パスワードを変更ã—ã¾ã—ãŸ', + 'Unable to change the password.' => 'パスワードを変更ã§ãã¾ã›ã‚“ã§ã—ãŸ', 'Change category' => 'カテゴリã®å¤‰æ›´', - '%s updated the task %s' => '%s ãŒã‚¿ã‚¹ã‚¯ %s をアップデートã—ã¾ã—ãŸ', - '%s opened the task %s' => '%s ãŒã‚¿ã‚¹ã‚¯ %s をオープンã—ã¾ã—ãŸ', + '%s updated the task %s' => '%s ãŒã‚¿ã‚¹ã‚¯ %s ã‚’æ›´æ–°ã—ã¾ã—ãŸ', + '%s opened the task %s' => '%s ãŒã‚¿ã‚¹ã‚¯ %s を作æˆã—ã¾ã—ãŸ', '%s moved the task %s to the position #%d in the column "%s"' => '%s ãŒã‚¿ã‚¹ã‚¯ %s ã‚’ãƒã‚¸ã‚·ãƒ§ãƒ³ #%d カラム%s ã«ç§»å‹•ã—ã¾ã—ãŸ', '%s moved the task %s to the column "%s"' => '%s ãŒã‚¿ã‚¹ã‚¯ %s をカラム「%sã€ã«ç§»å‹•ã—ã¾ã—ãŸ', '%s created the task %s' => '%s ãŒã‚¿ã‚¹ã‚¯ %s を作æˆã—ã¾ã—ãŸ', - '%s closed the task %s' => '%s ãŒã‚¿ã‚¹ã‚¯ %s をクãƒãƒ¼ã‚ºã—ã¾ã—ãŸ', + '%s closed the task %s' => '%s ãŒã‚¿ã‚¹ã‚¯ %s を完了ã—ã¾ã—ãŸ', '%s created a subtask for the task %s' => '%s ãŒã‚¿ã‚¹ã‚¯ %s ã®ã‚µãƒ–ã‚¿ã‚¹ã‚¯ã‚’è¿½åŠ ã—ã¾ã—ãŸ', '%s updated a subtask for the task %s' => '%s ãŒã‚¿ã‚¹ã‚¯ %s ã®ã‚µãƒ–タスクを更新ã—ã¾ã—ãŸ', - 'Assigned to %s with an estimate of %s/%sh' => '担当者 %s ã«äºˆæƒ³ %s/%sh ã«å¤‰æ›´ã•れã¾ã—ãŸ', - 'Not assigned, estimate of %sh' => '担当者無ã—ã§äºˆæƒ³ %sh ã«å¤‰æ›´ã•れã¾ã—ãŸ', + 'Assigned to %s with an estimate of %s/%sh' => '担当者 %s ã¯è¦‹ç©ã‚’ %s/%s時間 ã«å¤‰æ›´ã—ã¾ã—ãŸ', + 'Not assigned, estimate of %sh' => '担当者無ã—ã§è¦‹ç©ã‚’ %s時間 ã«å¤‰æ›´ã—ã¾ã—ãŸ', '%s updated a comment on the task %s' => '%s ãŒã‚¿ã‚¹ã‚¯ %s ã®ã‚³ãƒ¡ãƒ³ãƒˆã‚’æ›´æ–°ã—ã¾ã—ãŸ', '%s commented the task %s' => '%s ãŒã‚¿ã‚¹ã‚¯ %s ã«ã‚³ãƒ¡ãƒ³ãƒˆã—ã¾ã—ãŸ', - '%s\'s activity' => '%s ã®ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ティ', + '%s\'s activity' => '%s ã®æ´»å‹•状æ³', 'RSS feed' => 'RSS フィード', '%s updated a comment on the task #%d' => '%s ãŒã‚¿ã‚¹ã‚¯ #%d ã®ã‚³ãƒ¡ãƒ³ãƒˆã‚’æ›´æ–°ã—ã¾ã—ãŸ', '%s commented on the task #%d' => '%s ãŒã‚¿ã‚¹ã‚¯ #%d ã«ã‚³ãƒ¡ãƒ³ãƒˆã—ã¾ã—ãŸ', @@ -354,68 +352,63 @@ return array( '%s created a subtask for the task #%d' => '%s ãŒã‚¿ã‚¹ã‚¯ #%d ã®ã‚µãƒ–ã‚¿ã‚¹ã‚¯ã‚’è¿½åŠ ã—ã¾ã—ãŸ', '%s updated the task #%d' => '%s ãŒã‚¿ã‚¹ã‚¯ #%d ã‚’æ›´æ–°ã—ã¾ã—ãŸ', '%s created the task #%d' => '%s ãŒã‚¿ã‚¹ã‚¯ #%d ã‚’è¿½åŠ ã—ã¾ã—ãŸ', - '%s closed the task #%d' => '%s ãŒã‚¿ã‚¹ã‚¯ #%d をクãƒãƒ¼ã‚ºã—ã¾ã—ãŸ', - '%s opened the task #%d' => '%s ãŒã‚¿ã‚¹ã‚¯ #%d をオープンã—ã¾ã—ãŸ', - 'Activity' => 'アクティビティ', - 'Default values are "%s"' => 'デフォルト値ã¯ã€Œ%sã€', - 'Default columns for new projects (Comma-separated)' => 'æ–°è¦ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã®ãƒ‡ãƒ•ォルトカラム(コンマã§åŒºåˆ‡ã£ã¦å…¥åŠ›)', + '%s closed the task #%d' => '%s ãŒã‚¿ã‚¹ã‚¯ #%d を完了ã—ã¾ã—ãŸ', + '%s opened the task #%d' => '%s ãŒã‚¿ã‚¹ã‚¯ #%d を作æˆã—ã¾ã—ãŸ', + 'Activity' => '活動状æ³', + 'Default values are "%s"' => '既定値ã¯ã€Œ%sã€', + 'Default columns for new projects (Comma-separated)' => 'æ–°è¦ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã®æ—¢å®šã‚«ãƒ©ãƒ (コンマã§åŒºåˆ‡ã£ã¦å…¥åŠ›)', 'Task assignee change' => '担当者ã®å¤‰æ›´', '%s changed the assignee of the task #%d to %s' => '%s ãŒã‚¿ã‚¹ã‚¯ #%d ã®æ‹…当を %s ã«å¤‰æ›´ã—ã¾ã—ãŸ', '%s changed the assignee of the task %s to %s' => '%s ãŒã‚¿ã‚¹ã‚¯ %s ã®æ‹…当を %s ã«å¤‰æ›´ã—ã¾ã—ãŸ', - 'New password for the user "%s"' => 'ユーザ「%sã€ã®æ–°ã—ã„パスワード', + 'New password for the user "%s"' => 'ユーザー「%sã€ã®æ–°ã—ã„パスワード', 'Choose an event' => 'イベントã®é¸æŠž', - 'Create a task from an external provider' => 'タスクを外部サービスã‹ã‚‰ä½œæˆã™ã‚‹', - 'Change the assignee based on an external username' => '担当者を外部サービスã«åŸºã„ã¦å¤‰æ›´ã™ã‚‹', - 'Change the category based on an external label' => 'カテゴリを外部サービスã«åŸºã„ã¦å¤‰æ›´ã™ã‚‹', + 'Create a task from an external provider' => 'タスクを外部サービスã‹ã‚‰ä½œæˆ', + 'Change the assignee based on an external username' => '担当者を外部サービスã«åŸºã„ã¦å¤‰æ›´', + 'Change the category based on an external label' => 'カテゴリを外部サービスã«åŸºã„ã¦å¤‰æ›´', 'Reference' => 'å‚ç…§', 'Label' => 'ラベル', 'Database' => 'データベース', 'About' => 'æƒ…å ±', - 'Database driver:' => 'データベースドライãƒ:', + 'Database driver:' => 'データベースドライãƒï¼š', 'Board settings' => '基本è¨å®š', 'Webhook settings' => 'Webhook ã®è¨å®š', 'Reset token' => 'トークンã®ãƒªã‚»ãƒƒãƒˆ', - 'API endpoint:' => 'API エンドãƒã‚¤ãƒ³ãƒˆ:', + 'API endpoint:' => 'API エンドãƒã‚¤ãƒ³ãƒˆï¼š', 'Refresh interval for private board' => 'éžå…¬é–‹ãƒœãƒ¼ãƒ‰ã®æ›´æ–°é »åº¦', 'Refresh interval for public board' => 'å…¬é–‹ãƒœãƒ¼ãƒ‰ã®æ›´æ–°é »åº¦', 'Task highlight period' => 'タスクã®ãƒã‚¤ãƒ©ã‚¤ãƒˆæœŸé–“', - 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'ã‚¿ã‚¹ã‚¯ãŒæœ€è¿‘æ›´æ–°ã•れãŸã¨ã¿ãªã™æœŸé–“(0 ã¯ãƒã‚¤ãƒ©ã‚¤ãƒˆç„¡åйã€ãƒ‡ãƒ•ォルト 2 æ—¥)', - 'Frequency in second (60 seconds by default)' => 'ç§’æ•° (デフォルト 60 ç§’)', - 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'ç§’æ•° (0 ã¯æ©Ÿèƒ½ã‚’無効化ã€ãƒ‡ãƒ•ォルト 10 ç§’)', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'ã‚¿ã‚¹ã‚¯ãŒæœ€è¿‘æ›´æ–°ã•れãŸã¨ã¿ãªã™æœŸé–“(0 ã¯ãƒã‚¤ãƒ©ã‚¤ãƒˆç„¡åŠ¹ã€æ—¢å®š 2 æ—¥)', + 'Frequency in second (60 seconds by default)' => 'ç§’æ•° (既定 60 ç§’)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'ç§’æ•° (0 ã¯æ©Ÿèƒ½ã‚’ç„¡åŠ¹åŒ–ã€æ—¢å®š 10 ç§’)', 'Application URL' => 'アプリケーション㮠URL', - 'Token regenerated.' => 'トークンãŒå†ç”Ÿæˆã•れã¾ã—ãŸã€‚', + 'Token regenerated.' => 'トークンãŒå†ç”Ÿæˆã•れã¾ã—ãŸ', 'Date format' => 'データã®ãƒ•ォーマット', 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO フォーマットãŒå…¥åŠ›ã§ãã¾ã™(例: %s ã¾ãŸã¯ %s)', - 'New private project' => 'éžå…¬é–‹ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã‚’作る', + 'New private project' => 'éžå…¬é–‹ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆä½œæˆ', 'This project is private' => 'ã“ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã¯éžå…¬é–‹ã§ã™', 'Add' => 'è¿½åŠ ', 'Start date' => '開始時間', - 'Time estimated' => '予想時間', - 'There is nothing assigned to you.' => '何もアサインã•れã¦ã„ã¾ã›ã‚“。', + 'Time estimated' => 'è¦‹ç©æ™‚é–“', + 'There is nothing assigned to you.' => '何もアサインã•れã¦ã„ã¾ã›ã‚“', 'My tasks' => '自分ã®ã‚¿ã‚¹ã‚¯', - 'Activity stream' => 'アクティビティストリーム', + 'Activity stream' => '活動状æ³', 'Dashboard' => 'ダッシュボード', 'Confirmation' => '確èª', - 'Allow everybody to access to this project' => '全員ã«ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’許ã™', - 'Everybody have access to this project.' => '誰ã§ã‚‚ã“ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã™ã€‚', 'Webhooks' => 'Webhook', 'API' => 'API', - 'Create a comment from an external provider' => '外部サービスã‹ã‚‰ã‚³ãƒ¡ãƒ³ãƒˆã‚’作æˆã™ã‚‹', - 'Project management' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆãƒ»ãƒžãƒã‚¸ãƒ¡ãƒ³ãƒˆ', - 'My projects' => '自分ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆ', + 'Create a comment from an external provider' => '外部サービスã‹ã‚‰ã‚³ãƒ¡ãƒ³ãƒˆã‚’作æˆ', + 'Project management' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆç®¡ç†', 'Columns' => 'カラム', 'Task' => 'タスク', - 'Your are not member of any project.' => 'ã©ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã«ã‚‚属ã—ã¦ã„ã¾ã›ã‚“。', 'Percentage' => '割åˆ', 'Number of tasks' => 'タスク数', 'Task distribution' => 'タスク分布', 'Analytics' => '分æž', 'Subtask' => 'サブタスク', - 'My subtasks' => '自分ã®ã‚µãƒ–タスク', 'User repartition' => '担当者分布', - 'Clone this project' => 'ã“ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã‚’複製ã™ã‚‹', + 'Clone this project' => 'ã“ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã‚’複製', 'Column removed successfully.' => 'カラムを削除ã—ã¾ã—ãŸ', - 'Not enough data to show the graph.' => 'グラフをæç”»ã™ã‚‹ã«ã¯å‡ºãŸãŒè¶³ã‚Šã¾ã›ã‚“', + 'Not enough data to show the graph.' => 'グラフをæç”»ã™ã‚‹ãŸã‚ã®ãƒ‡ãƒ¼ã‚¿ãŒä¸è¶³ã—ã¦ã„ã¾ã™', 'Previous' => '戻る', 'The id must be an integer' => 'id ã¯æ•°å—ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“', 'The project id must be an integer' => 'project id ã¯æ•°å—ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“', @@ -435,19 +428,19 @@ return array( 'This export contains the number of tasks per column grouped per day.' => 'ã“ã®å‡ºåŠ›ã¯æ—¥æ™‚ã®ã‚«ãƒ©ãƒ ã”ã¨ã®ã‚¿ã‚¹ã‚¯æ•°ã‚’集計ã—ãŸã‚‚ã®ã§ã™', 'Active swimlanes' => 'アクティブãªã‚¹ã‚¤ãƒ レーン', 'Add a new swimlane' => 'æ–°ã—ã„スイムレーン', - 'Default swimlane' => 'デフォルトスイムレーン', - 'Do you really want to remove this swimlane: "%s"?' => 'ã“ã®ã‚¹ã‚¤ãƒ レーン「%sã€ã‚’本当ã«å‰Šé™¤ã—ã¾ã™ã‹ï¼Ÿ', + 'Default swimlane' => '既定スイムレーン', + 'Do you really want to remove this swimlane: "%s"?' => 'スイムレーン「%sã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ', 'Inactive swimlanes' => 'インタラクティブãªã‚¹ã‚¤ãƒ レーン', 'Remove a swimlane' => 'スイムレーンã®å‰Šé™¤', 'Swimlane modification for the project "%s"' => '「%sã€ã«å¯¾ã™ã‚‹ã‚¹ã‚¤ãƒ レーン変更', - 'Swimlane removed successfully.' => 'スイムレーンを削除ã—ã¾ã—ãŸã€‚', + 'Swimlane removed successfully.' => 'スイムレーンを削除ã—ã¾ã—ãŸ', 'Swimlanes' => 'スイムレーン', - 'Swimlane updated successfully.' => 'スイムレーンを更新ã—ã¾ã—ãŸã€‚', - 'Unable to remove this swimlane.' => 'スイムレーンを削除ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚', - 'Unable to update this swimlane.' => 'スイムレーンを更新ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚', - 'Your swimlane have been created successfully.' => 'スイムレーンãŒä½œæˆã•れã¾ã—ãŸã€‚', + 'Swimlane updated successfully.' => 'スイムレーンを更新ã—ã¾ã—ãŸ', + 'Unable to remove this swimlane.' => 'スイムレーンを削除ã§ãã¾ã›ã‚“ã§ã—ãŸ', + 'Unable to update this swimlane.' => 'スイムレーンを更新ã§ãã¾ã›ã‚“ã§ã—ãŸ', + 'Your swimlane have been created successfully.' => 'スイムレーンãŒä½œæˆã•れã¾ã—ãŸ', 'Example: "Bug, Feature Request, Improvement"' => '例: ãƒã‚°, 機能, 改善', - 'Default categories for new projects (Comma-separated)' => 'æ–°ã—ã„プãƒã‚¸ã‚§ã‚¯ãƒˆã®ãƒ‡ãƒ•ォルトカテゴリー (コンマ区切り)', + 'Default categories for new projects (Comma-separated)' => 'æ–°ã—ã„プãƒã‚¸ã‚§ã‚¯ãƒˆã®æ—¢å®šã‚«ãƒ†ã‚´ãƒªãƒ¼ (コンマ区切り)', 'Integrations' => '連æº', 'Integration with third-party services' => 'サードパーティサービスã¨ã®é€£æº', 'Subtask Id' => 'サブタスク Id', @@ -455,38 +448,37 @@ return array( 'Subtasks Export' => 'サブタスクã®å‡ºåŠ›', 'Task Title' => 'タスクタイトル', 'Untitled' => 'タイトル無ã—', - 'Application default' => 'アプリケーションデフォルト', - 'Language:' => '言語:', - 'Timezone:' => 'タイムゾーン:', + 'Application default' => 'ã‚¢ãƒ—ãƒªã‚±ãƒ¼ã‚·ãƒ§ãƒ³ã®æ—¢å®šå€¤', + 'Language:' => '言語:', + 'Timezone:' => 'タイムゾーン:', 'All columns' => 'å…¨ã¦ã®ã‚«ãƒ©ãƒ ', - 'Calendar' => 'カレンダー', 'Next' => '次ã¸', '#%d' => '#%d', 'All swimlanes' => 'å…¨ã¦ã®ã‚¹ã‚¤ãƒ レーン', 'All colors' => 'å…¨ã¦ã®è‰²', 'Moved to column %s' => 'カラム%s ã¸ç§»å‹•ã—ã¾ã—ãŸ', - 'User dashboard' => 'ユーザダッシュボード', - 'Allow only one subtask in progress at the same time for a user' => '一人ã®ãƒ¦ãƒ¼ã‚¶ã«ã¤ã一ã¤ã®ã‚¿ã‚¹ã‚¯ã®ã¿é€²è¡Œä¸ã«ã§ãã¾ã™', + 'User dashboard' => 'ユーザーダッシュボード', + 'Allow only one subtask in progress at the same time for a user' => '1ユーザーã«ã¤ã1ä»¶ã®ã‚¿ã‚¹ã‚¯ã®ã¿ã‚’進行ä¸ã«ã§ãるよã†åˆ¶é™', 'Edit column "%s"' => 'カラム「%sã€ã®ç·¨é›†', 'Select the new status of the subtask: "%s"' => 'サブタスク「%sã€ã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ã‚’é¸æŠž', 'Subtask timesheet' => 'サブタスクタイムシート', - 'There is nothing to show.' => '何も表示ã™ã‚‹ã‚‚ã®ãŒã‚りã¾ã›ã‚“。', + 'There is nothing to show.' => '何も表示ã™ã‚‹ã‚‚ã®ãŒã‚りã¾ã›ã‚“', 'Time Tracking' => 'タイムトラッã‚ング', - 'You already have one subtask in progress' => 'ã™ã§ã«é€²è¡Œä¸ã®ã‚µãƒ–タスクãŒã‚りã¾ã™ã€‚', + 'You already have one subtask in progress' => 'ã™ã§ã«é€²è¡Œä¸ã®ã‚µãƒ–タスクãŒã‚りã¾ã™', 'Which parts of the project do you want to duplicate?' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®ä½•を複製ã—ã¾ã™ã‹ï¼Ÿ', - // 'Disallow login form' => '', + 'Disallow login form' => 'ãƒã‚°ã‚¤ãƒ³ãƒ•ォームã‹ã‚‰ã®ãƒã‚°ã‚¤ãƒ³ã‚’許å¯ã—ãªã„', 'Start' => 'é–‹å§‹', 'End' => '終了', 'Task age in days' => 'タスクã®çµŒéŽæ—¥æ•°', 'Days in this column' => 'カラムã§ã®çµŒéŽæ—¥æ•°', '%dd' => '%d æ—¥', 'Add a new link' => 'æ–°ã—ã„リンクã®è¿½åŠ ', - 'Do you really want to remove this link: "%s"?' => 'リンク「%sã€ã‚’本当ã«å‰Šé™¤ã—ã¾ã™ã‹ï¼Ÿ', + 'Do you really want to remove this link: "%s"?' => 'リンク「%sã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ', 'Do you really want to remove this link with task #%d?' => 'ã“ã®ãƒªãƒ³ã‚¯ã¨ã‚¿ã‚¹ã‚¯#%dを削除ã—ã¾ã™ã‹ï¼Ÿ', 'Field required' => 'フィールドãŒå¿…è¦ã§ã™', - 'Link added successfully.' => 'ãƒªãƒ³ã‚¯ã‚’è¿½åŠ ã—ã¾ã—ãŸã€‚', - 'Link updated successfully.' => 'リンクを更新ã—ã¾ã—ãŸã€‚', - 'Link removed successfully.' => 'リンクを削除ã—ã¾ã—ãŸã€‚', + 'Link added successfully.' => 'ãƒªãƒ³ã‚¯ã‚’è¿½åŠ ã—ã¾ã—ãŸ', + 'Link updated successfully.' => 'リンクを更新ã—ã¾ã—ãŸ', + 'Link removed successfully.' => 'リンクを削除ã—ã¾ã—ãŸ', 'Link labels' => 'リンクラベル', 'Link modification' => 'リンクã®å¤‰æ›´', 'Links' => 'リンク', @@ -495,9 +487,9 @@ return array( 'The labels must be different' => 'ç•°ãªã‚‹ãƒ©ãƒ™ãƒ«ã‚’指定ã—ã¦ãã ã•ã„', 'There is no link.' => 'リンクãŒã‚りã¾ã›ã‚“', 'This label must be unique' => 'ラベルã¯ãƒ¦ãƒ‹ãƒ¼ã‚¯ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™', - 'Unable to create your link.' => 'リンクを作æˆã§ãã¾ã›ã‚“ã§ã—ãŸã€‚', - 'Unable to update your link.' => 'リンクを更新ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚', - 'Unable to remove this link.' => 'リンクを削除ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚', + 'Unable to create your link.' => 'リンクを作æˆã§ãã¾ã›ã‚“ã§ã—ãŸ', + 'Unable to update your link.' => 'リンクを更新ã§ãã¾ã›ã‚“ã§ã—ãŸ', + 'Unable to remove this link.' => 'リンクを削除ã§ãã¾ã›ã‚“ã§ã—ãŸ', 'relates to' => '次ã«é–¢é€£ã—ã¾ã™', 'blocks' => '次をブãƒãƒƒã‚¯ã—ã¦ã„ã¾ã™', 'is blocked by' => '次ã«ãƒ–ãƒãƒƒã‚¯ã•れã¦ã„ã¾ã™', @@ -512,11 +504,11 @@ return array( 'This task' => 'ã“ã®ã‚¿ã‚¹ã‚¯ã¯', '<1h' => '<1時間', '%dh' => '%d 時間', - 'Expand tasks' => 'タスクを展開ã™ã‚‹', - 'Collapse tasks' => 'タスクを閉ã˜ã‚‹', + 'Expand tasks' => 'タスクを詳細表示', + 'Collapse tasks' => 'タスクを簡略表示', 'Expand/collapse tasks' => 'タスクã®å±•é–‹ï¼é–‰ã˜ã‚‹', 'Close dialog box' => 'ダイアãƒã‚°ãƒœãƒƒã‚¯ã‚¹ã‚’é–‰ã˜ã‚‹', - 'Submit a form' => 'フォームをé€ä¿¡ã™ã‚‹', + 'Submit a form' => 'フォームをé€ä¿¡', 'Board view' => 'ボードビュー', 'Keyboard shortcuts' => 'ã‚ーボードショートカット', 'Open board switcher' => 'ボード切り替ãˆã‚’é–‹ã', @@ -537,801 +529,841 @@ return array( 'JPY - Japanese Yen' => 'JPY - 日本円', 'NZD - New Zealand Dollar' => 'NZD - NZ ドル', 'RSD - Serbian dinar' => 'RSD - セルビアデナール', - // 'CNY - Chinese Yuan' => '', + 'CNY - Chinese Yuan' => 'CNY - ä¸å›½å…ƒ', 'USD - US Dollar' => 'USD - 米ドル', 'Destination column' => '移動先ã®ã‚«ãƒ©ãƒ ', - 'Move the task to another column when assigned to a user' => 'ユーザã®å‰²ã‚Šå½“ã¦ã‚’ã—ãŸã‚‰ã‚¿ã‚¹ã‚¯ã‚’ä»–ã®ã‚«ãƒ©ãƒ ã«ç§»å‹•', - 'Move the task to another column when assignee is cleared' => 'ユーザã®å‰²ã‚Šå½“ã¦ãŒãªããªã£ãŸã‚‰ã‚¿ã‚¹ã‚¯ã‚’ä»–ã®ã‚«ãƒ©ãƒ ã«ç§»å‹•', + 'Move the task to another column when assigned to a user' => 'ユーザーã®å‰²å½“ã¦ã‚’ã—ãŸã‚‰ã‚¿ã‚¹ã‚¯ã‚’ä»–ã®ã‚«ãƒ©ãƒ ã«ç§»å‹•', + 'Move the task to another column when assignee is cleared' => 'ユーザーã®å‰²å½“ã¦ãŒãªããªã£ãŸã‚‰ã‚¿ã‚¹ã‚¯ã‚’ä»–ã®ã‚«ãƒ©ãƒ ã«ç§»å‹•', 'Source column' => '移動元ã®ã‚«ãƒ©ãƒ ', 'Transitions' => 'å±¥æ´', 'Executer' => '実行者', - 'Time spent in the column' => 'カラムã§ã®æ™‚間消費', + 'Time spent in the column' => 'カラムã§ã®çµŒéŽæ™‚é–“', 'Task transitions' => 'タスクã®é·ç§»', 'Task transitions export' => 'タスクã®é·ç§»ã‚’出力', - 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'ã“ã®ãƒ¬ãƒãƒ¼ãƒˆã¯ã‚¿ã‚¹ã‚¯ã®ã‚«ãƒ©ãƒ é–“ã«ãŠã‘る移動を時間ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã€çµŒéŽæ™‚é–“ã¨å…±ã«è¨˜éŒ²ã—ãŸç‰©ã§ã™ã€‚', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'ã“ã®ãƒ¬ãƒãƒ¼ãƒˆã¯ã‚¿ã‚¹ã‚¯ã®ã‚«ãƒ©ãƒ é–“ã«ãŠã‘る移動を時間ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã€çµŒéŽæ™‚é–“ã¨å…±ã«è¨˜éŒ²ã—ãŸç‰©ã§ã™', 'Currency rates' => '為替レート', 'Rate' => 'レート', 'Change reference currency' => 'ç¾åœ¨ã®åŸºè»¸é€šè²¨', 'Reference currency' => '基軸通貨', - // 'The currency rate have been added successfully.' => '', - 'Unable to add this currency rate.' => 'ã“ã®é€šè²¨ãƒ¬ãƒ¼ãƒˆã‚’è¿½åŠ ã§ãã¾ã›ã‚“。', + 'The currency rate have been added successfully.' => 'é€šè²¨ãƒ¬ãƒ¼ãƒˆãŒæ£å¸¸ã«è¿½åŠ ã•れã¾ã—ãŸ', + 'Unable to add this currency rate.' => 'ã“ã®é€šè²¨ãƒ¬ãƒ¼ãƒˆã‚’è¿½åŠ ã§ãã¾ã›ã‚“', 'Webhook URL' => 'Webhook URL', - '%s removed the assignee of the task %s' => '%s ãŒã‚¿ã‚¹ã‚¯ã€Œ%sã€ã®æ‹…当を解除ã—ã¾ã—ãŸã€‚', - 'Enable Gravatar images' => 'Gravatar イメージを有効化', + '%s removed the assignee of the task %s' => '%s ãŒã‚¿ã‚¹ã‚¯ã€Œ%sã€ã®æ‹…当を解除ã—ã¾ã—ãŸ', 'Information' => 'æƒ…å ±ã€€', - 'Check two factor authentication code' => '2 段èªè¨¼ã‚’ãƒã‚§ãƒƒã‚¯ã™ã‚‹', - 'The two factor authentication code is not valid.' => '2 段èªè¨¼ã‚³ãƒ¼ãƒ‰ã¯ç„¡åйã§ã™ã€‚', - 'The two factor authentication code is valid.' => '2 段èªè¨¼ã‚³ãƒ¼ãƒ‰ã¯æœ‰åйã§ã™ã€‚', + 'Check two factor authentication code' => '二è¦ç´ èªè¨¼ã‚’確èª', + 'The two factor authentication code is not valid.' => '二è¦ç´ èªè¨¼ã‚³ãƒ¼ãƒ‰ã¯ç„¡åйã§ã™', + 'The two factor authentication code is valid.' => '二è¦ç´ èªè¨¼ã‚³ãƒ¼ãƒ‰ã¯æœ‰åйã§ã™', 'Code' => 'コード', - 'Two factor authentication' => '2 段èªè¨¼', - 'This QR code contains the key URI: ' => 'ã“ã® QR コード㌠URI ã‚ーをå«ã‚“ã§ã„ã¾ã™: ', + 'Two factor authentication' => '二è¦ç´ èªè¨¼', + 'This QR code contains the key URI: ' => 'ã“ã® QR コード㌠URI ã‚ーをå«ã‚“ã§ã„ã¾ã™ï¼š', 'Check my code' => '自分ã®ã‚³ãƒ¼ãƒ‰ã‚’ãƒã‚§ãƒƒã‚¯', - 'Secret key: ' => '秘密éµ: ', - 'Test your device' => 'デãƒã‚¤ã‚¹ã‚’テストã™ã‚‹', - // 'Assign a color when the task is moved to a specific column' => '', - // '%s via Kanboard' => '', - // 'Burndown chart' => '', - // 'This chart show the task complexity over the time (Work Remaining).' => '', - // 'Screenshot taken %s' => '', - // 'Add a screenshot' => '', - // 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => '', - // 'Screenshot uploaded successfully.' => '', - // 'SEK - Swedish Krona' => '', - // 'Identifier' => '', - // 'Disable two factor authentication' => '', - // 'Do you really want to disable the two factor authentication for this user: "%s"?' => '', - // 'Edit link' => '', - // 'Start to type task title...' => '', - // 'A task cannot be linked to itself' => '', - // 'The exact same link already exists' => '', - // 'Recurrent task is scheduled to be generated' => '', - // 'Score' => '', - // 'The identifier must be unique' => '', - // 'This linked task id doesn\'t exists' => '', - // 'This value must be alphanumeric' => '', - // 'Edit recurrence' => '', - // 'Generate recurrent task' => '', - // 'Trigger to generate recurrent task' => '', - // 'Factor to calculate new due date' => '', - // 'Timeframe to calculate new due date' => '', - // 'Base date to calculate new due date' => '', - // 'Action date' => '', - // 'Base date to calculate new due date: ' => '', - // 'This task has created this child task: ' => '', - // 'Day(s)' => '', - // 'Existing due date' => '', - // 'Factor to calculate new due date: ' => '', - // 'Month(s)' => '', - // 'Recurrence' => '', - // 'This task has been created by: ' => '', - // 'Recurrent task has been generated:' => '', - // 'Timeframe to calculate new due date: ' => '', - // 'Trigger to generate recurrent task: ' => '', - // 'When task is closed' => '', - // 'When task is moved from first column' => '', - // 'When task is moved to last column' => '', - // 'Year(s)' => '', - // 'Calendar settings' => '', - // 'Project calendar view' => '', - // 'Project settings' => '', - // 'Show subtasks based on the time tracking' => '', - // 'Show tasks based on the creation date' => '', - // 'Show tasks based on the start date' => '', - // 'Subtasks time tracking' => '', - // 'User calendar view' => '', - // 'Automatically update the start date' => '', - // 'iCal feed' => '', - // 'Preferences' => '', - // 'Security' => '', - // 'Two factor authentication disabled' => '', - // 'Two factor authentication enabled' => '', - // 'Unable to update this user.' => '', - // 'There is no user management for private projects.' => '', - // 'User that will receive the email' => '', - // 'Email subject' => '', - // 'Date' => '', - // 'Add a comment log when moving the task between columns' => '', - // 'Move the task to another column when the category is changed' => '', - // 'Send a task by email to someone' => '', - // 'Reopen a task' => '', - // 'Notification' => '', - // '%s moved the task #%d to the first swimlane' => '', - // 'Swimlane' => '', - // 'Gravatar' => '', - // '%s moved the task %s to the first swimlane' => '', - // '%s moved the task %s to the swimlane "%s"' => '', - // 'This report contains all subtasks information for the given date range.' => '', - // 'This report contains all tasks information for the given date range.' => '', - // 'Project activities for %s' => '', - // 'view the board on Kanboard' => '', - // 'The task have been moved to the first swimlane' => '', - // 'The task have been moved to another swimlane:' => '', - // 'New title: %s' => '', - // 'The task is not assigned anymore' => '', - // 'New assignee: %s' => '', - // 'There is no category now' => '', - // 'New category: %s' => '', - // 'New color: %s' => '', - // 'New complexity: %d' => '', - // 'The due date have been removed' => '', - // 'There is no description anymore' => '', - // 'Recurrence settings have been modified' => '', - // 'Time spent changed: %sh' => '', - // 'Time estimated changed: %sh' => '', - // 'The field "%s" have been updated' => '', - // 'The description has been modified:' => '', - // 'Do you really want to close the task "%s" as well as all subtasks?' => '', - // 'I want to receive notifications for:' => '', - // 'All tasks' => '', - // 'Only for tasks assigned to me' => '', - // 'Only for tasks created by me' => '', - // 'Only for tasks created by me and assigned to me' => '', + 'Secret key: ' => '秘密éµï¼š', + 'Test your device' => 'ã‚ãªãŸã®ãƒ‡ãƒã‚¤ã‚¹ã‚’テスト', + 'Assign a color when the task is moved to a specific column' => 'タスクãŒç‰¹å®šã®ã‚«ãƒ©ãƒ ã«ç§»å‹•ã—ãŸæ™‚ã®è‰²ã‚’è¨å®š', + '%s via Kanboard' => '%s by Kanboard', + 'Burndown chart' => 'ãƒã‚¦ãƒ³ãƒ€ãƒªãƒ¼ãƒãƒ£ãƒ¼ãƒˆ', + 'This chart show the task complexity over the time (Work Remaining).' => 'グラフã¯ã€æ™‚é–“ã®çµŒéŽã¨ã¨ã‚‚ã«ã‚¿ã‚¹ã‚¯ã®è¤‡é›‘ã•を示ã—ã¦ã„ã¾ã™ï¼ˆæ®‹ã£ã¦ã„る作æ¥ï¼‰', + 'Screenshot taken %s' => 'スクリーンショット %s', + 'Add a screenshot' => 'ã‚¹ã‚¯ãƒªãƒ¼ãƒ³ã‚·ãƒ§ãƒƒãƒˆã‚’è¿½åŠ ', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'スクリーンショットを撮りCTRL + Vã¾ãŸã¯âŒ˜+ Vを押ã—ã¦ã“ã“ã«è²¼ã‚Šä»˜ã‘ã¦ãã ã•ã„', + 'Screenshot uploaded successfully.' => 'ã‚¹ã‚¯ãƒªãƒ¼ãƒ³ã‚·ãƒ§ãƒƒãƒˆãŒæ£å¸¸ã«ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰ã•れã¾ã—ãŸ', + 'SEK - Swedish Krona' => 'SEK - スウェーデン クãƒãƒ¼ãƒŠ', + 'Identifier' => 'è˜åˆ¥å', + 'Disable two factor authentication' => '二è¦ç´ èªè¨¼ã‚’無効ã«ã™ã‚‹', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => '本当ã«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã€Œ%sã€ã®äºŒè¦ç´ èªè¨¼ã‚’無効ã«ã—ã¾ã™ã‹ï¼Ÿ', + 'Edit link' => 'リンクを編集', + 'Start to type task title...' => 'タスクタイトルを入力...', + 'A task cannot be linked to itself' => 'タスクをタスク自身ã«ã¯ãƒªãƒ³ã‚¯ã§ãã¾ã›ã‚“', + 'The exact same link already exists' => 'åŒã˜ãƒªãƒ³ã‚¯ãŒæ—¢ã«å˜åœ¨ã—ã¦ã„ã¾ã™', + 'Recurrent task is scheduled to be generated' => 'å復タスクãŒç”Ÿæˆã•れるよã†ã«ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ«ã•れã¦ã„ã¾ã™', + 'Score' => 'スコア', + 'The identifier must be unique' => 'è˜åˆ¥åã¯ãƒ¦ãƒ‹ãƒ¼ã‚¯ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“', + 'This linked task id doesn\'t exists' => 'リンクã•れãŸã‚¿ã‚¹ã‚¯IDã¯å˜åœ¨ã—ã¾ã›ã‚“', + 'This value must be alphanumeric' => '英数å—ã§å…¥åŠ›ãã ã•ã„', + 'Edit recurrence' => 'å復タスクã®ç·¨é›†', + 'Generate recurrent task' => 'å復タスクを生æˆ', + 'Trigger to generate recurrent task' => 'å復タスクを生æˆã™ã‚‹ãŸã‚ã®ãƒˆãƒªã‚¬', + 'Factor to calculate new due date' => 'æ–°ã—ã„æœŸé™ã‚’計算ã™ã‚‹è¦ç´ ', + 'Timeframe to calculate new due date' => 'æ–°ã—ã„æœŸé™ã‚’計算ã™ã‚‹ãŸã‚ã®æ™‚é–“æž ', + 'Base date to calculate new due date' => 'æ–°ã—ã„æœŸé™ã‚’計算ã™ã‚‹ãŸã‚ã®åŸºæº–æ—¥', + 'Action date' => '実行日', + 'Base date to calculate new due date: ' => 'æ–°ã—ã„æœŸé™ã‚’計算ã™ã‚‹ãŸã‚ã®åŸºæº–日:', + 'This task has created this child task: ' => 'ã“ã®ã‚¿ã‚¹ã‚¯ã¯ã“ã®åタスクを作æˆã—ã¾ã—ãŸï¼š', + 'Day(s)' => 'æ—¥', + 'Existing due date' => 'æ—¢å˜ã®äºˆå®šæ—¥', + 'Factor to calculate new due date: ' => 'æ–°ã—ã„æ”¯æ‰•期é™ã‚’計算ã™ã‚‹è¦ç´ :', + 'Month(s)' => 'ヶ月', + 'Recurrence' => 'å復タスク', + 'This task has been created by: ' => 'タスク作æˆè€…', + 'Recurrent task has been generated:' => 'å復タスクãŒç”Ÿæˆã•れã¾ã—ãŸï¼š', + 'Timeframe to calculate new due date: ' => 'æ–°ã—ã„æœŸé™ã‚’計算ã™ã‚‹æ™‚é–“æž ï¼š', + 'Trigger to generate recurrent task: ' => 'å復タスクを生æˆã™ã‚‹ãŸã‚ã®ãƒˆãƒªã‚¬ãƒ¼ï¼š', + 'When task is closed' => 'タスクãŒå®Œäº†ã—ãŸã¨ã', + 'When task is moved from first column' => 'ã‚¿ã‚¹ã‚¯ãŒæœ€åˆã®ã‚«ãƒ©ãƒ ã‹ã‚‰ç§»å‹•ã•れãŸã¨ã', + 'When task is moved to last column' => 'ã‚¿ã‚¹ã‚¯ãŒæœ€å¾Œã®ã‚«ãƒ©ãƒ ã«ç§»å‹•ã—ãŸã¨ã', + 'Year(s)' => 'å¹´', + 'Project settings' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆè¨å®š', + 'Automatically update the start date' => 'é–‹å§‹æ—¥ã‚’è‡ªå‹•çš„ã«æ›´æ–°', + 'iCal feed' => 'iCal é…ä¿¡', + 'Preferences' => 'プリファレンス', + 'Security' => 'ã‚»ã‚ュリティ', + 'Two factor authentication disabled' => '二è¦ç´ èªè¨¼ã¯ç„¡åйã§ã™', + 'Two factor authentication enabled' => '二è¦ç´ èªè¨¼ã¯æœ‰åйã§ã™', + 'Unable to update this user.' => 'ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’æ›´æ–°ã§ãã¾ã›ã‚“', + 'There is no user management for private projects.' => 'プライベート・プãƒã‚¸ã‚§ã‚¯ãƒˆã®ãŸã‚ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ç®¡ç†ã¯ã‚りã¾ã›ã‚“', + 'User that will receive the email' => 'メールをå—ä¿¡ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼', + 'Email subject' => 'メールã®ä»¶å', + 'Date' => '日付', + 'Add a comment log when moving the task between columns' => 'カラム間ã§ã‚¿ã‚¹ã‚¯ã‚’移動ã™ã‚‹ã¨ãã«ã‚³ãƒ¡ãƒ³ãƒˆãƒã‚°ã‚’è¿½åŠ ', + 'Move the task to another column when the category is changed' => 'カテゴリãŒå¤‰æ›´ã•れãŸã¨ãã«ã‚¿ã‚¹ã‚¯ã‚’別ã®ã‚«ãƒ©ãƒ ã«ç§»å‹•', + 'Send a task by email to someone' => 'タスクをメールã§é€ä¿¡', + 'Reopen a task' => 'タスクをå†é–‹', + 'Notification' => '通知', + '%s moved the task #%d to the first swimlane' => '%sã¯ã‚¿ã‚¹ã‚¯ï¼ƒ%dを最åˆã®ã‚¹ã‚¤ãƒ レーンã«ç§»å‹•ã—ã¾ã—ãŸ', + 'Swimlane' => 'スイムレーン', + '%s moved the task %s to the first swimlane' => '%sã¯ã‚¿ã‚¹ã‚¯%sを最åˆã®ã‚¹ã‚¤ãƒ レーンã«ç§»å‹•ã—ã¾ã—ãŸ', + '%s moved the task %s to the swimlane "%s"' => '%sã¯ã‚¿ã‚¹ã‚¯%sをスイムレーン "%s"ã«ç§»å‹•ã—ã¾ã—ãŸ', + 'This report contains all subtasks information for the given date range.' => 'ã“ã®ãƒ¬ãƒãƒ¼ãƒˆã«ã¯ã€æŒ‡å®šã—ãŸæœŸé–“ã®ã™ã¹ã¦ã®ã‚µãƒ–ã‚¿ã‚¹ã‚¯æƒ…å ±ãŒå«ã¾ã‚Œã¦ã„ã¾ã™', + 'This report contains all tasks information for the given date range.' => 'ã“ã®ãƒ¬ãƒãƒ¼ãƒˆã«ã¯ã€æŒ‡å®šã—ãŸæœŸé–“ã®ã™ã¹ã¦ã®ã‚¿ã‚¹ã‚¯æƒ…å ±ãŒå«ã¾ã‚Œã¾ã™', + 'Project activities for %s' => '%sã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã®æ´»å‹•状æ³', + 'view the board on Kanboard' => 'Kanboard上ã®ãƒœãƒ¼ãƒ‰ã‚’見る', + 'The task have been moved to the first swimlane' => 'ã‚¿ã‚¹ã‚¯ã¯æœ€åˆã®ã‚¹ã‚¤ãƒ レーンã«ç§»å‹•ã•れã¾ã—ãŸ', + 'The task have been moved to another swimlane:' => 'ã‚¿ã‚¹ã‚¯ã¯æ¬¡ã®ã‚¹ã‚¤ãƒ レーンã«ç§»å‹•ã—ã¾ã—ãŸï¼š', + 'New title: %s' => 'æ–°ã—ã„タイトル:%s', + 'The task is not assigned anymore' => 'タスクã¯ã‚‚ã†å‰²å½“ã¦ã‚‰ã‚Œã¾ã›ã‚“', + 'New assignee: %s' => 'æ–°ã—ã„æ‹…当者:%s', + 'There is no category now' => 'カテゴリã¯ã‚りã¾ã›ã‚“', + 'New category: %s' => 'æ–°ã—ã„カテゴリ:%s', + 'New color: %s' => 'æ–°ã—ã„色:%s', + 'New complexity: %d' => 'æ–°ã—ã„複雑ã•:%d', + 'The due date have been removed' => '期é™ãŒå‰Šé™¤ã•れã¾ã—ãŸ', + 'There is no description anymore' => '説明ã¯ã‚りã¾ã›ã‚“', + 'Recurrence settings have been modified' => 'å復è¨å®šãŒå¤‰æ›´ã•れã¾ã—ãŸ', + 'Time spent changed: %sh' => 'çµŒéŽæ™‚é–“ãŒå¤‰æ›´ã•れã¾ã—ãŸï¼š%sh', + 'Time estimated changed: %sh' => 'è¦‹ç©æ™‚é–“ãŒå¤‰æ›´ã•れã¾ã—ãŸï¼š%sh', + 'The field "%s" have been updated' => 'フィールド "%s"ã¯æ›´æ–°ã•れã¾ã—ãŸ', + 'The description has been modified:' => '説明ãŒå¤‰æ›´ã•れã¾ã—ãŸï¼š', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'ã™ã¹ã¦ã®ã‚µãƒ–タスクをå«ã‚€ã‚¿ã‚¹ã‚¯"%s"を完了ã—ã¾ã™ã‹ï¼Ÿ', + 'I want to receive notifications for:' => '次ã®é€šçŸ¥ã‚’å—ä¿¡ã—ã¾ã™ï¼š', + 'All tasks' => 'ã™ã¹ã¦ã®ã‚¿ã‚¹ã‚¯', + 'Only for tasks assigned to me' => '自分ã®ã‚¿ã‚¹ã‚¯ã®ã¿', + 'Only for tasks created by me' => '自分ãŒä½œæˆã—ãŸã‚¿ã‚¹ã‚¯ã®ã¿', + 'Only for tasks created by me and assigned to me' => '自分ãŒä½œæˆã—ãŸè‡ªåˆ†ã®ã‚¿ã‚¹ã‚¯ã®ã¿', // '%%Y-%%m-%%d' => '', - // 'Total for all columns' => '', - // 'You need at least 2 days of data to show the chart.' => '', - // '<15m' => '', - // '<30m' => '', - // 'Stop timer' => '', - // 'Start timer' => '', - // 'My activity stream' => '', - // 'My calendar' => '', - // 'Search tasks' => '', - // 'Reset filters' => '', - // 'My tasks due tomorrow' => '', - // 'Tasks due today' => '', - // 'Tasks due tomorrow' => '', - // 'Tasks due yesterday' => '', - // 'Closed tasks' => '', - // 'Open tasks' => '', - // 'Not assigned' => '', - // 'View advanced search syntax' => '', - // 'Overview' => '', - // 'Board/Calendar/List view' => '', - // 'Switch to the board view' => '', - // 'Switch to the calendar view' => '', - // 'Switch to the list view' => '', - // 'Go to the search/filter box' => '', - // 'There is no activity yet.' => '', - // 'No tasks found.' => '', - // 'Keyboard shortcut: "%s"' => '', - // 'List' => '', - // 'Filter' => '', - // 'Advanced search' => '', - // 'Example of query: ' => '', - // 'Search by project: ' => '', - // 'Search by column: ' => '', - // 'Search by assignee: ' => '', - // 'Search by color: ' => '', - // 'Search by category: ' => '', - // 'Search by description: ' => '', - // 'Search by due date: ' => '', - // 'Average time spent into each column' => '', - // 'Average time spent' => '', - // 'This chart show the average time spent into each column for the last %d tasks.' => '', - // 'Average Lead and Cycle time' => '', - // 'Average lead time: ' => '', - // 'Average cycle time: ' => '', - // 'Cycle Time' => '', - // 'Lead Time' => '', - // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', - // 'Average time into each column' => '', - // 'Lead and cycle time' => '', - // 'Lead time: ' => '', - // 'Cycle time: ' => '', - // 'Time spent into each column' => '', - // 'The lead time is the duration between the task creation and the completion.' => '', - // 'The cycle time is the duration between the start date and the completion.' => '', - // 'If the task is not closed the current time is used instead of the completion date.' => '', - // 'Set automatically the start date' => '', - // 'Edit Authentication' => '', - // 'Remote user' => '', - // 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '', - // 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '', - // 'Default task color' => '', - // 'This feature does not work with all browsers.' => '', - // 'There is no destination project available.' => '', - // 'Trigger automatically subtask time tracking' => '', - // 'Include closed tasks in the cumulative flow diagram' => '', - // 'Current swimlane: %s' => '', - // 'Current column: %s' => '', - // 'Current category: %s' => '', - // 'no category' => '', - // 'Current assignee: %s' => '', - // 'not assigned' => '', - // 'Author:' => '', - // 'contributors' => '', - // 'License:' => '', - // 'License' => '', - // 'Enter the text below' => '', - // 'Sort by position' => '', - // 'Sort by date' => '', - // 'Add task' => '', - // 'Start date:' => '', - // 'Due date:' => '', - // 'There is no start date or due date for this task.' => '', - // 'Moving or resizing a task will change the start and due date of the task.' => '', - // 'There is no task in your project.' => '', - // 'Gantt chart' => '', - // 'People who are project managers' => '', - // 'People who are project members' => '', - // 'NOK - Norwegian Krone' => '', - // 'Show this column' => '', - // 'Hide this column' => '', - // 'open file' => '', - // 'End date' => '', - // 'Users overview' => '', - // 'Members' => '', - // 'Shared project' => '', - // 'Project managers' => '', - // 'Gantt chart for all projects' => '', - // 'Projects list' => '', - // 'Gantt chart for this project' => '', - // 'Project board' => '', - // 'End date:' => '', - // 'There is no start date or end date for this project.' => '', - // 'Projects Gantt chart' => '', - // 'Change task color when using a specific task link' => '', - // 'Task link creation or modification' => '', - // 'Milestone' => '', - // 'Documentation: %s' => '', - // 'Switch to the Gantt chart view' => '', - // 'Reset the search/filter box' => '', - // 'Documentation' => '', - // 'Table of contents' => '', - // 'Gantt' => '', - // 'Author' => '', - // 'Version' => '', - // 'Plugins' => '', - // 'There is no plugin loaded.' => '', - // 'My notifications' => '', - // 'Custom filters' => '', - // 'Your custom filter have been created successfully.' => '', - // 'Unable to create your custom filter.' => '', - // 'Custom filter removed successfully.' => '', - // 'Unable to remove this custom filter.' => '', - // 'Edit custom filter' => '', - // 'Your custom filter have been updated successfully.' => '', - // 'Unable to update custom filter.' => '', + 'Total for all columns' => 'ã™ã¹ã¦ã®ã‚«ãƒ©ãƒ ã®åˆè¨ˆ', + 'You need at least 2 days of data to show the chart.' => 'グラフを表示ã™ã‚‹ã«ã¯æœ€ä½Ž2日間ã®ãƒ‡ãƒ¼ã‚¿ãŒå¿…è¦ã§ã™', + '<15m' => '15分未満', + '<30m' => '30分未満', + 'Stop timer' => 'ã‚¿ã‚¤ãƒžãƒ¼åœæ¢', + 'Start timer' => 'タイマー開始', + 'My activity stream' => 'è‡ªåˆ†ã®æ´»å‹•状æ³', + 'Search tasks' => 'タスクを検索', + 'Reset filters' => 'フィルターをリセット', + 'My tasks due tomorrow' => '明日ã®è‡ªåˆ†ã®ã‚¿ã‚¹ã‚¯', + 'Tasks due today' => '今日ã®ã‚¿ã‚¹ã‚¯', + 'Tasks due tomorrow' => '明日ã®ã‚¿ã‚¹ã‚¯', + 'Tasks due yesterday' => '昨日ã®ã‚¿ã‚¹ã‚¯', + 'Closed tasks' => '完了タスク', + 'Open tasks' => '未完了タスク', + 'Not assigned' => '未割当', + 'View advanced search syntax' => 'è©³ç´°æ¤œç´¢ã®æ§‹æ–‡ã‚’見る', + 'Overview' => '概è¦', + 'Board/Calendar/List view' => 'ボード/カレンダー/リストビュー', + 'Switch to the board view' => 'ボードビューã«åˆ‡æ›¿', + 'Switch to the list view' => 'リストビューã«åˆ‡æ›¿', + 'Go to the search/filter box' => '検索/フィルタボックスã«ç§»å‹•', + 'There is no activity yet.' => 'ã¾ã 活動状æ³ã¯ã‚りã¾ã›ã‚“', + 'No tasks found.' => 'タスクãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“', + 'Keyboard shortcut: "%s"' => 'ã‚ーボード・ショートカット: "%s"', + 'List' => 'リスト', + 'Filter' => 'フィルター', + 'Advanced search' => '詳細検索', + 'Example of query: ' => 'クエリã®ä¾‹ï¼š', + 'Search by project: ' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã§æ¤œç´¢ï¼š', + 'Search by column: ' => 'ã‚«ãƒ©ãƒ ã§æ¤œç´¢ï¼š', + 'Search by assignee: ' => 'æ‹…å½“è€…ã§æ¤œç´¢ï¼š', + 'Search by color: ' => 'è‰²ã§æ¤œç´¢ï¼š', + 'Search by category: ' => 'ã‚«ãƒ†ã‚´ãƒªã§æ¤œç´¢ï¼š', + 'Search by description: ' => 'èª¬æ˜Žã§æ¤œç´¢ï¼š', + 'Search by due date: ' => '期é™ã§æ¤œç´¢ï¼š', + 'Average time spent into each column' => 'å„カラムã§çµŒéŽã—ãŸæ™‚é–“ã®å¹³å‡', + 'Average time spent' => 'å¹³å‡çµŒéŽæ™‚é–“', + 'This chart show the average time spent into each column for the last %d tasks.' => 'ã“ã®ã‚°ãƒ©ãƒ•ã¯æœ€å¾Œã®%d個ã®ã‚¿ã‚¹ã‚¯ã«å¯¾ã—ã¦å„カラムã§çµŒéŽã—ãŸå¹³å‡æ™‚間を示ã—ã¦ã„ã¾ã™', + 'Average Lead and Cycle time' => 'å¹³å‡ãƒªãƒ¼ãƒ‰ã‚¿ã‚¤ãƒ ・平å‡ã‚µã‚¤ã‚¯ãƒ«ã‚¿ã‚¤ãƒ ', + 'Average lead time: ' => 'å¹³å‡ãƒªãƒ¼ãƒ‰ã‚¿ã‚¤ãƒ :', + 'Average cycle time: ' => 'å¹³å‡ã‚µã‚¤ã‚¯ãƒ«ã‚¿ã‚¤ãƒ :', + 'Cycle Time' => 'サイクルタイム', + 'Lead Time' => 'リードタイム', + 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'ã“ã®ã‚°ãƒ©ãƒ•ã¯æ™‚é–“ã®çµŒéŽã«ä¼´ã†æœ€å¾Œã®%d個ã®ã‚¿ã‚¹ã‚¯ã®å¹³å‡ãƒªãƒ¼ãƒ‰ã‚¿ã‚¤ãƒ ã¨ã‚µã‚¤ã‚¯ãƒ«ã‚¿ã‚¤ãƒ を示ã—ã¾ã™', + 'Average time into each column' => 'å„カラムã®å¹³å‡æ™‚é–“', + 'Lead and cycle time' => 'リードï¼ã‚µã‚¤ã‚¯ãƒ«ã‚¿ã‚¤ãƒ ', + 'Lead time: ' => 'リードタイム:', + 'Cycle time: ' => 'サイクルタイム:', + 'Time spent into each column' => 'å„カラムã®çµŒéŽæ™‚é–“', + 'The lead time is the duration between the task creation and the completion.' => 'リードタイムã¯ã‚¿ã‚¹ã‚¯ã®ä½œæˆã€œå®Œäº†ã¾ã§æ™‚é–“ã§ã™', + 'The cycle time is the duration between the start date and the completion.' => 'サイクルタイムã¯ç€æ‰‹ã€œå®Œäº†ã¾ã§ã®æœŸé–“ã§ã™', + 'If the task is not closed the current time is used instead of the completion date.' => 'タスクãŒçµ‚了ã—ã¦ã„ãªã„å ´åˆã¯å®Œäº†æ—¥ã®ä»£ã‚りã«ç¾åœ¨æ™‚刻を使用ã—ã¾ã™', + 'Set automatically the start date' => '開始日を自動的ã«è¨å®š', + 'Edit Authentication' => 'èªè¨¼ã®ç·¨é›†', + 'Remote user' => 'リモートユーザー', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'リモートユーザーã¯ãƒ‘スワードをKanboardデータベースã«ä¿å˜ã—ã¾ã›ã‚“(例:LDAPã€Googleã€Githubã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆï¼‰', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '「ãƒã‚°ã‚¤ãƒ³ãƒ•ォームを許å¯ã—ãªã„ã€ãƒã‚§ãƒƒã‚¯ãƒœãƒƒã‚¯ã‚¹ã‚’オンã«ã™ã‚‹ã¨ã€ãƒã‚°ã‚¤ãƒ³ãƒ•ォームã«å…¥åŠ›ã•れãŸèªè¨¼æƒ…å ±ã¯ç„¡è¦–ã•れã¾ã™', + 'Default task color' => 'è¦å®šã®ã‚¿ã‚¹ã‚¯ã‚«ãƒ©ãƒ¼', + 'This feature does not work with all browsers.' => 'ã“ã®æ©Ÿèƒ½ã¯ä¸€éƒ¨ã®ãƒ–ラウザã§å‹•作ã—ã¾ã›ã‚“', + 'There is no destination project available.' => '利用å¯èƒ½ãªãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã¯ã‚りã¾ã›ã‚“', + 'Trigger automatically subtask time tracking' => '自動的ã«ã‚µãƒ–タスク時間トラッã‚ングを作動ã•ã›ã‚‹', + 'Include closed tasks in the cumulative flow diagram' => 'ç´¯ç©ãƒ•ãƒãƒ¼å›³ã«å®Œäº†ã—ãŸã‚¿ã‚¹ã‚¯ã‚’å«ã‚ã‚‹', + 'Current swimlane: %s' => 'ç¾åœ¨ã®ã‚¹ã‚¤ãƒ レーン:%s', + 'Current column: %s' => 'ç¾åœ¨ã®ã‚«ãƒ©ãƒ :%s', + 'Current category: %s' => 'ç¾åœ¨ã®ã‚«ãƒ†ã‚´ãƒªï¼š%s', + 'no category' => 'カテゴリãªã—', + 'Current assignee: %s' => 'ç¾åœ¨ã®æ‹…当者:%s', + 'not assigned' => '未割当', + 'Author:' => '著者:', + 'contributors' => 'å”力者', + 'License:' => 'ライセンス:', + 'License' => 'ライセンス', + 'Enter the text below' => 'テã‚ストを入力', + 'Start date:' => '開始日:', + 'Due date:' => '期é™ï¼š', + 'People who are project managers' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆç®¡ç†è€…', + 'People who are project members' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆãƒ¡ãƒ³ãƒãƒ¼', + 'NOK - Norwegian Krone' => 'NOK - ノルウェークãƒãƒ¼ãƒ', + 'Show this column' => 'ã“ã®ã‚«ãƒ©ãƒ を表示', + 'Hide this column' => 'ã“ã®ã‚«ãƒ©ãƒ ã‚’éš ã™', + 'open file' => 'ファイルを開ã', + 'End date' => '終了日', + 'Users overview' => 'ユーザー概è¦', + 'Members' => 'メンãƒãƒ¼', + 'Shared project' => '共有プãƒã‚¸ã‚§ã‚¯ãƒˆ', + 'Project managers' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆç®¡ç†è€…', + 'Projects list' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆãƒªã‚¹ãƒˆ', + 'End date:' => '終了日:', + 'Change task color when using a specific task link' => '特定ã®ã‚¿ã‚¹ã‚¯ãƒªãƒ³ã‚¯ã‚’使用ã™ã‚‹ã¨ã‚¿ã‚¹ã‚¯ã®è‰²ã‚’変更', + 'Task link creation or modification' => 'タスクリンクã®ä½œæˆã¾ãŸã¯å¤‰æ›´', + 'Milestone' => 'マイルストーン', + 'Documentation: %s' => 'ドã‚ュメント:%s', + 'Reset the search/filter box' => '検索/フィルタをリセット', + 'Documentation' => 'ドã‚ュメント', + 'Table of contents' => '目次', + 'Author' => '著者', + 'Version' => 'ãƒãƒ¼ã‚¸ãƒ§ãƒ³', + 'Plugins' => 'プラグイン', + 'There is no plugin loaded.' => 'プラグインãŒãƒãƒ¼ãƒ‰ã•れã¦ã„ã¾ã›ã‚“', + 'My notifications' => '自分ã®é€šçŸ¥', + 'Custom filters' => 'カスタムフィルタ', + 'Your custom filter have been created successfully.' => 'カスタムフィルタを作æˆã—ã¾ã—ãŸ', + 'Unable to create your custom filter.' => 'カスタムフィルタを作æˆã§ãã¾ã›ã‚“', + 'Custom filter removed successfully.' => 'カスタムフィルタを削除ã—ã¾ã—ãŸ', + 'Unable to remove this custom filter.' => 'ã“ã®ã‚«ã‚¹ã‚¿ãƒ フィルタã¯å‰Šé™¤ã§ãã¾ã›ã‚“', + 'Edit custom filter' => 'カスタムフィルタを編集', + 'Your custom filter have been updated successfully.' => 'カスタムフィルタを更新ã—ã¾ã—ãŸ', + 'Unable to update custom filter.' => 'カスタムフィルタを更新ã§ãã¾ã›ã‚“', // 'Web' => '', - // 'New attachment on task #%d: %s' => '', - // 'New comment on task #%d' => '', - // 'Comment updated on task #%d' => '', - // 'New subtask on task #%d' => '', - // 'Subtask updated on task #%d' => '', - // 'New task #%d: %s' => '', - // 'Task updated #%d' => '', - // 'Task #%d closed' => '', - // 'Task #%d opened' => '', - // 'Column changed for task #%d' => '', - // 'New position for task #%d' => '', - // 'Swimlane changed for task #%d' => '', - // 'Assignee changed on task #%d' => '', - // '%d overdue tasks' => '', - // 'Task #%d is overdue' => '', - // 'No notification.' => '', - // 'Mark all as read' => '', - // 'Mark as read' => '', - // 'Total number of tasks in this column across all swimlanes' => '', - // 'Collapse swimlane' => '', - // 'Expand swimlane' => '', - // 'Add a new filter' => '', - // 'Share with all project members' => '', - // 'Shared' => '', - // 'Owner' => '', - // 'Unread notifications' => '', - // 'Notification methods:' => '', - // 'Unable to read your file' => '', - // '%d task(s) have been imported successfully.' => '', - // 'Nothing have been imported!' => '', - // 'Import users from CSV file' => '', - // '%d user(s) have been imported successfully.' => '', - // 'Comma' => '', - // 'Semi-colon' => '', - // 'Tab' => '', - // 'Vertical bar' => '', - // 'Double Quote' => '', - // 'Single Quote' => '', - // '%s attached a file to the task #%d' => '', - // 'There is no column or swimlane activated in your project!' => '', - // 'Append filter (instead of replacement)' => '', - // 'Append/Replace' => '', - // 'Append' => '', - // 'Replace' => '', - // 'Import' => '', - // 'Change sorting' => '', - // 'Tasks Importation' => '', - // 'Delimiter' => '', - // 'Enclosure' => '', - // 'CSV File' => '', - // 'Instructions' => '', - // 'Your file must use the predefined CSV format' => '', - // 'Your file must be encoded in UTF-8' => '', - // 'The first row must be the header' => '', - // 'Duplicates are not verified for you' => '', - // 'The due date must use the ISO format: YYYY-MM-DD' => '', - // 'Download CSV template' => '', - // 'No external integration registered.' => '', - // 'Duplicates are not imported' => '', - // 'Usernames must be lowercase and unique' => '', - // 'Passwords will be encrypted if present' => '', - // '%s attached a new file to the task %s' => '', - // 'Link type' => '', - // 'Assign automatically a category based on a link' => '', + 'New attachment on task #%d: %s' => 'タスク#%dã®æ–°ã—ã„æ·»ä»˜ãƒ•ァイル:%s', + 'New comment on task #%d' => 'タスク#%dã®æ–°ã—ã„コメント', + 'Comment updated on task #%d' => 'タスク#%dã§æ›´æ–°ã•れãŸã‚³ãƒ¡ãƒ³ãƒˆ', + 'New subtask on task #%d' => 'タスク#%dã®æ–°ã—ã„サブタスク', + 'Subtask updated on task #%d' => 'タスク#%dã§æ›´æ–°ã•れãŸã‚µãƒ–タスク', + 'New task #%d: %s' => 'æ–°ã—ã„タスク#%d:%s', + 'Task updated #%d' => 'ã‚¿ã‚¹ã‚¯ãŒæ›´æ–°ã•れã¾ã—ãŸï¼ƒ%d', + 'Task #%d closed' => 'タスク#%dã¯å®Œäº†ã—ã¾ã—ãŸ', + 'Task #%d opened' => 'タスク#%dを作æˆã—ã¾ã—ãŸ', + 'Column changed for task #%d' => 'タスク#%dã®ã‚«ãƒ©ãƒ ãŒå¤‰æ›´ã•れã¾ã—ãŸ', + 'New position for task #%d' => 'タスク#%dã®æ–°ã—ã„ä½ç½®', + 'Swimlane changed for task #%d' => 'タスク#%dã®ã‚¹ã‚¤ãƒ レーンãŒå¤‰æ›´ã•れã¾ã—ãŸ', + 'Assignee changed on task #%d' => 'タスク#%dã®æ‹…当者ãŒå¤‰æ›´ã•れã¾ã—ãŸ', + '%d overdue tasks' => '%d ä»¶ã®æœŸé™åˆ‡ã‚Œã‚¿ã‚¹ã‚¯', + 'Task #%d is overdue' => 'タスク#%dã¯æœŸé™åˆ‡ã‚Œã§ã™', + 'No notification.' => '通知ãªã—', + 'Mark all as read' => 'ã™ã¹ã¦ã‚’æ—¢èªã«ã™ã‚‹', + 'Mark as read' => 'æ—¢èªã«ã™ã‚‹', + 'Total number of tasks in this column across all swimlanes' => 'ã™ã¹ã¦ã®ã‚¹ã‚¤ãƒ レーンã§ã“ã®ã‚«ãƒ©ãƒ ã®ã‚¿ã‚¹ã‚¯ã®åˆè¨ˆ', + 'Collapse swimlane' => 'スイムレーンをãŸãŸã‚€', + 'Expand swimlane' => 'スイムレーンを展開', + 'Add a new filter' => 'ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼ã‚’è¿½åŠ ', + 'Share with all project members' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆãƒ¡ãƒ³ãƒãƒ¼ã¨å…±æœ‰', + 'Shared' => '共有済ã¿', + 'Owner' => '所有者', + 'Unread notifications' => '未èªé€šçŸ¥', + 'Notification methods:' => '通知方法:', + 'Unable to read your file' => 'ã‚ãªãŸã®ãƒ•ァイルをèªã‚€ã“ã¨ãŒã§ãã¾ã›ã‚“', + '%d task(s) have been imported successfully.' => '%dä»¶ã®ã‚¿ã‚¹ã‚¯ãŒæ£å¸¸ã«ã‚¤ãƒ³ãƒãƒ¼ãƒˆã•れã¾ã—ãŸ', + 'Nothing have been imported!' => '何もインãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“ï¼', + 'Import users from CSV file' => 'CSVファイルã‹ã‚‰ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’インãƒãƒ¼ãƒˆ', + '%d user(s) have been imported successfully.' => '%d人ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒæ£å¸¸ã«ã‚¤ãƒ³ãƒãƒ¼ãƒˆã•れã¾ã—ãŸ', + 'Comma' => 'カンマ', + 'Semi-colon' => 'セミコãƒãƒ³', + 'Tab' => 'タブ', + 'Vertical bar' => '縦棒', + 'Double Quote' => '二é‡å¼•用符', + 'Single Quote' => '一é‡å¼•用符', + '%s attached a file to the task #%d' => '%sã•ã‚“ãŒã‚¿ã‚¹ã‚¯ã«ãƒ•ァイルを添付ã—ã¾ã—ãŸï¼ƒ%d', + 'There is no column or swimlane activated in your project!' => 'ã‚ãªãŸã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã§ã¯ã‚«ãƒ©ãƒ ã‚„ã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³ãŒæœ‰åйã«ãªã£ã¦ã„ã¾ã›ã‚“ï¼', + 'Append filter (instead of replacement)' => 'ç½®æ›ã®ä»£ã‚りã«ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼ã‚’è¿½åŠ ', + 'Append/Replace' => 'è¿½åŠ /ç½®æ›', + 'Append' => 'è¿½åŠ ', + 'Replace' => 'ç½®æ›', + 'Import' => 'インãƒãƒ¼ãƒˆ', + 'Change sorting' => 'ソートã®å¤‰æ›´', + 'Tasks Importation' => 'タスクã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ', + 'Delimiter' => '区切り', + 'Enclosure' => 'エンクãƒãƒ¼ã‚¸ãƒ£', + 'CSV File' => 'CSVファイル', + 'Instructions' => '指示', + 'Your file must use the predefined CSV format' => 'ファイルã¯å®šç¾©æ¸ˆã¿ã®CSVå½¢å¼ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™', + 'Your file must be encoded in UTF-8' => 'ファイルã¯UTF-8ã§ã‚¨ãƒ³ã‚³ãƒ¼ãƒ‰ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™', + 'The first row must be the header' => '最åˆã®è¡Œã¯è¦‹å‡ºã—ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“', + 'Duplicates are not verified for you' => 'é‡è¤‡ã¯æ¤œè¨¼ã•れã¾ã›ã‚“', + 'The due date must use the ISO format: YYYY-MM-DD' => '期é™ã¯ISOå½¢å¼ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ï¼šYYYY-MM-DD', + 'Download CSV template' => 'CSVテンプレートをダウンãƒãƒ¼ãƒ‰', + 'No external integration registered.' => '外部統åˆãŒç™»éŒ²ã•れã¦ã„ã¾ã›ã‚“', + 'Duplicates are not imported' => 'é‡è¤‡ã¯ã‚¤ãƒ³ãƒãƒ¼ãƒˆã•れã¾ã›ã‚“', + 'Usernames must be lowercase and unique' => 'ユーザーåã¯å°æ–‡å—ã§ãƒ¦ãƒ‹ãƒ¼ã‚¯ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“', + 'Passwords will be encrypted if present' => 'パスワードãŒã‚ã‚Œã°æš—å·åŒ–ã•れã¾ã™', + '%s attached a new file to the task %s' => '%sã‚¿ã‚¹ã‚¯ã«æ–°ã—ã„ファイルを添付ã—ã¾ã—ãŸ%s', + 'Link type' => 'リンクタイプ', + 'Assign automatically a category based on a link' => 'リンクã«åŸºã¥ã„ã¦ã‚«ãƒ†ã‚´ãƒªã‚’自動的ã«å‰²å½“ã¦ã‚‹', // 'BAM - Konvertible Mark' => '', - // 'Assignee Username' => '', - // 'Assignee Name' => '', - // 'Groups' => '', - // 'Members of %s' => '', - // 'New group' => '', - // 'Group created successfully.' => '', - // 'Unable to create your group.' => '', - // 'Edit group' => '', - // 'Group updated successfully.' => '', - // 'Unable to update your group.' => '', - // 'Add group member to "%s"' => '', - // 'Group member added successfully.' => '', - // 'Unable to add group member.' => '', - // 'Remove user from group "%s"' => '', - // 'User removed successfully from this group.' => '', - // 'Unable to remove this user from the group.' => '', - // 'Remove group' => '', - // 'Group removed successfully.' => '', - // 'Unable to remove this group.' => '', - // 'Project Permissions' => '', - // 'Manager' => '', - // 'Project Manager' => '', - // 'Project Member' => '', - // 'Project Viewer' => '', - // 'Your account is locked for %d minutes' => '', - // 'Invalid captcha' => '', - // 'The name must be unique' => '', - // 'View all groups' => '', - // 'There is no user available.' => '', - // 'Do you really want to remove the user "%s" from the group "%s"?' => '', - // 'There is no group.' => '', - // 'External Id' => '', - // 'Add group member' => '', - // 'Do you really want to remove this group: "%s"?' => '', - // 'There is no user in this group.' => '', - // 'Permissions' => '', - // 'Allowed Users' => '', - // 'No user have been allowed specifically.' => '', - // 'Role' => '', - // 'Enter user name...' => '', - // 'Allowed Groups' => '', - // 'No group have been allowed specifically.' => '', - // 'Group' => '', - // 'Group Name' => '', - // 'Enter group name...' => '', - // 'Role:' => '', - // 'Project members' => '', - // '%s mentioned you in the task #%d' => '', - // '%s mentioned you in a comment on the task #%d' => '', - // 'You were mentioned in the task #%d' => '', - // 'You were mentioned in a comment on the task #%d' => '', - // 'Estimated hours: ' => '', - // 'Actual hours: ' => '', - // 'Hours Spent' => '', - // 'Hours Estimated' => '', - // 'Estimated Time' => '', - // 'Actual Time' => '', - // 'Estimated vs actual time' => '', + 'Assignee Username' => '担当者ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼å', + 'Assignee Name' => '担当者å', + 'Groups' => 'グループ', + 'Members of %s' => '%sã®ãƒ¡ãƒ³ãƒãƒ¼', + 'New group' => 'æ–°ã—ã„グループ', + 'Group created successfully.' => 'ã‚°ãƒ«ãƒ¼ãƒ—ã¯æ£å¸¸ã«ä½œæˆã•れã¾ã—ãŸ', + 'Unable to create your group.' => 'ã‚ãªãŸã®ã‚°ãƒ«ãƒ¼ãƒ—を作æˆã§ãã¾ã›ã‚“', + 'Edit group' => 'グループを編集', + 'Group updated successfully.' => 'ã‚°ãƒ«ãƒ¼ãƒ—ã¯æ£å¸¸ã«æ›´æ–°ã•れã¾ã—ãŸ', + 'Unable to update your group.' => 'ã‚ãªãŸã®ã‚°ãƒ«ãƒ¼ãƒ—ã‚’æ›´æ–°ã§ãã¾ã›ã‚“', + 'Add group member to "%s"' => '「%sã€ã«ã‚°ãƒ«ãƒ¼ãƒ—メンãƒãƒ¼ã‚’è¿½åŠ ', + 'Group member added successfully.' => 'グループメンãƒãƒ¼ãŒæ£å¸¸ã«è¿½åŠ ã•れã¾ã—ãŸ', + 'Unable to add group member.' => 'グループメンãƒãƒ¼ã‚’è¿½åŠ ã§ãã¾ã›ã‚“', + 'Remove user from group "%s"' => 'グループ「%sã€ã‹ã‚‰ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’削除', + 'User removed successfully from this group.' => 'ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã‹ã‚‰ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒæ£å¸¸ã«å‰Šé™¤ã•れã¾ã—ãŸ', + 'Unable to remove this user from the group.' => 'ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’グループã‹ã‚‰å‰Šé™¤ã§ãã¾ã›ã‚“', + 'Remove group' => 'グループを削除', + 'Group removed successfully.' => 'ã‚°ãƒ«ãƒ¼ãƒ—ã¯æ£å¸¸ã«å‰Šé™¤ã•れã¾ã—ãŸ', + 'Unable to remove this group.' => 'ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—を削除ã§ãã¾ã›ã‚“', + 'Project Permissions' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®æ¨©é™', + 'Manager' => '組織ã®ç®¡ç†è€…', + 'Project Manager' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆç®¡ç†è€…', + 'Project Member' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆãƒ¡ãƒ³ãƒãƒ¼', + 'Project Viewer' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆãƒ“ューアー', + 'Your account is locked for %d minutes' => 'ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯%d分間ãƒãƒƒã‚¯ã•れã¦ã„ã¾ã™', + 'Invalid captcha' => '無効ãªcaptcha', + 'The name must be unique' => 'åå‰ã¯ãƒ¦ãƒ‹ãƒ¼ã‚¯ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™', + 'View all groups' => 'ã™ã¹ã¦ã®ã‚°ãƒ«ãƒ¼ãƒ—を表示', + 'There is no user available.' => '利用å¯èƒ½ãªãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ã‚りã¾ã›ã‚“', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'ユーザー「%sã€ã‚’グループ「%sã€ã‹ã‚‰å‰Šé™¤ã—ã¾ã™ã‹ï¼Ÿ', + 'There is no group.' => 'グループã¯ã‚りã¾ã›ã‚“', + 'Add group member' => 'グループメンãƒãƒ¼ã‚’è¿½åŠ ', + 'Do you really want to remove this group: "%s"?' => 'グループ「%sã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ', + 'There is no user in this group.' => 'ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã«ã¯ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒã„ã¾ã›ã‚“', + 'Permissions' => '権é™', + 'Allowed Users' => '許å¯ã•れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼', + 'No user have been allowed specifically.' => '許å¯ã•れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ã‚りã¾ã›ã‚“', + 'Role' => '役割', + 'Enter user name...' => 'ユーザーåを入力...', + 'Allowed Groups' => '許å¯ã•れãŸã‚°ãƒ«ãƒ¼ãƒ—', + 'No group have been allowed specifically.' => '許å¯ã•れãŸã‚°ãƒ«ãƒ¼ãƒ—ã¯ã‚りã¾ã›ã‚“', + 'Group' => 'グループ', + 'Group Name' => 'グループå', + 'Enter group name...' => 'グループåを入力...', + 'Role:' => '役割:', + 'Project members' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆãƒ¡ãƒ³ãƒãƒ¼', + '%s mentioned you in the task #%d' => '%sã¯ã‚¿ã‚¹ã‚¯#%dã§ã‚ãªãŸã®ã“ã¨ã«è¨€åŠã—ã¾ã—ãŸ', + '%s mentioned you in a comment on the task #%d' => '%sã¯ã‚¿ã‚¹ã‚¯#%dã®ã‚³ãƒ¡ãƒ³ãƒˆã§ã‚ãªãŸã®ã“ã¨ã«è¨€åŠã—ã¾ã—ãŸ', + 'You were mentioned in the task #%d' => 'タスク#%dã§ã‚ãªãŸã®ã“ã¨ãŒè¨€åŠã•れã¾ã—ãŸ', + 'You were mentioned in a comment on the task #%d' => 'タスク#%dã®ã‚³ãƒ¡ãƒ³ãƒˆã§ã‚ãªãŸã®ã“ã¨ãŒè¨€åŠã•れã¾ã—ãŸ', + 'Estimated hours: ' => 'è¦‹ç©æ™‚間:', + 'Actual hours: ' => 'å®Ÿéš›ã®æ™‚間:', + 'Hours Spent' => '時間(経éŽï¼‰', + 'Hours Estimated' => '時間(見ç©ï¼‰', + 'Estimated Time' => 'è¦‹ç©æ™‚é–“', + 'Actual Time' => 'å®Ÿéš›ã®æ™‚é–“', + 'Estimated vs actual time' => 'è¦‹ç©æ™‚é–“ã¨å®Ÿéš›ã®æ™‚é–“', // 'RUB - Russian Ruble' => '', - // 'Assign the task to the person who does the action when the column is changed' => '', - // 'Close a task in a specific column' => '', - // 'Time-based One-time Password Algorithm' => '', - // 'Two-Factor Provider: ' => '', - // 'Disable two-factor authentication' => '', - // 'Enable two-factor authentication' => '', - // 'There is no integration registered at the moment.' => '', - // 'Password Reset for Kanboard' => '', - // 'Forgot password?' => '', - // 'Enable "Forget Password"' => '', - // 'Password Reset' => '', - // 'New password' => '', - // 'Change Password' => '', - // 'To reset your password click on this link:' => '', - // 'Last Password Reset' => '', - // 'The password has never been reinitialized.' => '', - // 'Creation' => '', - // 'Expiration' => '', - // 'Password reset history' => '', - // 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => '', - // 'Do you really want to close all tasks of this column?' => '', - // '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '', - // 'Close all tasks of this column' => '', - // 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => '', - // 'My dashboard' => '', - // 'My profile' => '', - // 'Project owner: ' => '', - // 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '', - // 'Project owner' => '', - // 'Private projects do not have users and groups management.' => '', - // 'There is no project member.' => '', - // 'Priority' => '', - // 'Task priority' => '', - // 'General' => '', - // 'Dates' => '', - // 'Default priority' => '', - // 'Lowest priority' => '', - // 'Highest priority' => '', - // 'If you put zero to the low and high priority, this feature will be disabled.' => '', - // 'Close a task when there is no activity' => '', - // 'Duration in days' => '', - // 'Send email when there is no activity on a task' => '', - // 'Unable to fetch link information.' => '', - // 'Daily background job for tasks' => '', - // 'Auto' => '', - // 'Related' => '', - // 'Attachment' => '', - // 'Title not found' => '', - // 'Web Link' => '', - // 'External links' => '', - // 'Add external link' => '', - // 'Type' => '', - // 'Dependency' => '', - // 'Add internal link' => '', - // 'Add a new external link' => '', - // 'Edit external link' => '', - // 'External link' => '', - // 'Copy and paste your link here...' => '', + 'Assign the task to the person who does the action when the column is changed' => 'カラムãŒå¤‰æ›´ã•れãŸã¨ãã«ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’実行ã™ã‚‹äººã«ã‚¿ã‚¹ã‚¯ã‚’割当ã¦ã‚‹', + 'Close a task in a specific column' => '特定ã®ã‚«ãƒ©ãƒ ã®ã‚¿ã‚¹ã‚¯ã‚’完了ã•ã›ã‚‹', + 'Time-based One-time Password Algorithm' => '時間基準ã®ãƒ¯ãƒ³ã‚¿ã‚¤ãƒ ・パスワード・アルゴリズム', + 'Two-Factor Provider: ' => '二è¦ç´ èªè¨¼ã®æä¾›å…ƒï¼š', + 'Disable two-factor authentication' => '二è¦ç´ èªè¨¼ã‚’無効ã«ã™ã‚‹', + 'Enable two-factor authentication' => '二è¦ç´ èªè¨¼ã‚’有効ã«ã™ã‚‹', + 'There is no integration registered at the moment.' => 'ç¾åœ¨ç™»éŒ²ã•れã¦ã„ã‚‹çµ±åˆã¯ã‚りã¾ã›ã‚“', + 'Password Reset for Kanboard' => 'Kanboardã®ãƒ‘スワードリセット', + 'Forgot password?' => 'パスワードをãŠå¿˜ã‚Œã§ã™ã‹ï¼Ÿ', + 'Enable "Forget Password"' => '「パスワードをãŠå¿˜ã‚Œã§ã™ã‹ï¼Ÿã€ã‚’有効ã«ã™ã‚‹', + 'Password Reset' => 'パスワードリセット', + 'New password' => 'æ–°ã—ã„パスワード', + 'Change Password' => 'パスワードを変更', + 'To reset your password click on this link:' => 'ã“ã®ãƒªãƒ³ã‚¯ã‚’クリックã™ã‚‹ã¨ãƒ‘スワードをリセットã—ã¾ã™ï¼š', + 'Last Password Reset' => '最後ã®ãƒ‘スワードリセット', + 'The password has never been reinitialized.' => 'パスワードãŒå†è¨å®šã•れã¦ã„ã¾ã›ã‚“', + 'Creation' => '作æˆ', + 'Expiration' => '失効', + 'Password reset history' => 'パスワードリセット履æ´', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'ã™ã¹ã¦ã®ã‚¿ã‚¹ã‚¯ã€Œ%sã€ã¨ã‚¹ã‚¤ãƒ レーン「%sã€ã¯æ£å¸¸ã«çµ‚了ã—ã¾ã—ãŸ', + 'Do you really want to close all tasks of this column?' => 'ã“ã®ã‚«ãƒ©ãƒ ã®ã™ã¹ã¦ã®ã‚¿ã‚¹ã‚¯ã‚’終了ã—ã¾ã™ã‹ï¼Ÿ', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => 'カラム「%sã€ã¨ã‚¹ã‚¤ãƒ レーン 「%sã€ã«ã‚ã‚‹%dä»¶ã®ã‚¿ã‚¹ã‚¯ã‚’終了ã—ã¾ã™', + 'Close all tasks of this column' => 'ã“ã®ã‚«ãƒ©ãƒ ã®ã™ã¹ã¦ã®ã‚¿ã‚¹ã‚¯ã‚’終了', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'プラグインã¯ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆé€šçŸ¥ãƒ¡ã‚½ãƒƒãƒ‰ã‚’登録ã—ã¦ã„ã¾ã›ã‚“。ユーザープãƒãƒ•ィールã§å€‹åˆ¥ã«é€šçŸ¥ã‚’è¨å®šã™ã‚‹ã“ã¨ãŒã§ãã¾ã™', + 'My dashboard' => 'ダッシュボード', + 'My profile' => 'プãƒãƒ•ィール', + 'Project owner: ' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆè²¬ä»»è€…:', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆè˜åˆ¥åã¯ã‚ªãƒ—ションã§ã™ã€‚è¨å®šã™ã‚‹å ´åˆã¯è‹±æ•°å—ã«ã—ã¦ãã ã•ã„。 例:MYPROJECT', + 'Project owner' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆè²¬ä»»è€…', + 'Private projects do not have users and groups management.' => 'プライベートプãƒã‚¸ã‚§ã‚¯ãƒˆã«ã¯ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¨ã‚°ãƒ«ãƒ¼ãƒ—ã®ç®¡ç†ã¯ã‚りã¾ã›ã‚“', + 'There is no project member.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆãƒ¡ãƒ³ãƒãƒ¼ã¯ã‚りã¾ã›ã‚“', + 'Priority' => '優先度', + 'Task priority' => 'タスクã®å„ªå…ˆåº¦', + 'General' => '一般', + 'Dates' => '日付', + 'Default priority' => '既定ã®å„ªå…ˆåº¦', + 'Lowest priority' => '最低優先度', + 'Highest priority' => '最高優先度', + 'Close a task when there is no activity' => '活動ãŒãªã„ã¨ãã¯ã‚¿ã‚¹ã‚¯ã‚’終了', + 'Duration in days' => '期間(日)', + 'Send email when there is no activity on a task' => 'ã‚¿ã‚¹ã‚¯ã«æ´»å‹•ãŒãªã„ã¨ãã«ãƒ¡ãƒ¼ãƒ«ã‚’é€ä¿¡', + 'Unable to fetch link information.' => 'ãƒªãƒ³ã‚¯æƒ…å ±ã‚’å–å¾—ã§ãã¾ã›ã‚“', + 'Daily background job for tasks' => 'タスクã®ãŸã‚ã®ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰ã‚¸ãƒ§ãƒ–(毎日)', + 'Auto' => '自動', + 'Related' => '関連', + 'Attachment' => '添付', + 'Title not found' => 'タイトルãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“', + 'Web Link' => 'Webリンク', + 'External links' => '外部リンク', + 'Add external link' => 'å¤–éƒ¨ãƒªãƒ³ã‚¯ã‚’è¿½åŠ ', + 'Type' => 'タイプ', + 'Dependency' => 'ä¾å˜', + 'Add internal link' => 'å†…éƒ¨ãƒªãƒ³ã‚¯ã‚’è¿½åŠ ', + 'Add a new external link' => 'æ–°ã—ã„å¤–éƒ¨ãƒªãƒ³ã‚¯ã‚’è¿½åŠ ', + 'Edit external link' => '外部リンクを編集', + 'External link' => '外部リンク', + 'Copy and paste your link here...' => 'リンクをã“ã“ã«ã‚³ãƒ”ー&ペースト...', // 'URL' => '', - // 'Internal links' => '', - // 'Assign to me' => '', - // 'Me' => '', - // 'Do not duplicate anything' => '', - // 'Projects management' => '', - // 'Users management' => '', - // 'Groups management' => '', - // 'Create from another project' => '', - // 'open' => '', - // 'closed' => '', - // 'Priority:' => '', - // 'Reference:' => '', - // 'Complexity:' => '', - // 'Swimlane:' => '', - // 'Column:' => '', - // 'Position:' => '', - // 'Creator:' => '', - // 'Time estimated:' => '', - // '%s hours' => '', - // 'Time spent:' => '', - // 'Created:' => '', - // 'Modified:' => '', - // 'Completed:' => '', - // 'Started:' => '', - // 'Moved:' => '', - // 'Task #%d' => '', - // 'Time format' => '', - // 'Start date: ' => '', - // 'End date: ' => '', - // 'New due date: ' => '', - // 'Start date changed: ' => '', - // 'Disable private projects' => '', - // 'Do you really want to remove this custom filter: "%s"?' => '', - // 'Remove a custom filter' => '', - // 'User activated successfully.' => '', - // 'Unable to enable this user.' => '', - // 'User disabled successfully.' => '', - // 'Unable to disable this user.' => '', - // 'All files have been uploaded successfully.' => '', - // 'The maximum allowed file size is %sB.' => '', - // 'Drag and drop your files here' => '', - // 'choose files' => '', - // 'View profile' => '', - // 'Two Factor' => '', - // 'Disable user' => '', - // 'Do you really want to disable this user: "%s"?' => '', - // 'Enable user' => '', - // 'Do you really want to enable this user: "%s"?' => '', - // 'Download' => '', - // 'Uploaded: %s' => '', - // 'Size: %s' => '', - // 'Uploaded by %s' => '', - // 'Filename' => '', - // 'Size' => '', - // 'Column created successfully.' => '', - // 'Another column with the same name exists in the project' => '', - // 'Default filters' => '', - // 'Your board doesn\'t have any columns!' => '', - // 'Change column position' => '', - // 'Switch to the project overview' => '', - // 'User filters' => '', - // 'Category filters' => '', - // 'Upload a file' => '', - // 'View file' => '', - // 'Last activity' => '', - // 'Change subtask position' => '', - // 'This value must be greater than %d' => '', - // 'Another swimlane with the same name exists in the project' => '', - // 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => '', - // 'Actions duplicated successfully.' => '', - // 'Unable to duplicate actions.' => '', - // 'Add a new action' => '', - // 'Import from another project' => '', - // 'There is no action at the moment.' => '', - // 'Import actions from another project' => '', - // 'There is no available project.' => '', - // 'Local File' => '', - // 'Configuration' => '', - // 'PHP version:' => '', + 'Internal links' => '内部リンク', + 'Assign to me' => 'è‡ªåˆ†ãŒæ‹…当ã™ã‚‹', + 'Me' => '自分', + 'Do not duplicate anything' => '何も複製ã—ãªã„', + 'Projects management' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆç®¡ç†', + 'Users management' => 'ユーザー管ç†', + 'Groups management' => 'グループ管ç†', + 'Create from another project' => '別ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã‹ã‚‰ä½œæˆ', + 'open' => 'é–‹å§‹', + 'closed' => '終了', + 'Priority:' => '優先度:', + 'Reference:' => 'å‚照:', + 'Complexity:' => '複雑ã•:', + 'Swimlane:' => 'スイムレーン:', + 'Column:' => 'カラム:', + 'Position:' => 'ä½ç½®ï¼š', + 'Creator:' => '作æˆè€…:', + 'Time estimated:' => 'è¦‹ç©æ™‚間:', + '%s hours' => '%s時間', + 'Time spent:' => 'çµŒéŽæ™‚間:', + 'Created:' => '作æˆï¼š', + 'Modified:' => '更新:', + 'Completed:' => '完了:', + 'Started:' => '開始:', + 'Moved:' => '移動:', + 'Task #%d' => 'タスク #%d', + 'Time format' => '時刻形å¼', + 'Start date: ' => '開始日:', + 'End date: ' => '完了日:', + 'New due date: ' => 'æ–°ã—ã„æœŸé™ï¼š', + 'Start date changed: ' => 'é–‹å§‹æ—¥ãŒå¤‰æ›´ã•れã¾ã—ãŸï¼š', + 'Disable private projects' => 'éžå…¬é–‹ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã‚’無効ã«ã™ã‚‹', + 'Do you really want to remove this custom filter: "%s"?' => 'ã“ã®ã‚«ã‚¹ã‚¿ãƒ フィルタ「%sã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ', + 'Remove a custom filter' => 'カスタムフィルタを削除', + 'User activated successfully.' => 'ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯æœ‰åйã«ãªã‚Šã¾ã—ãŸ', + 'Unable to enable this user.' => 'ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’有効ã«ã§ãã¾ã›ã‚“', + 'User disabled successfully.' => 'ユーザーãŒç„¡åйã«ãªã‚Šã¾ã—ãŸ', + 'Unable to disable this user.' => 'ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’無効ã«ã§ãã¾ã›ã‚“', + 'All files have been uploaded successfully.' => 'ã™ã¹ã¦ã®ãƒ•ã‚¡ã‚¤ãƒ«ãŒæ£å¸¸ã«ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰ã•れã¾ã—ãŸ', + 'The maximum allowed file size is %sB.' => '最大許容ファイルサイズ:%sB', + 'Drag and drop your files here' => 'ファイルをドラッグアンドドãƒãƒƒãƒ—', + 'choose files' => 'ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠž', + 'View profile' => 'プãƒãƒ•ィールを見る', + 'Two Factor' => '二è¦ç´ èªè¨¼', + 'Disable user' => 'ユーザーを無効ã«ã™ã‚‹', + 'Do you really want to disable this user: "%s"?' => 'ユーザー「%sã€ã‚’無効ã«ã—ã¾ã™ã‹ï¼Ÿ', + 'Enable user' => 'ユーザーを有効ã«ã™ã‚‹', + 'Do you really want to enable this user: "%s"?' => 'ユーザー「%sã€ã‚’有効ã«ã—ã¾ã™ã‹ï¼Ÿ', + 'Download' => 'ダンウãƒãƒ¼ãƒ‰', + 'Uploaded: %s' => 'アップãƒãƒ¼ãƒ‰å®Œäº†ï¼š%s', + 'Size: %s' => 'サイズ:%s', + 'Uploaded by %s' => '%sã«ã‚ˆã£ã¦ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰ã•れã¾ã—ãŸ', + 'Filename' => 'ファイルå', + 'Size' => 'サイズ', + 'Column created successfully.' => 'ã‚«ãƒ©ãƒ ãŒæ£å¸¸ã«ä½œæˆã•れã¾ã—ãŸ', + 'Another column with the same name exists in the project' => 'åŒã˜åå‰ã®åˆ¥ã®ã‚«ãƒ©ãƒ ãŒãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã«ã‚りã¾ã™', + 'Default filters' => '既定フィルタ', + 'Your board doesn\'t have any columns!' => 'ã‚ãªãŸã®ãƒœãƒ¼ãƒ‰ã«ã¯ã‚«ãƒ©ãƒ ãŒã‚りã¾ã›ã‚“ï¼', + 'Change column position' => 'カラムã®ä½ç½®ã‚’変更', + 'Switch to the project overview' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆæ¦‚è¦ã«åˆ‡æ›¿', + 'User filters' => 'ユーザーフィルタ', + 'Category filters' => 'カテゴリフィルタ', + 'Upload a file' => 'ファイルをアップãƒãƒ¼ãƒ‰', + 'View file' => 'ファイルを見る', + 'Last activity' => 'æœ€æ–°ã®æ´»å‹•状æ³', + 'Change subtask position' => 'サブタスクã®ä½ç½®ã‚’変更', + 'This value must be greater than %d' => '%dより大ãããªã‘れã°ãªã‚Šã¾ã›ã‚“', + 'Another swimlane with the same name exists in the project' => 'åŒã˜åå‰ã®åˆ¥ã®ã‚¹ã‚¤ãƒ レーンãŒãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã«ã‚りã¾ã™', + 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => '例:http://example.kanboard.net/(絶対URLã®ç”Ÿæˆã«ä½¿ç”¨ï¼‰', + 'Actions duplicated successfully.' => 'アクションを複製ã—ã¾ã—ãŸ', + 'Unable to duplicate actions.' => 'アクションを複製ã§ãã¾ã›ã‚“', + 'Add a new action' => 'æ–°ã—ã„ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’è¿½åŠ ', + 'Import from another project' => '別ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã‹ã‚‰ã‚¤ãƒ³ãƒãƒ¼ãƒˆ', + 'There is no action at the moment.' => 'アクションã¯ã‚りã¾ã›ã‚“', + 'Import actions from another project' => '別ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã‹ã‚‰ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’インãƒãƒ¼ãƒˆ', + 'There is no available project.' => '利用å¯èƒ½ãªãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã¯ã‚りã¾ã›ã‚“', + 'Local File' => 'ãƒãƒ¼ã‚«ãƒ«ãƒ•ァイル', + 'Configuration' => 'æ§‹æˆ', + 'PHP version:' => 'PHPãƒãƒ¼ã‚¸ãƒ§ãƒ³ï¼š', // 'PHP SAPI:' => '', - // 'OS version:' => '', - // 'Database version:' => '', - // 'Browser:' => '', - // 'Task view' => '', - // 'Edit task' => '', - // 'Edit description' => '', - // 'New internal link' => '', - // 'Display list of keyboard shortcuts' => '', + 'OS version:' => 'OSãƒãƒ¼ã‚¸ãƒ§ãƒ³ï¼š', + 'Database version:' => 'データベース・ãƒãƒ¼ã‚¸ãƒ§ãƒ³ï¼š', + 'Browser:' => 'ブラウザー:', + 'Task view' => 'タスクビュー', + 'Edit task' => 'タスクを編集', + 'Edit description' => '説明を編集', + 'New internal link' => 'æ–°ã—ã„内部リンク', + 'Display list of keyboard shortcuts' => 'ã‚ーボードショートカットã®ãƒªã‚¹ãƒˆã‚’表示', // 'Menu' => '', - // 'Set start date' => '', - // 'Avatar' => '', - // 'Upload my avatar image' => '', - // 'Remove my image' => '', - // 'The OAuth2 state parameter is invalid' => '', - // 'User not found.' => '', - // 'Search in activity stream' => '', - // 'My activities' => '', - // 'Activity until yesterday' => '', - // 'Activity until today' => '', - // 'Search by creator: ' => '', - // 'Search by creation date: ' => '', - // 'Search by task status: ' => '', - // 'Search by task title: ' => '', - // 'Activity stream search' => '', - // 'Projects where "%s" is manager' => '', - // 'Projects where "%s" is member' => '', - // 'Open tasks assigned to "%s"' => '', - // 'Closed tasks assigned to "%s"' => '', - // 'Assign automatically a color based on a priority' => '', - // 'Overdue tasks for the project(s) "%s"' => '', - // 'Upload files' => '', - // 'Installed Plugins' => '', - // 'Plugin Directory' => '', - // 'Plugin installed successfully.' => '', - // 'Plugin updated successfully.' => '', - // 'Plugin removed successfully.' => '', - // 'Subtask converted to task successfully.' => '', - // 'Unable to convert the subtask.' => '', - // 'Unable to extract plugin archive.' => '', - // 'Plugin not found.' => '', - // 'You don\'t have the permission to remove this plugin.' => '', - // 'Unable to download plugin archive.' => '', - // 'Unable to write temporary file for plugin.' => '', - // 'Unable to open plugin archive.' => '', - // 'There is no file in the plugin archive.' => '', - // 'Create tasks in bulk' => '', - // 'Your Kanboard instance is not configured to install plugins from the user interface.' => '', - // 'There is no plugin available.' => '', - // 'Install' => '', - // 'Update' => '', - // 'Up to date' => '', - // 'Not available' => '', - // 'Remove plugin' => '', - // 'Do you really want to remove this plugin: "%s"?' => '', - // 'Uninstall' => '', - // 'Listing' => '', - // 'Metadata' => '', - // 'Manage projects' => '', - // 'Convert to task' => '', - // 'Convert sub-task to task' => '', - // 'Do you really want to convert this sub-task to a task?' => '', - // 'My task title' => '', - // 'Enter one task by line.' => '', - // 'Number of failed login:' => '', - // 'Account locked until:' => '', - // 'Email settings' => '', - // 'Email sender address' => '', - // 'Email transport' => '', - // 'Webhook token' => '', - // 'Project tags management' => '', - // 'Tag created successfully.' => '', - // 'Unable to create this tag.' => '', - // 'Tag updated successfully.' => '', - // 'Unable to update this tag.' => '', - // 'Tag removed successfully.' => '', - // 'Unable to remove this tag.' => '', - // 'Global tags management' => '', - // 'Tags' => '', - // 'Tags management' => '', - // 'Add new tag' => '', - // 'Edit a tag' => '', - // 'Project tags' => '', - // 'There is no specific tag for this project at the moment.' => '', - // 'Tag' => '', - // 'Remove a tag' => '', - // 'Do you really want to remove this tag: "%s"?' => '', - // 'Global tags' => '', - // 'There is no global tag at the moment.' => '', - // 'This field cannot be empty' => '', - // 'Close a task when there is no activity in an specific column' => '', - // '%s removed a subtask for the task #%d' => '', - // '%s removed a comment on the task #%d' => '', - // 'Comment removed on task #%d' => '', - // 'Subtask removed on task #%d' => '', - // 'Hide tasks in this column in the dashboard' => '', - // '%s removed a comment on the task %s' => '', - // '%s removed a subtask for the task %s' => '', - // 'Comment removed' => '', - // 'Subtask removed' => '', - // '%s set a new internal link for the task #%d' => '', - // '%s removed an internal link for the task #%d' => '', - // 'A new internal link for the task #%d have been defined' => '', - // 'Internal link removed for the task #%d' => '', - // '%s set a new internal link for the task %s' => '', - // '%s removed an internal link for the task %s' => '', - // 'Automatically set the due date on task creation' => '', - // 'Move the task to another column when closed' => '', - // 'Move the task to another column when not moved during a given period' => '', - // 'Dashboard for %s' => '', - // 'Tasks overview for %s' => '', - // 'Subtasks overview for %s' => '', - // 'Projects overview for %s' => '', - // 'Activity stream for %s' => '', - // 'Calendar for %s' => '', - // 'Notifications for %s' => '', - // 'Assign a color when the task is moved to a specific swimlane' => '', - // 'Assign a priority when the task is moved to a specific swimlane' => '', - // 'User unlocked successfully.' => '', - // 'Unable to unlock the user.' => '', - // 'Move a task to another swimlane' => '', - // 'Creator Name' => '', - // 'Time spent and estimated' => '', - // 'Move position' => '', - // 'Move task to another position on the board' => '', - // 'Insert before this task' => '', - // 'Insert after this task' => '', - // 'Unlock this user' => '', - // 'Custom Project Roles' => '', - // 'Add a new custom role' => '', - // 'Restrictions for the role "%s"' => '', - // 'Add a new project restriction' => '', - // 'Add a new drag and drop restriction' => '', - // 'Add a new column restriction' => '', - // 'Edit this role' => '', - // 'Remove this role' => '', - // 'There is no restriction for this role.' => '', - // 'Only moving task between those columns is permitted' => '', - // 'Close a task in a specific column when not moved during a given period' => '', - // 'Edit columns' => '', - // 'The column restriction has been created successfully.' => '', - // 'Unable to create this column restriction.' => '', - // 'Column restriction removed successfully.' => '', - // 'Unable to remove this restriction.' => '', - // 'Your custom project role has been created successfully.' => '', - // 'Unable to create custom project role.' => '', - // 'Your custom project role has been updated successfully.' => '', - // 'Unable to update custom project role.' => '', - // 'Custom project role removed successfully.' => '', - // 'Unable to remove this project role.' => '', - // 'The project restriction has been created successfully.' => '', - // 'Unable to create this project restriction.' => '', - // 'Project restriction removed successfully.' => '', - // 'You cannot create tasks in this column.' => '', - // 'Task creation is permitted for this column' => '', - // 'Closing or opening a task is permitted for this column' => '', - // 'Task creation is blocked for this column' => '', - // 'Closing or opening a task is blocked for this column' => '', - // 'Task creation is not permitted' => '', - // 'Closing or opening a task is not permitted' => '', - // 'New drag and drop restriction for the role "%s"' => '', - // 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '', - // 'Remove a column restriction' => '', - // 'Do you really want to remove this column restriction: "%s" to "%s"?' => '', - // 'New column restriction for the role "%s"' => '', - // 'Rule' => '', - // 'Do you really want to remove this column restriction?' => '', - // 'Custom roles' => '', - // 'New custom project role' => '', - // 'Edit custom project role' => '', - // 'Remove a custom role' => '', - // 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '', - // 'There is no custom role for this project.' => '', - // 'New project restriction for the role "%s"' => '', - // 'Restriction' => '', - // 'Remove a project restriction' => '', - // 'Do you really want to remove this project restriction: "%s"?' => '', - // 'Duplicate to multiple projects' => '', - // 'This field is required' => '', - // 'Moving a task is not permitted' => '', - // 'This value must be in the range %d to %d' => '', - // 'You are not allowed to move this task.' => '', - // 'API User Access' => '', - // 'Preview' => '', - // 'Write' => '', - // 'Write your text in Markdown' => '', - // 'New External Task: %s' => '', - // 'No personal API access token registered.' => '', - // 'Your personal API access token is "%s"' => '', - // 'Remove your token' => '', - // 'Generate a new token' => '', - // 'Showing %d-%d of %d' => '', - // 'Outgoing Emails' => '', - // 'Add or change currency rate' => '', - // 'Reference currency: %s' => '', - // 'Add custom filters' => '', - // 'Export' => '', - // 'Add link label' => '', - // 'Incompatible Plugins' => '', - // 'Compatibility' => '', - // 'Permissions and ownership' => '', - // 'Priorities' => '', - // 'Close this window' => '', - // 'Unable to upload this file.' => '', - // 'Import tasks' => '', - // 'Choose a project' => '', - // 'Profile' => '', - // 'Application role' => '', - // '%d invitations were sent.' => '', - // '%d invitation was sent.' => '', - // 'Unable to create this user.' => '', - // 'Kanboard Invitation' => '', - // 'Visible on dashboard' => '', - // 'Created at:' => '', - // 'Updated at:' => '', - // 'There is no custom filter.' => '', - // 'New User' => '', - // 'Authentication' => '', - // 'If checked, this user will use a third-party system for authentication.' => '', - // 'The password is necessary only for local users.' => '', - // 'You have been invited to register on Kanboard.' => '', - // 'Click here to join your team' => '', - // 'Invite people' => '', - // 'Emails' => '', - // 'Enter one email address by line.' => '', - // 'Add these people to this project' => '', - // 'Add this person to this project' => '', - // 'Sign-up' => '', - // 'Credentials' => '', - // 'New user' => '', - // 'This username is already taken' => '', - // 'A link to reset your password has been sent by email.' => '', - // 'Your profile must have a valid email address.' => '', - // 'Unfortunately, we are unable to reset your password. Did you entered a valid username? Do you have an email address in your profile?' => '', + 'Set start date' => 'é–‹å§‹ã™ã‚‹', + 'Avatar' => 'ã‚¢ãƒã‚¿ãƒ¼', + 'Upload my avatar image' => '自分ã®ã‚¢ãƒã‚¿ãƒ¼ç”»åƒã‚’アップãƒãƒ¼ãƒ‰', + 'Remove my image' => 'ç”»åƒã‚’削除', + 'The OAuth2 state parameter is invalid' => 'OAuth2ステートパラメータã¯ç„¡åйã§ã™', + 'User not found.' => 'ユーザーãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“', + 'Search in activity stream' => '活動状æ³ã§æ¤œç´¢', + 'My activities' => 'è‡ªåˆ†ã®æ´»å‹•状æ³', + 'Activity until yesterday' => '昨日ã¾ã§ã®æ´»å‹•状æ³', + 'Activity until today' => '今ã¾ã§ã®æ´»å‹•状æ³', + 'Search by creator: ' => '作æˆè€…ã§æ¤œç´¢ï¼š', + 'Search by creation date: ' => 'ä½œæˆæ—¥ã§æ¤œç´¢ï¼š', + 'Search by task status: ' => 'ã‚¿ã‚¹ã‚¯ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ã§æ¤œç´¢ï¼š', + 'Search by task title: ' => 'タスクåã§æ¤œç´¢ï¼š', + 'Activity stream search' => '活動状æ³ã‚’検索', + 'Projects where "%s" is manager' => '「%sã€ãŒç®¡ç†è€…ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆ', + 'Projects where "%s" is member' => '「%sã€ãŒãƒ¡ãƒ³ãƒãƒ¼ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆ', + 'Open tasks assigned to "%s"' => '「%sã€ãŒæ‹…当ã®ã‚¿ã‚¹ã‚¯ã‚’é–‹ã', + 'Closed tasks assigned to "%s"' => '「%sã€ãŒæ‹…当ã—ãŸçµ‚了ã—ãŸã‚¿ã‚¹ã‚¯', + 'Assign automatically a color based on a priority' => '優先度ã«åŸºã¥ã„ã¦è‡ªå‹•çš„ã«è‰²ã‚’割当ã¦', + 'Overdue tasks for the project(s) "%s"' => '期é™ã‚’è¶…éŽã—ãŸã‚¿ã‚¹ã‚¯ã€Œ%sã€', + 'Upload files' => 'ファイルをアップãƒãƒ¼ãƒ‰', + 'Installed Plugins' => '使用ä¸ã®ãƒ—ラグイン', + 'Plugin Directory' => 'プラグインディレクトリ', + 'Plugin installed successfully.' => 'プラグインãŒã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¾ã—ãŸ', + 'Plugin updated successfully.' => 'ãƒ—ãƒ©ã‚°ã‚¤ãƒ³ãŒæ›´æ–°ã•れã¾ã—ãŸ', + 'Plugin removed successfully.' => 'プラグインを削除ã—ã¾ã—ãŸ', + 'Subtask converted to task successfully.' => 'サブタスクをタスクã«å¤‰æ›ã—ã¾ã—ãŸ', + 'Unable to convert the subtask.' => 'サブタスクを変æ›ã§ãã¾ã›ã‚“', + 'Unable to extract plugin archive.' => 'プラグインアーカイブを解å‡ã§ãã¾ã›ã‚“', + 'Plugin not found.' => 'プラグインãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“', + 'You don\'t have the permission to remove this plugin.' => 'ã“ã®ãƒ—ラグインを削除ã™ã‚‹æ¨©é™ãŒã‚りã¾ã›ã‚“', + 'Unable to download plugin archive.' => 'プラグインアーカイブをダウンãƒãƒ¼ãƒ‰ã§ãã¾ã›ã‚“', + 'Unable to write temporary file for plugin.' => 'プラグインã®ä¸€æ™‚ファイルを書ãè¾¼ã‚ã¾ã›ã‚“', + 'Unable to open plugin archive.' => 'プラグインアーカイブを開ãã“ã¨ãŒã§ãã¾ã›ã‚“', + 'There is no file in the plugin archive.' => 'プラグインアーカイブã«ãƒ•ァイルãŒã‚りã¾ã›ã‚“', + 'Create tasks in bulk' => 'タスクを一括作æˆ', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'ã‚ãªãŸã®Kanboardインスタンスã¯ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ェースã‹ã‚‰ãƒ—ラグインをインストールã™ã‚‹ã‚ˆã†ã«è¨å®šã•れã¦ã„ã¾ã›ã‚“', + 'There is no plugin available.' => '利用å¯èƒ½ãªãƒ—ラグインã¯ã‚りã¾ã›ã‚“', + 'Install' => 'インストール', + 'Update' => 'æ›´æ–°', + 'Up to date' => 'æœ€æ–°ã®æ—¥ä»˜', + 'Not available' => '利用ã§ãã¾ã›ã‚“', + 'Remove plugin' => 'プラグインを削除', + 'Do you really want to remove this plugin: "%s"?' => 'ã“ã®ãƒ—ラグインを削除ã—ã¾ã™ã‹ï¼Ÿï¼š %s', + 'Uninstall' => 'アンインストール', + 'Listing' => 'リスト', + 'Metadata' => 'メタデータ', + 'Manage projects' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚’管ç†', + 'Convert to task' => 'タスクã«å¤‰æ›', + 'Convert sub-task to task' => 'サブタスクをタスクã«å¤‰æ›', + 'Do you really want to convert this sub-task to a task?' => 'ã“ã®ã‚µãƒ–タスクをタスクã«å¤‰æ›ã—ã¾ã™ã‹ï¼Ÿ', + 'My task title' => '自分ã®ã‚¿ã‚¹ã‚¯å', + 'Enter one task by line.' => '1行ã«1ä»¶ã®ã‚¿ã‚¹ã‚¯ã‚’入力', + 'Number of failed login:' => 'ãƒã‚°ã‚¤ãƒ³å¤±æ•—回数:', + 'Account locked until:' => 'ãƒãƒƒã‚¯ã•れãŸã‚¢ã‚«ã‚¦ãƒ³ãƒˆï¼š', + 'Email settings' => 'メールè¨å®š', + 'Email sender address' => 'é€ä¿¡è€…ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹', + 'Email transport' => 'メールã®é€ä¿¡', + 'Webhook token' => 'Webhookトークン', + 'Project tags management' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚¿ã‚°ç®¡ç†', + 'Tag created successfully.' => 'タグを作æˆã—ã¾ã—ãŸ', + 'Unable to create this tag.' => 'タグを作æˆã§ãã¾ã›ã‚“', + 'Tag updated successfully.' => 'ã‚¿ã‚°ã‚’æ›´æ–°ã—ã¾ã—ãŸ', + 'Unable to update this tag.' => 'ã‚¿ã‚°ã‚’æ›´æ–°ã§ãã¾ã›ã‚“', + 'Tag removed successfully.' => 'タグを削除ã—ã¾ã—ãŸ', + 'Unable to remove this tag.' => 'タグを削除ã§ãã¾ã›ã‚“', + 'Global tags management' => 'ã‚°ãƒãƒ¼ãƒãƒ«ã‚¿ã‚°ç®¡ç†', + 'Tags' => 'ã‚¿ã‚°', + 'Tags management' => 'タグ管ç†', + 'Add new tag' => 'æ–°ã—ã„ã‚¿ã‚°ã‚’è¿½åŠ ', + 'Edit a tag' => 'タグを編集', + 'Project tags' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚¿ã‚°', + 'There is no specific tag for this project at the moment.' => 'ã“ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã«ã¯ã‚¿ã‚°ã¯ã‚りã¾ã›ã‚“', + 'Tag' => 'ã‚¿ã‚°', + 'Remove a tag' => 'タグを削除', + 'Do you really want to remove this tag: "%s"?' => 'タグ「%sã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ', + 'Global tags' => 'ã‚°ãƒãƒ¼ãƒãƒ«ã‚¿ã‚°', + 'There is no global tag at the moment.' => 'ã‚°ãƒãƒ¼ãƒãƒ«ã‚¿ã‚°ã¯ã‚りã¾ã›ã‚“', + 'This field cannot be empty' => 'ã“ã®ãƒ•ィールドã¯å¿…é ˆã§ã™', + 'Close a task when there is no activity in an specific column' => '特定ã®ã‚«ãƒ©ãƒ ã«æ´»å‹•ãŒãªã„å ´åˆã«ã‚¿ã‚¹ã‚¯ã‚’終了', + '%s removed a subtask for the task #%d' => 'タスク#%dã‹ã‚‰ã‚µãƒ–タスク%s件を削除ã—ã¾ã—ãŸ', + '%s removed a comment on the task #%d' => 'タスク#%dã‹ã‚‰ã‚³ãƒ¡ãƒ³ãƒˆ%s件を削除ã—ã¾ã—ãŸ', + 'Comment removed on task #%d' => 'タスク#%dã‹ã‚‰ã‚³ãƒ¡ãƒ³ãƒˆã‚’削除ã—ã¾ã—ãŸ', + 'Subtask removed on task #%d' => 'タスク#%dã‹ã‚‰ã‚µãƒ–タスクを削除ã—ã¾ã—ãŸ', + 'Hide tasks in this column in the dashboard' => 'ダッシュボードã§ã“ã®ã‚«ãƒ©ãƒ ã®ã‚¿ã‚¹ã‚¯ã¯è¡¨ç¤ºã—ãªã„', + '%s removed a comment on the task %s' => '%sã¯ã‚¿ã‚¹ã‚¯ã€Œ%sã€ã‹ã‚‰ã‚³ãƒ¡ãƒ³ãƒˆã‚’削除ã—ã¾ã—ãŸ', + '%s removed a subtask for the task %s' => '%sã¯ã‚¿ã‚¹ã‚¯ã€Œ%sã€ã‹ã‚‰ã‚µãƒ–タスクを削除ã—ã¾ã—ãŸ', + 'Comment removed' => 'コメントを削除ã—ã¾ã—ãŸ', + 'Subtask removed' => 'サブタスクを削除ã—ã¾ã—ãŸ', + '%s set a new internal link for the task #%d' => '%sã¯ã‚¿ã‚¹ã‚¯ï¼ƒ%dã®æ–°ã—ã„内部リンクをè¨å®šã—ã¾ã—ãŸ', + '%s removed an internal link for the task #%d' => '%sã¯ã‚¿ã‚¹ã‚¯ï¼ƒ%dã®å†…部リンクを削除ã—ã¾ã—ãŸ', + 'A new internal link for the task #%d have been defined' => 'タスク#%dã®æ–°ã—ã„内部リンクãŒå®šç¾©ã•れã¦ã„ã¾ã™', + 'Internal link removed for the task #%d' => 'タスク#%dã®å†…部リンクãŒå‰Šé™¤ã•れã¾ã—ãŸ', + '%s set a new internal link for the task %s' => '%sã¯ã‚¿ã‚¹ã‚¯ã€Œ%sã€ã®æ–°ã—ã„内部リンクをè¨å®šã—ã¾ã—ãŸ', + '%s removed an internal link for the task %s' => '%sã¯ã‚¿ã‚¹ã‚¯ã€Œ%sã€ã®å†…部リンクを削除ã—ã¾ã—ãŸ', + 'Automatically set the due date on task creation' => 'ã‚¿ã‚¹ã‚¯ä½œæˆæ™‚ã«è‡ªå‹•çš„ã«æœŸé™ã‚’è¨å®š', + 'Move the task to another column when closed' => 'é–‰ã˜ãŸã¨ãã«ã‚¿ã‚¹ã‚¯ã‚’別ã®ã‚«ãƒ©ãƒ ã«ç§»å‹•', + 'Move the task to another column when not moved during a given period' => '指定ã•ã‚ŒãŸæœŸé–“ä¸ã«ç§»å‹•ã—ãªã‹ã£ãŸå ´åˆã€ã‚¿ã‚¹ã‚¯ã‚’別ã®ã‚«ãƒ©ãƒ ã«ç§»å‹•', + 'Dashboard for %s' => '%sã®ãƒ€ãƒƒã‚·ãƒ¥ãƒœãƒ¼ãƒ‰', + 'Tasks overview for %s' => '%sã®ã‚¿ã‚¹ã‚¯æ¦‚è¦', + 'Subtasks overview for %s' => '%sã®ã‚µãƒ–タスク概è¦', + 'Projects overview for %s' => '%sã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆæ¦‚è¦', + 'Activity stream for %s' => '%sã®æ´»å‹•状æ³', + 'Assign a color when the task is moved to a specific swimlane' => 'タスクãŒç‰¹å®šã®ã‚¹ã‚¤ãƒ レーンã«ç§»å‹•ã—ãŸã¨ãã«è‰²ã‚’割当ã¦ã‚‹', + 'Assign a priority when the task is moved to a specific swimlane' => 'タスクãŒç‰¹å®šã®ã‚¹ã‚¤ãƒ レーンã«ç§»å‹•ã—ãŸã¨ãã«å„ªå…ˆåº¦ã‚’割当ã¦ã‚‹', + 'User unlocked successfully.' => 'ユーザーã®ãƒãƒƒã‚¯ã‚’解除ã—ã¾ã—ãŸ', + 'Unable to unlock the user.' => 'ユーザーã®ãƒãƒƒã‚¯ã‚’解除ã§ãã¾ã›ã‚“', + 'Move a task to another swimlane' => '別ã®ã‚¹ã‚¤ãƒ レーンã«ã‚¿ã‚¹ã‚¯ã‚’移動', + 'Creator Name' => '作æˆè€…', + 'Time spent and estimated' => '経éŽï¼è¦‹ç©æ™‚é–“', + 'Move position' => '移動', + 'Move task to another position on the board' => 'ボード上ã®åˆ¥ã®ä½ç½®ã«ã‚¿ã‚¹ã‚¯ã‚’移動', + 'Insert before this task' => 'ã“ã®ã‚¿ã‚¹ã‚¯ã®å‰ã«æŒ¿å…¥', + 'Insert after this task' => 'ã“ã®ã‚¿ã‚¹ã‚¯ã®å¾Œã«æŒ¿å…¥', + 'Unlock this user' => 'ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’ãƒãƒƒã‚¯è§£é™¤', + 'Custom Project Roles' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆç‹¬è‡ªã®å½¹å‰²', + 'Add a new custom role' => 'æ–°ã—ã„å½¹å‰²ã‚’è¿½åŠ ', + 'Restrictions for the role "%s"' => '役割「%sã€ã®åˆ¶é™', + 'Add a new project restriction' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆåˆ¶ç´„ã‚’è¿½åŠ ', + 'Add a new drag and drop restriction' => 'ドラッグアンドドãƒãƒƒãƒ—ã®åˆ¶é™ã‚’è¿½åŠ ', + 'Add a new column restriction' => 'カラムã«åˆ¶é™ã‚’è¿½åŠ ', + 'Edit this role' => '役割を編集', + 'Remove this role' => '役割を削除', + 'There is no restriction for this role.' => 'ã“ã®å½¹å‰²ã«ã¯åˆ¶é™ã¯ã‚りã¾ã›ã‚“', + 'Only moving task between those columns is permitted' => 'ã“れらã®ã‚«ãƒ©ãƒ é–“ã¯ã‚¿ã‚¹ã‚¯ã®ç§»å‹•ã®ã¿ãŒè¨±å¯ã•れã¦ã„ã¾ã™', + 'Close a task in a specific column when not moved during a given period' => '指定ã•ã‚ŒãŸæœŸé–“ä¸ã«ç§»å‹•ã—ãªã‹ã£ãŸå ´åˆã€ç‰¹å®šã®ã‚«ãƒ©ãƒ ã®ã‚¿ã‚¹ã‚¯ã‚’終了', + 'Edit columns' => 'カラムを編集', + 'The column restriction has been created successfully.' => 'カラムã®åˆ¶é™ã‚’作æˆã—ã¾ã—ãŸ', + 'Unable to create this column restriction.' => 'ã“ã®ã‚«ãƒ©ãƒ ã®åˆ¶é™ã‚’作æˆã§ãã¾ã›ã‚“', + 'Column restriction removed successfully.' => 'カラムã®åˆ¶é™ã‚’削除ã—ã¾ã—ãŸ', + 'Unable to remove this restriction.' => 'ã“ã®åˆ¶é™ã‚’削除ã§ãã¾ã›ã‚“', + 'Your custom project role has been created successfully.' => 'ã‚ãªãŸã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã§ç‹¬è‡ªã®å½¹å‰²ã‚’作æˆã—ã¾ã—ãŸ', + 'Unable to create custom project role.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆç‹¬è‡ªã®å½¹å‰²ã‚’作æˆã§ãã¾ã›ã‚“', + 'Your custom project role has been updated successfully.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆç‹¬è‡ªã®å½¹å‰²ã‚’æ›´æ–°ã—ã¾ã—ãŸ', + 'Unable to update custom project role.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆç‹¬è‡ªã®å½¹å‰²ã‚’æ›´æ–°ã§ãã¾ã›ã‚“', + 'Custom project role removed successfully.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆç‹¬è‡ªã®å½¹å‰²ã‚’削除ã—ã¾ã—ãŸ', + 'Unable to remove this project role.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‹ã‚‰å½¹å‰²ã‚’削除ã§ãã¾ã›ã‚“', + 'The project restriction has been created successfully.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®åˆ¶é™ãŒä½œæˆã—ã¾ã—ãŸ', + 'Unable to create this project restriction.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®åˆ¶é™ã‚’作æˆã§ãã¾ã›ã‚“', + 'Project restriction removed successfully.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®åˆ¶é™ã‚’削除ã—ã¾ã—ãŸ', + 'You cannot create tasks in this column.' => 'ã“ã®ã‚«ãƒ©ãƒ ã«ã‚¿ã‚¹ã‚¯ã¯ä½œæˆã§ãã¾ã›ã‚“', + 'Task creation is permitted for this column' => 'ã“ã®ã‚«ãƒ©ãƒ ã§ã¯ã‚¿ã‚¹ã‚¯ã®ä½œæˆãŒè¨±å¯ã•れã¦ã„ã¾ã™', + 'Closing or opening a task is permitted for this column' => 'ã“ã®ã‚«ãƒ©ãƒ ã§ã¯ã‚¿ã‚¹ã‚¯ã‚’作æˆï¼å®Œäº†ãŒè¨±å¯ã•れã¦ã„ã¾ã™', + 'Task creation is blocked for this column' => 'ã“ã®ã‚«ãƒ©ãƒ ã§ã®ã‚¿ã‚¹ã‚¯ä½œæˆã¯ãƒ–ãƒãƒƒã‚¯ã•れã¦ã„ã¾ã™', + 'Closing or opening a task is blocked for this column' => 'ã“ã®ã‚«ãƒ©ãƒ ã§ã®ã‚¿ã‚¹ã‚¯ã®ä½œæˆï¼å®Œäº†ã¯ãƒ–ãƒãƒƒã‚¯ã•れã¦ã„ã¾ã™', + 'Task creation is not permitted' => 'タスクã®ä½œæˆã¯è¨±å¯ã•れã¦ã„ã¾ã›ã‚“', + 'Closing or opening a task is not permitted' => 'タスクã®ä½œæˆï¼å®Œäº†ã¯è¨±å¯ã•れã¦ã„ã¾ã›ã‚“', + 'New drag and drop restriction for the role "%s"' => '役割「%sã€ã®æ–°ã—ã„ドラッグアンドドãƒãƒƒãƒ—制é™', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'ã“ã®å½¹å‰²ã«å±žã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ã€èµ·ç‚¹ã‚«ãƒ©ãƒ ã¨å®›å…ˆã‚«ãƒ©ãƒ ã®é–“ã§ã®ã¿ã‚¿ã‚¹ã‚¯ã‚’移動ã§ãã¾ã™', + 'Remove a column restriction' => 'カラムã®åˆ¶é™ã‚’解除', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'ã“ã®ã‚«ãƒ©ãƒ ã®åˆ¶é™ã‚’解除ã—ã¾ã™ã‹ï¼Ÿï¼š 「%sã€â†’「%sã€', + 'New column restriction for the role "%s"' => '役割「%sã€ã®ã‚«ãƒ©ãƒ ã§ã®æ–°ã—ã„制é™', + 'Rule' => '役割', + 'Do you really want to remove this column restriction?' => 'ã“ã®åˆ—ã®åˆ¶é™ã‚’解除ã—ã¾ã™ã‹ï¼Ÿ', + 'Custom roles' => '独自ã®å½¹å‰²', + 'New custom project role' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã§ã®æ–°ã—ã„独自ã®å½¹å‰²', + 'Edit custom project role' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã§ã®ç‹¬è‡ªã®å½¹å‰²ã‚’編集', + 'Remove a custom role' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã§ã®ç‹¬è‡ªã®å½¹å‰²ã‚’削除', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '役割「%sã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿã“ã®å½¹å‰²ã«å‰²å½“ã¦ã‚‰ã‚ŒãŸã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆãƒ¡ãƒ³ãƒãƒ¼ã«ãªã‚Šã¾ã™', + 'There is no custom role for this project.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã§ã®ç‹¬è‡ªã®å½¹å‰²ã¯ã‚りã¾ã›ã‚“', + 'New project restriction for the role "%s"' => '役割「%sã€ã®æ–°ã—ã„プãƒã‚¸ã‚§ã‚¯ãƒˆåˆ¶é™', + 'Restriction' => '制é™', + 'Remove a project restriction' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®åˆ¶é™ã‚’解除', + 'Do you really want to remove this project restriction: "%s"?' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®åˆ¶é™ã€Œ%sã€ã‚’解除ã—ã¾ã™ã‹ï¼Ÿ', + 'Duplicate to multiple projects' => '複数プãƒã‚¸ã‚§ã‚¯ãƒˆã«è¤‡è£½', + 'This field is required' => 'ã“ã®ãƒ•ィールドã¯å¿…é ˆã§ã™', + 'Moving a task is not permitted' => 'タスクを移動ã§ãã¾ã›ã‚“', + 'This value must be in the range %d to %d' => 'ã“ã®å€¤ã¯%d〜%dã®ç¯„囲ã«ãªã‘れã°ãªã‚Šã¾ã›ã‚“', + 'You are not allowed to move this task.' => 'ã“ã®ã‚¿ã‚¹ã‚¯ã‚’移動ã™ã‚‹æ¨©é™ãŒã‚りã¾ã›ã‚“', + 'API User Access' => 'API ユーザーアクセス', + 'Preview' => 'プレビュー', + 'Write' => '書込ã¿', + 'Write your text in Markdown' => 'Markdownã§å…¥åŠ›', + 'No personal API access token registered.' => '個人APIアクセストークンã¯ç™»éŒ²ã•れã¦ã„ã¾ã›ã‚“', + 'Your personal API access token is "%s"' => 'ã‚ãªãŸã®ãƒ‘ーソナルAPIアクセストークン "%s"', + 'Remove your token' => 'ã‚ãªãŸã®ãƒˆãƒ¼ã‚¯ãƒ³ã‚’削除', + 'Generate a new token' => 'æ–°ã—ã„トークンを生æˆ', + 'Showing %d-%d of %d' => '%d〜%dï¼%dを表示', + 'Outgoing Emails' => 'é€ä¿¡ãƒ¡ãƒ¼ãƒ«', + 'Add or change currency rate' => 'é€šè²¨ãƒ¬ãƒ¼ãƒˆã‚’è¿½åŠ ã¾ãŸã¯å¤‰æ›´', + 'Reference currency: %s' => 'å‚照通貨:%s', + 'Add custom filters' => 'ã‚«ã‚¹ã‚¿ãƒ ãƒ•ã‚£ãƒ«ã‚¿ã‚’è¿½åŠ ', + 'Export' => 'エクスãƒãƒ¼ãƒˆ', + 'Add link label' => 'ãƒªãƒ³ã‚¯ãƒ©ãƒ™ãƒ«ã‚’è¿½åŠ ', + 'Incompatible Plugins' => 'äº’æ›æ€§ã®ãªã„プラグイン', + 'Compatibility' => 'äº’æ›æ€§', + 'Permissions and ownership' => 'ã‚¢ã‚¯ã‚»ã‚¹æ¨©ã¨æ‰€æœ‰æ¨©', + 'Priorities' => 'プãƒãƒ‘ティ', + 'Close this window' => 'ウィンドウを閉ã˜ã‚‹', + 'Unable to upload this file.' => 'ファイルをアップãƒãƒ¼ãƒ‰ã§ãã¾ã›ã‚“', + 'Import tasks' => 'タスクã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ', + 'Choose a project' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚’é¸æŠž', + 'Profile' => 'プãƒãƒ•ィール', + 'Application role' => 'アプリケーションã§ã®å½¹å‰²', + '%d invitations were sent.' => '%dé€šã®æ‹›å¾…状をé€ä¿¡ã—ã¾ã—ãŸ', + '%d invitation was sent.' => '%dã«æ‹›å¾…状をé€ä¿¡ã—ã¾ã—ãŸ', + 'Unable to create this user.' => 'ユーザーを作æˆã§ãã¾ã›ã‚“', + 'Kanboard Invitation' => 'Kanboardã¸ã®æ‹›å¾…', + 'Visible on dashboard' => 'ダッシュボード表示', + 'Created at:' => 'ä½œæˆæ—¥æ™‚:', + 'Updated at:' => '更新日時:', + 'There is no custom filter.' => 'カスタムフィルタã¯ã‚りã¾ã›ã‚“', + 'New User' => 'æ–°è¦ãƒ¦ãƒ¼ã‚¶ãƒ¼', + 'Authentication' => 'èªè¨¼', + 'If checked, this user will use a third-party system for authentication.' => 'ãƒã‚§ãƒƒã‚¯ã™ã‚‹ã¨ã€ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯èªè¨¼ã«ç¬¬ä¸‰è€…システムを使用ã—ã¾ã™', + 'The password is necessary only for local users.' => 'パスワードã¯ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã®ã¿å¿…è¦ã§ã™', + 'You have been invited to register on Kanboard.' => 'ã‚ãªãŸã¯Kanboardã«ç™»éŒ²ã•れã¾ã—ãŸ', + 'Click here to join your team' => 'クリックã—ã¦ã‚ãªãŸã®ãƒãƒ¼ãƒ ã«å‚åŠ ', + 'Invite people' => '招待', + 'Emails' => 'Eメール', + 'Enter one email address by line.' => '行ã”ã¨ã«1ã¤ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’入力ã—ã¦ãã ã•ã„', + 'Add these people to this project' => 'ã“れらã®äººã€…をプãƒã‚¸ã‚§ã‚¯ãƒˆã«è¿½åŠ ', + 'Add this person to this project' => 'ã“ã®äººã‚’プãƒã‚¸ã‚§ã‚¯ãƒˆã«è¿½åŠ ', + 'Sign-up' => 'サインアップ', + 'Credentials' => 'è³‡æ ¼æƒ…å ±', + 'New user' => 'æ–°è¦ãƒ¦ãƒ¼ã‚¶ãƒ¼', + 'This username is already taken' => 'ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼åã¯ã™ã§ã«ä½¿ç”¨ã•れã¦ã„ã¾ã™', + 'A link to reset your password has been sent by email.' => 'パスワードをリセットã™ã‚‹ãƒªãƒ³ã‚¯ã‚’メールã§é€ä¿¡ã—ã¾ã—ãŸ', + 'Your profile must have a valid email address.' => 'プãƒãƒ•ィールã«ã¯æœ‰åйãªãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ãŒå¿…è¦ã§ã™', + 'Unfortunately, we are unable to reset your password. Did you entered a valid username? Do you have an email address in your profile?' => 'パスワードをリセットã§ãã¾ã›ã‚“。有効ãªãƒ¦ãƒ¼ã‚¶ãƒ¼åを入力ã—ã¾ã—ãŸã‹ï¼Ÿã¾ãŸã¯ã€ã‚ãªãŸã®ãƒ—ãƒãƒ•ィールã«ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’登録ã—ã¦ã„ã¾ã™ã‹ï¼Ÿ', // 'TRL - Turkish Lira' => '', - // 'The project email is optional and could be used by several plugins.' => '', - // 'The project email must be unique across all projects' => '', - // 'The email configuration has been disabled by the administrator.' => '', - // 'Close this project' => '', - // 'Open this project' => '', - // 'Close a project' => '', - // 'Do you really want to close this project: "%s"?' => '', - // 'Reopen a project' => '', - // 'Do you really want to reopen this project: "%s"?' => '', - // 'This project is open' => '', - // 'This project is closed' => '', - // 'Unable to upload files, check the permissions of your data folder.' => '', - // 'Another category with the same name exists in this project' => '', - // 'Comment sent by email successfully.' => '', - // 'Sent by email to [%s](mailto:%s) (%s)' => '', - // 'Unable to read uploaded file.' => '', - // 'Database uploaded successfully.' => '', - // 'Task sent by email successfully.' => '', - // 'There is no category in this project.' => '', - // 'Send by email' => '', - // 'Create and send a comment by email' => '', - // 'Subject' => '', - // 'Upload the database' => '', - // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', - // 'Database file' => '', - // 'Upload' => '', - // 'Remove this user from group' => '', - // 'Your project must have at least one active swimlane.' => '', - // 'Project: %s' => '', - // 'Automatic action not found: "%s"' => '', - // '%d projects' => '', - // '%d project' => '', - // 'There is no project.' => '', - // 'Sort' => '', - // 'Project ID' => '', - // 'Project name' => '', - // 'Public' => '', - // 'Private' => '', - // '%d tasks' => '', - // '%d task' => '', - // 'Task ID' => '', - // 'Assign automatically a color when due date is expired' => '', - // 'Total score in this column across all swimlanes' => '', + 'The project email is optional and could be used by several plugins.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®Eメールã¯ã‚ªãƒ—ションï¼è¤‡æ•°ã®ãƒ—ラグインã§ä½¿ç”¨å¯èƒ½', + 'The project email must be unique across all projects' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã®Eメールã¯ã™ã¹ã¦ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã§ãƒ¦ä¸€æ„ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™', + 'The email configuration has been disabled by the administrator.' => '管ç†è€…ãŒEメールã®è¨å®šã‚’無効ã«ã—ã¾ã—ãŸ', + 'Close this project' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚’終了', + 'Open this project' => 'ã“ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã‚’é–‹ã', + 'Close a project' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚’終了', + 'Do you really want to close this project: "%s"?' => 'ã“ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã‚’本当ã«çµ‚了ã—ã¾ã™ã‹ï¼Ÿï¼š %s', + 'Reopen a project' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã‚’é–‹ã', + 'Do you really want to reopen this project: "%s"?' => 'ã“ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã‚’é–‹ãã¾ã™ã‹ï¼Ÿï¼š %s', + 'This project is open' => 'ã“ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã¯é€²è¡Œä¸', + 'This project is closed' => 'ã“ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã¯çµ‚了ã—ã¾ã—ãŸ', + 'Unable to upload files, check the permissions of your data folder.' => 'ファイルをアップãƒãƒ¼ãƒ‰ã§ãã¾ã›ã‚“。データフォルダã®ã‚¢ã‚¯ã‚»ã‚¹æ¨©ã‚’確èªã—ã¦ãã ã•ã„。', + 'Another category with the same name exists in this project' => 'åŒã˜åå‰ã®åˆ¥ã®ã‚«ãƒ†ã‚´ãƒªãŒãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã«å˜åœ¨ã—ã¾ã™', + 'Comment sent by email successfully.' => 'メールã§ã‚³ãƒ¡ãƒ³ãƒˆã‚’é€ä¿¡ã—ã¾ã—ãŸ', + 'Sent by email to [%s](mailto:%s) (%s)' => '%s をメールã§é€ä¿¡ã—ã¾ã—ãŸ(mailto:%s) (%s)', + 'Unable to read uploaded file.' => 'アップãƒãƒ¼ãƒ‰ã•れãŸãƒ•ァイルをèªã¿è¾¼ã‚ã¾ã›ã‚“', + 'Database uploaded successfully.' => 'データベースをアップãƒãƒ¼ãƒ‰ã—ã¾ã—ãŸ', + 'Task sent by email successfully.' => 'タスクをメールã§é€ä¿¡ã—ã¾ã—ãŸ', + 'There is no category in this project.' => 'ã“ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã«ã‚«ãƒ†ã‚´ãƒªã¯ã‚りã¾ã›ã‚“', + 'Send by email' => 'メールã§é€ã‚‹', + 'Create and send a comment by email' => 'Eメールã§ã‚³ãƒ¡ãƒ³ãƒˆã‚’é€ä¿¡', + 'Subject' => 'ä»¶å', + 'Upload the database' => 'データベースをアップãƒãƒ¼ãƒ‰', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => '以å‰ã«ãƒ€ã‚¦ãƒ³ãƒãƒ¼ãƒ‰ã—ãŸSqliteデータベース(Gzipå½¢å¼ï¼‰ã‚’アップãƒãƒ¼ãƒ‰ã§ãã¾ã™', + 'Database file' => 'データベースファイル', + 'Upload' => 'アップãƒãƒ¼ãƒ‰', + 'Your project must have at least one active swimlane.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã«ã¯å°‘ãªãã¨ã‚‚1ã¤ã®ã‚¢ã‚¯ãƒ†ã‚£ãƒ–ãªã‚¹ã‚¤ãƒ レーンãŒå¿…è¦ã§ã™', + 'Project: %s' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆï¼š%s', + 'Automatic action not found: "%s"' => '自動アクションãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“: %s', + '%d projects' => '%d ä»¶ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆ', + '%d project' => '%d プãƒã‚¸ã‚§ã‚¯ãƒˆ', + 'There is no project.' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆã¯ã‚りã¾ã›ã‚“', + 'Sort' => 'ソート', + 'Project ID' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆID', + 'Project name' => 'プãƒã‚¸ã‚§ã‚¯ãƒˆå', + 'Public' => '公開', + 'Private' => 'プライベート', + '%d tasks' => '%d ä»¶ã®ã‚¿ã‚¹ã‚¯', + '%d task' => '%d タスク', + 'Task ID' => 'タスクID', + 'Assign automatically a color when due date is expired' => '期é™ãŒåˆ‡ã‚ŒãŸã‚‰è‡ªå‹•çš„ã«è‰²ã‚’割当ã¦', + 'Total score in this column across all swimlanes' => 'ã“ã®ã‚«ãƒ©ãƒ ã®ã™ã¹ã¦ã®ã‚¹ã‚¤ãƒ レーンã§åˆè¨ˆå¾—点', // 'HRK - Kuna' => '', + 'ARS - Argentine Peso' => 'ARS - アルゼンãƒãƒ³ãƒšã‚½', + 'COP - Colombian Peso' => 'COP - コãƒãƒ³ãƒ“アペソ', + '%d groups' => '%d個ã®ã‚°ãƒ«ãƒ¼ãƒ—', + '%d group' => '%d グループ', + 'Group ID' => 'グループID', + 'External ID' => '外部ID', + '%d users' => '%d 人ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼', + '%d user' => '%d ユーザー', + 'Hide subtasks' => 'ã‚µãƒ–ã‚¿ã‚¹ã‚¯ã‚’éš ã™', + 'Show subtasks' => 'サブタスクを表示', + 'Authentication Parameters' => 'èªè¨¼ãƒ‘ラメータ', + 'API Access' => 'APIアクセス', + 'No users found.' => 'ユーザーãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“', + 'User ID' => 'ユーザーID', + 'Notifications are activated' => 'é€šçŸ¥ã¯æœ‰åй', + 'Notifications are disabled' => '通知ã¯ç„¡åй', + 'User disabled' => 'ユーザーを無効ã«ã—ã¾ã—ãŸ', + '%d notifications' => '%d ä»¶ã®é€šçŸ¥', + '%d notification' => '%d 通知', + 'There is no external integration installed.' => '外部インテグレーションã¯ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ã„ã¾ã›ã‚“', + 'You are not allowed to update tasks assigned to someone else.' => 'ä»–ã®äººã«å‰²å½“ã¦ã‚‰ã‚ŒãŸã‚¿ã‚¹ã‚¯ã‚’æ›´æ–°ã™ã‚‹æ¨©é™ãŒã‚りã¾ã›ã‚“', + 'You are not allowed to change the assignee.' => '担当者を変更ã™ã‚‹æ¨©é™ã¯ã‚りã¾ã›ã‚“', + 'Task suppression is not permitted' => 'ã‚¿ã‚¹ã‚¯ã®æŠ‘åˆ¶ã¯è¨±å¯ã•れã¦ã„ã¾ã›ã‚“', + 'Changing assignee is not permitted' => '担当者ã¯å¤‰æ›´ã§ãã¾ã›ã‚“', + 'Update only assigned tasks is permitted' => '割当ã¦ã‚‰ã‚ŒãŸã‚¿ã‚¹ã‚¯ã®ã¿æ›´æ–°ã§ãã¾ã™', + 'Only for tasks assigned to the current user' => 'ç¾åœ¨ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«å‰²å½“ã¦ã‚‰ã‚ŒãŸã‚¿ã‚¹ã‚¯ã®ã¿', + 'My projects' => '自分ã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆ', + 'Your are not member of any project.' => 'ã‚ãªãŸãŒæ‰€å±žã—ã¦ã„るプãƒã‚¸ã‚§ã‚¯ãƒˆã«ã¯ã‚りã¾ã›ã‚“', + 'My subtasks' => '自分ã®ã‚µãƒ–タスク', + '%d subtasks' => '%d ä»¶ã®ã‚µãƒ–タスク', + '%d subtask' => '%d サブタスク', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'ç¾åœ¨ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«å‰²å½“ã¦ã‚‰ã‚ŒãŸã‚¿ã‚¹ã‚¯ã¯ã€ã“れらã®ã‚«ãƒ©ãƒ é–“ã®ç§»å‹•ã®ã¿ãŒè¨±å¯ã•れã¾ã™', + '[DUPLICATE]' => '[コピー]', + 'DKK - Danish Krona' => 'DKK - デンマーククãƒãƒ¼ãƒŠ', + 'Remove user from group' => 'ユーザーをグループã‹ã‚‰å‰Šé™¤', + 'Assign the task to its creator' => '作æˆè€…ã«ã‚¿ã‚¹ã‚¯ã‚’割当ã¦', + 'This task was sent by email to "%s" with subject "%s".' => 'ã“ã®ã‚¿ã‚¹ã‚¯ã‚’メール㧠「%s〠ã«é€ä¿¡ã—ã¾ã—ãŸï¼ˆä»¶å "%s")', + 'Predefined Email Subjects' => '既定ã®ãƒ¡ãƒ¼ãƒ«ã®ä»¶å', + 'Write one subject by line.' => '行ã”ã¨ã«1ã¤ãšã¤ä»¶åを書ã', + 'Create another link' => '別ã®ãƒªãƒ³ã‚¯ã‚’作æˆ', + 'BRL - Brazilian Real' => 'BRL - ブラジルレアル', + 'Add a new Kanboard task' => 'Kanboard ã‚¿ã‚¹ã‚¯ã‚’è¿½åŠ ', + 'Subtask not started' => 'サブタスクã¯é–‹å§‹ã•れã¦ã„ã¾ã›ã‚“', + 'Subtask currently in progress' => 'サブタスクã¯é€²è¡Œä¸', + 'Subtask completed' => 'サブタスクã¯å®Œäº†', + 'Subtask added successfully.' => 'ã‚µãƒ–ã‚¿ã‚¹ã‚¯ã‚’è¿½åŠ ã—ã¾ã—ãŸ', + '%d subtasks added successfully.' => '%d ä»¶ã®ã‚µãƒ–ã‚¿ã‚¹ã‚¯ã‚’è¿½åŠ ã—ã¾ã—ãŸ', + 'Enter one subtask by line.' => '1行ã«1ä»¶ã®ã‚µãƒ–タスクを入力', + 'Predefined Contents' => '定義済ã¿ã‚³ãƒ³ãƒ†ãƒ³ãƒ„', + 'Predefined contents' => '定義済ã¿ã‚³ãƒ³ãƒ†ãƒ³ãƒ„', + 'Predefined Task Description' => '定義済ã¿ã‚¿ã‚¹ã‚¯ã®èª¬æ˜Ž', + 'Do you really want to remove this template? "%s"' => 'ã“ã®ãƒ†ãƒ³ãƒ—レートを削除ã—ã¾ã™ã‹ï¼Ÿï¼š %s', + 'Add predefined task description' => '定義済ã¿ã®ã‚¿ã‚¹ã‚¯ã®èª¬æ˜Žã‚’è¿½åŠ ', + 'Predefined Task Descriptions' => '定義済ã¿ã‚¿ã‚¹ã‚¯ã®èª¬æ˜Ž', + 'Template created successfully.' => 'テンプレートを作æˆã—ã¾ã—ãŸ', + 'Unable to create this template.' => 'テンプレートを作æˆã§ãã¾ã›ã‚“', + 'Template updated successfully.' => 'テンプレートを更新ã—ã¾ã—ãŸ', + 'Unable to update this template.' => 'テンプレートを更新ã§ãã¾ã›ã‚“', + 'Template removed successfully.' => 'テンプレートを削除ã—ã¾ã—ãŸ', + 'Unable to remove this template.' => 'テンプレートを削除ã§ãã¾ã›ã‚“', + 'Template for the task description' => 'タスク説明ã®ãƒ†ãƒ³ãƒ—レート', + 'The start date is greater than the end date' => 'é–‹å§‹æ—¥ãŒçµ‚了日を超ãˆã¦ã„ã¾ã™', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/ko_KR/translations.php b/app/Locale/ko_KR/translations.php index 5e068fd7..a4ae9319 100644 --- a/app/Locale/ko_KR/translations.php +++ b/app/Locale/ko_KR/translations.php @@ -39,7 +39,6 @@ return array( 'Administrator' => '관리ìž', 'Sign in' => '로그ì¸', 'Users' => '사용ìž', - 'No user' => '사용ìžê°€ 없습니다', 'Forbidden' => 'ì ‘ê·¼ ê±°ë¶€', 'Access Forbidden' => 'ì ‘ì†ì´ ê±°ë¶€ë˜ì—ˆìŠµë‹ˆë‹¤', 'Edit user' => '사용ìžë¥¼ 변경하는 ', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => '서브 í• ì¼ì„ ê°±ì‹ í–ˆìŠµë‹ˆë‹¤.', 'Unable to update your sub-task.' => '서브 í• ì¼ì˜ ê²½ì‹ ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.', 'Unable to create your sub-task.' => '서브 í• ì¼ì˜ ì¶”ê°€ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.', - 'Sub-task added successfully.' => '서브 í• ì¼ì„ 추가했습니다.', 'Maximum size: ' => '최대: ', 'Display another project' => '프로ì 트 보기', 'Created by %s' => 'ìž‘ì„±ìž %s', @@ -396,22 +394,17 @@ return array( 'Activity stream' => '활ë™ê¸°ë¡', 'Dashboard' => '대시보드', 'Confirmation' => '확ì¸', - 'Allow everybody to access to this project' => 'ëª¨ë“ ì‚¬ëžŒì´ ì´ í”„ë¡œì íŠ¸ì— ì ‘ê·¼í• ìˆ˜ 있ë„ë¡ í•©ë‹ˆë‹¤', - 'Everybody have access to this project.' => '누구나 ì´ í”„ë¡œì íŠ¸ì— ì•¡ì„¸ìŠ¤ í• ìˆ˜ 있습니다', 'Webhooks' => 'Webhook', 'API' => 'API', 'Create a comment from an external provider' => '외부 서비스로부터 ì˜ê²¬ì„ 작성한다', 'Project management' => '프로ì 트 관리', - 'My projects' => 'ë‚´ 프로ì 트', 'Columns' => '컬럼', 'Task' => 'í• ì¼', - 'Your are not member of any project.' => 'ì–´ë–¤ 프로ì 트ì—ë„ ì†í•˜ì§€ 않습니다.', 'Percentage' => '비중', 'Number of tasks' => 'í• ì¼ ìˆ˜', 'Task distribution' => 'í• ì¼ ë¶„í¬', 'Analytics' => 'ë¶„ì„', 'Subtask' => '서브 í• ì¼', - 'My subtasks' => 'ë‚´ 서브 í• ì¼', 'User repartition' => 'ë‹´ë‹¹ìž ë¶„í¬', 'Clone this project' => 'ì´ í”„ë¡œì 트를 ë³µì œí•˜ëŠ” ', 'Column removed successfully.' => '(※)ì»¬ëŸ¼ì„ ì‚ì œí–ˆìŠµë‹ˆë‹¤', @@ -459,7 +452,6 @@ return array( 'Language:' => '언어:', 'Timezone:' => '시간대:', 'All columns' => 'ëª¨ë“ ì»¬ëŸ¼', - 'Calendar' => 'ë‹¬ë ¥', 'Next' => '다ìŒì— ', '#%d' => '#%d', 'All swimlanes' => 'ëª¨ë“ ìŠ¤ìœ”ë ˆì¸', @@ -557,7 +549,6 @@ return array( 'Unable to add this currency rate.' => 'ì´ í†µí™” í™˜ìœ¨ì„ ì¶”ê°€í• ìˆ˜ 없습니다.', 'Webhook URL' => 'Webhook URL', '%s removed the assignee of the task %s' => '%sì´ í• ì¼ %sì˜ ë‹´ë‹¹ì„ ì‚ì œí–ˆìŠµë‹ˆë‹¤', - 'Enable Gravatar images' => 'Gravatarì´ë¯¸ì§€ë¥¼ 활성화', 'Information' => 'ì •ë³´', 'Check two factor authentication code' => '2단 ì¸ì¦ì„ ì²´í¬í•œë‹¤', 'The two factor authentication code is not valid.' => '2단 ì¸ì¦ 코드는 무효입니다.', @@ -611,14 +602,7 @@ return array( 'When task is moved from first column' => 'í• ì¼ì´ 첫번째 컬럼으로 옮겨졌ì„때', 'When task is moved to last column' => 'í• ì¼ì´ 마지막 컬럼으로 옮겨졌ì„때', 'Year(s)' => 'ë…„', - 'Calendar settings' => 'ë‹¬ë ¥ ì„¤ì •', - 'Project calendar view' => '프로ì 트 ë‹¬ë ¥ 보기', 'Project settings' => '프로ì 트 ì„¤ì •', - 'Show subtasks based on the time tracking' => '시간 íŠ¸ëž˜í‚¹ì˜ ì„œë¸Œ í• ì¼ ë³´ê¸°', - 'Show tasks based on the creation date' => 'ìƒì„± ë‚ ì§œë¡œ í• ì¼ ë³´ê¸°', - 'Show tasks based on the start date' => '시작 ë‚ ì§œë¡œ í• ì¼ ë³´ê¸°', - 'Subtasks time tracking' => '서브 í• ì¼ ì‹œê°„ 트래킹', - 'User calendar view' => 'ë‹´ë‹¹ìž ë‹¬ë ¥ 보기', 'Automatically update the start date' => '시작ì¼ì— ìžë™ ê°±ì‹ ', 'iCal feed' => 'iCal 피드', 'Preferences' => 'ìš°ì„ ê¶Œ', @@ -637,7 +621,6 @@ return array( 'Notification' => '알림', '%s moved the task #%d to the first swimlane' => '%sê°€ í• ì¼ #%d를 첫번째 ìŠ¤ì›œë ˆì¸ìœ¼ë¡œ ì´ë™ì‹œì¼°ìŠµë‹ˆë‹¤', 'Swimlane' => 'ìŠ¤ìœ”ë ˆì¸', - 'Gravatar' => 'Gravatar', '%s moved the task %s to the first swimlane' => '%sê°€ í• ì¼ %s를 첫번째 ìŠ¤ì›œë ˆì¸ìœ¼ë¡œ ì´ë™ì‹œì¼°ìŠµë‹ˆë‹¤', '%s moved the task %s to the swimlane "%s"' => '%sê°€ í• ì¼ %s를 %s ìŠ¤ì›œë ˆì¸ìœ¼ë¡œ ì´ë™ì‹œì¼°ìŠµë‹ˆë‹¤', 'This report contains all subtasks information for the given date range.' => '해당 ê¸°ê°„ì˜ ëª¨ë“ ì„œë¸Œ í• ì¼ ì •ë³´ê°€ ë³´ê³ ì„œì— í¬í•¨ë©ë‹ˆë‹¤', @@ -674,7 +657,6 @@ return array( 'Stop timer' => '타ì´ë¨¸ ì •ì§€', 'Start timer' => '타ì´ë¨¸ 시작', 'My activity stream' => 'ë‚´ 활ë™ê¸°ë¡', - 'My calendar' => 'ë‚´ 캘린ë”', 'Search tasks' => 'í• ì¼ ì°¾ê¸°', 'Reset filters' => 'í•„í„° 리셋', 'My tasks due tomorrow' => 'ë‚´ì¼ê¹Œì§€ ë‚´ í• ì¼', @@ -688,7 +670,6 @@ return array( 'Overview' => '개요', 'Board/Calendar/List view' => '보드/ë‹¬ë ¥/리스트 보기', 'Switch to the board view' => '보드 보기로 ì „í™˜', - 'Switch to the calendar view' => 'ë‹¬ë ¥ 보기로 ì „í™˜', 'Switch to the list view' => '리스트 보기로 ì „í™˜', 'Go to the search/filter box' => '검색/í•„í„° 박스로 ì´ë™', 'There is no activity yet.' => '활ë™ì´ 없습니다', @@ -743,15 +724,8 @@ return array( 'License:' => 'ë¼ì´ì„¼ìФ:', 'License' => 'ë¼ì´ì„¼ìФ', 'Enter the text below' => '아랫글로 들어가기', - 'Sort by position' => '위치별 ì •ë ¬', - 'Sort by date' => 'ë‚ ì§œë³„ ì •ë ¬', - 'Add task' => 'í• ì¼ ì¶”ê°€', 'Start date:' => '시작ì¼:', 'Due date:' => '만기ì¼:', - 'There is no start date or due date for this task.' => 'í• ì¼ì˜ ì‹œìž‘ì¼ ë˜ëŠ” 만기ì¼ì´ 없습니다', - 'Moving or resizing a task will change the start and due date of the task.' => 'í• ì¼ì˜ ì´ë™ í˜¹ì€ ë¦¬ì‚¬ì´ì§•으로 시작시간과 마ê°ì‹œê°„ì´ ë³€ê²½ë©ë‹ˆë‹¤.', - 'There is no task in your project.' => '프로ì íŠ¸ì— í• ì¼ì´ 없습니다.', - 'Gantt chart' => '간트 차트', 'People who are project managers' => '프로ì 트 ë§¤ë‹ˆì €', 'People who are project members' => '프로ì 트 멤버', 'NOK - Norwegian Krone' => 'NOK - ë…¸ë¥´ì›¨ì´ í¬ë¡œë„¤', @@ -763,22 +737,15 @@ return array( 'Members' => '멤버', 'Shared project' => '프로ì 트 ê³µìœ ', 'Project managers' => '프로ì 트 ë§¤ë‹ˆì €', - 'Gantt chart for all projects' => 'ëª¨ë“ í”„ë¡œì íŠ¸ì˜ ê°„íŠ¸ 차트', 'Projects list' => '프로ì 트 리스트', - 'Gantt chart for this project' => 'ì´ í”„ë¡œì 트 간트차트', - 'Project board' => '프로ì 트 보드', 'End date:' => 'ë‚ ì§œ ìˆ˜ì •', - 'There is no start date or end date for this project.' => 'ì´ í”„ë¡œì 트ì—는 ì‹œìž‘ë‚ ì§œì™€ ì¢…ë£Œë‚ ì§œê°€ 없습니다', - 'Projects Gantt chart' => '프로ì 트 간트차트', 'Change task color when using a specific task link' => 'íŠ¹ì • í• ì¼ ë§í¬ë¥¼ ì‚¬ìš©í• ë•Œ í• ì¼ì˜ 색깔 변경', 'Task link creation or modification' => 'í• ì¼ ë§í¬ ìƒì„± í˜¹ì€ ìˆ˜ì •', 'Milestone' => '마ì¼ìŠ¤í†¤', 'Documentation: %s' => '문서: %s', - 'Switch to the Gantt chart view' => '간트 차트 보기로 변경', 'Reset the search/filter box' => '찾기/í•„í„° 박스 초기화', 'Documentation' => '문서', 'Table of contents' => '목차', - 'Gantt' => '간트', 'Author' => '글쓴ì´', 'Version' => 'ë²„ì „', 'Plugins' => '플러그ì¸', @@ -889,7 +856,6 @@ return array( 'There is no user available.' => '가능한 사용ìžê°€ 없습니다', 'Do you really want to remove the user "%s" from the group "%s"?' => '"%s" 사용ìžë¥¼ "%s" ì—서 ì‚ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?', 'There is no group.' => 'ê·¸ë£¹ì´ ì—†ìŠµë‹ˆë‹¤', - 'External Id' => '외부 ì•„ì´ë””', 'Add group member' => '멤버추가', 'Do you really want to remove this group: "%s"?' => 'ê·¸ë£¹ì„ ì‚ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ: "%s"?', 'There is no user in this group.' => 'ì´ ê·¸ë£¹ì—는 사용ìžê°€ 없습니다', @@ -955,7 +921,6 @@ return array( 'Default priority' => '기본 ìš°ì„ ìˆœìœ„', 'Lowest priority' => 'ë‚®ì€ ìš°ì„ ìˆœìœ„', 'Highest priority' => 'ë†’ì€ ìš°ì„ ìˆœìœ„', - 'If you put zero to the low and high priority, this feature will be disabled.' => '만약 ë‚®ì€/ë†’ì€ ìš°ì„ ìˆœìœ„ì— 0ì„ ìž…ë ¥í•˜ë©´ ì´ íŠ¹ì„±ì€ ë¹„í™œì„±í™”ë©ë‹ˆë‹¤', 'Close a task when there is no activity' => '활ë™ì´ 없는 í• ì¼ì„ 종료합니다', 'Duration in days' => '기간', 'Send email when there is no activity on a task' => '활ë™ì´ 없는 í• ì¼ì„ ì´ë©”ì¼ë¡œ 보냅니다', @@ -1169,8 +1134,6 @@ return array( 'Subtasks overview for %s' => '%sì˜ ì„œë¸Œ í• ì¼ ê°œìš”', 'Projects overview for %s' => '%sì˜ í”„ë¡œì 트 개요', 'Activity stream for %s' => '%sì˜ í™œë™ê¸°ë¡', - 'Calendar for %s' => '%sì˜ ë‹¬ë ¥', - 'Notifications for %s' => '%sì˜ ì•Œë¦¼', 'Assign a color when the task is moved to a specific swimlane' => 'í• ì¼ì´ íŠ¹ì • 스웜ë¼ì¸ìœ¼ë¡œ 옮겨질 때 ìƒ‰ìƒ ì§€ì •', 'Assign a priority when the task is moved to a specific swimlane' => 'í• ì¼ì´ íŠ¹ì • 스웜ë¼ì¸ìœ¼ë¡œ 옮겨질 때 ìš°ì„ ìˆœìœ„ ì§€ì •', 'User unlocked successfully.' => 'ì‚¬ìš©ìž ìž ê¸ˆ 성공', @@ -1241,7 +1204,6 @@ return array( // 'Preview' => '', // 'Write' => '', // 'Write your text in Markdown' => '', - // 'New External Task: %s' => '', // 'No personal API access token registered.' => '', // 'Your personal API access token is "%s"' => '', // 'Remove your token' => '', @@ -1316,7 +1278,6 @@ return array( // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', // 'Database file' => '', // 'Upload' => '', - // 'Remove this user from group' => '', // 'Your project must have at least one active swimlane.' => '', // 'Project: %s' => '', // 'Automatic action not found: "%s"' => '', @@ -1334,4 +1295,75 @@ return array( // 'Assign automatically a color when due date is expired' => '', // 'Total score in this column across all swimlanes' => '', // 'HRK - Kuna' => '', + // 'ARS - Argentine Peso' => '', + // 'COP - Colombian Peso' => '', + // '%d groups' => '', + // '%d group' => '', + // 'Group ID' => '', + // 'External ID' => '', + // '%d users' => '', + // '%d user' => '', + // 'Hide subtasks' => '', + // 'Show subtasks' => '', + // 'Authentication Parameters' => '', + // 'API Access' => '', + // 'No users found.' => '', + // 'User ID' => '', + // 'Notifications are activated' => '', + // 'Notifications are disabled' => '', + // 'User disabled' => '', + // '%d notifications' => '', + // '%d notification' => '', + // 'There is no external integration installed.' => '', + // 'You are not allowed to update tasks assigned to someone else.' => '', + // 'You are not allowed to change the assignee.' => '', + // 'Task suppression is not permitted' => '', + // 'Changing assignee is not permitted' => '', + // 'Update only assigned tasks is permitted' => '', + // 'Only for tasks assigned to the current user' => '', + // 'My projects' => '', + // 'Your are not member of any project.' => '', + // 'My subtasks' => '', + // '%d subtasks' => '', + // '%d subtask' => '', + // 'Only moving task between those columns is permitted for tasks assigned to the current user' => '', + // '[DUPLICATE]' => '', + // 'DKK - Danish Krona' => '', + // 'Remove user from group' => '', + // 'Assign the task to its creator' => '', + // 'This task was sent by email to "%s" with subject "%s".' => '', + // 'Predefined Email Subjects' => '', + // 'Write one subject by line.' => '', + // 'Create another link' => '', + // 'BRL - Brazilian Real' => '', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/my_MY/translations.php b/app/Locale/my_MY/translations.php index 2fc12a35..eee1428c 100644 --- a/app/Locale/my_MY/translations.php +++ b/app/Locale/my_MY/translations.php @@ -39,7 +39,6 @@ return array( 'Administrator' => 'Pentadbir', 'Sign in' => 'Masuk', 'Users' => 'Para Pengguna', - 'No user' => 'Tiada pengguna', 'Forbidden' => 'Larangan', 'Access Forbidden' => 'Akses Dilarang', 'Edit user' => 'Ubah Pengguna', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'Sub-tugas berhasil diperbaharui.', 'Unable to update your sub-task.' => 'Tidak dapat memperbaharui sub-tugas anda.', 'Unable to create your sub-task.' => 'Tidak dapat membuat sub-tugas anda.', - 'Sub-task added successfully.' => 'Sub-tugas berhasil dibuat.', 'Maximum size: ' => 'Ukuran maksimum: ', 'Display another project' => 'Lihat projek lain', 'Created by %s' => 'Dibuat oleh %s', @@ -396,22 +394,17 @@ return array( 'Activity stream' => 'Arus aktifitas', 'Dashboard' => 'Dasbor', 'Confirmation' => 'Konfirmasi', - 'Allow everybody to access to this project' => 'Memungkinkan semua orang untuk mengakses projek ini', - 'Everybody have access to this project.' => 'Semua orang mendapat akses untuk projek ini.', 'Webhooks' => 'Webhooks', 'API' => 'API', 'Create a comment from an external provider' => 'Buat komentar dari pemasok eksternal', 'Project management' => 'Manajemen projek', - 'My projects' => 'projek saya', 'Columns' => 'Kolom', 'Task' => 'Tugas', - 'Your are not member of any project.' => 'Anda bukan anggota dari setiap projek.', 'Percentage' => 'Persentasi', 'Number of tasks' => 'Jumlah dari tugas', 'Task distribution' => 'Pembagian tugas', 'Analytics' => 'Analitis', 'Subtask' => 'Subtugas', - 'My subtasks' => 'Subtugas saya', 'User repartition' => 'Partisi ulang pengguna', 'Clone this project' => 'Gandakan projek ini', 'Column removed successfully.' => 'Kolom berhasil dihapus.', @@ -459,7 +452,6 @@ return array( 'Language:' => 'Bahasa:', 'Timezone:' => 'Zon masa:', 'All columns' => 'Semua kolom', - 'Calendar' => 'Kalender', 'Next' => 'Selanjutnya', '#%d' => 'n°%d', 'All swimlanes' => 'Semua swimlane', @@ -557,7 +549,6 @@ return array( 'Unable to add this currency rate.' => 'Tidak dapat menambahkan nilai tukar mata uang', 'Webhook URL' => 'URL webhook', '%s removed the assignee of the task %s' => '%s menghapus penugasan dari tugas %s', - 'Enable Gravatar images' => 'Mengaktifkan gambar Gravatar', 'Information' => 'Informasi', 'Check two factor authentication code' => 'Cek dua faktor kode otentifikasi', 'The two factor authentication code is not valid.' => 'Kode dua faktor kode otentifikasi tidak valid.', @@ -611,14 +602,7 @@ return array( 'When task is moved from first column' => 'Ketika tugas dipindahkan dari kolom pertama', 'When task is moved to last column' => 'Ketika tugas dipindahkan ke kolom terakhir', 'Year(s)' => 'Tahun', - 'Calendar settings' => 'Pengaturan kalender', - 'Project calendar view' => 'Tampilan kalender projek', 'Project settings' => 'Pengaturan projek', - 'Show subtasks based on the time tracking' => 'Tampilkan subtugas berdasarkan pelacakan waktu', - 'Show tasks based on the creation date' => 'Tampilkan tugas berdasarkan tanggal pembuatan', - 'Show tasks based on the start date' => 'Tampilkan tugas berdasarkan tanggal mulai', - 'Subtasks time tracking' => 'Pelacakan waktu subtgas', - 'User calendar view' => 'Pengguna tampilan kalender', 'Automatically update the start date' => 'Otomatikkan pengemaskinian tanggal', 'iCal feed' => 'iCal feed', 'Preferences' => 'Keutamaan', @@ -637,7 +621,6 @@ return array( 'Notification' => 'Pemberitahuan', '%s moved the task #%d to the first swimlane' => '%s memindahkan tugas n°%d ke swimlane pertama', 'Swimlane' => 'Swimlane', - 'Gravatar' => 'Gravatar', '%s moved the task %s to the first swimlane' => '%s memindahkan tugas %s ke swimlane pertama', '%s moved the task %s to the swimlane "%s"' => '%s memindahkan tugas %s ke swimlane « %s »', 'This report contains all subtasks information for the given date range.' => 'Laporan ini berisi semua informasi subtugas untuk rentang tanggal tertentu.', @@ -674,7 +657,6 @@ return array( 'Stop timer' => 'Hentikan timer', 'Start timer' => 'Mulai timer', 'My activity stream' => 'Aliran kegiatan saya', - 'My calendar' => 'Kalender saya', 'Search tasks' => 'Cari tugas', 'Reset filters' => 'Reset ulang filter', 'My tasks due tomorrow' => 'Tugas saya yang berakhir besok', @@ -688,7 +670,6 @@ return array( 'Overview' => 'Ikhtisar', 'Board/Calendar/List view' => 'Tampilan Papan/Kalender/Daftar', 'Switch to the board view' => 'Beralih ke tampilan papan', - 'Switch to the calendar view' => 'Beralih ke tampilan kalender', 'Switch to the list view' => 'Beralih ke tampilan daftar', 'Go to the search/filter box' => 'Pergi ke kotak pencarian/filter', 'There is no activity yet.' => 'Tidak ada aktifitas saat ini.', @@ -743,15 +724,8 @@ return array( 'License:' => 'Lesen:', 'License' => 'Lesen', 'Enter the text below' => 'Masukkan teks di bawah', - 'Sort by position' => 'Urutkan berdasarkan posisi', - 'Sort by date' => 'Urutkan berdasarkan tanggal', - 'Add task' => 'Tambah tugas', 'Start date:' => 'Tanggal mulai:', 'Due date:' => 'Batas waktu:', - 'There is no start date or due date for this task.' => 'Tiada tanggal mulai dan batas waktu untuk tugas ini.', - 'Moving or resizing a task will change the start and due date of the task.' => 'Memindahkan atau mengubah ukuran tugas anda akan mengubah tanggal mulai dan batas waktu dari tugas ini.', - 'There is no task in your project.' => 'Tiada tugas didalam projek anda.', - 'Gantt chart' => 'Carta Gantt', 'People who are project managers' => 'Orang-orang yang menjadi pengurus projek', 'People who are project members' => 'Orang-orang yang menjadi anggota projek', 'NOK - Norwegian Krone' => 'NOK - Krone Norwegia', @@ -763,22 +737,15 @@ return array( 'Members' => 'Anggota', 'Shared project' => 'projek bersama', 'Project managers' => 'Pengurus projek', - 'Gantt chart for all projects' => 'Carta Gantt untuk kesemua projek', 'Projects list' => 'Senarai projek', - 'Gantt chart for this project' => 'Carta Gantt untuk projek ini', - 'Project board' => 'Papan projek', 'End date:' => 'Waktu berakhir :', - 'There is no start date or end date for this project.' => 'Tidak ada waktu mula atau waktu berakhir pada projek ini', - 'Projects Gantt chart' => 'projekkan carta Gantt', 'Change task color when using a specific task link' => 'Rubah warna tugas ketika menggunakan Pautan tugas yang spesifik', 'Task link creation or modification' => 'Pautan tugas pada penciptaan atau penyuntingan', 'Milestone' => 'Batu Tanda', 'Documentation: %s' => 'Dokumentasi : %s', - 'Switch to the Gantt chart view' => 'Beralih ke tampilan Carta Gantt', 'Reset the search/filter box' => 'Tetap semula pencarian/saringan', 'Documentation' => 'Dokumentasi', 'Table of contents' => 'Isi kandungan', - 'Gantt' => 'Gantt', // 'Author' => '', // 'Version' => '', // 'Plugins' => '', @@ -889,7 +856,6 @@ return array( // 'There is no user available.' => '', // 'Do you really want to remove the user "%s" from the group "%s"?' => '', // 'There is no group.' => '', - // 'External Id' => '', // 'Add group member' => '', // 'Do you really want to remove this group: "%s"?' => '', // 'There is no user in this group.' => '', @@ -955,7 +921,6 @@ return array( // 'Default priority' => '', // 'Lowest priority' => '', // 'Highest priority' => '', - // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Close a task when there is no activity' => '', // 'Duration in days' => '', // 'Send email when there is no activity on a task' => '', @@ -1169,8 +1134,6 @@ return array( // 'Subtasks overview for %s' => '', // 'Projects overview for %s' => '', // 'Activity stream for %s' => '', - // 'Calendar for %s' => '', - // 'Notifications for %s' => '', // 'Assign a color when the task is moved to a specific swimlane' => '', // 'Assign a priority when the task is moved to a specific swimlane' => '', // 'User unlocked successfully.' => '', @@ -1241,7 +1204,6 @@ return array( // 'Preview' => '', // 'Write' => '', // 'Write your text in Markdown' => '', - // 'New External Task: %s' => '', // 'No personal API access token registered.' => '', // 'Your personal API access token is "%s"' => '', // 'Remove your token' => '', @@ -1316,7 +1278,6 @@ return array( // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', // 'Database file' => '', // 'Upload' => '', - // 'Remove this user from group' => '', // 'Your project must have at least one active swimlane.' => '', // 'Project: %s' => '', // 'Automatic action not found: "%s"' => '', @@ -1334,4 +1295,75 @@ return array( // 'Assign automatically a color when due date is expired' => '', // 'Total score in this column across all swimlanes' => '', // 'HRK - Kuna' => '', + // 'ARS - Argentine Peso' => '', + // 'COP - Colombian Peso' => '', + // '%d groups' => '', + // '%d group' => '', + // 'Group ID' => '', + // 'External ID' => '', + // '%d users' => '', + // '%d user' => '', + // 'Hide subtasks' => '', + // 'Show subtasks' => '', + // 'Authentication Parameters' => '', + // 'API Access' => '', + // 'No users found.' => '', + // 'User ID' => '', + // 'Notifications are activated' => '', + // 'Notifications are disabled' => '', + // 'User disabled' => '', + // '%d notifications' => '', + // '%d notification' => '', + // 'There is no external integration installed.' => '', + // 'You are not allowed to update tasks assigned to someone else.' => '', + // 'You are not allowed to change the assignee.' => '', + // 'Task suppression is not permitted' => '', + // 'Changing assignee is not permitted' => '', + // 'Update only assigned tasks is permitted' => '', + // 'Only for tasks assigned to the current user' => '', + // 'My projects' => '', + // 'Your are not member of any project.' => '', + // 'My subtasks' => '', + // '%d subtasks' => '', + // '%d subtask' => '', + // 'Only moving task between those columns is permitted for tasks assigned to the current user' => '', + // '[DUPLICATE]' => '', + // 'DKK - Danish Krona' => '', + // 'Remove user from group' => '', + // 'Assign the task to its creator' => '', + // 'This task was sent by email to "%s" with subject "%s".' => '', + // 'Predefined Email Subjects' => '', + // 'Write one subject by line.' => '', + // 'Create another link' => '', + // 'BRL - Brazilian Real' => '', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/nb_NO/translations.php b/app/Locale/nb_NO/translations.php index 59c4e7f1..0ffc7ee1 100644 --- a/app/Locale/nb_NO/translations.php +++ b/app/Locale/nb_NO/translations.php @@ -39,7 +39,6 @@ return array( 'Administrator' => 'Administrator', 'Sign in' => 'Logg inn', 'Users' => 'Brukere', - 'No user' => 'Ingen bruker', 'Forbidden' => 'Ikke tillatt', 'Access Forbidden' => 'Adgang ikke tillatt', 'Edit user' => 'Rediger bruker', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'Deloppgaven er opdateret.', 'Unable to update your sub-task.' => 'Deloppgaven kunne ikke opdateres.', 'Unable to create your sub-task.' => 'Deloppgaven kunne ikke oprettes.', - 'Sub-task added successfully.' => 'Deloppgaven er lagt til.', 'Maximum size: ' => 'Maksimum størrelse: ', 'Display another project' => 'Vis annet prosjekt...', 'Created by %s' => 'Opprettet av %s', @@ -396,22 +394,17 @@ return array( 'Activity stream' => 'Aktivitetslogg', 'Dashboard' => 'Hovedsiden', 'Confirmation' => 'Bekreftelse', - 'Allow everybody to access to this project' => 'Gi alle tilgang til dette prosjektet', - 'Everybody have access to this project.' => 'Alle har tilgang til dette prosjektet', // 'Webhooks' => '', // 'API' => '', 'Create a comment from an external provider' => 'Opprett en kommentar fra en ekstern tilbyder', 'Project management' => 'Prosjektinnstillinger', - 'My projects' => 'Mine prosjekter', 'Columns' => 'Kolonner', 'Task' => 'Oppgave', - // 'Your are not member of any project.' => '', 'Percentage' => 'Prosent', 'Number of tasks' => 'Antall oppgaver', 'Task distribution' => 'Kolonnefordeling', 'Analytics' => 'Analyser', 'Subtask' => 'Deloppgave', - 'My subtasks' => 'Mine deloppgaver', 'User repartition' => 'Brukerfordeling', 'Clone this project' => 'Kopier dette prosjektet', 'Column removed successfully.' => 'Kolonne flyttet', @@ -459,7 +452,6 @@ return array( 'Language:' => 'SprÃ¥k', 'Timezone:' => 'Tidssone', 'All columns' => 'Alle kolonner', - 'Calendar' => 'Kalender', 'Next' => 'Neste', // '#%d' => '', 'All swimlanes' => 'Alle svømmebaner', @@ -557,7 +549,6 @@ return array( // 'Unable to add this currency rate.' => '', // 'Webhook URL' => '', // '%s removed the assignee of the task %s' => '', - // 'Enable Gravatar images' => '', // 'Information' => '', // 'Check two factor authentication code' => '', // 'The two factor authentication code is not valid.' => '', @@ -611,14 +602,7 @@ return array( 'When task is moved from first column' => 'NÃ¥r oppgaven er flyttet fra første kolon', 'When task is moved to last column' => 'NÃ¥r oppgaven er flyttet til siste kolonne', 'Year(s)' => 'Ã¥r', - 'Calendar settings' => 'Kalenderinstillinger', - 'Project calendar view' => 'Visning prosjektkalender', 'Project settings' => 'Prosjektinnstillinger', - // 'Show subtasks based on the time tracking' => '', - // 'Show tasks based on the creation date' => '', - // 'Show tasks based on the start date' => '', - // 'Subtasks time tracking' => '', - // 'User calendar view' => '', 'Automatically update the start date' => 'Oppdater automatisk start-datoen', // 'iCal feed' => '', 'Preferences' => 'Preferanser', @@ -637,7 +621,6 @@ return array( 'Notification' => 'Varsel', // '%s moved the task #%d to the first swimlane' => '', 'Swimlane' => 'Svømmebane', - // 'Gravatar' => '', // '%s moved the task %s to the first swimlane' => '', // '%s moved the task %s to the swimlane "%s"' => '', // 'This report contains all subtasks information for the given date range.' => '', @@ -674,7 +657,6 @@ return array( 'Stop timer' => 'Stopp timer', 'Start timer' => 'Start timer', 'My activity stream' => 'Aktivitetslogg', - 'My calendar' => 'Min kalender', 'Search tasks' => 'Søk oppgave', 'Reset filters' => 'Nullstill filter', 'My tasks due tomorrow' => 'Mine oppgaver med frist i morgen', @@ -688,7 +670,6 @@ return array( 'Overview' => 'Oversikt', 'Board/Calendar/List view' => 'Oversikt/kalender/listevisning', 'Switch to the board view' => 'Oversiktsvisning', - 'Switch to the calendar view' => 'Kalendevisning', 'Switch to the list view' => 'Listevisning', 'Go to the search/filter box' => 'GÃ¥ til søk/filter', 'There is no activity yet.' => 'Ingen aktiviteter ennÃ¥.', @@ -743,15 +724,8 @@ return array( 'License:' => 'Lisens:', 'License' => 'Lisens', 'Enter the text below' => 'Legg inn teksten nedenfor', - // 'Sort by position' => '', - 'Sort by date' => 'Sorter etter dato', - 'Add task' => 'Legg til oppgave', 'Start date:' => 'Startdato:', 'Due date:' => 'Frist:', - 'There is no start date or due date for this task.' => 'Det er ingen startdato eller frist for denne oppgaven', - // 'Moving or resizing a task will change the start and due date of the task.' => '', - 'There is no task in your project.' => 'Det er ingen oppgaver i dette prosjektet', - 'Gantt chart' => 'Gantt skjema', 'People who are project managers' => 'Prosjektledere', 'People who are project members' => 'Prosjektmedlemmer', // 'NOK - Norwegian Krone' => '', @@ -763,22 +737,15 @@ return array( 'Members' => 'Medlemmer', 'Shared project' => 'Delt prosjekt', 'Project managers' => 'Prosjektledere', - // 'Gantt chart for all projects' => '', 'Projects list' => 'Prosjektliste', - 'Gantt chart for this project' => 'Gantt skjema for dette prosjektet', - 'Project board' => 'Prosjektsiden', 'End date:' => 'Sluttdato:', - // 'There is no start date or end date for this project.' => '', - 'Projects Gantt chart' => 'Gantt skjema for prosjekter', // 'Change task color when using a specific task link' => '', // 'Task link creation or modification' => '', 'Milestone' => 'Milepæl', 'Documentation: %s' => 'Dokumentasjon: %s', - 'Switch to the Gantt chart view' => 'Gantt skjema visning', 'Reset the search/filter box' => 'Nullstill søk/filter', 'Documentation' => 'Dokumentasjon', 'Table of contents' => 'Innholdsfortegnelse', - 'Gantt' => 'Gantt', // 'Author' => '', // 'Version' => '', // 'Plugins' => '', @@ -889,7 +856,6 @@ return array( // 'There is no user available.' => '', // 'Do you really want to remove the user "%s" from the group "%s"?' => '', // 'There is no group.' => '', - // 'External Id' => '', // 'Add group member' => '', // 'Do you really want to remove this group: "%s"?' => '', // 'There is no user in this group.' => '', @@ -955,7 +921,6 @@ return array( // 'Default priority' => '', // 'Lowest priority' => '', // 'Highest priority' => '', - // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Close a task when there is no activity' => '', // 'Duration in days' => '', // 'Send email when there is no activity on a task' => '', @@ -1169,8 +1134,6 @@ return array( // 'Subtasks overview for %s' => '', // 'Projects overview for %s' => '', // 'Activity stream for %s' => '', - // 'Calendar for %s' => '', - // 'Notifications for %s' => '', // 'Assign a color when the task is moved to a specific swimlane' => '', // 'Assign a priority when the task is moved to a specific swimlane' => '', // 'User unlocked successfully.' => '', @@ -1241,7 +1204,6 @@ return array( // 'Preview' => '', // 'Write' => '', // 'Write your text in Markdown' => '', - // 'New External Task: %s' => '', // 'No personal API access token registered.' => '', // 'Your personal API access token is "%s"' => '', // 'Remove your token' => '', @@ -1316,7 +1278,6 @@ return array( // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', // 'Database file' => '', // 'Upload' => '', - // 'Remove this user from group' => '', // 'Your project must have at least one active swimlane.' => '', // 'Project: %s' => '', // 'Automatic action not found: "%s"' => '', @@ -1334,4 +1295,75 @@ return array( // 'Assign automatically a color when due date is expired' => '', // 'Total score in this column across all swimlanes' => '', // 'HRK - Kuna' => '', + // 'ARS - Argentine Peso' => '', + // 'COP - Colombian Peso' => '', + // '%d groups' => '', + // '%d group' => '', + // 'Group ID' => '', + // 'External ID' => '', + // '%d users' => '', + // '%d user' => '', + // 'Hide subtasks' => '', + // 'Show subtasks' => '', + // 'Authentication Parameters' => '', + // 'API Access' => '', + // 'No users found.' => '', + // 'User ID' => '', + // 'Notifications are activated' => '', + // 'Notifications are disabled' => '', + // 'User disabled' => '', + // '%d notifications' => '', + // '%d notification' => '', + // 'There is no external integration installed.' => '', + // 'You are not allowed to update tasks assigned to someone else.' => '', + // 'You are not allowed to change the assignee.' => '', + // 'Task suppression is not permitted' => '', + // 'Changing assignee is not permitted' => '', + // 'Update only assigned tasks is permitted' => '', + // 'Only for tasks assigned to the current user' => '', + // 'My projects' => '', + // 'Your are not member of any project.' => '', + // 'My subtasks' => '', + // '%d subtasks' => '', + // '%d subtask' => '', + // 'Only moving task between those columns is permitted for tasks assigned to the current user' => '', + // '[DUPLICATE]' => '', + // 'DKK - Danish Krona' => '', + // 'Remove user from group' => '', + // 'Assign the task to its creator' => '', + // 'This task was sent by email to "%s" with subject "%s".' => '', + // 'Predefined Email Subjects' => '', + // 'Write one subject by line.' => '', + // 'Create another link' => '', + // 'BRL - Brazilian Real' => '', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/nl_NL/translations.php b/app/Locale/nl_NL/translations.php index 11308340..12db3656 100644 --- a/app/Locale/nl_NL/translations.php +++ b/app/Locale/nl_NL/translations.php @@ -39,7 +39,6 @@ return array( 'Administrator' => 'Administrator', 'Sign in' => 'Inloggen', 'Users' => 'Gebruikers', - 'No user' => 'Geen gebruiker', 'Forbidden' => 'Geweigerd', 'Access Forbidden' => 'Toegang geweigerd', 'Edit user' => 'Gebruiker bewerken', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'Subtaak succesvol aangepast.', 'Unable to update your sub-task.' => 'Subtaak aanpassen niet gelukt.', 'Unable to create your sub-task.' => 'Subtaak aanmaken niet gelukt.', - 'Sub-task added successfully.' => 'Subtaak succesvol aangemaakt.', 'Maximum size: ' => 'Maximale grootte : ', 'Display another project' => 'Een ander project weergeven', 'Created by %s' => 'Aangemaakt door %s', @@ -396,22 +394,17 @@ return array( 'Activity stream' => 'Activiteiten', 'Dashboard' => 'Dashboard', // 'Confirmation' => '', - 'Allow everybody to access to this project' => 'Geef iedereen toegang tot dit project', - 'Everybody have access to this project.' => 'Iedereen heeft toegang tot dit project.', 'Webhooks' => 'Webhooks', 'API' => 'API', 'Create a comment from an external provider' => 'Voeg een commentaar toe van een externe provider', 'Project management' => 'Project management', - 'My projects' => 'Mijn projecten', 'Columns' => 'Kolommen', 'Task' => 'Taak', - 'Your are not member of any project.' => 'U bent van geen enkel project lid.', 'Percentage' => 'Percentage', 'Number of tasks' => 'Aantal taken', 'Task distribution' => 'Distributie van taken', 'Analytics' => 'Analytics', 'Subtask' => 'Subtaak', - 'My subtasks' => 'Mijn subtaken', 'User repartition' => 'Gebruikerverdeling', 'Clone this project' => 'Kloon dit project', 'Column removed successfully.' => 'Kolom succesvol verwijderd.', @@ -459,7 +452,6 @@ return array( 'Language:' => 'Taal :', 'Timezone:' => 'Tijdzone :', 'All columns' => 'Alle kolommen', - 'Calendar' => 'Agenda', 'Next' => 'Volgende', '#%d' => '%d', 'All swimlanes' => 'Alle swimlanes', @@ -557,7 +549,6 @@ return array( // 'Unable to add this currency rate.' => '', 'Webhook URL' => 'Webhook URL', // '%s removed the assignee of the task %s' => '', - // 'Enable Gravatar images' => '', // 'Information' => '', // 'Check two factor authentication code' => '', // 'The two factor authentication code is not valid.' => '', @@ -573,7 +564,7 @@ return array( // 'Burndown chart' => '', // 'This chart show the task complexity over the time (Work Remaining).' => '', // 'Screenshot taken %s' => '', - // 'Add a screenshot' => '', + 'Add a screenshot' => 'Screenshot toevoegen', // 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => '', // 'Screenshot uploaded successfully.' => '', // 'SEK - Swedish Krona' => '', @@ -589,7 +580,7 @@ return array( // 'The identifier must be unique' => '', // 'This linked task id doesn\'t exists' => '', // 'This value must be alphanumeric' => '', - // 'Edit recurrence' => '', + 'Edit recurrence' => 'Herhaling instellen', // 'Generate recurrent task' => '', // 'Trigger to generate recurrent task' => '', // 'Factor to calculate new due date' => '', @@ -611,14 +602,7 @@ return array( // 'When task is moved from first column' => '', // 'When task is moved to last column' => '', 'Year(s)' => 'Jaar/Jaren', - 'Calendar settings' => 'Kalender instellingen', - // 'Project calendar view' => '', 'Project settings' => 'Project instellingen', - // 'Show subtasks based on the time tracking' => '', - // 'Show tasks based on the creation date' => '', - // 'Show tasks based on the start date' => '', - // 'Subtasks time tracking' => '', - // 'User calendar view' => '', // 'Automatically update the start date' => '', // 'iCal feed' => '', 'Preferences' => 'Voorkeuren', @@ -637,7 +621,6 @@ return array( // 'Notification' => '', // '%s moved the task #%d to the first swimlane' => '', 'Swimlane' => 'Swimlane', - 'Gravatar' => 'Gravatar', '%s moved the task %s to the first swimlane' => '%s heeft de taak %s naar de eerste swimlane verplaatst', '%s moved the task %s to the swimlane "%s"' => '%s heeft taak %s naar swimlane "%s" verplaatst', // 'This report contains all subtasks information for the given date range.' => '', @@ -674,7 +657,6 @@ return array( 'Stop timer' => 'Stop timer', 'Start timer' => 'Start timer', 'My activity stream' => 'Mijn activiteiten', - 'My calendar' => 'Mijn kalender', 'Search tasks' => 'Zoek taken', 'Reset filters' => 'Reset filters', // 'My tasks due tomorrow' => '', @@ -688,7 +670,6 @@ return array( 'Overview' => 'Overzicht', // 'Board/Calendar/List view' => '', // 'Switch to the board view' => '', - // 'Switch to the calendar view' => '', // 'Switch to the list view' => '', // 'Go to the search/filter box' => '', // 'There is no activity yet.' => '', @@ -743,15 +724,8 @@ return array( // 'License:' => '', // 'License' => '', // 'Enter the text below' => '', - // 'Sort by position' => '', - // 'Sort by date' => '', - // 'Add task' => '', // 'Start date:' => '', // 'Due date:' => '', - // 'There is no start date or due date for this task.' => '', - // 'Moving or resizing a task will change the start and due date of the task.' => '', - // 'There is no task in your project.' => '', - // 'Gantt chart' => '', // 'People who are project managers' => '', // 'People who are project members' => '', // 'NOK - Norwegian Krone' => '', @@ -763,22 +737,15 @@ return array( 'Members' => 'Leden', // 'Shared project' => '', // 'Project managers' => '', - // 'Gantt chart for all projects' => '', // 'Projects list' => '', - // 'Gantt chart for this project' => '', - // 'Project board' => '', // 'End date:' => '', - // 'There is no start date or end date for this project.' => '', - // 'Projects Gantt chart' => '', // 'Change task color when using a specific task link' => '', // 'Task link creation or modification' => '', 'Milestone' => 'Mijlpaal', // 'Documentation: %s' => '', - // 'Switch to the Gantt chart view' => '', // 'Reset the search/filter box' => '', // 'Documentation' => '', // 'Table of contents' => '', - // 'Gantt' => '', // 'Author' => '', // 'Version' => '', // 'Plugins' => '', @@ -889,7 +856,6 @@ return array( // 'There is no user available.' => '', // 'Do you really want to remove the user "%s" from the group "%s"?' => '', // 'There is no group.' => '', - // 'External Id' => '', // 'Add group member' => '', // 'Do you really want to remove this group: "%s"?' => '', // 'There is no user in this group.' => '', @@ -941,21 +907,20 @@ return array( // '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '', // 'Close all tasks of this column' => '', // 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => '', - // 'My dashboard' => '', - // 'My profile' => '', - // 'Project owner: ' => '', + 'My dashboard' => 'Mijn dashboard', + 'My profile' => 'Mijn profiel', + 'Project owner: ' => 'Project eigenaar: ', // 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '', - // 'Project owner' => '', + 'Project owner' => 'Project eigenaar', // 'Private projects do not have users and groups management.' => '', // 'There is no project member.' => '', - // 'Priority' => '', - // 'Task priority' => '', - // 'General' => '', + 'Priority' => 'Prioriteit', + 'Task priority' => 'Taak prioriteit', + 'General' => 'Algemeen', // 'Dates' => '', // 'Default priority' => '', // 'Lowest priority' => '', // 'Highest priority' => '', - // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Close a task when there is no activity' => '', // 'Duration in days' => '', // 'Send email when there is no activity on a task' => '', @@ -984,28 +949,28 @@ return array( // 'Users management' => '', // 'Groups management' => '', // 'Create from another project' => '', - // 'open' => '', - // 'closed' => '', - // 'Priority:' => '', + 'open' => 'open', + 'closed' => 'gesloten', + 'Priority:' => 'Prioriteit:', // 'Reference:' => '', - // 'Complexity:' => '', + 'Complexity:' => 'Complexiteit:', // 'Swimlane:' => '', - // 'Column:' => '', - // 'Position:' => '', - // 'Creator:' => '', - // 'Time estimated:' => '', + 'Column:' => 'Kolom:', + 'Position:' => 'Positie:', + 'Creator:' => 'Aanmaker:', + 'Time estimated:' => 'Ingeschatte tijd:', // '%s hours' => '', - // 'Time spent:' => '', - // 'Created:' => '', - // 'Modified:' => '', - // 'Completed:' => '', - // 'Started:' => '', - // 'Moved:' => '', + 'Time spent:' => 'Bestede tijd:', + 'Created:' => 'Aangemaakt:', + 'Modified:' => 'Gewijzigd:', + 'Completed:' => 'Afgerond:', + 'Started:' => 'Begonnen:', + 'Moved:' => 'Verplaatst:', // 'Task #%d' => '', - // 'Time format' => '', - // 'Start date: ' => '', - // 'End date: ' => '', - // 'New due date: ' => '', + 'Time format' => 'Tijd formaat', + 'Start date: ' => 'Begindatum: ', + 'End date: ' => 'Einddatum: ', + 'New due date: ' => 'Nieuwe einddatum: ', // 'Start date changed: ' => '', // 'Disable private projects' => '', // 'Do you really want to remove this custom filter: "%s"?' => '', @@ -1125,7 +1090,7 @@ return array( // 'Email sender address' => '', // 'Email transport' => '', // 'Webhook token' => '', - // 'Project tags management' => '', + 'Project tags management' => 'Project tags beheer', // 'Tag created successfully.' => '', // 'Unable to create this tag.' => '', // 'Tag updated successfully.' => '', @@ -1135,10 +1100,10 @@ return array( // 'Global tags management' => '', // 'Tags' => '', // 'Tags management' => '', - // 'Add new tag' => '', + 'Add new tag' => 'Voeg een nieuwe tag toe', // 'Edit a tag' => '', // 'Project tags' => '', - // 'There is no specific tag for this project at the moment.' => '', + 'There is no specific tag for this project at the moment.' => 'Er zijn geen tags gedefinieerd voor dit project.', // 'Tag' => '', // 'Remove a tag' => '', // 'Do you really want to remove this tag: "%s"?' => '', @@ -1169,8 +1134,6 @@ return array( // 'Subtasks overview for %s' => '', // 'Projects overview for %s' => '', // 'Activity stream for %s' => '', - // 'Calendar for %s' => '', - // 'Notifications for %s' => '', // 'Assign a color when the task is moved to a specific swimlane' => '', // 'Assign a priority when the task is moved to a specific swimlane' => '', // 'User unlocked successfully.' => '', @@ -1241,7 +1204,6 @@ return array( // 'Preview' => '', // 'Write' => '', // 'Write your text in Markdown' => '', - // 'New External Task: %s' => '', // 'No personal API access token registered.' => '', // 'Your personal API access token is "%s"' => '', // 'Remove your token' => '', @@ -1250,14 +1212,14 @@ return array( // 'Outgoing Emails' => '', // 'Add or change currency rate' => '', // 'Reference currency: %s' => '', - // 'Add custom filters' => '', + 'Add custom filters' => 'Voeg nieuw aangepast filter toe', // 'Export' => '', // 'Add link label' => '', // 'Incompatible Plugins' => '', // 'Compatibility' => '', // 'Permissions and ownership' => '', // 'Priorities' => '', - // 'Close this window' => '', + 'Close this window' => 'Sluit dit scherm', // 'Unable to upload this file.' => '', // 'Import tasks' => '', // 'Choose a project' => '', @@ -1267,10 +1229,10 @@ return array( // '%d invitation was sent.' => '', // 'Unable to create this user.' => '', // 'Kanboard Invitation' => '', - // 'Visible on dashboard' => '', + 'Visible on dashboard' => 'Tonen op dashboard', // 'Created at:' => '', // 'Updated at:' => '', - // 'There is no custom filter.' => '', + 'There is no custom filter.' => 'Er zijn geen aangepaste filters.', // 'New User' => '', // 'Authentication' => '', // 'If checked, this user will use a third-party system for authentication.' => '', @@ -1293,14 +1255,14 @@ return array( // 'The project email is optional and could be used by several plugins.' => '', // 'The project email must be unique across all projects' => '', // 'The email configuration has been disabled by the administrator.' => '', - // 'Close this project' => '', - // 'Open this project' => '', + 'Close this project' => 'Dit project sluiten', + 'Open this project' => 'Dit project openen', // 'Close a project' => '', - // 'Do you really want to close this project: "%s"?' => '', - // 'Reopen a project' => '', - // 'Do you really want to reopen this project: "%s"?' => '', - // 'This project is open' => '', - // 'This project is closed' => '', + 'Do you really want to close this project: "%s"?' => 'Weet u zeker dat u dit project wilt sluiten: "%s"?', + 'Reopen a project' => 'Heropen dit project', + 'Do you really want to reopen this project: "%s"?' => 'Weet u zeker dat u dit project wilt heropenen: "%s"?', + 'This project is open' => 'Dit project is open', + 'This project is closed' => 'Dit project is gesloten', // 'Unable to upload files, check the permissions of your data folder.' => '', // 'Another category with the same name exists in this project' => '', // 'Comment sent by email successfully.' => '', @@ -1316,7 +1278,6 @@ return array( // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', // 'Database file' => '', // 'Upload' => '', - // 'Remove this user from group' => '', // 'Your project must have at least one active swimlane.' => '', // 'Project: %s' => '', // 'Automatic action not found: "%s"' => '', @@ -1334,4 +1295,75 @@ return array( // 'Assign automatically a color when due date is expired' => '', // 'Total score in this column across all swimlanes' => '', // 'HRK - Kuna' => '', + // 'ARS - Argentine Peso' => '', + // 'COP - Colombian Peso' => '', + // '%d groups' => '', + // '%d group' => '', + // 'Group ID' => '', + // 'External ID' => '', + // '%d users' => '', + // '%d user' => '', + // 'Hide subtasks' => '', + // 'Show subtasks' => '', + // 'Authentication Parameters' => '', + // 'API Access' => '', + // 'No users found.' => '', + // 'User ID' => '', + // 'Notifications are activated' => '', + // 'Notifications are disabled' => '', + // 'User disabled' => '', + // '%d notifications' => '', + // '%d notification' => '', + // 'There is no external integration installed.' => '', + // 'You are not allowed to update tasks assigned to someone else.' => '', + // 'You are not allowed to change the assignee.' => '', + // 'Task suppression is not permitted' => '', + // 'Changing assignee is not permitted' => '', + // 'Update only assigned tasks is permitted' => '', + // 'Only for tasks assigned to the current user' => '', + 'My projects' => 'Mijn projecten', + // 'Your are not member of any project.' => '', + 'My subtasks' => 'Mijn subtaken', + // '%d subtasks' => '', + // '%d subtask' => '', + // 'Only moving task between those columns is permitted for tasks assigned to the current user' => '', + // '[DUPLICATE]' => '', + // 'DKK - Danish Krona' => '', + // 'Remove user from group' => '', + // 'Assign the task to its creator' => '', + // 'This task was sent by email to "%s" with subject "%s".' => '', + // 'Predefined Email Subjects' => '', + // 'Write one subject by line.' => '', + // 'Create another link' => '', + // 'BRL - Brazilian Real' => '', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/pl_PL/translations.php b/app/Locale/pl_PL/translations.php index f075eb3a..78c0c2ea 100644 --- a/app/Locale/pl_PL/translations.php +++ b/app/Locale/pl_PL/translations.php @@ -39,7 +39,6 @@ return array( 'Administrator' => 'Administrator', 'Sign in' => 'Zaloguj', 'Users' => 'Użytkownicy', - 'No user' => 'Brak użytkowników', 'Forbidden' => 'Zabroniony', 'Access Forbidden' => 'DostÄ™p zabroniony', 'Edit user' => 'Edytuj użytkownika', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'Pod-zadanie zaktualizowane pomyÅ›lnie.', 'Unable to update your sub-task.' => 'Nie można zaktualizować tego pod-zadania.', 'Unable to create your sub-task.' => 'Nie można utworzyć tego pod-zadania.', - 'Sub-task added successfully.' => 'Pod-zadanie utworzone pomyÅ›lnie', 'Maximum size: ' => 'Maksymalny rozmiar: ', 'Display another project' => 'WyÅ›wietl inny projekt', 'Created by %s' => 'Utworzone przez %s', @@ -396,22 +394,17 @@ return array( 'Activity stream' => 'StrumieÅ„ aktywnoÅ›ci', 'Dashboard' => 'Dashboard', 'Confirmation' => 'Potwierdzenie', - 'Allow everybody to access to this project' => 'UdostÄ™pnij ten projekt wszystkim', - 'Everybody have access to this project.' => 'Wszyscy majÄ… dostÄ™p do tego projektu.', 'Webhooks' => 'Webhooki', // 'API' => '', 'Create a comment from an external provider' => 'Utwórz komentarz od zewnÄ™trznego dostawcy', 'Project management' => 'Menadżer projektu', - 'My projects' => 'Moje projekty', 'Columns' => 'Kolumny', 'Task' => 'Zadanie', - 'Your are not member of any project.' => 'Nie bierzesz udziaÅ‚u w żadnym projekcie', 'Percentage' => 'Procent', 'Number of tasks' => 'Liczba zadaÅ„', 'Task distribution' => 'Rozmieszczenie zadaÅ„', 'Analytics' => 'Analizy', 'Subtask' => 'Pod-zadanie', - 'My subtasks' => 'Moje pod-zadania', 'User repartition' => 'PrzydziaÅ‚ użytkownika', 'Clone this project' => 'Sklonuj ten projekt', 'Column removed successfully.' => 'Kolumna usuniÄ™ta pomyÅ›lnie.', @@ -459,7 +452,6 @@ return array( 'Language:' => 'JÄ™zyk:', 'Timezone:' => 'Strefa czasowa:', 'All columns' => 'Wszystkie kolumny', - 'Calendar' => 'Kalendarz', 'Next' => 'NastÄ™pny', '#%d' => 'nr %d', 'All swimlanes' => 'Wszystkie tory', @@ -557,7 +549,6 @@ return array( 'Unable to add this currency rate.' => 'Nie można dodać kursu waluty', 'Webhook URL' => 'Adres webhooka', '%s removed the assignee of the task %s' => '%s usunÄ…Å‚ osobÄ™ przypisanÄ… do zadania %s', - 'Enable Gravatar images' => 'Włącz Gravatar', 'Information' => 'Informacje', 'Check two factor authentication code' => 'Sprawdź kod weryfikujÄ…cy', 'The two factor authentication code is not valid.' => 'Kod weryfikujÄ…cy niepoprawny', @@ -611,14 +602,7 @@ return array( 'When task is moved from first column' => 'Przeniesienie zadania z pierwszej kolumny', 'When task is moved to last column' => 'Przeniesienie zadania do ostatniej kolumny', 'Year(s)' => 'Lat', - 'Calendar settings' => 'Ustawienia kalendarza', - 'Project calendar view' => 'Widok kalendarza projektu', 'Project settings' => 'Ustawienia Projektu', - 'Show subtasks based on the time tracking' => 'Pokaż pod-zadania w Å›ledzeniu czasu', - 'Show tasks based on the creation date' => 'Pokaż zadania wzglÄ™dem daty utworzenia', - 'Show tasks based on the start date' => 'Pokaż zadania wzglÄ™dem daty rozpoczÄ™cia', - 'Subtasks time tracking' => 'Åšledzenie czasu pod-zadaÅ„', - 'User calendar view' => 'Widok kalendarza użytkownika', 'Automatically update the start date' => 'Automatycznie aktualizuj datÄ™ rozpoczÄ™cia', // 'iCal feed' => '', 'Preferences' => 'Ustawienia', @@ -637,7 +621,6 @@ return array( 'Notification' => 'Powiadomienie', '%s moved the task #%d to the first swimlane' => '%s przeniosÅ‚ zadanie #%d na pierwszy tor', 'Swimlane' => 'Tor', - // 'Gravatar' => '', '%s moved the task %s to the first swimlane' => '%s przeniosÅ‚ zadanie %s na pierwszy tor', '%s moved the task %s to the swimlane "%s"' => '%s przeniosÅ‚ zadanie %s na tor "%s"', 'This report contains all subtasks information for the given date range.' => 'Niniejszy raport zawiera wszystkie informacje o pod-zadaniach dla podanego zakresu dat.', @@ -674,7 +657,6 @@ return array( 'Stop timer' => 'Zatrzymaj pomiar czasu', 'Start timer' => 'Uruchom pomiar czasu', 'My activity stream' => 'Moja aktywność', - 'My calendar' => 'Mój kalendarz', 'Search tasks' => 'Szukaj zadaÅ„', 'Reset filters' => 'Resetuj zastosowane filtry', 'My tasks due tomorrow' => 'Moje zadania do jutra', @@ -688,7 +670,6 @@ return array( 'Overview' => 'Podsumowanie', 'Board/Calendar/List view' => 'Widok: Tablica/Kalendarz/Lista', 'Switch to the board view' => 'Przełącz na tablicÄ™', - 'Switch to the calendar view' => 'Przełącz na kalendarz', 'Switch to the list view' => 'Przełącz na listÄ™', 'Go to the search/filter box' => 'Użyj pola wyszukiwania/filtrów', 'There is no activity yet.' => 'Brak powiadomieÅ„', @@ -743,15 +724,8 @@ return array( 'License:' => 'Licencja:', 'License' => 'Licencja', 'Enter the text below' => 'Wpisz tekst poniżej', - 'Sort by position' => 'Sortuj wg pozycji', - 'Sort by date' => 'Sortuj wg daty', - 'Add task' => 'Dodaj zadanie', 'Start date:' => 'Data rozpoczÄ™cia:', 'Due date:' => 'Termin', - 'There is no start date or due date for this task.' => 'Brak daty rozpoczÄ™cia lub terminu zadania', - 'Moving or resizing a task will change the start and due date of the task.' => 'Przeniesienie bÄ…dź edycja zmieni datÄ™ rozpoczÄ™cia oraz termin ukoÅ„czenia zadania.', - 'There is no task in your project.' => 'Brak zadaÅ„ w projekcie.', - 'Gantt chart' => 'Wykres Gantta', 'People who are project managers' => 'Użytkownicy bÄ™dÄ…cy menedżerami projektu', 'People who are project members' => 'Użytkownicy bÄ™dÄ…cy uczestnikami projektu', 'NOK - Norwegian Krone' => 'NOK - Korona norweska', @@ -763,22 +737,15 @@ return array( 'Members' => 'CzÅ‚onkowie', 'Shared project' => 'Projekt udostÄ™pniony', 'Project managers' => 'Menedżerowie projektu', - 'Gantt chart for all projects' => 'Wykres Gantta dla wszystkich projektów', 'Projects list' => 'Lista projektów', - 'Gantt chart for this project' => 'Wykres Gantta dla bieżacego projektu', - 'Project board' => 'Talica projektu', 'End date:' => 'Data zakoÅ„czenia:', - 'There is no start date or end date for this project.' => 'Nie zdefiniowano czasu trwania projektu', - 'Projects Gantt chart' => 'Wykres Gantta dla projektów', 'Change task color when using a specific task link' => 'ZmieÅ„ kolor zadania używajÄ…c specjalnego adresu URL', 'Task link creation or modification' => 'Adres URL do utworzenia zadania lub modyfikacji', 'Milestone' => 'KamieÅ„ milowy', 'Documentation: %s' => 'Dokumentacja: %s', - 'Switch to the Gantt chart view' => 'Przełącz na wykres Gantta', 'Reset the search/filter box' => 'Zresetuj pole wyszukiwania/filtrowania', 'Documentation' => 'Dokumentacja', 'Table of contents' => 'Tablica zawartoÅ›ci', - // 'Gantt' => '', 'Author' => 'Autor', 'Version' => 'Wersja', 'Plugins' => 'Wtyczki', @@ -889,7 +856,6 @@ return array( 'There is no user available.' => 'Å»aden użytkownik nie jest dostÄ™pny.', 'Do you really want to remove the user "%s" from the group "%s"?' => 'Czy na pewno chcesz usunąć użytkownika "%s" z grupy "%s"?', 'There is no group.' => 'Nie utworzono jeszcze żadnej grupy.', - 'External Id' => 'ZewnÄ™trzny Id', 'Add group member' => 'Dodaj czÅ‚onka grupy', 'Do you really want to remove this group: "%s"?' => 'Czy na pewno chcesz usunąć grupÄ™ "%s"?', 'There is no user in this group.' => 'Wybrana grupa nie posiada czÅ‚onków.', @@ -955,7 +921,6 @@ return array( 'Default priority' => 'DomyÅ›lny priorytet', 'Lowest priority' => 'Najniższy priorytet', 'Highest priority' => 'Najwyższy priorytet', - 'If you put zero to the low and high priority, this feature will be disabled.' => 'Jeżeli dla najniższego i najwyższego priorytetu ustawisz 0, to priorytety dla tablicy zostanÄ… wyłączone.', 'Close a task when there is no activity' => 'Zamknij zadanie gdy nie jest aktywne', 'Duration in days' => 'Czas trwania w dniach', 'Send email when there is no activity on a task' => 'WyÅ›lij email gdy zadanie nie jest aktywne', @@ -1169,8 +1134,6 @@ return array( // 'Subtasks overview for %s' => '', // 'Projects overview for %s' => '', // 'Activity stream for %s' => '', - // 'Calendar for %s' => '', - // 'Notifications for %s' => '', // 'Assign a color when the task is moved to a specific swimlane' => '', // 'Assign a priority when the task is moved to a specific swimlane' => '', // 'User unlocked successfully.' => '', @@ -1241,7 +1204,6 @@ return array( // 'Preview' => '', // 'Write' => '', // 'Write your text in Markdown' => '', - // 'New External Task: %s' => '', // 'No personal API access token registered.' => '', // 'Your personal API access token is "%s"' => '', // 'Remove your token' => '', @@ -1316,7 +1278,6 @@ return array( // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', // 'Database file' => '', // 'Upload' => '', - // 'Remove this user from group' => '', // 'Your project must have at least one active swimlane.' => '', // 'Project: %s' => '', // 'Automatic action not found: "%s"' => '', @@ -1334,4 +1295,75 @@ return array( // 'Assign automatically a color when due date is expired' => '', // 'Total score in this column across all swimlanes' => '', // 'HRK - Kuna' => '', + // 'ARS - Argentine Peso' => '', + // 'COP - Colombian Peso' => '', + // '%d groups' => '', + // '%d group' => '', + // 'Group ID' => '', + // 'External ID' => '', + // '%d users' => '', + // '%d user' => '', + // 'Hide subtasks' => '', + // 'Show subtasks' => '', + // 'Authentication Parameters' => '', + // 'API Access' => '', + // 'No users found.' => '', + // 'User ID' => '', + // 'Notifications are activated' => '', + // 'Notifications are disabled' => '', + // 'User disabled' => '', + // '%d notifications' => '', + // '%d notification' => '', + // 'There is no external integration installed.' => '', + // 'You are not allowed to update tasks assigned to someone else.' => '', + // 'You are not allowed to change the assignee.' => '', + // 'Task suppression is not permitted' => '', + // 'Changing assignee is not permitted' => '', + // 'Update only assigned tasks is permitted' => '', + // 'Only for tasks assigned to the current user' => '', + // 'My projects' => '', + // 'Your are not member of any project.' => '', + // 'My subtasks' => '', + // '%d subtasks' => '', + // '%d subtask' => '', + // 'Only moving task between those columns is permitted for tasks assigned to the current user' => '', + // '[DUPLICATE]' => '', + // 'DKK - Danish Krona' => '', + // 'Remove user from group' => '', + // 'Assign the task to its creator' => '', + // 'This task was sent by email to "%s" with subject "%s".' => '', + // 'Predefined Email Subjects' => '', + // 'Write one subject by line.' => '', + // 'Create another link' => '', + // 'BRL - Brazilian Real' => '', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/pt_BR/translations.php b/app/Locale/pt_BR/translations.php index 32d37f38..ebe4582a 100644 --- a/app/Locale/pt_BR/translations.php +++ b/app/Locale/pt_BR/translations.php @@ -2,7 +2,7 @@ return array( 'number.decimals_separator' => ',', - 'number.thousands_separator' => ' ', + 'number.thousands_separator' => '.', 'None' => 'Nenhum', 'Edit' => 'Editar', 'Remove' => 'Remover', @@ -20,7 +20,7 @@ return array( 'Brown' => 'Marrom', 'Deep Orange' => 'Laranja escuro', 'Dark Grey' => 'Cinza escuro', - 'Pink' => 'Roza', + 'Pink' => 'Rosa', 'Teal' => 'Turquesa', 'Cyan' => 'Azul intenso', 'Lime' => 'Verde limão', @@ -39,7 +39,6 @@ return array( 'Administrator' => 'Administrador', 'Sign in' => 'Entrar', 'Users' => 'Usuários', - 'No user' => 'Sem usuário', 'Forbidden' => 'Proibido', 'Access Forbidden' => 'Acesso negado', 'Edit user' => 'Editar usuário', @@ -52,17 +51,17 @@ return array( 'Project' => 'Projeto', 'Status' => 'Status', 'Tasks' => 'Tarefas', - 'Board' => 'Board', + 'Board' => 'Quadro', 'Actions' => 'Ações', 'Inactive' => 'Inativo', 'Active' => 'Ativo', - 'Unable to update this board.' => 'Não foi possÃvel atualizar este board.', + 'Unable to update this board.' => 'Não foi possÃvel atualizar este quadro.', 'Disable' => 'Desativar', 'Enable' => 'Ativar', 'New project' => 'Novo projeto', 'Do you really want to remove this project: "%s"?' => 'Você realmente deseja remover este projeto: "%s"?', 'Remove project' => 'Remover projeto', - 'Edit the board for "%s"' => 'Editar o board para "%s"', + 'Edit the board for "%s"' => 'Editar o quadro para "%s"', 'Add a new column' => 'Adicionar uma nova coluna', 'Title' => 'TÃtulo', 'Assigned to %s' => 'Designado para %s', @@ -73,9 +72,9 @@ return array( 'Application settings' => 'Configurações da aplicação', 'Language' => 'Idioma', 'Webhook token:' => 'Token de webhooks:', - 'API token:' => 'API Token:', + 'API token:' => 'Token de API:', 'Database size:' => 'Tamanho do banco de dados:', - 'Download the database' => 'Download do banco de dados', + 'Download the database' => 'Baixar o banco de dados', 'Optimize the database' => 'Otimizar o banco de dados', '(VACUUM command)' => '(Comando VACUUM)', '(Gzip compressed Sqlite file)' => '(Arquivo Sqlite comprimido com Gzip)', @@ -87,9 +86,9 @@ return array( 'New task' => 'Nova tarefa', 'Open a task' => 'Abrir uma tarefa', 'Do you really want to open this task: "%s"?' => 'Você realmente deseja abrir esta tarefa: "%s"?', - 'Back to the board' => 'Voltar ao board', + 'Back to the board' => 'Voltar ao quadro', 'There is nobody assigned' => 'Não há ninguém designado', - 'Column on the board:' => 'Coluna no board:', + 'Column on the board:' => 'Coluna no quadro:', 'Close this task' => 'Finalizar esta tarefa', 'Open this task' => 'Abrir esta tarefa', 'There is no description.' => 'Não há descrição.', @@ -110,7 +109,7 @@ return array( 'The title is required' => 'O tÃtulo é obrigatório', 'Settings saved successfully.' => 'Configurações salvas com sucesso.', 'Unable to save your settings.' => 'Não é possÃvel salvar suas configurações.', - 'Database optimization done.' => 'Otimização do banco de dados concluida.', + 'Database optimization done.' => 'Otimização do banco de dados concluÃda.', 'Your project have been created successfully.' => 'Seu projeto foi criado com sucesso.', 'Unable to create your project.' => 'Não é possÃvel criar o seu projeto.', 'Project updated successfully.' => 'Projeto atualizado com sucesso.', @@ -134,7 +133,7 @@ return array( 'User updated successfully.' => 'Usuário atualizado com sucesso.', 'User removed successfully.' => 'Usuário removido com sucesso.', 'Unable to remove this user.' => 'Não é possÃvel remover este usuário.', - 'Board updated successfully.' => 'Board atualizado com sucesso.', + 'Board updated successfully.' => 'Quadro atualizado com sucesso.', 'Ready' => 'A fazer', 'Backlog' => 'Backlog', 'Work in progress' => 'Em andamento', @@ -156,7 +155,7 @@ return array( 'Due Date' => 'Data fim estimada', 'Invalid date' => 'Data inválida', 'Automatic actions' => 'Ações automáticas', - 'Your automatic action have been created successfully.' => 'Sua ação automética foi criada com sucesso.', + 'Your automatic action have been created successfully.' => 'Sua ação automática foi criada com sucesso.', 'Unable to create your automatic action.' => 'Não é possÃvel criar sua ação automática.', 'Remove an action' => 'Remover uma ação', 'Unable to remove this action.' => 'Não é possÃvel remover esta ação.', @@ -168,7 +167,7 @@ return array( 'Event' => 'Evento', 'When the selected event occurs execute the corresponding action.' => 'Quando o evento selecionado ocorrer execute a ação correspondente.', 'Next step' => 'Próximo passo', - 'Define action parameters' => 'Definir parêmetros da ação', + 'Define action parameters' => 'Definir parâmetros da ação', 'Do you really want to remove this action: "%s"?' => 'Você realmente deseja remover esta ação: "%s"?', 'Remove an automatic action' => 'Remover uma ação automática', 'Assign the task to a specific user' => 'Designar a tarefa para um usuário especÃfico', @@ -239,7 +238,7 @@ return array( 'Add a new category' => 'Adicionar uma nova categoria', 'Do you really want to remove this category: "%s"?' => 'Você realmente deseja remover esta categoria: "%s"?', 'All categories' => 'Todas as categorias', - 'No category' => 'Nenhum categoria', + 'No category' => 'Nenhuma categoria', 'The name is required' => 'O nome é obrigatório', 'Remove a file' => 'Remover um arquivo', 'Unable to remove this file.' => 'Não foi possÃvel remover este arquivo.', @@ -267,14 +266,13 @@ return array( 'Edit a sub-task' => 'Editar uma subtarefa', 'Remove a sub-task' => 'Remover uma subtarefa', 'The time must be a numeric value' => 'O tempo deve ser um valor numérico', - 'Todo' => 'À fazer', + 'Todo' => 'A fazer', 'In progress' => 'Em andamento', 'Sub-task removed successfully.' => 'Subtarefa removida com sucesso.', 'Unable to remove this sub-task.' => 'Não foi possÃvel remover esta subtarefa.', 'Sub-task updated successfully.' => 'Subtarefa atualizada com sucesso.', 'Unable to update your sub-task.' => 'Não foi possÃvel atualizar a sua subtarefa.', 'Unable to create your sub-task.' => 'Não é possÃvel criar a sua subtarefa.', - 'Sub-task added successfully.' => 'Subtarefa adicionada com sucesso.', 'Maximum size: ' => 'Tamanho máximo: ', 'Display another project' => 'Exibir outro projeto', 'Created by %s' => 'Criado por %s', @@ -288,7 +286,7 @@ return array( 'Clone' => 'Clonar', 'Project cloned successfully.' => 'Projeto clonado com sucesso.', 'Unable to clone this project.' => 'Não foi possÃvel clonar este projeto.', - 'Enable email notifications' => 'Habilitar notificações por email', + 'Enable email notifications' => 'Habilitar notificações por e-mail', 'Task position:' => 'Posição da tarefa:', 'The task #%d have been opened.' => 'A tarefa #%d foi aberta.', 'The task #%d have been closed.' => 'A tarefa #%d foi finalizada.', @@ -346,7 +344,7 @@ return array( 'Not assigned, estimate of %sh' => 'Não designado, estimado em %sh', '%s updated a comment on the task %s' => '%s atualizou o comentário na tarefa %s', '%s commented the task %s' => '%s comentou a tarefa %s', - '%s\'s activity' => 'Atividades de%s', + '%s\'s activity' => 'Atividades de %s', 'RSS feed' => 'Feed RSS', '%s updated a comment on the task #%d' => '%s atualizou um comentário sobre a tarefa #%d', '%s commented on the task #%d' => '%s comentou sobre a tarefa #%d', @@ -372,12 +370,12 @@ return array( 'Database' => 'Banco de dados', 'About' => 'Sobre', 'Database driver:' => 'Driver do banco de dados:', - 'Board settings' => 'Configurações do board', + 'Board settings' => 'Configurações do quadro', 'Webhook settings' => 'Configurações do Webhook', 'Reset token' => 'Resetar token', 'API endpoint:' => 'API endpoint:', - 'Refresh interval for private board' => 'Intervalo de atualização para um board privado', - 'Refresh interval for public board' => 'Intervalo de atualização para um board público', + 'Refresh interval for private board' => 'Intervalo de atualização para um quadro privado', + 'Refresh interval for public board' => 'Intervalo de atualização para um quadro público', 'Task highlight period' => 'PerÃodo de Tarefa em destaque', 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'PerÃodo (em segundos) para considerar que uma tarefa foi modificada recentemente (0 para desativar, 2 dias por padrão)', 'Frequency in second (60 seconds by default)' => 'Frequência em segundos (60 segundos por padrão)', @@ -391,27 +389,22 @@ return array( 'Add' => 'Adicionar', 'Start date' => 'Data de inÃcio', 'Time estimated' => 'Tempo estimado', - 'There is nothing assigned to you.' => 'Não há nada designado à você.', + 'There is nothing assigned to you.' => 'Não há nada designado a você.', 'My tasks' => 'Minhas tarefas', 'Activity stream' => 'Atividades Recentes', 'Dashboard' => 'Painel de Controle', 'Confirmation' => 'Confirmação', - 'Allow everybody to access to this project' => 'Permitir que todos acessem este projeto', - 'Everybody have access to this project.' => 'Todos possuem acesso a este projeto.', 'Webhooks' => 'Webhooks', 'API' => 'API', 'Create a comment from an external provider' => 'Criar um comentário por meio de um serviço externo', 'Project management' => 'Gerenciamento de projetos', - 'My projects' => 'Meus projetos', 'Columns' => 'Colunas', 'Task' => 'Tarefas', - 'Your are not member of any project.' => 'Você não é membro de nenhum projeto.', 'Percentage' => 'Porcentagem', 'Number of tasks' => 'Número de tarefas', 'Task distribution' => 'Distribuição de tarefas', 'Analytics' => 'EstatÃsticas', 'Subtask' => 'Subtarefa', - 'My subtasks' => 'Minhas subtarefas', 'User repartition' => 'Redistribuição de usuário', 'Clone this project' => 'Clonar este projeto', 'Column removed successfully.' => 'Coluna removida com sucesso.', @@ -433,19 +426,19 @@ return array( 'Daily project summary export' => 'Exportação diária do resumo do projeto', 'Exports' => 'Exportar', 'This export contains the number of tasks per column grouped per day.' => 'Esta exportação contém o número de tarefas por coluna agrupada por dia.', - 'Active swimlanes' => 'Ativar swimlanes', - 'Add a new swimlane' => 'Adicionar swimlane', - 'Default swimlane' => 'Swimlane padrão', - 'Do you really want to remove this swimlane: "%s"?' => 'Você realmente deseja remover esta swimlane: "%s"?', - 'Inactive swimlanes' => 'Desativar swimlanes', - 'Remove a swimlane' => 'Remover uma swimlane', - 'Swimlane modification for the project "%s"' => 'Modificação de swimlane para o projeto "%s"', - 'Swimlane removed successfully.' => 'Swimlane removida com sucesso.', - 'Swimlanes' => 'Swimlanes', - 'Swimlane updated successfully.' => 'Swimlane atualizada com sucesso.', - 'Unable to remove this swimlane.' => 'Não foi possÃvel remover esta swimlane.', - 'Unable to update this swimlane.' => 'Não foi possÃvel atualizar esta swimlane.', - 'Your swimlane have been created successfully.' => 'Sua swimlane foi criada com sucesso.', + 'Active swimlanes' => 'Ativar raias', + 'Add a new swimlane' => 'Adicionar uma nova raia', + 'Default swimlane' => 'Raia padrão', + 'Do you really want to remove this swimlane: "%s"?' => 'Você realmente deseja remover esta raia: "%s"?', + 'Inactive swimlanes' => 'Desativar raias', + 'Remove a swimlane' => 'Remover uma raia', + 'Swimlane modification for the project "%s"' => 'Modificação de raia para o projeto "%s"', + 'Swimlane removed successfully.' => 'Raia removida com sucesso.', + 'Swimlanes' => 'Raias', + 'Swimlane updated successfully.' => 'Raia atualizada com sucesso.', + 'Unable to remove this swimlane.' => 'Não foi possÃvel remover esta raia.', + 'Unable to update this swimlane.' => 'Não foi possÃvel atualizar esta raia.', + 'Your swimlane have been created successfully.' => 'Sua raia foi criada com sucesso.', 'Example: "Bug, Feature Request, Improvement"' => 'Exemplo: "Bug, Solicitação de Recurso, Melhoria"', 'Default categories for new projects (Comma-separated)' => 'Categorias padrões para novos projetos (separadas por vÃrgula)', 'Integrations' => 'Integrações', @@ -459,10 +452,9 @@ return array( 'Language:' => 'Idioma', 'Timezone:' => 'Fuso horário', 'All columns' => 'Todas as colunas', - 'Calendar' => 'Calendário', 'Next' => 'Próximo', '#%d' => '#%d', - 'All swimlanes' => 'Todas as swimlanes', + 'All swimlanes' => 'Todas as raias', 'All colors' => 'Todas as cores', 'Moved to column %s' => 'Mover para a coluna %s', 'User dashboard' => 'Painel de Controle do usuário', @@ -494,7 +486,7 @@ return array( 'Remove a link' => 'Remover uma associação', 'The labels must be different' => 'As etiquetas devem ser diferentes', 'There is no link.' => 'Não há nenhuma associação.', - 'This label must be unique' => 'Esta etiqueta deve ser unica', + 'This label must be unique' => 'Esta etiqueta deve ser única', 'Unable to create your link.' => 'ImpossÃvel de adicionar sua associação.', 'Unable to update your link.' => 'ImpossÃvel de atualizar sua associação.', 'Unable to remove this link.' => 'ImpossÃvel de remover sua associação.', @@ -517,9 +509,9 @@ return array( 'Expand/collapse tasks' => 'Expandir/Contrair tarefas', 'Close dialog box' => 'Fechar a caixa de diálogo', 'Submit a form' => 'Envia o formulário', - 'Board view' => 'Página do painel', + 'Board view' => 'Visão do quadro', 'Keyboard shortcuts' => 'Atalhos de teclado', - 'Open board switcher' => 'Abrir o comutador de painel', + 'Open board switcher' => 'Abrir opções de quadros', 'Application' => 'Aplicação', 'Compact view' => 'Vista reduzida', 'Horizontal scrolling' => 'Rolagem horizontal', @@ -557,7 +549,6 @@ return array( 'Unable to add this currency rate.' => 'ImpossÃvel de adicionar essa taxa de câmbio.', 'Webhook URL' => 'URL do webhook', '%s removed the assignee of the task %s' => '%s removeu a pessoa designada para a tarefa %s', - 'Enable Gravatar images' => 'Ativar imagens do Gravatar', 'Information' => 'Informações', 'Check two factor authentication code' => 'Verifique o código de autenticação em duas etapas', 'The two factor authentication code is not valid.' => 'O código de autenticação em duas etapas não é válido.', @@ -574,7 +565,7 @@ return array( 'This chart show the task complexity over the time (Work Remaining).' => 'Este gráfico mostra a complexidade da tarefa ao longo do tempo (Trabalho Restante).', 'Screenshot taken %s' => 'Captura de tela tirada em %s', 'Add a screenshot' => 'Adicionar uma captura de tela', - 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Tire uma captura de tela e pressione CTRL + V ou ⌘ + V para colar aqui.', + // 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => '', 'Screenshot uploaded successfully.' => 'Captura de tela enviada com sucesso.', 'SEK - Swedish Krona' => 'SEK - Coroa sueca', 'Identifier' => 'Identificador', @@ -611,14 +602,7 @@ return array( 'When task is moved from first column' => 'Quando a tarefa é movida fora da primeira coluna', 'When task is moved to last column' => 'Quando a tarefa é movida para a última coluna', 'Year(s)' => 'Ano(s)', - 'Calendar settings' => 'Configurações do calendário', - 'Project calendar view' => 'Vista em modo projeto do calendário', 'Project settings' => 'Configurações dos projetos', - 'Show subtasks based on the time tracking' => 'Mostrar as subtarefas com base no controle de tempo', - 'Show tasks based on the creation date' => 'Mostrar as tarefas em função da data de criação', - 'Show tasks based on the start date' => 'Mostrar as tarefas em função da data de inÃcio', - 'Subtasks time tracking' => 'Monitoramento do tempo comparado as subtarefas', - 'User calendar view' => 'Vista em modo utilizador do calendário', 'Automatically update the start date' => 'Atualizar automaticamente a data de inÃcio', 'iCal feed' => 'Subscrição iCal', 'Preferences' => 'Preferências', @@ -635,17 +619,16 @@ return array( 'Send a task by email to someone' => 'Enviar uma tarefa por e-mail a alguém', 'Reopen a task' => 'Reabrir uma tarefa', 'Notification' => 'Notificação', - '%s moved the task #%d to the first swimlane' => '%s moveu a tarefa #%d para a primeira swimlane', - 'Swimlane' => 'Swimlane', - 'Gravatar' => 'Gravatar', - '%s moved the task %s to the first swimlane' => '%s moveu a tarefa %s para a primeira swimlane', - '%s moved the task %s to the swimlane "%s"' => '%s moveu a tarefa %s para a swimlane "%s"', + '%s moved the task #%d to the first swimlane' => '%s moveu a tarefa #%d para a primeira raia', + 'Swimlane' => 'Raia', + '%s moved the task %s to the first swimlane' => '%s moveu a tarefa %s para a primeira raia', + '%s moved the task %s to the swimlane "%s"' => '%s moveu a tarefa %s para a raia "%s"', 'This report contains all subtasks information for the given date range.' => 'Este relatório contém informações de todas as subtarefas para o perÃodo selecionado.', 'This report contains all tasks information for the given date range.' => 'Este relatório contém informações de todas as tarefas para o perÃodo selecionado.', 'Project activities for %s' => 'Atividade do projeto "%s"', - 'view the board on Kanboard' => 'ver o painel no Kanboard', - 'The task have been moved to the first swimlane' => 'A tarefa foi movida para a primeira swimlane', - 'The task have been moved to another swimlane:' => 'A tarefa foi movida para outra swimlane:', + 'view the board on Kanboard' => 'ver o quadro no Kanboard', + 'The task have been moved to the first swimlane' => 'A tarefa foi movida para a primeira raia', + 'The task have been moved to another swimlane:' => 'A tarefa foi movida para outra raia:', 'New title: %s' => 'Novo tÃtulo: %s', 'The task is not assigned anymore' => 'Agora a tarefa não está mais atribuÃda', 'New assignee: %s' => 'Novo designado: %s', @@ -671,10 +654,9 @@ return array( 'You need at least 2 days of data to show the chart.' => 'Você precisa de pelo menos 2 dias de dados para visualizar o gráfico.', '<15m' => '<15m', '<30m' => '<30m', - 'Stop timer' => 'Stop timer', - 'Start timer' => 'Start timer', + 'Stop timer' => 'Parar temporizador', + 'Start timer' => 'Start temporizador', 'My activity stream' => 'Meu feed de atividades', - 'My calendar' => 'Minha agenda', 'Search tasks' => 'Pesquisar tarefas', 'Reset filters' => 'Redefinir os filtros', 'My tasks due tomorrow' => 'Minhas tarefas que expirarão amanhã', @@ -686,11 +668,10 @@ return array( 'Not assigned' => 'Não designada', 'View advanced search syntax' => 'Ver a sintaxe para pesquisa avançada', 'Overview' => 'Visão global', - 'Board/Calendar/List view' => 'Vista Painel/Calendário/Lista', - 'Switch to the board view' => 'Mudar para o modo Painel', - 'Switch to the calendar view' => 'Mudar par o modo Calendário', + 'Board/Calendar/List view' => 'Quadro/Calendário/Lista', + 'Switch to the board view' => 'Mudar para a visão de quadro', 'Switch to the list view' => 'Mudar par o modo Lista', - 'Go to the search/filter box' => 'Ir para o campo de pesquisa', + 'Go to the search/filter box' => 'Ir para o campo de filtro/pesquisa', 'There is no activity yet.' => 'Não há nenhuma atividade ainda.', 'No tasks found.' => 'Nenhuma tarefa encontrada.', 'Keyboard shortcut: "%s"' => 'Tecla de atalho: "%s"', @@ -698,7 +679,7 @@ return array( 'Filter' => 'Filtro', 'Advanced search' => 'Pesquisa avançada', 'Example of query: ' => 'Exemplo de consulta: ', - 'Search by project: ' => 'Pesquisar por projet: ', + 'Search by project: ' => 'Pesquisar por projeto: ', 'Search by column: ' => 'Pesquisar por coluna: ', 'Search by assignee: ' => 'Pesquisar por designado: ', 'Search by color: ' => 'Pesquisar por cor: ', @@ -708,31 +689,31 @@ return array( 'Average time spent into each column' => 'Tempo médio gasto em cada coluna', 'Average time spent' => 'Tempo médio gasto', 'This chart show the average time spent into each column for the last %d tasks.' => 'Este gráfico mostra o tempo médio gasto em cada coluna para as %d últimas tarefas.', - 'Average Lead and Cycle time' => 'Tempo médio do Lead and cycle time', - 'Average lead time: ' => 'Lead time medio: ', - 'Average cycle time: ' => 'Cycle time medio: ', - 'Cycle Time' => 'Cycle time', - 'Lead Time' => 'Lead time', - 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Este gráfico mostra o tempo médio do Lead and cycle time das últimas %d tarefas.', + 'Average Lead and Cycle time' => 'Tempo médio do tempo de condução e tempo de ciclo', + 'Average lead time: ' => 'Tempo de condução médio: ', + 'Average cycle time: ' => 'Tempo de ciclo médio: ', + 'Cycle Time' => 'Tempo de ciclo', + 'Lead Time' => 'Tempo de condução', + 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Este gráfico mostra o tempo médio do tempo de condução e tempo de ciclo das últimas %d tarefas.', 'Average time into each column' => 'Tempo médio de cada coluna', - 'Lead and cycle time' => 'Lead and cycle time', - 'Lead time: ' => 'Lead time: ', - 'Cycle time: ' => 'Cycle time: ', + 'Lead and cycle time' => 'Tempo de condução e tempo de ciclo', + 'Lead time: ' => 'Tempo de condução: ', + 'Cycle time: ' => 'Tempo de ciclo: ', 'Time spent into each column' => 'Tempo gasto em cada coluna', - 'The lead time is the duration between the task creation and the completion.' => 'O Lead time é o tempo gasto entre a criação da tarefa e a sua conclusão.', - 'The cycle time is the duration between the start date and the completion.' => 'O Cycle time é o tempo gasto entre a data de inÃcio e a sua conclusão.', + 'The lead time is the duration between the task creation and the completion.' => 'O tempo de condução é o tempo gasto entre a criação da tarefa e a sua conclusão.', + 'The cycle time is the duration between the start date and the completion.' => 'O Tempo de ciclo é o tempo gasto entre a data de inÃcio e a sua conclusão.', 'If the task is not closed the current time is used instead of the completion date.' => 'Se a tarefa não está finalizada, a hora atual é usada no lugar da data de conclusão.', 'Set automatically the start date' => 'Definir automaticamente a data de inÃcio', 'Edit Authentication' => 'Modificar a autenticação', 'Remote user' => 'Usuário remoto', 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Os usuários remotos não conservam as suas senhas no banco de dados Kanboard, exemplos: contas LDAP, Github ou Google.', - 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Se você marcar "Interdir o formulário de autenticação", os identificadores entrados no formulário de login serão ignorado.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Se você marcar "Impedir o formulário de autenticação", os identificadores entrados no formulário de login serão ignorado.', 'Default task color' => 'Cor padrão para as tarefas', 'This feature does not work with all browsers.' => 'Esta funcionalidade não é compatÃvel com todos os navegadores.', 'There is no destination project available.' => 'Não há nenhum projeto de destino disponÃvel.', 'Trigger automatically subtask time tracking' => 'Ativar automaticamente o monitoramento do tempo para as subtarefas', 'Include closed tasks in the cumulative flow diagram' => 'Incluir as tarefas finalizadas no diagrama de fluxo acumulado', - 'Current swimlane: %s' => 'Swimlane atual: %s', + 'Current swimlane: %s' => 'Raia atual: %s', 'Current column: %s' => 'Coluna atual: %s', 'Current category: %s' => 'Categoria atual: %s', 'no category' => 'nenhuma categoria', @@ -742,16 +723,9 @@ return array( 'contributors' => 'contribuidores', 'License:' => 'Licença:', 'License' => 'Licença', - 'Enter the text below' => 'Entre o texto abaixo', - 'Sort by position' => 'Ordenar por posição', - 'Sort by date' => 'Ordenar por data', - 'Add task' => 'Adicionar uma tarefa', + 'Enter the text below' => 'Digite o texto abaixo', 'Start date:' => 'Data de inÃcio:', 'Due date:' => 'Data fim estimada:', - 'There is no start date or due date for this task.' => 'Não existe data de inÃcio ou data fim estimada para esta tarefa.', - 'Moving or resizing a task will change the start and due date of the task.' => 'Mover ou redimensionar uma tarefa irá alterar a data de inÃcio e conclusão estimada da tarefa.', - 'There is no task in your project.' => 'Não há tarefas em seu projeto.', - 'Gantt chart' => 'Gráfico de Gantt', 'People who are project managers' => 'Pessoas que são gerente de projeto', 'People who are project members' => 'Pessoas que são membro de projeto', 'NOK - Norwegian Krone' => 'NOK - Coroa Norueguesa', @@ -763,26 +737,19 @@ return array( 'Members' => 'Membros', 'Shared project' => 'Projeto compartilhado', 'Project managers' => 'Gerentes de projeto', - 'Gantt chart for all projects' => 'Gráfico de Gantt para todos os projetos', 'Projects list' => 'Lista dos projetos', - 'Gantt chart for this project' => 'Gráfico de Gantt para este projeto', - 'Project board' => 'Painel do projeto', 'End date:' => 'Data de término:', - 'There is no start date or end date for this project.' => 'Não há data de inÃcio ou data de término para este projeto.', - 'Projects Gantt chart' => 'Gráfico de Gantt dos projetos', 'Change task color when using a specific task link' => 'Mudar a cor da tarefa quando um link especÃfico é utilizado', 'Task link creation or modification' => 'Criação ou modificação de um link em uma tarefa', 'Milestone' => 'Milestone', 'Documentation: %s' => 'Documentação: %s', - 'Switch to the Gantt chart view' => 'Mudar para a vista gráfico de Gantt', 'Reset the search/filter box' => 'Reiniciar o campo de pesquisa', 'Documentation' => 'Documentação', 'Table of contents' => 'Ãndice', - 'Gantt' => 'Gantt', 'Author' => 'Autor', 'Version' => 'Versão', - 'Plugins' => 'Extensões', - 'There is no plugin loaded.' => 'Não há extensões carregadas.', + 'Plugins' => 'Plugins', + 'There is no plugin loaded.' => 'Não há plugins carregados.', 'My notifications' => 'Minhas notificações', 'Custom filters' => 'Filtros personalizados', 'Your custom filter have been created successfully.' => 'Seu filtro personalizado foi criado com sucesso.', @@ -804,16 +771,16 @@ return array( 'Task #%d opened' => 'Tarefa #%d aberta', 'Column changed for task #%d' => 'Coluna alterada para a tarefa #%d', 'New position for task #%d' => 'Nova posição para a tarefa #%d', - 'Swimlane changed for task #%d' => 'Swimlane alterada para a tarefa #%d', + 'Swimlane changed for task #%d' => 'Raia alterada para a tarefa #%d', 'Assignee changed on task #%d' => 'Designação alterada na tarefa #%d', '%d overdue tasks' => '%d tarefas atrasadas', 'Task #%d is overdue' => 'Tarefa #%d está atrasada', 'No notification.' => 'Nenhuma notificação nova.', 'Mark all as read' => 'Marcar todas como lidas', 'Mark as read' => 'Marcar como lida', - 'Total number of tasks in this column across all swimlanes' => 'Número total de tarefas nesta coluna através de todas as swimlanes', - 'Collapse swimlane' => 'Contrair swimlane', - 'Expand swimlane' => 'Expandir swimlane', + 'Total number of tasks in this column across all swimlanes' => 'Número total de tarefas nesta coluna através de todas as raias', + 'Collapse swimlane' => 'Contrair raia', + 'Expand swimlane' => 'Expandir raia', 'Add a new filter' => 'Adicionar filtro', 'Share with all project members' => 'Compartilhar com todos os membros do projeto', 'Shared' => 'Compartilhado', @@ -832,7 +799,7 @@ return array( 'Double Quote' => 'Aspas duplas', 'Single Quote' => 'Aspas simples', '%s attached a file to the task #%d' => '%s anexou um arquivo à tarefa #%d', - 'There is no column or swimlane activated in your project!' => 'Não há coluna ou swimlane ativa em seu projeto!', + 'There is no column or swimlane activated in your project!' => 'Não há coluna ou raia ativa em seu projeto!', 'Append filter (instead of replacement)' => 'Adicionar filtro (em vez de substituir)', 'Append/Replace' => 'Adicionar/Substituir', 'Append' => 'Adicionar', @@ -870,10 +837,10 @@ return array( 'Unable to update your group.' => 'Não foi possÃvel atualizar o seu grupo.', 'Add group member to "%s"' => 'Adicionar um membro ao grupo "%s"', 'Group member added successfully.' => 'Membro adicionado com sucesso ao o grupo.', - 'Unable to add group member.' => 'Não foi possivel adicionar esse membro ao grupo.', + 'Unable to add group member.' => 'Não foi possÃvel adicionar esse membro ao grupo.', 'Remove user from group "%s"' => 'Remover usuário do grupo "%s"', 'User removed successfully from this group.' => 'Usuário removido com sucesso deste grupo.', - 'Unable to remove this user from the group.' => 'Não foi possivel remover este Usuário do grupo.', + 'Unable to remove this user from the group.' => 'Não foi possÃvel remover este Usuário do grupo.', 'Remove group' => 'Remover o grupo', 'Group removed successfully.' => 'Grupo removido com sucesso.', 'Unable to remove this group.' => 'Não foi possÃvel remover este grupo.', @@ -889,7 +856,6 @@ return array( 'There is no user available.' => 'Não há nenhum usuário disponÃvel', 'Do you really want to remove the user "%s" from the group "%s"?' => 'Você realmente deseja remover o usuário "%s" do grupo "%s"?', 'There is no group.' => 'Não há nenhum grupo.', - 'External Id' => 'Id externo', 'Add group member' => 'Adicionar um membro ao grupo', 'Do you really want to remove this group: "%s"?' => 'Você realmente deseja excluir este grupo: "%s"?', 'There is no user in this group.' => 'Não há usuários neste grupo.', @@ -926,22 +892,22 @@ return array( 'There is no integration registered at the moment.' => 'Não há nenhuma integração registrada por enquanto.', 'Password Reset for Kanboard' => 'Redefinir a senha para Kanboard', 'Forgot password?' => 'Esqueceu a senha?', - 'Enable "Forget Password"' => 'Activar a funcionalidade "Esqueceu a senha"', + 'Enable "Forget Password"' => 'Ativar a funcionalidade "Esqueceu a senha"', 'Password Reset' => 'Redefinir a senha', 'New password' => 'Nova senha', 'Change Password' => 'Alterar a senha', 'To reset your password click on this link:' => 'Para redefinir a sua senha, clique neste link:', 'Last Password Reset' => 'Última redefinição de senha', - 'The password has never been reinitialized.' => 'A senha nunca foi redifinida.', + 'The password has never been reinitialized.' => 'A senha nunca foi redefinida.', 'Creation' => 'Criação', 'Expiration' => 'Expiração', 'Password reset history' => 'Histórico da redefinição da senha', - 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Todas as tarefas da coluna "%s" e da Swimlane "%s" foram finalizadas com sucesso.', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Todas as tarefas da coluna "%s" e da raia "%s" foram finalizadas com sucesso.', 'Do you really want to close all tasks of this column?' => 'Você realmente deseja finalizar todas as tarefas desta coluna?', - '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d tarefa(s) na coluna "%s" e na Swimlane "%s" serão finalizadas.', + // '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '', 'Close all tasks of this column' => 'Finalizar todas as tarefas desta coluna', - 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Nenhuma extensão tem registado um método de notificação de projecto. Você ainda pode definir notificações individuais em seu perfil de usuário.', - 'My dashboard' => 'Meu Painel', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Nenhum plugin registrou método de notificação de projeto. Você ainda pode definir notificações individuais em seu perfil de usuário.', + 'My dashboard' => 'Meu Painel de Controle', 'My profile' => 'Meu perfil', 'Project owner: ' => 'LÃder do projeto: ', 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'O identificador do projeto é opcional e deve ser alfanumérico, por exemplo MEUPROJETO.', @@ -955,7 +921,6 @@ return array( 'Default priority' => 'Prioridade padrão', 'Lowest priority' => 'Prioridade baixa', 'Highest priority' => 'Prioridade alta', - 'If you put zero to the low and high priority, this feature will be disabled.' => 'Se você colocar zero para a prioridade alta e baixa, essa funcionalidade será desativada.', 'Close a task when there is no activity' => 'Finalizar uma tarefa sem atividade', 'Duration in days' => 'Duração em dias', 'Send email when there is no activity on a task' => 'Enviar um e-mail quando não há nenhuma atividade em uma tarefa', @@ -989,7 +954,7 @@ return array( 'Priority:' => 'Prioridade:', 'Reference:' => 'Referência:', 'Complexity:' => 'Complexidade:', - 'Swimlane:' => 'Swimlane:', + 'Swimlane:' => 'Raia:', 'Column:' => 'Coluna:', 'Position:' => 'Posição:', 'Creator:' => 'Criador:', @@ -1033,7 +998,7 @@ return array( 'Column created successfully.' => 'A coluna criada com sucesso.', 'Another column with the same name exists in the project' => 'Uma outra coluna com o mesmo nome já existe no projeto', 'Default filters' => 'Filtros padrão', - 'Your board doesn\'t have any columns!' => 'O seu painel não tem nenhuma coluna', + 'Your board doesn\'t have any columns!' => 'O seu quadro não tem nenhuma coluna', 'Change column position' => 'Alterar a posição da coluna', 'Switch to the project overview' => 'Mudar para a vista geral do projeto', 'User filters' => 'Filtros dos usuários', @@ -1041,15 +1006,15 @@ return array( 'Upload a file' => 'Enviar um arquivo', 'View file' => 'Ver um arquivo', 'Last activity' => 'Últimas atividades', - 'Change subtask position' => 'Alterar a posição da sub-tarefa', + 'Change subtask position' => 'Alterar a posição da subtarefa', 'This value must be greater than %d' => 'Este valor deve ser maior que %d', - 'Another swimlane with the same name exists in the project' => 'Outra Swimlane existe com o mesmo nome no projeto', + 'Another swimlane with the same name exists in the project' => 'Outra raia existe com o mesmo nome no projeto', 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Exemplo: http://exemple.kanboard.net/ (usado para gerar URLs absolutos)', 'Actions duplicated successfully.' => 'Ações duplicadas com sucesso.', 'Unable to duplicate actions.' => 'Não foi possÃvel duplicar as ações.', 'Add a new action' => 'Adicionar uma nova ação', 'Import from another project' => 'Importar a partir de outro projeto', - 'There is no action at the moment.' => 'Não há nenhuma ação actualmente.', + 'There is no action at the moment.' => 'Não há nenhuma ação atualmente.', 'Import actions from another project' => 'Importar ações a partir de outro projeto', 'There is no available project.' => 'Não há projetos disponÃveis.', 'Local File' => 'Arquivo local', @@ -1093,45 +1058,45 @@ return array( 'Plugin updated successfully.' => 'Plugin atualizado com sucesso.', 'Plugin removed successfully.' => 'Plugin removido com sucesso.', 'Subtask converted to task successfully.' => 'Sub-tarefa convertida para tarefa com sucesso.', - 'Unable to convert the subtask.' => 'Não foi possivel converter a sub-tarefa.', - 'Unable to extract plugin archive.' => 'Não foi possivel extrair o arquivo do plugin.', + 'Unable to convert the subtask.' => 'Não foi possÃvel converter a subtarefa.', + 'Unable to extract plugin archive.' => 'Não foi possÃvel extrair o arquivo do plugin.', 'Plugin not found.' => 'Plugin não encontrado.', 'You don\'t have the permission to remove this plugin.' => 'Você não tem permissão para remover este plugin.', - 'Unable to download plugin archive.' => 'Não foi possivel transferir o arquivo do plugin.', - 'Unable to write temporary file for plugin.' => 'Não foi possivel escrever o arquivo temporário para o plugin.', - 'Unable to open plugin archive.' => 'Não foi possivel abrir o arquivo do plugin.', + 'Unable to download plugin archive.' => 'Não foi possÃvel transferir o arquivo do plugin.', + 'Unable to write temporary file for plugin.' => 'Não foi possÃvel escrever o arquivo temporário para o plugin.', + 'Unable to open plugin archive.' => 'Não foi possÃvel abrir o arquivo do plugin.', 'There is no file in the plugin archive.' => 'Ficheiro não encontrado dentro do arquivo do plugin.', 'Create tasks in bulk' => 'Criar tarefas em massa', - 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Este Kanboard não está configurado para instalar plugins através do interface de usuário.', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Este Kanboard não está configurado para instalar plugins através da interface de usuário.', 'There is no plugin available.' => 'Não existe nenhum plugin instalado.', 'Install' => 'Instalar', 'Update' => 'Atualizar', 'Up to date' => 'Atualizado', - 'Not available' => 'Não disponivel', + 'Not available' => 'Não disponÃvel', 'Remove plugin' => 'Remover plugin', - 'Do you really want to remove this plugin: "%s"?' => 'Tem a certeza que quer remover este plugin: "%s%"?', + 'Do you really want to remove this plugin: "%s"?' => 'Tem a certeza que quer remover este plugin: "%s"?', 'Uninstall' => 'Desinstalar', 'Listing' => 'Listando', 'Metadata' => 'Metadata', 'Manage projects' => 'Gerir projetos', 'Convert to task' => 'Converter para tarefa', - 'Convert sub-task to task' => 'Converter sub-tarefa para tarefa', - 'Do you really want to convert this sub-task to a task?' => 'Tem a certeza que pretende converter esta sub-tarefa para tarefa?', - 'My task title' => 'Titulo da minha tarefa', + 'Convert sub-task to task' => 'Converter subtarefa para tarefa', + 'Do you really want to convert this sub-task to a task?' => 'Tem a certeza que pretende converter esta subtarefa para tarefa?', + 'My task title' => 'TÃtulo da minha tarefa', 'Enter one task by line.' => 'Escreva uma tarefa por linha.', 'Number of failed login:' => 'Número de logins falhados:', 'Account locked until:' => 'Conta bloqueada até:', - 'Email settings' => 'Definições de Email', - 'Email sender address' => 'Endereço de envio de Email', - 'Email transport' => 'Transportador de Email', + 'Email settings' => 'Configurações de e-mail', + 'Email sender address' => 'Endereço de envio de e-mail', + 'Email transport' => 'Transportador de e-mail', 'Webhook token' => 'Token do Webhook', 'Project tags management' => 'Gestão de etiquetas do Projeto', 'Tag created successfully.' => 'Etiqueta criada com sucesso.', - 'Unable to create this tag.' => 'Não foi possivel criar esta etiqueta.', + 'Unable to create this tag.' => 'Não foi possÃvel criar esta etiqueta.', 'Tag updated successfully.' => 'Etiqueta atualizada com sucesso.', - 'Unable to update this tag.' => 'Não foi possivel atualizar esta etiqueta.', + 'Unable to update this tag.' => 'Não foi possÃvel atualizar esta etiqueta.', 'Tag removed successfully.' => 'Etiqueta removida com sucesso.', - 'Unable to remove this tag.' => 'Não foi possivel remover esta etiqueta.', + 'Unable to remove this tag.' => 'Não foi possÃvel remover esta etiqueta.', 'Global tags management' => 'Gestão de etiquetas globais', 'Tags' => 'Etiquetas', 'Tags management' => 'Gestão de Etiquetas', @@ -1146,13 +1111,13 @@ return array( 'There is no global tag at the moment.' => 'Atualmente não existe nenhuma etiqueta global.', 'This field cannot be empty' => 'Este campo não pode ficar vazio', 'Close a task when there is no activity in an specific column' => 'Fechar a tarefa quando não houver atividade numa coluna especifica', - '%s removed a subtask for the task #%d' => '%s removeu uma sub-tarefa da tarefa #%d', + '%s removed a subtask for the task #%d' => '%s removeu uma subtarefa da tarefa #%d', '%s removed a comment on the task #%d' => '%s removeu um comentário da tarefa #%d ', 'Comment removed on task #%d' => 'Comentário removido da tarefa #%d', 'Subtask removed on task #%d' => 'Sub-tarefa removida da tarefa #%d', - 'Hide tasks in this column in the dashboard' => 'Esconder tarefas desta coluna no painel', + 'Hide tasks in this column in the dashboard' => 'Esconder tarefas desta coluna no Painel de Controle', '%s removed a comment on the task %s' => '%s removeu um comentário da tarefa %s', - '%s removed a subtask for the task %s' => '%s removeu uma sub-tarefa da tarefa %s', + '%s removed a subtask for the task %s' => '%s removeu uma subtarefa da tarefa %s', 'Comment removed' => 'Comentário removido', 'Subtask removed' => 'Sub-tarefa removida', '%s set a new internal link for the task #%d' => '%s definiu uma nova ligação interna para a tarefa #%d', @@ -1163,19 +1128,17 @@ return array( '%s removed an internal link for the task %s' => '%s removeu uma ligação interna da tarefa %s', 'Automatically set the due date on task creation' => 'Definir data de vencimento automaticamente ao criar uma tarefa', 'Move the task to another column when closed' => 'Mover a tarefa para outra coluna quando fechada', - 'Move the task to another column when not moved during a given period' => 'Mover a tarefa para outra coluna quando não movida dentro de determinado periodo', - 'Dashboard for %s' => 'Painel de %s', + 'Move the task to another column when not moved during a given period' => 'Mover a tarefa para outra coluna quando não movida dentro de determinado perÃodo', + 'Dashboard for %s' => 'Painel de Controle de %s', 'Tasks overview for %s' => 'Visão geral das tarefas de %s', - 'Subtasks overview for %s' => 'Visão geral das sub-tarefas de %s', + 'Subtasks overview for %s' => 'Visão geral das subtarefas de %s', 'Projects overview for %s' => 'Visão geral dos projetos de %s', 'Activity stream for %s' => 'Fluxo de atividade de %s', - 'Calendar for %s' => 'Calendário de %s', - 'Notifications for %s' => 'Notificações de %s', - 'Assign a color when the task is moved to a specific swimlane' => 'Atribuir uma cor quando a tarefa é movida para uma swimlane especifica', - 'Assign a priority when the task is moved to a specific swimlane' => 'Atribuir uma prioridade quando a tarefa é movida para uma swimlane especifica', + 'Assign a color when the task is moved to a specific swimlane' => 'Atribuir uma cor quando a tarefa for movida para uma raia especifica', + 'Assign a priority when the task is moved to a specific swimlane' => 'Atribuir uma prioridade quando a tarefa for movida para uma raia especifica', 'User unlocked successfully.' => 'Usuário desbloqueado com sucesso.', - 'Unable to unlock the user.' => 'Não foi possivel desbloquear o usuário.', - 'Move a task to another swimlane' => 'Mover a tarefa para outra swimlane', + 'Unable to unlock the user.' => 'Não foi possÃvel desbloquear o usuário.', + 'Move a task to another swimlane' => 'Mover a tarefa para outra raia', 'Creator Name' => 'Nome do Criador', 'Time spent and estimated' => 'Tempo gasto e estimado', 'Move position' => 'Mover posição', @@ -1187,26 +1150,26 @@ return array( 'Add a new custom role' => 'Adicionar uma nova função personalizada', 'Restrictions for the role "%s"' => 'Restrições para a função: "%s"', 'Add a new project restriction' => 'Adicionar uma nova restrição de Projeto', - 'Add a new drag and drop restriction' => 'Adicionar uma nova restrição de arrastar e largar', + 'Add a new drag and drop restriction' => 'Adicionar uma nova restrição de arrastar e soltar', 'Add a new column restriction' => 'Adicionar uma nova restrição de colunas', 'Edit this role' => 'Editar esta função', 'Remove this role' => 'Remover esta função', 'There is no restriction for this role.' => 'Não existem restrições para esta função.', 'Only moving task between those columns is permitted' => 'Só é permitido mover a tarefa entre as seguintes colunas', - 'Close a task in a specific column when not moved during a given period' => 'Fecha a tarefa numa coluna especifica quando não é movimentada durante um dado periodo', + 'Close a task in a specific column when not moved during a given period' => 'Fecha a tarefa numa coluna especifica quando não é movimentada durante um dado perÃodo', 'Edit columns' => 'Editar colunas', 'The column restriction has been created successfully.' => 'A restrição de coluna foi criada com sucesso.', - 'Unable to create this column restriction.' => 'Não foi possivel criar esta restrição de coluna.', + 'Unable to create this column restriction.' => 'Não foi possÃvel criar esta restrição de coluna.', 'Column restriction removed successfully.' => 'Restrição de coluna removida com sucesso.', - 'Unable to remove this restriction.' => 'Não foi possivel remover esta restrição.', + 'Unable to remove this restriction.' => 'Não foi possÃvel remover esta restrição.', 'Your custom project role has been created successfully.' => 'A sua função de projeto personalizada foi criada com sucesso.', - 'Unable to create custom project role.' => 'Não foi possivel criar a função de projeto personalizada.', + 'Unable to create custom project role.' => 'Não foi possÃvel criar a função de projeto personalizada.', 'Your custom project role has been updated successfully.' => 'A sua função de projeto personalizada foi atualizada com sucesso.', - 'Unable to update custom project role.' => 'Não foi possivel atualizar a função de projeto personalizada.', + 'Unable to update custom project role.' => 'Não foi possÃvel atualizar a função de projeto personalizada.', 'Custom project role removed successfully.' => 'Função de projeto removida com sucesso.', - 'Unable to remove this project role.' => 'Não foi possivel remover esta função de projeto.', + 'Unable to remove this project role.' => 'Não foi possÃvel remover esta função de projeto.', 'The project restriction has been created successfully.' => 'A restrição de projeto foi criada com sucesso.', - 'Unable to create this project restriction.' => 'Não foi possivel remover esta restrição de projeto.', + 'Unable to create this project restriction.' => 'Não foi possÃvel remover esta restrição de projeto.', 'Project restriction removed successfully.' => 'Restrição de projeto removida com sucesso.', 'You cannot create tasks in this column.' => 'Não pode criar tarefas nesta coluna.', 'Task creation is permitted for this column' => 'A criação de tarefas é permitida nesta coluna', @@ -1215,7 +1178,7 @@ return array( 'Closing or opening a task is blocked for this column' => 'Fechar ou abrir tarefas está bloqueado nesta coluna', 'Task creation is not permitted' => 'A criação de tarefas não é permitida', 'Closing or opening a task is not permitted' => 'Fechar ou abrir tarefas não é permitido', - 'New drag and drop restriction for the role "%s"' => 'Nova restrição de arrastar e largar para a função: "%s"', + 'New drag and drop restriction for the role "%s"' => 'Nova restrição de arrastar e soltar para a função: "%s"', 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Pessoas pertecentes a esta função poderam mover terefas só entre as colunas de origem e de destino.', 'Remove a column restriction' => 'Remover a restrição de coluna', 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Tem a certeza que quer remover a restrição de coluna: "%s" para "%s"?', @@ -1236,102 +1199,171 @@ return array( 'This field is required' => 'Este campo é obrigatório', 'Moving a task is not permitted' => 'Mover uma tarefa não é permitido', 'This value must be in the range %d to %d' => 'Este valor precisa estar no intervalo %d até %d', - // 'You are not allowed to move this task.' => '', - // 'API User Access' => '', - // 'Preview' => '', - // 'Write' => '', - // 'Write your text in Markdown' => '', - // 'New External Task: %s' => '', - // 'No personal API access token registered.' => '', - // 'Your personal API access token is "%s"' => '', - // 'Remove your token' => '', - // 'Generate a new token' => '', - // 'Showing %d-%d of %d' => '', - // 'Outgoing Emails' => '', - // 'Add or change currency rate' => '', - // 'Reference currency: %s' => '', - // 'Add custom filters' => '', - // 'Export' => '', - // 'Add link label' => '', - // 'Incompatible Plugins' => '', - // 'Compatibility' => '', - // 'Permissions and ownership' => '', - // 'Priorities' => '', - // 'Close this window' => '', - // 'Unable to upload this file.' => '', - // 'Import tasks' => '', - // 'Choose a project' => '', - // 'Profile' => '', - // 'Application role' => '', - // '%d invitations were sent.' => '', - // '%d invitation was sent.' => '', - // 'Unable to create this user.' => '', - // 'Kanboard Invitation' => '', - // 'Visible on dashboard' => '', - // 'Created at:' => '', - // 'Updated at:' => '', - // 'There is no custom filter.' => '', - // 'New User' => '', - // 'Authentication' => '', - // 'If checked, this user will use a third-party system for authentication.' => '', - // 'The password is necessary only for local users.' => '', - // 'You have been invited to register on Kanboard.' => '', - // 'Click here to join your team' => '', - // 'Invite people' => '', - // 'Emails' => '', - // 'Enter one email address by line.' => '', - // 'Add these people to this project' => '', - // 'Add this person to this project' => '', - // 'Sign-up' => '', - // 'Credentials' => '', - // 'New user' => '', - // 'This username is already taken' => '', - // 'A link to reset your password has been sent by email.' => '', - // 'Your profile must have a valid email address.' => '', - // 'Unfortunately, we are unable to reset your password. Did you entered a valid username? Do you have an email address in your profile?' => '', - // 'TRL - Turkish Lira' => '', - // 'The project email is optional and could be used by several plugins.' => '', - // 'The project email must be unique across all projects' => '', - // 'The email configuration has been disabled by the administrator.' => '', - // 'Close this project' => '', - // 'Open this project' => '', - // 'Close a project' => '', - // 'Do you really want to close this project: "%s"?' => '', - // 'Reopen a project' => '', - // 'Do you really want to reopen this project: "%s"?' => '', - // 'This project is open' => '', - // 'This project is closed' => '', - // 'Unable to upload files, check the permissions of your data folder.' => '', - // 'Another category with the same name exists in this project' => '', - // 'Comment sent by email successfully.' => '', - // 'Sent by email to [%s](mailto:%s) (%s)' => '', - // 'Unable to read uploaded file.' => '', - // 'Database uploaded successfully.' => '', - // 'Task sent by email successfully.' => '', - // 'There is no category in this project.' => '', - // 'Send by email' => '', - // 'Create and send a comment by email' => '', - // 'Subject' => '', - // 'Upload the database' => '', - // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', - // 'Database file' => '', - // 'Upload' => '', - // 'Remove this user from group' => '', - // 'Your project must have at least one active swimlane.' => '', - // 'Project: %s' => '', - // 'Automatic action not found: "%s"' => '', - // '%d projects' => '', - // '%d project' => '', - // 'There is no project.' => '', - // 'Sort' => '', - // 'Project ID' => '', - // 'Project name' => '', - // 'Public' => '', - // 'Private' => '', - // '%d tasks' => '', - // '%d task' => '', - // 'Task ID' => '', - // 'Assign automatically a color when due date is expired' => '', - // 'Total score in this column across all swimlanes' => '', - // 'HRK - Kuna' => '', + 'You are not allowed to move this task.' => 'Você não está autorizado a mover esta tarefa.', + 'API User Access' => 'Acesso à API do usuário', + 'Preview' => 'Pré-visualizar', + 'Write' => 'Escrever', + 'Write your text in Markdown' => 'Escrever seu texto em Markdown', + 'No personal API access token registered.' => 'Nenhum token de acesso pessoal à API registrado.', + 'Your personal API access token is "%s"' => 'Seu token de acesso pessoal à API é "%s"', + 'Remove your token' => 'Remover seu token', + 'Generate a new token' => 'Gerar um novo token', + 'Showing %d-%d of %d' => 'Mostrando %d-%d de %d', + 'Outgoing Emails' => 'E-mails de saÃda', + 'Add or change currency rate' => 'Adicionar ou alterar taxa de câmbio', + 'Reference currency: %s' => 'Câmbio de referência: %s', + 'Add custom filters' => 'Adicionar filtros personalizados', + 'Export' => 'Exportar', + 'Add link label' => 'Adicionar rótulo de link', + 'Incompatible Plugins' => 'Plugin incompatÃvel', + 'Compatibility' => 'Compatibilidade', + 'Permissions and ownership' => 'Permissões e propriedade', + 'Priorities' => 'Propriedades', + 'Close this window' => 'Fechar esta janela', + 'Unable to upload this file.' => 'Não é possÃvel carregar este arquivo', + 'Import tasks' => 'Importar tarefas', + 'Choose a project' => 'Escolher um projeto', + 'Profile' => 'Perfil', + 'Application role' => 'Regra de aplicação', + '%d invitations were sent.' => '%d convites foram enviados.', + '%d invitation was sent.' => '%d convite foi enviado.', + 'Unable to create this user.' => 'Não foi possÃvel criar este usuário.', + 'Kanboard Invitation' => 'Convite do Kanboard', + 'Visible on dashboard' => 'VisÃvel no painel de controle', + 'Created at:' => 'Criado em:', + 'Updated at:' => 'Atualizado em:', + 'There is no custom filter.' => 'Não existe nenhum filtro personalizado.', + 'New User' => 'Novo Usuário', + 'Authentication' => 'Autenticação', + 'If checked, this user will use a third-party system for authentication.' => 'Se marcado, este usuário usará um sistema de autenticação de terceiros.', + 'The password is necessary only for local users.' => 'A senha é necessária somente para usuários locais.', + 'You have been invited to register on Kanboard.' => 'Você foi convidado a se registrar no Kanboard', + 'Click here to join your team' => 'Clique aqui para se unir ao seu time', + 'Invite people' => 'Convidar pessoas', + 'Emails' => 'E-mails', + 'Enter one email address by line.' => 'Digite um e-mail por linha. ', + 'Add these people to this project' => 'Adicionar estas pessoas para este projeto', + 'Add this person to this project' => 'Adicionar esta pessoa para este projeto', + 'Sign-up' => 'Inscrever-se', + 'Credentials' => 'Credenciais', + 'New user' => 'Novo usuário', + 'This username is already taken' => 'Este nome de usuário já está em uso', + 'A link to reset your password has been sent by email.' => 'Um link para reiniciar sua senha foi enviado por e-mail.', + 'Your profile must have a valid email address.' => 'Seu perfil precisa ter um endereço de e-mail válido.', + 'Unfortunately, we are unable to reset your password. Did you entered a valid username? Do you have an email address in your profile?' => 'Infelizmente, não foi possÃvel reiniciar sua senha. Você digitou um usuário válido?', + 'TRL - Turkish Lira' => 'TRL - Lira turca', + 'The project email is optional and could be used by several plugins.' => 'O e-mail do projeto é opcional e pode ser usado por diversos plugins.', + 'The project email must be unique across all projects' => 'O e-mail do projeto deve ser exclusivo em relação aos demais os projetos', + 'The email configuration has been disabled by the administrator.' => 'A configuração de e-mail foi desabilitada pelo administrador.', + 'Close this project' => 'Fechar este projeto', + 'Open this project' => 'Abrir este projeto', + 'Close a project' => 'Fechar um projeto', + 'Do you really want to close this project: "%s"?' => 'Deseja realmente fechar este projeto: "%s"?', + 'Reopen a project' => 'Reabrir um projeto', + 'Do you really want to reopen this project: "%s"?' => 'Deseja realmente reabrir este projeto: "%s"?', + 'This project is open' => 'Este projeto está aberto', + 'This project is closed' => 'Este projeto está fechado', + 'Unable to upload files, check the permissions of your data folder.' => 'Incapaz de enviar arquivos. Verifique as permissões da sua pasta de dados.', + 'Another category with the same name exists in this project' => 'Outra categoria com o mesmo nome existe neste projeto', + 'Comment sent by email successfully.' => 'Comentário enviado por e-mail com sucesso.', + 'Sent by email to [%s](mailto:%s) (%s)' => 'Enviar por e-mail para [%s](mailto:%s) (%s)', + 'Unable to read uploaded file.' => 'Incapaz de ler arquivos enviados.', + 'Database uploaded successfully.' => 'Base de dados enviado com sucesso.', + 'Task sent by email successfully.' => 'Tarefa enviada por e-mail com sucesso.', + 'There is no category in this project.' => 'Não há categoria neste projeto.', + 'Send by email' => 'Enviar por e-mail', + 'Create and send a comment by email' => 'Criar e enviar um comentário por e-mail', + 'Subject' => 'Assunto', + 'Upload the database' => 'Enviar uma base de dados', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Você pode enviar uma base de dados Sqlite baixada anteriormente (formato Gzip).', + 'Database file' => 'Arquivo de base de dados', + 'Upload' => 'Enviar', + 'Your project must have at least one active swimlane.' => 'Seu projeto precisa ter pelo menos uma raia ativa.', + 'Project: %s' => 'Projeto: %s', + 'Automatic action not found: "%s"' => 'Ação automática não encontrada: "%s"', + '%d projects' => '%d projetos', + '%d project' => '%d projeto', + 'There is no project.' => 'Não há projeto.', + 'Sort' => 'Ordenar', + 'Project ID' => 'ID do projeto', + 'Project name' => 'Nome do projeto', + 'Public' => 'Público', + 'Private' => 'Privado', + '%d tasks' => '%d tarefas', + '%d task' => '%d tarefa', + 'Task ID' => 'ID da tarefa', + 'Assign automatically a color when due date is expired' => 'Designar automaticamente uma cor quando a data de vencimento está expirada', + 'Total score in this column across all swimlanes' => 'Pontuação total nesta coluna através de todas as raias', + 'HRK - Kuna' => 'HRK - Kuna', + 'ARS - Argentine Peso' => 'ARS - Peso argentino', + 'COP - Colombian Peso' => 'COP - Peso colombiano', + '%d groups' => '%d grupos', + '%d group' => '%d grupo', + 'Group ID' => 'ID do grupo', + 'External ID' => 'ID externo', + '%d users' => '%d usuários', + '%d user' => '%d usuário', + 'Hide subtasks' => 'Esconder subtarefa', + 'Show subtasks' => 'Mostrar subtarefa', + 'Authentication Parameters' => 'Parâmetros de autenticação', + 'API Access' => 'Acesso à API', + 'No users found.' => 'Usuários não encontrados.', + 'User ID' => 'ID do usuário', + 'Notifications are activated' => 'As notificações estão ativadas', + 'Notifications are disabled' => 'As notificações estão desativadas', + 'User disabled' => 'Usuário desativado', + '%d notifications' => '%d notificações', + '%d notification' => '%d notificação', + 'There is no external integration installed.' => 'Não existe integração externa instalada.', + 'You are not allowed to update tasks assigned to someone else.' => 'Você não está autorizado a atualizar tarefas designadas para outros.', + 'You are not allowed to change the assignee.' => 'Você não está autorizado a mudar a designação.', + 'Task suppression is not permitted' => 'Supressão de tarefa não é permitida', + 'Changing assignee is not permitted' => 'Mudança de designação não é permitida', + 'Update only assigned tasks is permitted' => 'Atualizar somente tarefas designadas é permitida', + 'Only for tasks assigned to the current user' => 'Somente para tarefas designadas para o usuário atual', + 'My projects' => 'Meus projetos', + 'Your are not member of any project.' => 'Você não é membro de nenhum projeto.', + 'My subtasks' => 'Minhas subtarefas', + '%d subtasks' => '%d subtarefas', + '%d subtask' => '%d subtarefa', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Somente movimentação de tarefas entre aquelas colunas são permitidas para tarefas designadas para o usuário atual', + '[DUPLICATE]' => '[DUPLICADO]', + 'DKK - Danish Krona' => 'DKK - Coroa Dinamarquesa', + 'Remove user from group' => 'Remover usuário do grupo', + 'Assign the task to its creator' => 'Atribuir a tarefa ao seu criador', + 'This task was sent by email to "%s" with subject "%s".' => 'Esta tarefa foi enviada por e-mail para "%s" com o assunto "%s".', + 'Predefined Email Subjects' => 'Assuntos predefinidos de e-mail', + 'Write one subject by line.' => 'Escreva um assunto por linha.', + 'Create another link' => 'Criar outro link', + 'BRL - Brazilian Real' => 'BRL - Real Brasileiro', + 'Add a new Kanboard task' => 'Adicionar uma nova tarefa do Kanboard', + 'Subtask not started' => 'Subtarefa não iniciada', + 'Subtask currently in progress' => 'Subtarefa atualmente em progresso', + 'Subtask completed' => 'Subtarefa finalizada', + 'Subtask added successfully.' => 'Subtarefa adicionada com sucesso.', + '%d subtasks added successfully.' => '%d subtarefas adicionadas com sucesso.', + 'Enter one subtask by line.' => 'Escreva uma subtarefa por linha.', + 'Predefined Contents' => 'Conteúdos predefinidos', + 'Predefined contents' => 'Conteúdos predefinidos', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/pt_PT/translations.php b/app/Locale/pt_PT/translations.php index 88751ab6..091e6c68 100644 --- a/app/Locale/pt_PT/translations.php +++ b/app/Locale/pt_PT/translations.php @@ -39,7 +39,6 @@ return array( 'Administrator' => 'Administrador', 'Sign in' => 'Entrar', 'Users' => 'Utilizadores', - 'No user' => 'Sem utilizador', 'Forbidden' => 'Proibido', 'Access Forbidden' => 'Acesso negado', 'Edit user' => 'Editar utilizador', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'Subtarefa atualizada com sucesso.', 'Unable to update your sub-task.' => 'Não foi possÃvel atualizar a sua subtarefa.', 'Unable to create your sub-task.' => 'Não é possÃvel criar a sua subtarefa.', - 'Sub-task added successfully.' => 'Subtarefa adicionada com sucesso.', 'Maximum size: ' => 'Tamanho máximo: ', 'Display another project' => 'Mostrar outro projeto', 'Created by %s' => 'Criado por %s', @@ -396,22 +394,17 @@ return array( 'Activity stream' => 'Atividades Recentes', 'Dashboard' => 'Painel de Controlo', 'Confirmation' => 'Confirmação', - 'Allow everybody to access to this project' => 'Permitir a todos os acesso a este projeto', - 'Everybody have access to this project.' => 'Todos possuem acesso a este projeto.', 'Webhooks' => 'Webhooks', 'API' => 'API', 'Create a comment from an external provider' => 'Criar um comentário por meio de um serviço externo', 'Project management' => 'Gestão de projetos', - 'My projects' => 'Os meus projetos', 'Columns' => 'Colunas', 'Task' => 'Tarefas', - 'Your are not member of any project.' => 'Você não é membro de nenhum projeto.', 'Percentage' => 'Percentagem', 'Number of tasks' => 'Número de tarefas', 'Task distribution' => 'Distribuição de tarefas', 'Analytics' => 'EstatÃsticas', 'Subtask' => 'Subtarefa', - 'My subtasks' => 'As minhas subtarefas', 'User repartition' => 'Redistribuição de utilizador', 'Clone this project' => 'Clonar este projeto', 'Column removed successfully.' => 'Coluna removida com sucesso.', @@ -459,7 +452,6 @@ return array( 'Language:' => 'Idioma:', 'Timezone:' => 'Fuso horário:', 'All columns' => 'Todas as colunas', - 'Calendar' => 'Calendário', 'Next' => 'Próximo', // '#%d' => '', 'All swimlanes' => 'Todos os swimlane', @@ -526,19 +518,19 @@ return array( 'Compact/wide view' => 'Alternar entre a vista compacta e ampliada', 'Currency' => 'Moeda', 'Private project' => 'Projeto privado', - 'AUD - Australian Dollar' => 'AUD - Dólar australiano', - 'CAD - Canadian Dollar' => 'CAD - Dólar canadense', + 'AUD - Australian Dollar' => 'AUD - Dólar Australiano', + 'CAD - Canadian Dollar' => 'CAD - Dólar Canadense', 'CHF - Swiss Francs' => 'CHF - Francos SuÃços', 'Custom Stylesheet' => 'Folha de estilos personalizada', 'download' => 'transferir', 'EUR - Euro' => 'EUR - Euro', 'GBP - British Pound' => 'GBP - Libra Esterlina', - 'INR - Indian Rupee' => 'INR - Rúpia indiana', - 'JPY - Japanese Yen' => 'JPY - Iene japonês', + 'INR - Indian Rupee' => 'INR - Rúpia Indiana', + 'JPY - Japanese Yen' => 'JPY - Iene Japonês', 'NZD - New Zealand Dollar' => 'NZD - Dólar Neozelandês', - 'RSD - Serbian dinar' => 'RSD - Dinar sérvio', - // 'CNY - Chinese Yuan' => '', - 'USD - US Dollar' => 'USD - Dólar norte-americano', + 'RSD - Serbian dinar' => 'RSD - Dinar Sérvio', + 'CNY - Chinese Yuan' => 'CNY - Yuan Chinês', + 'USD - US Dollar' => 'USD - Dólar Norte-americano', 'Destination column' => 'Coluna de destino', 'Move the task to another column when assigned to a user' => 'Mover a tarefa para uma outra coluna quando esta está atribuÃda a um utilizador', 'Move the task to another column when assignee is cleared' => 'Mover a tarefa para uma outra coluna quando esta não está atribuÃda', @@ -557,7 +549,6 @@ return array( 'Unable to add this currency rate.' => 'ImpossÃvel adicionar essa taxa de câmbio.', 'Webhook URL' => 'URL do webhook', '%s removed the assignee of the task %s' => '%s removeu a pessoa assignada à tarefa %s', - 'Enable Gravatar images' => 'Activar imagem Gravatar', 'Information' => 'Informações', 'Check two factor authentication code' => 'Verificação do código de autenticação com factor duplo', 'The two factor authentication code is not valid.' => 'O código de autenticação com factor duplo não é válido', @@ -576,7 +567,7 @@ return array( 'Add a screenshot' => 'Adicionar uma Screenshot', 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Tire um screenshot e pressione CTRL + V ou ⌘ + V para colar aqui.', 'Screenshot uploaded successfully.' => 'Screenshot enviada com sucesso.', - 'SEK - Swedish Krona' => 'SEK - Coroa sueca', + 'SEK - Swedish Krona' => 'SEK - Coroa Sueca', 'Identifier' => 'Identificador', 'Disable two factor authentication' => 'Desactivar autenticação com dois factores', 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Tem a certeza que quer desactivar a autenticação com dois factores para esse utilizador: "%s"?', @@ -611,14 +602,7 @@ return array( 'When task is moved from first column' => 'Quando a tarefa é movida fora da primeira coluna', 'When task is moved to last column' => 'Quando a tarefa é movida para a última coluna', 'Year(s)' => 'Ano(s)', - 'Calendar settings' => 'Configurações do calendário', - 'Project calendar view' => 'Vista em modo projeto do calendário', 'Project settings' => 'Configurações dos projetos', - 'Show subtasks based on the time tracking' => 'Mostrar as subtarefas com base no controle de tempo', - 'Show tasks based on the creation date' => 'Mostrar as tarefas em função da data de criação', - 'Show tasks based on the start date' => 'Mostrar as tarefas em função da data de inÃcio', - 'Subtasks time tracking' => 'Monitoramento do tempo comparado as subtarefas', - 'User calendar view' => 'Vista em modo utilizador do calendário', 'Automatically update the start date' => 'Actualizar automaticamente a data de inÃcio', 'iCal feed' => 'Subscrição iCal', 'Preferences' => 'Preferências', @@ -637,7 +621,6 @@ return array( 'Notification' => 'Notificação', '%s moved the task #%d to the first swimlane' => '%s moveu a tarefa n° %d no primeiro swimlane', 'Swimlane' => 'Swimlane', - 'Gravatar' => 'Gravatar', '%s moved the task %s to the first swimlane' => '%s moveu a tarefa %s no primeiro swimlane', '%s moved the task %s to the swimlane "%s"' => '%s moveu a tarefa %s no swimlane "%s"', 'This report contains all subtasks information for the given date range.' => 'Este relatório contém informações de todas as sub-tarefas para o perÃodo selecionado.', @@ -674,7 +657,6 @@ return array( 'Stop timer' => 'Parar temporizador', 'Start timer' => 'Iniciar temporizador', 'My activity stream' => 'O meu feed de actividade', - 'My calendar' => 'A minha agenda', 'Search tasks' => 'Pesquisar tarefas', 'Reset filters' => 'Redefinir os filtros', 'My tasks due tomorrow' => 'A minhas tarefas que expiram amanhã', @@ -688,7 +670,6 @@ return array( 'Overview' => 'Visão global', 'Board/Calendar/List view' => 'Vista Painel/Calendário/Lista', 'Switch to the board view' => 'Mudar para o modo Painel', - 'Switch to the calendar view' => 'Mudar para o modo Calendário', 'Switch to the list view' => 'Mudar para o modo Lista', 'Go to the search/filter box' => 'Ir para o campo de pesquisa', 'There is no activity yet.' => 'Ainda não há nenhuma actividade.', @@ -743,15 +724,8 @@ return array( 'License:' => 'Licença:', 'License' => 'Licença', 'Enter the text below' => 'Escreva o texto em baixo', - 'Sort by position' => 'Ordenar por posição', - 'Sort by date' => 'Ordenar por data', - 'Add task' => 'Adicionar tarefa', 'Start date:' => 'Data de inicio:', 'Due date:' => 'Data de vencimento:', - 'There is no start date or due date for this task.' => 'Não existe data de inicio ou data de vencimento para esta tarefa.', - 'Moving or resizing a task will change the start and due date of the task.' => 'Mover ou redimensionar a tarefa irá alterar a data de inicio e vencimento da tarefa.', - 'There is no task in your project.' => 'Não existe tarefa no seu projeto.', - 'Gantt chart' => 'Gráfico de Gantt', 'People who are project managers' => 'Pessoas que são gestores do projeto', 'People who are project members' => 'Pessoas que são membros do projeto', 'NOK - Norwegian Krone' => 'NOK - Coroa Norueguesa', @@ -763,22 +737,15 @@ return array( 'Members' => 'Membros', 'Shared project' => 'Projeto partilhado', 'Project managers' => 'Gestores do projeto', - 'Gantt chart for all projects' => 'Gráfico de Gantt para todos os projetos', 'Projects list' => 'Lista de projetos', - 'Gantt chart for this project' => 'Gráfico de Gantt para este projeto', - 'Project board' => 'Quadro de projeto', 'End date:' => 'Data de fim:', - 'There is no start date or end date for this project.' => 'Não existe data de inicio ou fim para este projeto.', - 'Projects Gantt chart' => 'Gráfico de Gantt dos projetos', 'Change task color when using a specific task link' => 'Alterar cor da tarefa quando se usar um tipo especifico de ligação de tarefa', 'Task link creation or modification' => 'Criação ou modificação de ligação de tarefa', 'Milestone' => 'Objectivo', 'Documentation: %s' => 'Documentação: %s', - 'Switch to the Gantt chart view' => 'Mudar para vista de gráfico de Gantt', 'Reset the search/filter box' => 'Repor caixa de procura/filtro', 'Documentation' => 'Documentação', 'Table of contents' => 'Tabela de conteúdos', - 'Gantt' => 'Gantt', 'Author' => 'Autor', 'Version' => 'Versão', 'Plugins' => 'Plugins', @@ -889,7 +856,6 @@ return array( 'There is no user available.' => 'Não existe utilizador disponivel.', 'Do you really want to remove the user "%s" from the group "%s"?' => 'Tem a certeza que quer remover o utilizador "%s" do grupo "%s"?', 'There is no group.' => 'Não existe grupo.', - 'External Id' => 'Id externo', 'Add group member' => 'Adicionar membro de grupo', 'Do you really want to remove this group: "%s"?' => 'Tem a certeza que quer remover este grupo: "%s"?', 'There is no user in this group.' => 'Não existe utilizadores neste grupo.', @@ -955,7 +921,6 @@ return array( 'Default priority' => 'Prioridade por defeito', 'Lowest priority' => 'Prioridade mais baixa', 'Highest priority' => 'Prioridade mais alta', - 'If you put zero to the low and high priority, this feature will be disabled.' => 'Se colocar zero na prioridade baixa ou alta, essa funcionalidade será desactivada.', 'Close a task when there is no activity' => 'Fechar tarefa quando não há actividade', 'Duration in days' => 'Duração em dias', 'Send email when there is no activity on a task' => 'Enviar e-mail quando não há actividade numa tarefa', @@ -1169,8 +1134,6 @@ return array( 'Subtasks overview for %s' => 'Vista geral das sub-tarefas de %s', 'Projects overview for %s' => 'Vista geral dos projetos de %s', 'Activity stream for %s' => 'Fluxo de actividade de %s', - 'Calendar for %s' => 'Calendário de %s', - 'Notifications for %s' => 'Notificações de %s', 'Assign a color when the task is moved to a specific swimlane' => 'Atribuir uma cor quando a tarefa é movida para uma swimlane especifica', 'Assign a priority when the task is moved to a specific swimlane' => 'Atribuir uma prioridade quando a tarefa é movida para uma swimlane especifica', 'User unlocked successfully.' => 'Utilizador desbloqueado com sucesso.', @@ -1241,7 +1204,6 @@ return array( 'Preview' => 'Pré-Visualizar', 'Write' => 'Escrever', 'Write your text in Markdown' => 'Escreva o seu texto em Markdown', - 'New External Task: %s' => 'Nova Tarefa Externa: %s', 'No personal API access token registered.' => 'Nenhum token pessoal de acesso ao API ', 'Your personal API access token is "%s"' => 'O seu token de acesso pessoal ao API é "%s"', 'Remove your token' => 'Remover o seu token', @@ -1316,7 +1278,6 @@ return array( 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Poderia enviar a base de dados Sqlite transferida anteriormente (formato Gzip).', 'Database file' => 'Ficheiro base de dados', 'Upload' => 'Enviar', - 'Remove this user from group' => 'Remover este utilizador do grupo', 'Your project must have at least one active swimlane.' => 'O seu projecto deve ter pelo menos uma swimlane activa.', 'Project: %s' => 'Projecto: %s', 'Automatic action not found: "%s"' => 'Acção automática não encontrada: "%s"', @@ -1331,7 +1292,78 @@ return array( '%d tasks' => '%d tarefas', '%d task' => '%d tarefa', 'Task ID' => 'ID da Tarefa', - // 'Assign automatically a color when due date is expired' => '', - // 'Total score in this column across all swimlanes' => '', - // 'HRK - Kuna' => '', + 'Assign automatically a color when due date is expired' => 'Atribuir automaticamente uma cor quando a data de vencimento expirar', + 'Total score in this column across all swimlanes' => 'Pontuação total nesta coluna em todos os swimlanes', + 'HRK - Kuna' => 'HRK - Kuna Croata', + 'ARS - Argentine Peso' => 'ARS - Peso Argentino', + 'COP - Colombian Peso' => 'COP - Peso Colombiano', + '%d groups' => '%d grupos', + '%d group' => '%d grupo', + 'Group ID' => 'ID do Grupo', + 'External ID' => 'ID Externo', + '%d users' => '%d utilizadores', + '%d user' => '%d utilizador', + 'Hide subtasks' => 'Esconder subtarefas', + 'Show subtasks' => 'Mostrar subtarefas', + 'Authentication Parameters' => 'Parâmetros de autenticação', + 'API Access' => 'Acesso à API', + 'No users found.' => 'Não foram encontrados utilizadores.', + 'User ID' => 'ID de Utilizador', + 'Notifications are activated' => 'Notificações ativadas', + 'Notifications are disabled' => 'Notificações desativadas', + 'User disabled' => 'Utilizador desactivado', + '%d notifications' => '%d notificações', + '%d notification' => '%d notificação', + 'There is no external integration installed.' => 'Não há integração externa instalada.', + 'You are not allowed to update tasks assigned to someone else.' => 'Não tem permissão para atualizar tarefas atribuÃdas a outra pessoa.', + 'You are not allowed to change the assignee.' => 'Não tem permissão para alterar a atribuição', + 'Task suppression is not permitted' => 'A supressão de tarefas não é permitida', + 'Changing assignee is not permitted' => 'Não é permitido alterar a atribuição', + 'Update only assigned tasks is permitted' => 'Apenas é permitido atualizar as tarefas atribuÃdas', + 'Only for tasks assigned to the current user' => 'Apenas para tarefas atribuÃdas ao utilizador atual', + 'My projects' => 'Meus projetos', + 'Your are not member of any project.' => 'Não é membro de nenhum projeto.', + 'My subtasks' => 'Minhas subtarefas', + '%d subtasks' => '%d subtarefas', + '%d subtask' => '%d subtarefa', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Apenas é permitido mover a tarefa entre essas colunas para tarefas atribuÃdas ao utilizador atual', + '[DUPLICATE]' => '[DUPLICADA]', + 'DKK - Danish Krona' => 'DKK - Coroa Dinamarquesa', + 'Remove user from group' => 'Remover utilizador do grupo', + 'Assign the task to its creator' => 'Atribuir a tarefa ao seu criador', + 'This task was sent by email to "%s" with subject "%s".' => 'Esta tarefa foi enviada por email para "%s" com o assunto "%s".', + 'Predefined Email Subjects' => 'Assuntos de email predefinidos', + 'Write one subject by line.' => 'Escreva um assunto por linha.', + 'Create another link' => 'Criar outro link', + 'BRL - Brazilian Real' => 'BRL - Real Brasileiro', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/ro_RO/translations.php b/app/Locale/ro_RO/translations.php new file mode 100644 index 00000000..9bdd19cd --- /dev/null +++ b/app/Locale/ro_RO/translations.php @@ -0,0 +1,1369 @@ +<?php + +return array( + 'number.decimals_separator' => '.', + 'number.thousands_separator' => ' ', + 'None' => 'Nimic', + 'Edit' => 'Modifică', + 'Remove' => 'Șterge', + 'Yes' => 'Da', + 'No' => 'Nu', + 'cancel' => 'anulare', + 'or' => 'sau', + 'Yellow' => 'Galben', + 'Blue' => 'Albastru', + 'Green' => 'Verde', + 'Purple' => 'Violet', + 'Red' => 'RoÈ™u', + 'Orange' => 'Portocaliu', + 'Grey' => 'Gri', + 'Brown' => 'Maro', + 'Deep Orange' => 'Portocaliu închis', + 'Dark Grey' => 'Gri închis', + 'Pink' => 'Roz', + 'Teal' => 'Turcoaz', + 'Cyan' => 'Bleu', + 'Lime' => 'Lime', + 'Light Green' => 'Verde deschis', + 'Amber' => 'Ambră', + 'Save' => 'Salvează', + 'Login' => 'Conectare', + 'Official website:' => 'Site web oficial:', + 'Unassigned' => 'Neatribuit', + 'View this task' => 'Vezi sarcina aceasta', + 'Remove user' => 'Șterge utilizator', + 'Do you really want to remove this user: "%s"?' => 'Vrei să È™tergi acest utilizator: "%s" ?', + 'All users' => 'ToÈ›i utilizatorii', + 'Username' => 'Nume utilizator', + 'Password' => 'Parolă', + 'Administrator' => 'Administrator', + 'Sign in' => 'Conectare', + 'Users' => 'Utilizatori', + 'Forbidden' => 'Interzis', + 'Access Forbidden' => 'Acces interzis', + 'Edit user' => 'Modifică utilizator', + 'Logout' => 'Deconectare', + 'Bad username or password' => 'Utilizator sau parolă incorectă', + 'Edit project' => 'Modifică proiect', + 'Name' => 'Nume', + 'Projects' => 'Proiecte', + 'No project' => 'Fără proiect', + 'Project' => 'Proiect', + 'Status' => 'Stare', + 'Tasks' => 'Sarcini', + 'Board' => 'Bord', + 'Actions' => 'Actiuni', + 'Inactive' => 'Inactiv', + 'Active' => 'Activ', + 'Unable to update this board.' => 'Nu am putut actualiza acest bord.', + 'Disable' => 'Dezactivează', + 'Enable' => 'Activează', + 'New project' => 'Proiect nou', + 'Do you really want to remove this project: "%s"?' => 'Vrei să È™tergi acest proiect: "%s" ?', + 'Remove project' => 'Șterge proiect', + 'Edit the board for "%s"' => 'Modifică bordul pentru "%s"', + 'Add a new column' => 'Adaugă o coloană nouă', + 'Title' => 'Titlu', + 'Assigned to %s' => 'Atribuie lui %s', + 'Remove a column' => 'Șterge o coloană', + 'Unable to remove this column.' => 'Nu pot È™terge această coloană.', + 'Do you really want to remove this column: "%s"?' => 'Vrei să È™tergi acestă coloană: "%s" ?', + 'Settings' => 'PreferinÈ›e', + 'Application settings' => 'PreferinÈ›ele aplicaÈ›iei', + 'Language' => 'Limbă', + 'Webhook token:' => 'Token de securitate pentru webhook:', + 'API token:' => 'Token de securitate pentru API:', + 'Database size:' => 'Dimensiune bază de date:', + 'Download the database' => 'Descarcă baza de date', + 'Optimize the database' => 'Optimizează baza de date', + '(VACUUM command)' => '(Comanda VACUUM)', + '(Gzip compressed Sqlite file)' => '(FiÈ™ier Sqlite comprimat cu Gzip)', + 'Close a task' => 'ÃŽnchide o sarcină', + 'Column' => 'Coloană', + 'Color' => 'Culoare', + 'Assignee' => 'Persoană desemnată', + 'Create another task' => 'Creează altă sarcină', + 'New task' => 'Sarcină nouă', + 'Open a task' => 'Deschide o sarcină', + 'Do you really want to open this task: "%s"?' => 'Vrei să deschizi sarcina: "%s" ?', + 'Back to the board' => 'ÃŽnapoi la bord', + 'There is nobody assigned' => 'Nu este desemnată o persoană', + 'Column on the board:' => 'Coloană pe bord: ', + 'Close this task' => 'ÃŽnchide sarcina', + 'Open this task' => 'Deschide Sarcina', + 'There is no description.' => 'Nu există o descriere.', + 'Add a new task' => 'Adaugă o sarcină nouă', + 'The username is required' => 'Numele este obligatoriu', + 'The maximum length is %d characters' => 'Lungimea maximă este de %d caractere', + 'The minimum length is %d characters' => 'Lungimea minimă este de %d caractere', + 'The password is required' => 'Parola este obligatorie', + 'This value must be an integer' => 'Valoarea trebuie să fie număr întreg', + 'The username must be unique' => 'Utilizatorul trebuie să fie unic', + 'The user id is required' => 'ID-ul de utilizator este obligatoriu', + 'Passwords don\'t match' => 'Parolele nu corespund', + 'The confirmation is required' => 'Confirmarea este obligatorie', + 'The project is required' => 'Proiectul este obligatoriu', + 'The id is required' => 'Identificatorul este obligatoriu', + 'The project id is required' => 'ID-ul proiectului este obligatoriu', + 'The project name is required' => 'Numele proiectului este obligatoriu', + 'The title is required' => 'Titlul este obligatoriu', + 'Settings saved successfully.' => 'PreferinÈ›ele au fost salvate.', + 'Unable to save your settings.' => 'Nu am putut salva preferinÈ›ele.', + 'Database optimization done.' => 'Optimizarea bazei de date s-a încheiat.', + 'Your project have been created successfully.' => 'Proiectul dumneavoastră a fost creat cu succes.', + 'Unable to create your project.' => 'Nu am putut crea proiectul dumneavoastră.', + 'Project updated successfully.' => 'Proiectul a fost actualizat.', + 'Unable to update this project.' => 'Nu am putut actualiza proiectul.', + 'Unable to remove this project.' => 'Nu am putut È™terge proiectul.', + 'Project removed successfully.' => 'Proiectul a fost È™ters.', + 'Project activated successfully.' => 'Proiectul a fost activat.', + 'Unable to activate this project.' => 'Nu am putut activa proiectul.', + 'Project disabled successfully.' => 'Proiectul a fost dezactivat.', + 'Unable to disable this project.' => 'Nu am putut dezactiva proiectul', + 'Unable to open this task.' => 'Nu am putut deschide sarcina.', + 'Task opened successfully.' => 'Sarcina a fost deschisă', + 'Unable to close this task.' => 'Nu am putut închide sarcina.', + 'Task closed successfully.' => 'Sarcina a fost închisă.', + 'Unable to update your task.' => 'Nu am putut actualiza sarcina.', + 'Task updated successfully.' => 'Sarcina fost actualizată.', + 'Unable to create your task.' => 'Nu am putut crea sarcina.', + 'Task created successfully.' => 'Sarcina a fost creată.', + 'User created successfully.' => 'Utilizatorul a fost creat.', + 'Unable to create your user.' => 'Nu am putut crea utilizatorul.', + 'User updated successfully.' => 'Utilizatorul a fost actualizat.', + 'User removed successfully.' => 'Utilizatorul a fost È™ters.', + 'Unable to remove this user.' => 'Nu am putut È™terge utilizatorul.', + 'Board updated successfully.' => 'Bordul a fost actualizat.', + 'Ready' => 'Pregătit', + 'Backlog' => 'Restant', + 'Work in progress' => 'ÃŽn desfășurare', + 'Done' => 'Finalizat', + 'Application version:' => 'Versiunea aplicaÈ›iei:', + 'Id' => 'Id', + 'Public link' => 'Legătură publică', + 'Timezone' => 'Fus orar', + 'Sorry, I didn\'t find this information in my database!' => 'Scuze, nu am găsit informaÈ›ia asta în baza mea de date!', + 'Page not found' => 'Pagina nu a fost găsită', + 'Complexity' => 'Complexitate', + 'Task limit' => 'Limită sarcini.', + 'Task count' => 'Număr de sarcini', + 'User' => 'Utilizator', + 'Comments' => 'Comentarii', + 'Comment is required' => 'Comentariul este obligatoriu', + 'Comment added successfully.' => 'Comentariul a fost adăugat', + 'Unable to create your comment.' => 'Nu am putut crea comentariul.', + 'Due Date' => 'Data scadentă', + 'Invalid date' => 'Dată invalidă', + 'Automatic actions' => 'AcÈ›iuni automatizate', + 'Your automatic action have been created successfully.' => 'AcÈ›iunea automatizată a fost creată.', + 'Unable to create your automatic action.' => 'Nu am putut crea acÈ›iunea automatizată.', + 'Remove an action' => 'Șterge o acÈ›iune', + 'Unable to remove this action.' => 'Nu am putut È™terge acÈ›iunea', + 'Action removed successfully.' => 'AcÈ›iunea a fost È™tearsă.', + 'Automatic actions for the project "%s"' => 'AcÈ›iuni automatizate pentru proiectul "%s"', + 'Add an action' => 'Adaugă o acÈ›iune', + 'Event name' => 'Nume eveniment', + 'Action' => 'AcÈ›iune', + 'Event' => 'Eveniment', + 'When the selected event occurs execute the corresponding action.' => 'Când are loc evenimentul ales execută acÈ›iunea corespunzătoare.', + 'Next step' => 'Pasul următor', + 'Define action parameters' => 'DefineÈ™te parametrii acÈ›iunii', + 'Do you really want to remove this action: "%s"?' => 'Vrei să È™tergi acÈ›iunea: "%s" ?', + 'Remove an automatic action' => 'Șterge o acÈ›iune automatizată', + 'Assign the task to a specific user' => 'Atribuie sarcina unui utilizator specific', + 'Assign the task to the person who does the action' => 'Atribuie sarcina persoanei care îndeplineÈ™te acÈ›iunea', + 'Duplicate the task to another project' => 'Duplichează sarcina în alt proiect', + 'Move a task to another column' => 'Mută sarcina în altă coloană', + 'Task modification' => 'Modificare sarcină', + 'Task creation' => 'Creare sarcină', + 'Closing a task' => 'ÃŽnchidere sarcină', + 'Assign a color to a specific user' => 'Atribuie o culoare unui utilizator specific', + 'Position' => 'PoziÈ›ie', + 'Duplicate to another project' => 'Duplichează în alt proiect', + 'Duplicate' => 'Duplicare', + 'Link' => 'Legătură', + 'Comment updated successfully.' => 'Comentariu actualizat.', + 'Unable to update your comment.' => 'Nu am putut actualiza comentariul.', + 'Remove a comment' => 'Șterge un comentariu', + 'Comment removed successfully.' => 'Comentariul a fost È™ters.', + 'Unable to remove this comment.' => 'Nu am putut È™terge comentariul.', + 'Do you really want to remove this comment?' => 'Vrei să È™tergi acest comentariu?', + 'Current password for the user "%s"' => 'Parola actuală pentru utilizatorul "%s"', + 'The current password is required' => 'Parola actuală este obligatorie', + 'Wrong password' => 'Parolă greÈ™ită', + 'Unknown' => 'Necunoscut', + 'Last logins' => 'Ultimele conectări', + 'Login date' => 'Data conectării', + 'Authentication method' => 'Metodă de autentificare', + 'IP address' => 'Adresă IP', + 'User agent' => 'Agent utilizator', + 'Persistent connections' => 'Conexiuni persistente', + 'No session.' => 'Fără sesiune.', + 'Expiration date' => 'Data expirării', + 'Remember Me' => 'AminteÈ™te-È›i de mine', + 'Creation date' => 'Data creării', + 'Everybody' => 'Toată lumea', + 'Open' => 'Deschis', + 'Closed' => 'ÃŽnchis', + 'Search' => 'Caută', + 'Nothing found.' => 'Nimic găsit.', + 'Due date' => 'Dată scadentă', + 'Description' => 'Descriere', + '%d comments' => '%d comentarii', + '%d comment' => '%d comentariu', + 'Email address invalid' => 'Adresă e-mail invalidă', + 'Your external account is not linked anymore to your profile.' => 'Contul tău extern nu mai este legat cu profilul.', + 'Unable to unlink your external account.' => 'Nu am putut dezlega contul tău extern.', + 'External authentication failed' => 'Autentificarea externă a eÈ™uat', + 'Your external account is linked to your profile successfully.' => 'Contul tău extern a fost legat de profil.', + 'Email' => 'E-mail', + 'Task removed successfully.' => 'Sarcină È™tearsă.', + 'Unable to remove this task.' => 'Nu pot È™terge sarcina.', + 'Remove a task' => 'Șterge o sarcină', + 'Do you really want to remove this task: "%s"?' => 'Vrei să È™tergi sarcina: "%s" ?', + 'Assign automatically a color based on a category' => 'Atribuie automat o culoare bazat pe categorie', + 'Assign automatically a category based on a color' => 'Atribuie automat o categorie bazat pe culoare', + 'Task creation or modification' => 'Creare sau modificare sarcină', + 'Category' => 'Categorie', + 'Category:' => 'Categorie:', + 'Categories' => 'Categorii', + 'Your category have been created successfully.' => 'Categoria a fost creată.', + 'This category has been updated successfully.' => 'Categoria a fost actualizată.', + 'Unable to update this category.' => 'Nu am putut actualiza categoria.', + 'Remove a category' => 'Șterge o categorie', + 'Category removed successfully.' => 'Categoria a fost È™tearsă.', + 'Unable to remove this category.' => 'Nu am putut È™terge categoria.', + 'Category modification for the project "%s"' => 'Modificarea categoriei pentru proiectul "%s"', + 'Category Name' => 'Numele categoriei', + 'Add a new category' => 'Adaugă o categorie nouă', + 'Do you really want to remove this category: "%s"?' => 'Vrei să È™tergi categoria: "%s" ?', + 'All categories' => 'Toate categoriile', + 'No category' => 'Fără categorie', + 'The name is required' => 'Numele este obligatoriu', + 'Remove a file' => 'Șterge un fiÈ™ier', + 'Unable to remove this file.' => 'Nu am putut È™terge fiÈ™ierul.', + 'File removed successfully.' => 'FiÈ™ierul a fost È™ters.', + 'Attach a document' => 'AtaÈ™ează un document', + 'Do you really want to remove this file: "%s"?' => 'Vrei să È™tergi acest fiÈ™ier: "%s" ?', + 'Attachments' => 'AtaÈ™amente', + 'Edit the task' => 'Modifică sarcina', + 'Add a comment' => 'Adaugă un comentariu', + 'Edit a comment' => 'Modifică un comentariu', + 'Summary' => 'Sumar', + 'Time tracking' => 'Urmărirea timpului', + 'Estimate:' => 'Estimat:', + 'Spent:' => 'Petrecut:', + 'Do you really want to remove this sub-task?' => 'Vrei să È™tergi această sub-sarcină?', + 'Remaining:' => 'Rămas:', + 'hours' => 'ore', + 'spent' => 'trecute', + 'estimated' => 'estimate', + 'Sub-Tasks' => 'Sub-sarcini', + 'Add a sub-task' => 'Adaugă o sub-sarcină', + 'Original estimate' => 'Estimat original', + 'Create another sub-task' => 'Creează altă sub-sarcină', + 'Time spent' => 'Timp petrecut', + 'Edit a sub-task' => 'Modifică o sub-sarcină', + 'Remove a sub-task' => 'Șterge o sub-sarcină', + 'The time must be a numeric value' => 'Timpul trebuie să fie o valoare numerică', + 'Todo' => 'De făcut', + 'In progress' => 'ÃŽn curs', + 'Sub-task removed successfully.' => 'Sub-sarcină È™tearsă.', + 'Unable to remove this sub-task.' => 'Nu am putut È™terge sub-sarcina.', + 'Sub-task updated successfully.' => 'Sub-sarcină actualizată.', + 'Unable to update your sub-task.' => 'Nu am putut actualiza sub-sarcina.', + 'Unable to create your sub-task.' => 'Nu am putut crea sub-sarcina.', + 'Maximum size: ' => 'Dimensiune maximă: ', + 'Display another project' => 'AfiÈ™ează alt proiect', + 'Created by %s' => 'Creat de %s', + 'Tasks Export' => 'Exportă sarcini', + 'Start Date' => 'Data pornirii', + 'Execute' => 'Execută', + 'Task Id' => 'ID Sarcină', + 'Creator' => 'Creator', + 'Modification date' => 'Data modificării', + 'Completion date' => 'Data finalizării', + 'Clone' => 'Clonează', + 'Project cloned successfully.' => 'Proiectul a fost clonat.', + 'Unable to clone this project.' => 'Nu am putut clona proiectul.', + 'Enable email notifications' => 'Activează notificarile pe e-mail', + 'Task position:' => 'PoziÈ›ia sarcinii:', + 'The task #%d have been opened.' => 'Sarcina #%d a fost deschisă.', + 'The task #%d have been closed.' => 'Sarcina #%d a fost închisă.', + 'Sub-task updated' => 'Sub-sarcină actualizată', + 'Title:' => 'Titlu:', + 'Status:' => 'Stare:', + 'Assignee:' => 'Atribuire:', + 'Time tracking:' => 'Gestionarea timpului:', + 'New sub-task' => 'Sub-sarcină nouă', + 'New attachment added "%s"' => 'AtaÈ™ament nou adăugat "%s"', + 'New comment posted by %s' => 'Comentariu nou postat de "%s"', + 'New comment' => 'Comentariu nou', + 'Comment updated' => 'Comentariu actualizat', + 'New subtask' => 'Sub-sarcină nouă', + 'I want to receive notifications only for those projects:' => 'Vreau să primesc notificări numai pentru acele proiecte:', + 'view the task on Kanboard' => 'vezi sarcina pe Kanboard', + 'Public access' => 'Acces public', + 'Disable public access' => 'Dezactivează accesul public', + 'Enable public access' => 'Activează accesul public', + 'Public access disabled' => 'Accesul public dezactivat', + 'Move the task to another project' => 'Mută sarcina în alt proiect', + 'Move to another project' => 'Mută în alt proiect', + 'Do you really want to duplicate this task?' => 'Vrei să duplichezi această sarcină?', + 'Duplicate a task' => 'Duplichează o sarcină', + 'External accounts' => 'Conturi externe', + 'Account type' => 'Tip de cont', + 'Local' => 'Local', + 'Remote' => 'La distanță', + 'Enabled' => 'Activat', + 'Disabled' => 'Dezactivat', + 'Login:' => 'Utilizator:', + 'Full Name:' => 'Nume :', + 'Email:' => 'E-mail :', + 'Notifications:' => 'Notificări:', + 'Notifications' => 'Notificări', + 'Account type:' => 'Tip de cont:', + 'Edit profile' => 'Modifică profilul', + 'Change password' => 'Schimbă parola', + 'Password modification' => 'Modificare parolă', + 'External authentications' => 'Autentificări externe', + 'Never connected.' => 'Niciodată conectat.', + 'No external authentication enabled.' => 'Nici o autentificare externă activă.', + 'Password modified successfully.' => 'Parolă modificată.', + 'Unable to change the password.' => 'Nu am putut modifica parola.', + 'Change category' => 'Schimbă categoria', + '%s updated the task %s' => '%s a actualizat sarcina %s', + '%s opened the task %s' => '%s a deschis sarcina %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s a mutat sarcina %s la poziÈ›ia #%d în coloana "%s"', + '%s moved the task %s to the column "%s"' => '%s a mutat sarcina %s în coloana "%s"', + '%s created the task %s' => '%s a creat sarcina %s', + '%s closed the task %s' => '%s a închis sarcina %s', + '%s created a subtask for the task %s' => '%s a creat o sub-sarcină pentru sarcina %s', + '%s updated a subtask for the task %s' => '%s a actualizat o sub-sarcină pentru sarcina %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Atribuit lui %s cu o estimare de %s/%s h', + 'Not assigned, estimate of %sh' => 'Nimeni atribuit, estimare de %s h', + '%s updated a comment on the task %s' => '%s a actualizat u comentariu în sarcina %s', + '%s commented the task %s' => '%s a comentat în sarcina %s', + '%s\'s activity' => 'Activitatea pentru %s', + 'RSS feed' => 'Flux RSS', + '%s updated a comment on the task #%d' => '%s a actualizat un comentariu în sarcina #%d', + '%s commented on the task #%d' => '%s a comentat în sarcina #%d', + '%s updated a subtask for the task #%d' => '%s a actualizat o sub-sarcină în sarcina #%d', + '%s created a subtask for the task #%d' => '%s a creat o sub-sarcină în sarcina #%d', + '%s updated the task #%d' => '%s a actualizat sarcina #%d', + '%s created the task #%d' => '%s a creat sarcina #%d', + '%s closed the task #%d' => '%s a închis sarcina #%d', + '%s opened the task #%d' => '%s a deschis sarcina #%d', + 'Activity' => 'Activitate', + 'Default values are "%s"' => 'Valorile implicite sunt "%s"', + 'Default columns for new projects (Comma-separated)' => 'Coloane implicite pentru proiectele noi (Separate prin virgulă)', + 'Task assignee change' => 'Modificare persoană desemnată sarcinii', + '%s changed the assignee of the task #%d to %s' => '%s a schimbat persoana desemnată sarcinii #%d lui %s', + '%s changed the assignee of the task %s to %s' => '%s a schimbat persoana desemnată sarcinii %s lui %s', + 'New password for the user "%s"' => 'Parolă nouă pentru utilizatorul "%s"', + 'Choose an event' => 'Alege un eveniment', + 'Create a task from an external provider' => 'Creează o sarcină de la un furnizor extern', + 'Change the assignee based on an external username' => 'Schimbă persoana desemnată bazat pe un nume de utilizator extern', + 'Change the category based on an external label' => 'Schimbă categoria bazat pe o etichetă externă', + 'Reference' => 'Referință', + 'Label' => 'Etichetă', + 'Database' => 'Bază de date', + 'About' => 'Despre', + 'Database driver:' => 'Tip de bază de date:', + 'Board settings' => 'PreferinÈ›e bord', + 'Webhook settings' => 'PreferinÈ›e webhook', + 'Reset token' => 'Resetare token de securitate', + 'API endpoint:' => 'URL-ul pentru API:', + 'Refresh interval for private board' => 'Interval de reîmprospătare pentru bord privat', + 'Refresh interval for public board' => 'Interval de reîmprospătare pentru bord public', + 'Task highlight period' => 'Perioada de evidenÈ›iere a sarcinii', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Durata în secunde pentru a considera o sarcină recent modificată (0 pentru dezactivare, 2 zile implicit)', + 'Frequency in second (60 seconds by default)' => 'Frecvență în secunde (60 secunde implicit)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frecvență în secunde (0 pentru dezactivare, 10 secunde implicit)', + 'Application URL' => 'URL-ul aplicaÈ›iei', + 'Token regenerated.' => 'Token de securitate regenerat.', + 'Date format' => 'Formatare dată', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Formatul ISO este întotdeauna acceptat, exemplu: "%s" È™i "%s"', + 'New private project' => 'Proiect privat nou', + 'This project is private' => 'Acest proiect este privat', + 'Add' => 'Adaugă', + 'Start date' => 'Dată pornire', + 'Time estimated' => 'Timp estimat', + 'There is nothing assigned to you.' => 'Nu îți este atribuit nimic.', + 'My tasks' => 'Sarcinile mele', + 'Activity stream' => 'Flux de activitate', + 'Dashboard' => 'Bord', + 'Confirmation' => 'Confirmare', + 'Webhooks' => 'Webhook-uri', + 'API' => 'API', + 'Create a comment from an external provider' => 'Creează un comentariu de la un furnizor extern', + 'Project management' => 'Gestionare proiect', + 'Columns' => 'Coloane', + 'Task' => 'Sarcini', + 'Percentage' => 'Procentaj', + 'Number of tasks' => 'Număr de sarcini', + 'Task distribution' => 'DistribuÈ›ia sarcinilor', + 'Analytics' => 'Analitică', + 'Subtask' => 'Sub-sarcină', + 'User repartition' => 'Repartizare utilizatori', + 'Clone this project' => 'Clonează proiectul', + 'Column removed successfully.' => 'Coloana a fost È™tearsă.', + 'Not enough data to show the graph.' => 'Nu sunt destule date pentru afiÈ™area graficului.', + 'Previous' => 'Anterior', + 'The id must be an integer' => 'ID-ul trebuie să fie un număr întreg', + 'The project id must be an integer' => 'ID-ul proiectului trebuie să fie un număr întreg', + 'The status must be an integer' => 'Starea trebuie să fie un număr întreg', + 'The subtask id is required' => 'ID-ul sub-sarcinii este obligatoriu', + 'The subtask id must be an integer' => 'ID-ul sub-sarcinii trebuie sa fie un număr întreg', + 'The task id is required' => 'ID-ul sarcinii este obligatoriu', + 'The task id must be an integer' => 'ID-ul sarcinii trebuie să fie un număr întreg', + 'The user id must be an integer' => 'ID-ul utilizatorului trebuie să fie un număr întreg', + 'This value is required' => 'Valoarea aceasta este obligatorie', + 'This value must be numeric' => 'Valoarea aceasta trebuie să fie numerică', + 'Unable to create this task.' => 'Nu am putut crea sarcina', + 'Cumulative flow diagram' => 'Diagrama fluxului cumulat', + 'Daily project summary' => 'Rezumatul zilnic al proiectului', + 'Daily project summary export' => 'Export rezumat zilnic al proiectului', + 'Exports' => 'Exporturi', + 'This export contains the number of tasks per column grouped per day.' => 'Acest export conÈ›ine numărul de sarcini per coloană grupat pe zile.', + 'Active swimlanes' => 'Culoare active', + 'Add a new swimlane' => 'Adaugă culoar nou', + 'Default swimlane' => 'Culoar implicit', + 'Do you really want to remove this swimlane: "%s"?' => 'Vrei să È™tergi culoarul: "%s" ?', + 'Inactive swimlanes' => 'Culoare inactive', + 'Remove a swimlane' => 'Șterge un culoar', + 'Swimlane modification for the project "%s"' => 'Modificarea culoarului pentru proiectul "%s"', + 'Swimlane removed successfully.' => 'Culoarul a fost È™ters', + 'Swimlanes' => 'Culoare', + 'Swimlane updated successfully.' => 'Culoar actualizat.', + 'Unable to remove this swimlane.' => 'Nu am putut È™terge culoarul.', + 'Unable to update this swimlane.' => 'Nu am putut actualiza culoarul.', + 'Your swimlane have been created successfully.' => 'Culoarul a fost creat.', + 'Example: "Bug, Feature Request, Improvement"' => 'Exemplu: "Incident, Cere caracteristică, ÃŽmbunătățire"', + 'Default categories for new projects (Comma-separated)' => 'Categorii implicite pentru proiecte noi (Separate prin virgulă)', + 'Integrations' => 'Integrări', + 'Integration with third-party services' => 'Integrări cu servicii externe', + 'Subtask Id' => 'ID sub-sarcină', + 'Subtasks' => 'Sub-sarcini', + 'Subtasks Export' => 'Export sub-sarcini', + 'Task Title' => 'Titlu sarcină', + 'Untitled' => 'Fără titlu', + 'Application default' => 'Implicit din aplicaÈ›ie', + 'Language:' => 'Limbă:', + 'Timezone:' => 'Fus orar:', + 'All columns' => 'Toate coloanele', + 'Next' => 'Următor', + '#%d' => '#%d', + 'All swimlanes' => 'Toate culoarele', + 'All colors' => 'Toate culorile', + 'Moved to column %s' => 'Mutat în coloana %s', + 'User dashboard' => 'Bordul utilizatorului', + 'Allow only one subtask in progress at the same time for a user' => 'Permite o singură sub-sarcină în derulare simultană pentru un utilizator', + 'Edit column "%s"' => 'Modifică coloana "%s"', + 'Select the new status of the subtask: "%s"' => 'Alege noua stare a sarcinii: "%s"', + 'Subtask timesheet' => 'Pontajul sub-sarcinii', + 'There is nothing to show.' => 'Nimic de afiÈ™at', + 'Time Tracking' => 'Urmărirea timpului', + 'You already have one subtask in progress' => 'Deja ai o sub-sarcină în desfășurare', + 'Which parts of the project do you want to duplicate?' => 'Care părÈ›i ale proiectului vrei să fie duplicate?', + 'Disallow login form' => 'Interzice formularul de autentificare', + 'Start' => 'Start', + 'End' => 'Final', + 'Task age in days' => 'Vârsta sarcinii în zile', + 'Days in this column' => 'Zile în această coloană', + '%dd' => '%d z', + 'Add a new link' => 'Adaugă legătură nouă', + 'Do you really want to remove this link: "%s"?' => 'Vrei să È™tergi legătura: "%s" ?', + 'Do you really want to remove this link with task #%d?' => 'Vrei să È™tergi legătura cu sarcina #%d ?', + 'Field required' => 'Câmp obligatoriu', + 'Link added successfully.' => 'Legătură adăugată.', + 'Link updated successfully.' => 'Legătură actualizată.', + 'Link removed successfully.' => 'Legătură È™tearsă.', + 'Link labels' => 'Etichete de legături', + 'Link modification' => 'Modificare legătură', + 'Links' => 'Legături', + 'Opposite label' => 'Etichetă opusă', + 'Remove a link' => 'Șterge o legătură', + 'The labels must be different' => 'Etichetele trebuie să difere', + 'There is no link.' => 'Nu există legătură.', + 'This label must be unique' => 'Eticheta trebuie să fie unică', + 'Unable to create your link.' => 'Nu pot crea legătura.', + 'Unable to update your link.' => 'Nu pot actualiza legătura.', + 'Unable to remove this link.' => 'Nu pot È™terge legătura.', + 'relates to' => 'se leagă de', + 'blocks' => 'blochează', + 'is blocked by' => 'este blocat de', + 'duplicates' => 'duplichează', + 'is duplicated by' => 'este duplicat de', + 'is a child of' => 'este element moÈ™tenitor al', + 'is a parent of' => 'este element parental al', + 'targets milestone' => 'vizează È›elul', + 'is a milestone of' => 'este un È›el a', + 'fixes' => 'corectează', + 'is fixed by' => 'este corectat de', + 'This task' => 'Această sarcină', + '<1h' => '< 1 h', + '%dh' => '%d h', + 'Expand tasks' => 'Extinde sarcinile', + 'Collapse tasks' => 'Restrânge sarcinile', + 'Expand/collapse tasks' => 'Extinde/restrânge sarcinile', + 'Close dialog box' => 'ÃŽnchide dialogul', + 'Submit a form' => 'Depune un formular', + 'Board view' => 'Vizualizare bord', + 'Keyboard shortcuts' => 'Scurtături de tastatură', + 'Open board switcher' => 'Deschide comutatorul de bord', + 'Application' => 'AplicaÈ›ie', + 'Compact view' => 'Vizualizare restrânsă', + 'Horizontal scrolling' => 'Derulare orizontală', + 'Compact/wide view' => 'Vizualizare compactă/lată', + 'Currency' => 'Monedă', + 'Private project' => 'Proiect privat', + 'AUD - Australian Dollar' => 'AUD - Dolar australian', + 'CAD - Canadian Dollar' => 'CAD - Dolar canadian', + 'CHF - Swiss Francs' => 'CHF - Franc elvetian', + 'Custom Stylesheet' => 'Stylesheet personalizat', + 'download' => 'descarcă', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Liră sterlină', + 'INR - Indian Rupee' => 'INR - Rupie indiană', + 'JPY - Japanese Yen' => 'JPY - Yen japonez', + 'NZD - New Zealand Dollar' => 'NZD - Dolar neo-zeelandez', + 'RSD - Serbian dinar' => 'RSD - Dinar sârbesc', + 'CNY - Chinese Yuan' => 'CNY - Yuan chinez', + 'USD - US Dollar' => 'USD - Dolar american', + 'Destination column' => 'Coloana destinaÈ›ie', + 'Move the task to another column when assigned to a user' => 'Mută sarcina în altă coloană când este atribuită unui utilizator', + 'Move the task to another column when assignee is cleared' => 'Mută sarcina în altă coloană când este eliberată atribuirea', + 'Source column' => 'Coloana sursă', + 'Transitions' => 'Tranzitii', + 'Executer' => 'Executant', + 'Time spent in the column' => 'Timp petrecut în coloană', + 'Task transitions' => 'TranziÈ›iile sarcinilor', + 'Task transitions export' => 'Exportă tranziÈ›iile sarcinilor', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Acest raport conÈ›ine toate mutările de coloană pentru sarcinile cu data, utilizatorul È™i timpul petrecut pentru fiecare tranziÈ›ie.', + 'Currency rates' => 'Rate de schimb', + 'Rate' => 'Rată', + 'Change reference currency' => 'Schimbă moneda de referință', + 'Reference currency' => 'Moneda de referință', + 'The currency rate have been added successfully.' => 'Rata de schimb a fost adăugată.', + 'Unable to add this currency rate.' => 'Nu am putut adăuga rata de schimb', + 'Webhook URL' => 'URL pentru webhook', + '%s removed the assignee of the task %s' => '%s a eliberat persoana atribuită sarcinii %s', + 'Information' => 'InformaÈ›ii', + 'Check two factor authentication code' => 'Verificare cod autentificare în doi factori', + 'The two factor authentication code is not valid.' => 'Codul de autentificare în doi factori nu este valid.', + 'The two factor authentication code is valid.' => 'Codul de autentificare în doi factori este valid.', + 'Code' => 'Cod', + 'Two factor authentication' => 'Autentificare în doi factori', + 'This QR code contains the key URI: ' => 'Codul QR contine URI-ul cheii: ', + 'Check my code' => 'Verifică codul meu', + 'Secret key: ' => 'Cheia secretă: ', + 'Test your device' => 'Testează dispozitivul tău', + 'Assign a color when the task is moved to a specific column' => 'Atribuie o culoare când sarcina este mutată într-o anumită coloană', + '%s via Kanboard' => '%s via Kanboard', + 'Burndown chart' => 'Graficul progresului', + 'This chart show the task complexity over the time (Work Remaining).' => 'Acest grafic arată complexitatea sarcinii de-a lungul timpului (munca rămasă).', + 'Screenshot taken %s' => 'Captură de ecran %s', + 'Add a screenshot' => 'Adaugă captură de ecran', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'FaceÈ›i o captura de ecran si apăsaÈ›i CTRL+V sau ⌘+V pentru a lipi aici.', + 'Screenshot uploaded successfully.' => 'Captura de ecran a fost încărcată.', + 'SEK - Swedish Krona' => 'SEK - coroană suedeză', + 'Identifier' => 'Identificator', + 'Disable two factor authentication' => 'Dezactivează autentificarea în doi factori', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Vrei să dezactivezi autentificarea în doi factori pentru utilizatorul: "%s" ?', + 'Edit link' => 'Modifică o legătură', + 'Start to type task title...' => 'Scrie titlul sarcinii…', + 'A task cannot be linked to itself' => 'O sarcină nu se poate lega de ea însăși', + 'The exact same link already exists' => 'O legătură identică există deja', + 'Recurrent task is scheduled to be generated' => 'Sarcina recurentă este programată să fie generată', + 'Score' => 'Complexitate', + 'The identifier must be unique' => 'Identificatorul trebuie să fie unic', + 'This linked task id doesn\'t exists' => 'ID-ul de sarcină legat nu există', + 'This value must be alphanumeric' => 'Valoarea trebuie să fie alfanumerică', + 'Edit recurrence' => 'Modifică recurenÈ›a', + 'Generate recurrent task' => 'Generează sarcină recurentă', + 'Trigger to generate recurrent task' => 'DeclanÈ™ator pentru generarea sarcinii recurente', + 'Factor to calculate new due date' => 'Factor de calculare a noii date scadente', + 'Timeframe to calculate new due date' => 'Interval de timp pentru calcularea noii date scadente', + 'Base date to calculate new due date' => 'Data de bază pentru calcularea noii date scadente', + 'Action date' => 'Data acÈ›iunii', + 'Base date to calculate new due date: ' => 'Data de bază pentru calcularea noii date scadente: ', + 'This task has created this child task: ' => 'Sarcina aceasta a creat această sarcină-moÈ™tenitor: ', + 'Day(s)' => 'Zi(le)', + 'Existing due date' => 'Dată scadentă existentă', + 'Factor to calculate new due date: ' => 'Factor de calculare a noii date scadente: ', + 'Month(s)' => 'Lună(/i)', + 'Recurrence' => 'Recurență', + 'This task has been created by: ' => 'Această sarcină a fost creată de:', + 'Recurrent task has been generated:' => 'Sarcina recurentă a fost generată:', + 'Timeframe to calculate new due date: ' => 'Interval de timp pentru calcularea noii date scadente: ', + 'Trigger to generate recurrent task: ' => 'DeclanÈ™ator pentru generarea sarcinii recurente: ', + 'When task is closed' => 'Când sarcina este închisă', + 'When task is moved from first column' => 'Când sarcina este mutată din prima coloană', + 'When task is moved to last column' => 'Când sarcina este mutată în ultima coloană', + 'Year(s)' => 'An(i)', + 'Project settings' => 'PreferinÈ›e de proiect', + 'Automatically update the start date' => 'Actualizează automat data de start', + 'iCal feed' => 'Flux iCal', + 'Preferences' => 'PreferinÈ›e', + 'Security' => 'Securitate', + 'Two factor authentication disabled' => 'Autentificare în doi factori dezactivată', + 'Two factor authentication enabled' => 'Autentificare în doi factori activată', + 'Unable to update this user.' => 'Nu am putut actualiza utilizatorul.', + 'There is no user management for private projects.' => 'Nu poÈ›i gestiona utilizatori pentru proiecte private.', + 'User that will receive the email' => 'Utilizatorul care va primi e-mail-ul', + 'Email subject' => 'Subiectul e-mail-ului', + 'Date' => 'Data', + 'Add a comment log when moving the task between columns' => 'ÃŽnregistrează un comentariu în jurnal când se mută sarcina între coloane', + 'Move the task to another column when the category is changed' => 'Mută sarcina în altă coloană când se schimbă categoria', + 'Send a task by email to someone' => 'Trimite o sarcină prin e-mail cuiva', + 'Reopen a task' => 'Redeschide o sarcină', + 'Notification' => 'Notificare', + '%s moved the task #%d to the first swimlane' => '%s a mutat sarcina #%d în primul culoar', + 'Swimlane' => 'Culoar', + '%s moved the task %s to the first swimlane' => '%s a mutat sarcina %s în primul culoar', + '%s moved the task %s to the swimlane "%s"' => '%s a mutat sarcina %s în culoarul "%s"', + 'This report contains all subtasks information for the given date range.' => 'Raportul conÈ›ine toate informaÈ›iile sub-sarcinilor pentru intervalul dat.', + 'This report contains all tasks information for the given date range.' => 'Raportul conÈ›ine toate informaÈ›iile sarcinilor pentru intervalul dat.', + 'Project activities for %s' => 'Activități de proiect pentru "%s"', + 'view the board on Kanboard' => 'vezi bordul în Kanboard', + 'The task have been moved to the first swimlane' => 'Sarcina a fost mutată în primul culoar', + 'The task have been moved to another swimlane:' => 'Sarcina a fost mutată în alt culoar:', + 'New title: %s' => 'Titlu nou: %s', + 'The task is not assigned anymore' => 'Sarcina nu mai este desemnată', + 'New assignee: %s' => 'Desemnat nou: %s', + 'There is no category now' => 'Nu mai există categorii acum', + 'New category: %s' => 'Categorie nouă: %s', + 'New color: %s' => 'Culoare nouă: %s', + 'New complexity: %d' => 'Complexitate nouă: %d', + 'The due date have been removed' => 'Data scadentă a fost È™tearsă', + 'There is no description anymore' => 'Nu mai există o descriere', + 'Recurrence settings have been modified' => 'PreferinÈ›ele de recurență au fost modificate', + 'Time spent changed: %sh' => 'Timpul petrecut s-a schimbat: %s h', + 'Time estimated changed: %sh' => 'Timpul estimat s-a schimbat: %s h', + 'The field "%s" have been updated' => 'Câmpul "%s" a fost actualizat', + 'The description has been modified:' => 'Descrierea a fost modificată', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Vrei să închizi sarcina "%s" inclusiv toate sub-sarcinile?', + 'I want to receive notifications for:' => 'Vreau sa primesc notificări pentru:', + 'All tasks' => 'Toate Sarcinile', + 'Only for tasks assigned to me' => 'Numai sarcinile atribuite mie', + 'Only for tasks created by me' => 'Numai sarcinile create de mine', + 'Only for tasks created by me and assigned to me' => 'Numai sarcinile create de mine si atribuite mie.', + '%%Y-%%m-%%d' => '%%d/%%m/%%Y', + 'Total for all columns' => 'Totalul tuturor coloanelor', + 'You need at least 2 days of data to show the chart.' => 'Ai nevoie de cel putin 2 zile de date pentru afiÈ™area graficului.', + '<15m' => '< 15 min', + '<30m' => '< 30 min', + 'Stop timer' => 'OpreÈ™te cronometrul', + 'Start timer' => 'PorneÈ›e cronometrul', + 'My activity stream' => 'Fluxul meu de activități', + 'Search tasks' => 'Caută sarcini', + 'Reset filters' => 'Resetează filtre', + 'My tasks due tomorrow' => 'Sarcinile mele scadente mâine', + 'Tasks due today' => 'Sarcini scadente astăzi', + 'Tasks due tomorrow' => 'Sarcini scadente mâine', + 'Tasks due yesterday' => 'Sarcini scadente ieri', + 'Closed tasks' => 'Sarcini închise', + 'Open tasks' => 'Sarcini deschise', + 'Not assigned' => 'Fără atribuire', + 'View advanced search syntax' => 'Vezi sintaxa de căutare avansată', + 'Overview' => 'ÃŽn ansamblu', + 'Board/Calendar/List view' => 'Bord/Calendar/Listă', + 'Switch to the board view' => 'Schimbă la bord', + 'Switch to the list view' => 'Schimbă la listă', + 'Go to the search/filter box' => 'Mergi la câmpul de căutare/filtre', + 'There is no activity yet.' => 'Nu există activitate încă.', + 'No tasks found.' => 'Nu s-au găsit sarcini.', + 'Keyboard shortcut: "%s"' => 'Scurtătură tastatură : "%s"', + 'List' => 'Listă', + 'Filter' => 'Filtru', + 'Advanced search' => 'Căutare avansată', + 'Example of query: ' => 'Exemplu de interogare: ', + 'Search by project: ' => 'Caută după proiect: ', + 'Search by column: ' => 'Caută după coloană: ', + 'Search by assignee: ' => 'Caută după desemnat: ', + 'Search by color: ' => 'Caută după culoare: ', + 'Search by category: ' => 'Caută după categorie: ', + 'Search by description: ' => 'Caută după descriere: ', + 'Search by due date: ' => 'Caută după dată scadentă: ', + 'Average time spent into each column' => 'Timp mediu petrecut în fiecare coloană', + 'Average time spent' => 'Timp mediu utilizat', + 'This chart show the average time spent into each column for the last %d tasks.' => 'Acest grafic arată timpul mediu petrecut în fiecare coloană pentru ultimele %d sarcini.', + 'Average Lead and Cycle time' => 'Durată medie de Avans si Ciclu', + 'Average lead time: ' => 'Medie durată avans: ', + 'Average cycle time: ' => 'Medie durata ciclu: ', + 'Cycle Time' => 'Timp ciclu', + 'Lead Time' => 'Timp avans', + 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Acest grafic arată media duratelor de avans È™i ciclu pentru ultimele %d sarcini de-a lungul timpului.', + 'Average time into each column' => 'Durată medie în fiecare coloană', + 'Lead and cycle time' => 'Timpuri de avans È™i ciclu', + 'Lead time: ' => 'Timp avans: ', + 'Cycle time: ' => 'Timp ciclu: ', + 'Time spent into each column' => 'Timp petrecut prin fiecare coloană', + 'The lead time is the duration between the task creation and the completion.' => 'Timpul de avans este durata între crearea sarcinii È™i finalizarea.', + 'The cycle time is the duration between the start date and the completion.' => 'Timpul de ciclu este durata între data de pornire È™i finalizare.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Dacă sarcina nu este închisă data curentă este folosită în locul dății de finalizare.', + 'Set automatically the start date' => 'Setează automat data pornirii.', + 'Edit Authentication' => 'Modifică autentificarea', + 'Remote user' => 'Utilizator la distanță', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Utilizatorii la distanță nu păstrează parola în baza de date locală, de exemplu : conturi LDAP, GitHub sau Google.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Dacă bifezi "Interzice formularul de autentificare", acreditările introduse în dialogul de conectare vor fi ignorate.', + 'Default task color' => 'Culoarea implicită a sarcinii', + 'This feature does not work with all browsers.' => 'Această funcÈ›ionalitate nu funcÈ›ionează cu toate browserele.', + 'There is no destination project available.' => 'Nu este disponibil un proiect destinaÈ›ie.', + 'Trigger automatically subtask time tracking' => 'DeclanÈ™ează automat cronometrarea timpului în sub-sarcini.', + 'Include closed tasks in the cumulative flow diagram' => 'Include sarcini închise în graficul fluxurilor cumulate', + 'Current swimlane: %s' => 'Culoarul curent: %s', + 'Current column: %s' => 'Coloana curentă: %s', + 'Current category: %s' => 'Categoria curentă: %s', + 'no category' => 'fără categorie', + 'Current assignee: %s' => 'Desemnat curent: %s', + 'not assigned' => 'fără desemnare', + 'Author:' => 'Autor:', + 'contributors' => 'contribuitori', + 'License:' => 'Licență:', + 'License' => 'Licență', + 'Enter the text below' => 'Introdu textul mai jos', + 'Start date:' => 'Data pornirii:', + 'Due date:' => 'Data scadentă:', + 'People who are project managers' => 'Persoane care gestionează proiectul', + 'People who are project members' => 'Persoane care participă la proiect', + 'NOK - Norwegian Krone' => 'NOK - Coroană norvegiană', + 'Show this column' => 'Arată această coloană', + 'Hide this column' => 'Ascunde această coloană', + 'open file' => 'deschide fiÈ™ier', + 'End date' => 'Dată finalizare', + 'Users overview' => 'Prezentare generală utilizatori', + 'Members' => 'Membrii', + 'Shared project' => 'Proiect partajat', + 'Project managers' => 'Manageri de proiect', + 'Projects list' => 'Listă proiecte', + 'End date:' => 'Dată finalizare:', + 'Change task color when using a specific task link' => 'Schimbă culoarea sarcinii când se foloseÈ™te o anumită legătură în sarcină', + 'Task link creation or modification' => 'Creare sau modificare legături sarcină', + 'Milestone' => 'Èšel', + 'Documentation: %s' => 'DocumentaÈ›ie: %s', + 'Reset the search/filter box' => 'Resetează dialogul de căutare/filtre', + 'Documentation' => 'DocumentaÈ›ie', + 'Table of contents' => 'Cuprins', + 'Author' => 'Autor', + 'Version' => 'Versiuni', + 'Plugins' => 'Extensii', + 'There is no plugin loaded.' => 'Nu sunt încărcate extensii.', + 'My notifications' => 'Notificările mele', + 'Custom filters' => 'Filtre personalizate', + 'Your custom filter have been created successfully.' => 'Filtrul tău personalizat a fost creat.', + 'Unable to create your custom filter.' => 'Nu am putut crea filtrul tău personalizat.', + 'Custom filter removed successfully.' => 'Filtrul personalizat a fost È™ters.', + 'Unable to remove this custom filter.' => 'Nu am putut È™terge filtrul presonalizat.', + 'Edit custom filter' => 'Modifică un filtru personalizat', + 'Your custom filter have been updated successfully.' => 'Filtrul tău personalizat a fost actualizat.', + 'Unable to update custom filter.' => 'Nu am putut actualiza filtrul personalizat.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'AtaÈ™ament nou în sarcina #%d: %s', + 'New comment on task #%d' => 'Comentariu nou în sarcina #%d', + 'Comment updated on task #%d' => 'Comentariu actualizat în sarcina #%d', + 'New subtask on task #%d' => 'Sub-sarcină nouă în sarcina #%d', + 'Subtask updated on task #%d' => 'Sub-sarcină actualizată în sarcina #%d', + 'New task #%d: %s' => 'Sarcină nouă #%d: %s', + 'Task updated #%d' => 'Sarcina #%d a fost actualizată', + 'Task #%d closed' => 'Sarcina #%d a fost închisă', + 'Task #%d opened' => 'Sarcina #%d a fost deschisă', + 'Column changed for task #%d' => 'Coloană schimbată pentru sarcina #%d', + 'New position for task #%d' => 'PoziÈ›ie nouă pentru sarcina #%d', + 'Swimlane changed for task #%d' => 'Culoarul schimbat pentru sarcina #%d', + 'Assignee changed on task #%d' => 'Persoana desemnată a fost schimbată în sarcina #%d', + '%d overdue tasks' => '%d sarcini întârziate', + 'Task #%d is overdue' => 'Sarcina #%d întârzie', + 'No notification.' => 'Fără notificări.', + 'Mark all as read' => 'Marchează toate ca citite', + 'Mark as read' => 'Marchează citit', + 'Total number of tasks in this column across all swimlanes' => 'Număr total de sarcini în această coloană peste toate culoarele', + 'Collapse swimlane' => 'Restrânge culoarul', + 'Expand swimlane' => 'Extinde culoarul', + 'Add a new filter' => 'Adaugă un filtru nou', + 'Share with all project members' => 'Partajează cu toÈ›i membrii proiectului', + 'Shared' => 'Partajat', + 'Owner' => 'Proprietar', + 'Unread notifications' => 'Notificări necitite', + 'Notification methods:' => 'Metode de notificare:', + 'Unable to read your file' => 'Nu am putut citi fiÈ™ierul', + '%d task(s) have been imported successfully.' => 'Sarcini importate cu succes: %d.', + 'Nothing have been imported!' => 'Nu s-a importat nimic!', + 'Import users from CSV file' => 'Importă utilizatori dintr-un fiÈ™ier CSV', + '%d user(s) have been imported successfully.' => 'Utilizatori importaÈ›i cu succes: %d.', + 'Comma' => 'Virgulă', + 'Semi-colon' => 'Punct È™i virgulă', + 'Tab' => 'Tab', + 'Vertical bar' => 'Bară verticală', + 'Double Quote' => 'Ghilimele duble', + 'Single Quote' => 'Ghilimele simple', + '%s attached a file to the task #%d' => '%s a ataÈ™at un fiÈ™ier sarcinii #%d', + 'There is no column or swimlane activated in your project!' => 'Nu există vreo coloană sau culoar activ în proiectul tău!', + 'Append filter (instead of replacement)' => 'Adaugă filtru (în loc de înlocuire)', + 'Append/Replace' => 'Adaugă/ÃŽnlocuieÈ™te', + 'Append' => 'Adaugă', + 'Replace' => 'ÃŽnlocuieÈ™te', + 'Import' => 'Import', + 'Change sorting' => 'Schimbă ordonarea', + 'Tasks Importation' => 'Import de sarcini', + 'Delimiter' => 'Delimitare', + 'Enclosure' => 'Caractere de încadrare', + 'CSV File' => 'FiÈ™ier CSV', + 'Instructions' => 'InstrucÈ›iuni', + 'Your file must use the predefined CSV format' => 'Trebuie să foloseÈ™ti formatarea CSV predefinită', + 'Your file must be encoded in UTF-8' => 'FiÈ™ierul trebuie să fie codificat în UTF-8', + 'The first row must be the header' => 'Primul rând trebuie să fie antetul', + 'Duplicates are not verified for you' => 'Nu este verificată existenÈ›a duplicatelor', + 'The due date must use the ISO format: YYYY-MM-DD' => 'Data scadentă trebuie să folosească formatarea ISO : AAAA-LL-ZZ', + 'Download CSV template' => 'Descarcă modelul CSV', + 'No external integration registered.' => 'Nu a fost înregistrată o integrare externă.', + 'Duplicates are not imported' => 'Duplicatele nu sunt importate', + 'Usernames must be lowercase and unique' => 'Numele de utilizator trebuie să aibă caractere minuscule È™i să fie unice', + 'Passwords will be encrypted if present' => 'Parolele vor fi criptate dacă există', + '%s attached a new file to the task %s' => '%s a ataÈ™at un fiÈ™ier nou la sarcina %s', + 'Link type' => 'Tip de legătură', + 'Assign automatically a category based on a link' => 'Desemnează categoria automat în funcÈ›ie de legătură', + 'BAM - Konvertible Mark' => 'BAM - Marcă bosniană convertibilă', + 'Assignee Username' => 'Utilizatorul desemnat', + 'Assignee Name' => 'Numele desemnatului', + 'Groups' => 'Groupuri', + 'Members of %s' => 'Membri al %s', + 'New group' => 'Grup nou', + 'Group created successfully.' => 'Grupul a fost creat.', + 'Unable to create your group.' => 'Nu am putut crea grupul.', + 'Edit group' => 'Modifică grup', + 'Group updated successfully.' => 'Grupul a fost actualizat.', + 'Unable to update your group.' => 'Nu am putut actualiza grupul.', + 'Add group member to "%s"' => 'Adaugă membru de grup în "%s"', + 'Group member added successfully.' => 'Membru de grup adăugat.', + 'Unable to add group member.' => 'Nu am putut adăuga membrul de grup.', + 'Remove user from group "%s"' => 'Șterge utilizatorul din grupul "%s"', + 'User removed successfully from this group.' => 'Utilizatorul a fost È™ters din grup.', + 'Unable to remove this user from the group.' => 'Nu am putut È™terge utilizatorul din grup.', + 'Remove group' => 'Șterge grupul', + 'Group removed successfully.' => 'Grupul a fost È™ters.', + 'Unable to remove this group.' => 'Nu am putut È™terge grupul.', + 'Project Permissions' => 'Permisiunile proiectului', + 'Manager' => 'Gestionar', + 'Project Manager' => 'Șef de proiect', + 'Project Member' => 'Membru de proiect', + 'Project Viewer' => 'Vizualizator de proiect', + 'Your account is locked for %d minutes' => 'Contul tău este blocat %d minute', + 'Invalid captcha' => 'Captcha invalid', + 'The name must be unique' => 'Numele trebuie să fie unic', + 'View all groups' => 'Vezi toate grupurile', + 'There is no user available.' => 'Nu există utilizator disponibil', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Vrei să È™tergi utilizatorul "%s" din grupul "%s" ?', + 'There is no group.' => 'Nu există grup.', + 'Add group member' => 'Adaugă membru în grup', + 'Do you really want to remove this group: "%s"?' => 'Vrei să È™tergi acest grup: "%s" ?', + 'There is no user in this group.' => 'Nu există utilizatori în acest grup', + 'Permissions' => 'Permisiuni', + 'Allowed Users' => 'Utilizatori autorizaÈ›i', + 'No user have been allowed specifically.' => 'Nu a fost autorizat vreun utilizator.', + 'Role' => 'Rol', + 'Enter user name...' => 'Introdu numele de utilizator…', + 'Allowed Groups' => 'Grupuri autorizate', + 'No group have been allowed specifically.' => 'Nu a fost autorizat vreun grup.', + 'Group' => 'Grup', + 'Group Name' => 'Nume grup', + 'Enter group name...' => 'Introdu numele de grup…', + 'Role:' => 'Rol:', + 'Project members' => 'Membri de proiect', + '%s mentioned you in the task #%d' => '%s te-a menÈ›ionat în sarcina #%d', + '%s mentioned you in a comment on the task #%d' => '%s te-a menÈ›ionat într-un comentariu în sarcina #%d', + 'You were mentioned in the task #%d' => 'Ai fost menÈ›ionat în sarcina #%d', + 'You were mentioned in a comment on the task #%d' => 'Ai fost menÈ›ionat într-un comentariu în sarcina #%d', + 'Estimated hours: ' => 'Ore estimate: ', + 'Actual hours: ' => 'Ore actuale: ', + 'Hours Spent' => 'Ore petrecute', + 'Hours Estimated' => 'Ore estimate', + 'Estimated Time' => 'Timp estimat', + 'Actual Time' => 'Timp actual', + 'Estimated vs actual time' => 'Timp estimat vs actual', + 'RUB - Russian Ruble' => 'RUB - Rublă rusească', + 'Assign the task to the person who does the action when the column is changed' => 'Atribuie sarcina persoanei care acÈ›ionează când este schimbată coloana', + 'Close a task in a specific column' => 'ÃŽnchide o sarcină într-o anumită coloană', + 'Time-based One-time Password Algorithm' => 'Parolă de unică folosință bazată pe timp', + 'Two-Factor Provider: ' => 'Furnizor autentificare în doi factori: ', + 'Disable two-factor authentication' => 'Dezactivează autentificarea în doi factori', + 'Enable two-factor authentication' => 'Activează autentificarea în doi factori', + 'There is no integration registered at the moment.' => 'Nu este vreo integrare înregistrată momentan.', + 'Password Reset for Kanboard' => 'Resetare parolă pentru Kanboard', + 'Forgot password?' => 'Parolă uitată?', + 'Enable "Forget Password"' => 'Activează "Parolă Uitată"', + 'Password Reset' => 'Resetare de parolă', + 'New password' => 'Parolă nouă', + 'Change Password' => 'Schimbă parola', + 'To reset your password click on this link:' => 'Pentru a reseta parola apasă pe acest link:', + 'Last Password Reset' => 'Ultima resetare de parolă', + 'The password has never been reinitialized.' => 'Parola nu a fost resetată vreodată.', + 'Creation' => 'Creare', + 'Expiration' => 'Expirare', + 'Password reset history' => 'Istoricul resetărilor de parolă', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Toate sarcinile coloanei "%s" si a culoarului "%s" au fost închise.', + 'Do you really want to close all tasks of this column?' => 'Vrei să închizi toate sarcinile acestei coloane?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d sarcini în coloana "%s" si culoarul "%s" vor fi închise.', + 'Close all tasks of this column' => 'ÃŽnchide toate sarcinile acestei coloane', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Nici o extensie nu a înregistrat o metodă de notificare a proiectelor. Mai poÈ›i însă configura notificări individuale în profilul tău de utilizator.', + 'My dashboard' => 'Bordul meu', + 'My profile' => 'Profilul meu', + 'Project owner: ' => 'Responsabil de proiect: ', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Identificatorul de proiect este opÈ›ional È™i trebuie să fie alfanumeric, exemplu: PROIECTULMEU.', + 'Project owner' => 'Responsabil de proiect', + 'Private projects do not have users and groups management.' => 'Proiectele private nu au utilizatori È™i gestionare de grupuri.', + 'There is no project member.' => 'Nu există membri de proiect.', + 'Priority' => 'Prioritate', + 'Task priority' => 'Priorități de sarcini', + 'General' => 'General', + 'Dates' => 'Date', + 'Default priority' => 'Prioritate implicită', + 'Lowest priority' => 'Prioritate mică', + 'Highest priority' => 'Prioritate maximă', + 'Close a task when there is no activity' => 'ÃŽnchide o sarcină când nu are activitate', + 'Duration in days' => 'Durată în zile', + 'Send email when there is no activity on a task' => 'Trimite e-mail când o sarcină nu are activitate', + 'Unable to fetch link information.' => 'Nu am putut aduce informaÈ›ia legăturii.', + 'Daily background job for tasks' => 'FuncÈ›ia zilnică de fundal pentru sarcini', + 'Auto' => 'Auto', + 'Related' => 'Asociat', + 'Attachment' => 'AtaÈ™ament', + 'Title not found' => 'Titlul nu a fost găsit', + 'Web Link' => 'Link web', + 'External links' => 'Legături externe', + 'Add external link' => 'Adaugă legătură externă', + 'Type' => 'Tip', + 'Dependency' => 'Dependență', + 'Add internal link' => 'Adaugă legătură internă', + 'Add a new external link' => 'Adaugă o legătură externă nouă', + 'Edit external link' => 'Modifică legătura externă', + 'External link' => 'Legătură externă', + 'Copy and paste your link here...' => 'LipeÈ™te link-ul tău aici…', + 'URL' => 'URL', + 'Internal links' => 'Legături interne', + 'Assign to me' => 'Atribuie mie', + 'Me' => 'Eu', + 'Do not duplicate anything' => 'Nu duplica nimic', + 'Projects management' => 'Gestionează proiecte', + 'Users management' => 'Gestionează utilizatori', + 'Groups management' => 'Gestionează grupuri', + 'Create from another project' => 'Creează din alt proiect', + 'open' => 'deschis', + 'closed' => 'închis', + 'Priority:' => 'Prioritate:', + 'Reference:' => 'Referință:', + 'Complexity:' => 'Complexitate:', + 'Swimlane:' => 'Culoar:', + 'Column:' => 'Coloană:', + 'Position:' => 'PoziÈ›ie:', + 'Creator:' => 'Creator:', + 'Time estimated:' => 'Timp estimat:', + '%s hours' => '%s ore', + 'Time spent:' => 'Timp petrecut:', + 'Created:' => 'Creat de:', + 'Modified:' => 'Modificat:', + 'Completed:' => 'Finalizat:', + 'Started:' => 'Pornit:', + 'Moved:' => 'Mutat: ', + 'Task #%d' => 'Sarcina #%d', + 'Time format' => 'Format oră', + 'Start date: ' => 'Data pornirii: ', + 'End date: ' => 'Data finalizării: ', + 'New due date: ' => 'Dată scadentă nouă: ', + 'Start date changed: ' => 'Data pornirii modificată: ', + 'Disable private projects' => 'Dezactivează proiectele private', + 'Do you really want to remove this custom filter: "%s"?' => 'Vrei să È™tergi filtrul personalizat: "%s" ?', + 'Remove a custom filter' => 'Șterge un filtru personalizat', + 'User activated successfully.' => 'Utilizatorul a fost activat.', + 'Unable to enable this user.' => 'Nu am putut activa utilizatorul.', + 'User disabled successfully.' => 'Utilizatorul a fost dezactivat.', + 'Unable to disable this user.' => 'Nu am putut dezactiva utilizatorul.', + 'All files have been uploaded successfully.' => 'Toate fiÈ™ierele au fost încărcate.', + 'The maximum allowed file size is %sB.' => 'Dimensiunea maximă a fiÈ™ierului este %sB.', + 'Drag and drop your files here' => 'Trage fiÈ™ierele tale aici', + 'choose files' => 'alege fiÈ™iere', + 'View profile' => 'Vezi profilul', + 'Two Factor' => 'Doi Factori', + 'Disable user' => 'Dezactivează utilizator', + 'Do you really want to disable this user: "%s"?' => 'Vrei să dezactivezi utilizatorul: "%s" ?', + 'Enable user' => 'Activează utilizator', + 'Do you really want to enable this user: "%s"?' => 'Vrei să activezi utilizatorul: "%s" ?', + 'Download' => 'Descarcă', + 'Uploaded: %s' => 'ÃŽncărcat: %s', + 'Size: %s' => 'Dimensiune: %s', + 'Uploaded by %s' => 'ÃŽncărcat de %s', + 'Filename' => 'Nume fiÈ™ier', + 'Size' => 'Dimensiune', + 'Column created successfully.' => 'Coloana a fost creată.', + 'Another column with the same name exists in the project' => 'Există o coloană cu acelaÈ™i nume în proiect', + 'Default filters' => 'Filtre implicite', + 'Your board doesn\'t have any columns!' => 'Bordul tău nu are coloane!', + 'Change column position' => 'Schimbă poziÈ›ia coloanei', + 'Switch to the project overview' => 'Treci la vederea de ansamblu a proiectului', + 'User filters' => 'Filtre utilizatori', + 'Category filters' => 'Filtre categorii', + 'Upload a file' => 'ÃŽncarcă un fiÈ™ier', + 'View file' => 'Vezi fiÈ™ier', + 'Last activity' => 'Ultima activitate', + 'Change subtask position' => 'Schimbă poziÈ›ia sub-sarcinii', + 'This value must be greater than %d' => 'Valoarea aceasta trebuie să fie mai mare decât %d', + 'Another swimlane with the same name exists in the project' => 'Există un culoar cu acelaÈ™i nume în proiect', + 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Exemplu : http://exemplu.kanboard.net/ (utilizat în generarea URL-urilor absolute)', + 'Actions duplicated successfully.' => 'AcÈ›iuni duplicate cu succes.', + 'Unable to duplicate actions.' => 'Nu am putut duplica acÈ›iunile.', + 'Add a new action' => 'Adaugă o nouă acÈ›iune', + 'Import from another project' => 'Importă din alt proiect', + 'There is no action at the moment.' => 'Nu există vreo acÈ›iune momentan.', + 'Import actions from another project' => 'Importă acÈ™iuni din alt proiect', + 'There is no available project.' => 'Nu există un proiect disponibil.', + 'Local File' => 'FiÈ™ier local', + 'Configuration' => 'ConfiguraÈ›ie', + 'PHP version:' => 'Versiune de PHP:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'Versiune sistem de operare:', + 'Database version:' => 'Versiune bază de date:', + 'Browser:' => 'Browser:', + 'Task view' => 'Detaliere sarcină', + 'Edit task' => 'Modifică sarcina', + 'Edit description' => 'Modifică descrierea', + 'New internal link' => 'Legătură internă nouă', + 'Display list of keyboard shortcuts' => 'AfiÈ™ează lista de scurtături pe tastatură', + 'Menu' => 'Meniu', + 'Set start date' => 'DefineÈ™te data pornirii', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'ÃŽncarcă o imagine de avatar', + 'Remove my image' => 'Șterge imaginea mea', + 'The OAuth2 state parameter is invalid' => 'Parametrul "Stare" din OAuth2 este invalid', + 'User not found.' => 'Utilizatorul nu a fost găsit.', + 'Search in activity stream' => 'Caută în fluxul de activități', + 'My activities' => 'Activitățile mele', + 'Activity until yesterday' => 'Activitate până ieri', + 'Activity until today' => 'Activitate până azi', + 'Search by creator: ' => 'Caută după creator: ', + 'Search by creation date: ' => 'Caută după data creării: ', + 'Search by task status: ' => 'Caută după starea sarcinii: ', + 'Search by task title: ' => 'Caută după titlul sarcinii: ', + 'Activity stream search' => 'Căutare în fluxul de activități', + 'Projects where "%s" is manager' => 'Proiecte unde "%s" este gestionar', + 'Projects where "%s" is member' => 'Proiecte unde "%s" este membru', + 'Open tasks assigned to "%s"' => 'Sarcini deschise atribuite lui "%s"', + 'Closed tasks assigned to "%s"' => 'Sarcini închise atribuite lui "%s"', + 'Assign automatically a color based on a priority' => 'Atribuie în mod automat o culoare în funcÈ›ie de prioritate', + 'Overdue tasks for the project(s) "%s"' => 'Sarcini întârziate pentru proiect "%s"', + 'Upload files' => 'ÃŽncarcă fiÈ™iere', + 'Installed Plugins' => 'Extensii instalate', + 'Plugin Directory' => 'Director extensii', + 'Plugin installed successfully.' => 'Extensie instalată cu succes.', + 'Plugin updated successfully.' => 'Extensie actualizată cu succes.', + 'Plugin removed successfully.' => 'Extensie dezinstalată cu succes.', + 'Subtask converted to task successfully.' => 'Sub-sarcina a fost transformată în sarcină.', + 'Unable to convert the subtask.' => 'Nu am putut transforma sub-sarcina.', + 'Unable to extract plugin archive.' => 'Nu am putut extrage arhiva extensiei.', + 'Plugin not found.' => 'Extensia nu a fost găsită.', + 'You don\'t have the permission to remove this plugin.' => 'Nu ai dreptul să È™tergi această extensie.', + 'Unable to download plugin archive.' => 'Nu am putut descărca arhiva extensiei.', + 'Unable to write temporary file for plugin.' => 'Nu am putut scrie fiÈ™ierul temporar pentru extensie.', + 'Unable to open plugin archive.' => 'Nu am putut deschide arhiva extensiei.', + 'There is no file in the plugin archive.' => 'Nu există fiÈ™iere în arhiva extensiei.', + 'Create tasks in bulk' => 'Creează sarcini în vrac', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'InstanÈ›a ta de Kanboard nu a fost configurată pentru a instala extensii din interfaÈ›a de utilizator.', + 'There is no plugin available.' => 'Nu există extensii disponibile.', + 'Install' => 'Instalează', + 'Update' => 'Actualizează', + 'Up to date' => 'La zi', + 'Not available' => 'Indisponibil', + 'Remove plugin' => 'Șterge extensia', + 'Do you really want to remove this plugin: "%s"?' => 'Vrei să È™tergi această extensie: "%s" ?', + 'Uninstall' => 'Dezinstalare', + 'Listing' => 'Listare', + 'Metadata' => 'Metadate', + 'Manage projects' => 'Gestionează proiecte', + 'Convert to task' => 'Transformă în sarcină', + 'Convert sub-task to task' => 'Transformă sub-sarcina în sarcină', + 'Do you really want to convert this sub-task to a task?' => 'Vrei să transformi sub-sarcina în sarcină?', + 'My task title' => 'Titlul pentru sarcină', + 'Enter one task by line.' => 'Introdu o sarcină per linie.', + 'Number of failed login:' => 'Număr de autentificări eÈ™uate:', + 'Account locked until:' => 'Cont blocat până la:', + 'Email settings' => 'PreferinÈ›e e-mail', + 'Email sender address' => 'Adresa e-mail expeditor', + 'Email transport' => 'Transport e-mail', + 'Webhook token' => 'Token de securitate webhook', + 'Project tags management' => 'Gestionare etichete de proiect', + 'Tag created successfully.' => 'Etichetă creată.', + 'Unable to create this tag.' => 'Nu am putut crea eticheta.', + 'Tag updated successfully.' => 'Eticheta a fost actualizată.', + 'Unable to update this tag.' => 'Nu am putut actualiza eticheta.', + 'Tag removed successfully.' => 'Eticheta a fost È™tearsă.', + 'Unable to remove this tag.' => 'Impossible de supprimer ce libellé.', + 'Global tags management' => 'Gestionare etichete globale', + 'Tags' => 'Etichete', + 'Tags management' => 'Gestionare etichete', + 'Add new tag' => 'Adaugă etichetă nouă', + 'Edit a tag' => 'Modifică o etichetă', + 'Project tags' => 'Etichetele proiectului', + 'There is no specific tag for this project at the moment.' => 'Proiectul nu are vreo etichetă specifică momentan.', + 'Tag' => 'Etichetă', + 'Remove a tag' => 'Șterge o etichetă', + 'Do you really want to remove this tag: "%s"?' => 'Vrei să È™tergi eticheta: "%s" ?', + 'Global tags' => 'Etichete globale', + 'There is no global tag at the moment.' => 'Nu există etichete globale momentan.', + 'This field cannot be empty' => 'Acest câmp nu poate fi gol', + 'Close a task when there is no activity in an specific column' => 'ÃŽnchide o sarcină când nu există activitate într-o anumită coloană', + '%s removed a subtask for the task #%d' => '%s a È™ters o sub-sarcină din sarcina#%d', + '%s removed a comment on the task #%d' => '%s a È™ters un comentariu din sarcina #%d', + 'Comment removed on task #%d' => 'Comentariu È™ters în sarcina #%d', + 'Subtask removed on task #%d' => 'Sub-sarcină È™tearsă din sarcina #%d', + 'Hide tasks in this column in the dashboard' => 'Ascunde sarcinile din această coloană în bord', + '%s removed a comment on the task %s' => '%s a È™ters un comentariu din sarcina %s', + '%s removed a subtask for the task %s' => '%s a È™ters o sub-sarcină din sarcina %s', + 'Comment removed' => 'Comentariu È™ters', + 'Subtask removed' => 'Sub-sarcină È™tearsă', + '%s set a new internal link for the task #%d' => '%s a definit o legătură internă nouă pentru sarcina #%d', + '%s removed an internal link for the task #%d' => '%s a È™ters o legătură internă pentru sarcina #%d', + 'A new internal link for the task #%d have been defined' => 'O nouă legătură internă a fost definită pentru sarcina #%d', + 'Internal link removed for the task #%d' => 'Legătură internă È™tearsă pentru sarcina #%d', + '%s set a new internal link for the task %s' => '%s a definit o nouă legătură internă pentru sarcina %s', + '%s removed an internal link for the task %s' => '%s a È™ters o legătură internă pentru sarcina %s', + 'Automatically set the due date on task creation' => 'DefineÈ™te automat data scadentă la crearea sarcinii', + 'Move the task to another column when closed' => 'Mută sarcina în altă coloană când este închisă', + 'Move the task to another column when not moved during a given period' => 'Mută sarcina în altă coloană dacă nu a fost mutată într-o anumită perioadă', + 'Dashboard for %s' => 'Bordul pentru %s', + 'Tasks overview for %s' => 'Prezentare generală sarcini pentru %s', + 'Subtasks overview for %s' => 'Prezentare generală sub-sarcini pentru %s', + 'Projects overview for %s' => 'Prezentare generală proiecte pentru %s', + 'Activity stream for %s' => 'Flux de activități pentru %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Atribuie o culoare când sarcina este mutată într-un anumit culoar', + 'Assign a priority when the task is moved to a specific swimlane' => 'Atribuie o prioritate când sarcina este mutată într-un anumit culoar', + 'User unlocked successfully.' => 'Utilizatorul a fost deblocat.', + 'Unable to unlock the user.' => 'Nu am putut debloca utilizatorul.', + 'Move a task to another swimlane' => 'Mută sarcina în alt culoar', + 'Creator Name' => 'Nume creator', + 'Time spent and estimated' => 'Timp petrecut si estimat', + 'Move position' => 'Mută poziÈ›ia', + 'Move task to another position on the board' => 'Mută sarcina pe altă poziÈ›ie pe bord', + 'Insert before this task' => 'Inserează înaintea sarcinii', + 'Insert after this task' => 'Inserează după sarcină', + 'Unlock this user' => 'Deblochează utilizatorul', + 'Custom Project Roles' => 'Roluri personalizate în proiect', + 'Add a new custom role' => 'Adaugă rol personalizat nou', + 'Restrictions for the role "%s"' => 'RestricÈ›ii pentru rolul "%s"', + 'Add a new project restriction' => 'Adaugă o restricÈ›ie nouă de proiect', + 'Add a new drag and drop restriction' => 'Adaugă o restricÈ›ie nouă de mutare', + 'Add a new column restriction' => 'Adaugă o restricÈ›ie nouă de coloană', + 'Edit this role' => 'Modifică rolul', + 'Remove this role' => 'Șterge rolul', + 'There is no restriction for this role.' => 'Nu există restricÈ›ii pe acest rol.', + 'Only moving task between those columns is permitted' => 'Sarcina poate fi mutată numai între aceste coloane', + 'Close a task in a specific column when not moved during a given period' => 'ÃŽnchide o sarcină într-o anumită coloană când nu a fost mutată într-o anumită perioadă', + 'Edit columns' => 'Modifică coloane', + 'The column restriction has been created successfully.' => 'RestricÈ›ia pe coloane a fost creată.', + 'Unable to create this column restriction.' => 'Nu am putut crea restricÈ›ia pe coloană.', + 'Column restriction removed successfully.' => 'RestricÈ›ia pe coloană a fost È™tearsă.', + 'Unable to remove this restriction.' => 'Nu am putut È™terge restricÈ›ia.', + 'Your custom project role has been created successfully.' => 'Rolul personalizat a fost creat.', + 'Unable to create custom project role.' => 'Nu am putut crea rolul personalizat.', + 'Your custom project role has been updated successfully.' => 'Rolul personalizat a fost actualizat.', + 'Unable to update custom project role.' => 'Nu am putut actualiza rolul personalizat.', + 'Custom project role removed successfully.' => 'Rolul personalizat a fost È™ters.', + 'Unable to remove this project role.' => 'Nu am putut È™terge rolul.', + 'The project restriction has been created successfully.' => 'RestricÈ›ia pe proiect a fost creată.', + 'Unable to create this project restriction.' => 'Nu am putut crea restricÈ›ia pe proiect.', + 'Project restriction removed successfully.' => 'RestricÈ›ia pe proiect a fost È™tearsă.', + 'You cannot create tasks in this column.' => 'Nu poÈ›i crea sarcini în această coloană.', + 'Task creation is permitted for this column' => 'Crearea sarcinilor este permisă în această coloană', + 'Closing or opening a task is permitted for this column' => 'ÃŽnchiderea sau deschiderea sarcinilor este permisă în această coloană', + 'Task creation is blocked for this column' => 'Crearea sarcinilor este blocată în această coloană', + 'Closing or opening a task is blocked for this column' => 'ÃŽnchiderea sau deschiderea sarcinilor este blocată în această coloană', + 'Task creation is not permitted' => 'Crearea sarcinilor nu este permisă', + 'Closing or opening a task is not permitted' => 'ÃŽnchiderea sau deschiderea sarcinilor nu este permisă', + 'New drag and drop restriction for the role "%s"' => 'RestricÈ›ie nouă de mutare pentru rolul "%s"', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Persoanele cu acest rol vor putea muta sarcini numai între coloana sursă È™i destinaÈ›ie.', + 'Remove a column restriction' => 'Șterge o restricÈ›ie pe coloană', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Vrei să È™tergi această restricÈ›ie pe coloană: "%s" la "%s" ?', + 'New column restriction for the role "%s"' => 'RestricÈ›ie pe coloană nouă pentru rolul "%s"', + 'Rule' => 'Reguli', + 'Do you really want to remove this column restriction?' => 'Vrei să È™tergi această restricÈ›ie pe coloană?', + 'Custom roles' => 'Roluri personalizate', + 'New custom project role' => 'Nou rol personalizat pe proiect', + 'Edit custom project role' => 'Modifică rol personalizat pe proiect', + 'Remove a custom role' => 'Șterge rol personalizat pe proiect', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Vrei să È™tergi acest rol personalizat "%s" ? ToÈ›i membrii cu acest rol vor deveni membri de proiect.', + 'There is no custom role for this project.' => 'Nu există roluri personalizate în acest proiect.', + 'New project restriction for the role "%s"' => 'RestricÈ›ie de proiect nouă pentru rolul "%s"', + 'Restriction' => 'RestricÈ›ie', + 'Remove a project restriction' => 'Șterge o restricÈ›ie de proiect', + 'Do you really want to remove this project restriction: "%s"?' => 'Vrei să È™tergi această restricÈ›ie de proiect: "%s" ?', + 'Duplicate to multiple projects' => 'Duplichează în mai multe proiecte', + 'This field is required' => 'Câmpul este obligatoriu', + 'Moving a task is not permitted' => 'Nu este permisă mutarea sarcinii', + 'This value must be in the range %d to %d' => 'Valoarea trebuie să se afle între %d È™i %d', + 'You are not allowed to move this task.' => 'Nu ai permisiunea să muÈ›i această sarcină.', + 'API User Access' => 'Acces utilizator in API', + 'Preview' => 'Previzualizare', + 'Write' => 'Scrie', + 'Write your text in Markdown' => 'Scrie-È›i textul în Markdown', + 'No personal API access token registered.' => 'Nu este înregistrat vreun token personal de acces API.', + 'Your personal API access token is "%s"' => 'Token-ul tău personal de acces API este "%s"', + 'Remove your token' => 'Șterge token-ul tău', + 'Generate a new token' => 'Generează un nou token', + 'Showing %d-%d of %d' => 'AfiÈ™ez %d - %d din %d', + 'Outgoing Emails' => 'E-mail-uri trimise', + 'Add or change currency rate' => 'Adaugă sau modifică rată de schimb', + 'Reference currency: %s' => 'Moneda de referință: %s', + 'Add custom filters' => 'Adaugă filtre personalizate', + 'Export' => 'Export', + 'Add link label' => 'Adaugă etichetă de legătură', + 'Incompatible Plugins' => 'Extensii incompatibile', + 'Compatibility' => 'Compatibilitate', + 'Permissions and ownership' => 'Permisii si proprietate', + 'Priorities' => 'Priorități', + 'Close this window' => 'ÃŽnchide fereastra', + 'Unable to upload this file.' => 'Nu pot încărca fiÈ™ierul acesta.', + 'Import tasks' => 'Importă sarcini', + 'Choose a project' => 'Alege un proiect', + 'Profile' => 'Profil', + 'Application role' => 'Rolul aplicaÈ›iei', + '%d invitations were sent.' => '%d invitaÈ›ii au fost trimise.', + '%d invitation was sent.' => '%d invitaÈ›ie a fost trimisă.', + 'Unable to create this user.' => 'Nu am putut crea acest utilizator.', + 'Kanboard Invitation' => 'InvitaÈ›ie în Kanboard', + 'Visible on dashboard' => 'Vizibil pe bord', + 'Created at:' => 'Creat la:', + 'Updated at:' => 'Actualizat la:', + 'There is no custom filter.' => 'Nu există filtru personalizat.', + 'New User' => 'Utilizator nou', + 'Authentication' => 'Autentificare', + 'If checked, this user will use a third-party system for authentication.' => 'Dacă este bifat, acest utilizator va folosi un sistem terÈ› pentru autentificare.', + 'The password is necessary only for local users.' => 'Parola este obligatorie pentru membrii locali.', + 'You have been invited to register on Kanboard.' => 'Ai fost invitat să te înregistrezi pe Kanboard.', + 'Click here to join your team' => 'Apasă aici pentru a te alătura echipei tale', + 'Invite people' => 'Invită persoane', + 'Emails' => 'E-mail-uri', + 'Enter one email address by line.' => 'Introdu o adresă de e-mail per linie.', + 'Add these people to this project' => 'Adaugă aceste persoane în proiect', + 'Add this person to this project' => 'Adaugă această persoană în proiect', + 'Sign-up' => 'ÃŽnregistrare', + 'Credentials' => 'Acreditări', + 'New user' => 'Utilizator nou', + 'This username is already taken' => 'Acest nume de utilizator a fost luat deja', + 'A link to reset your password has been sent by email.' => 'Un link pentru resetarea parolei a fost trimis prin e-mail.', + 'Your profile must have a valid email address.' => 'Profilul tău trebuie să aibă o adresă de e-mail validă.', + 'Unfortunately, we are unable to reset your password. Did you entered a valid username? Do you have an email address in your profile?' => 'Din păcate nu am putut reseta parola ta. Ai introdus un nume de utilizator valid? Ai o adresă de e-mail în profil?', + 'TRL - Turkish Lira' => 'TRL - Liră turcească', + 'The project email is optional and could be used by several plugins.' => 'E-mail-ul proiectului este opÈ›ional si ar putea fi folosit de mai multe extensii.', + 'The project email must be unique across all projects' => 'E-mail-ul proiectului trebuie să fie diferit de toate celelalte proiecte.', + 'The email configuration has been disabled by the administrator.' => 'Configurarea de e-mail-uri a fost dezactivată de administrator.', + 'Close this project' => 'ÃŽnchide acest proiect', + 'Open this project' => 'Deschide acest proiect', + 'Close a project' => 'ÃŽnchide un proiect', + 'Do you really want to close this project: "%s"?' => 'Vrei să închizi proiectul: "%s" ?', + 'Reopen a project' => 'Redeschide un proiect', + 'Do you really want to reopen this project: "%s"?' => 'Vrei să redeschizi proiectul: "%s" ?', + 'This project is open' => 'Proiectul este deschis', + 'This project is closed' => 'Proiectul este închis', + 'Unable to upload files, check the permissions of your data folder.' => 'Nu am putut încărca fiÈ™ierele, verifică permisiunile directorului de date.', + 'Another category with the same name exists in this project' => 'Altă categorie cu acelaÈ™i nume există deja în proiect', + 'Comment sent by email successfully.' => 'Comentariul a fost trimis prin e-mail.', + 'Sent by email to [%s](mailto:%s) (%s)' => 'Trimite prin e-mail la [%s](mailto:%s) (%s)', + 'Unable to read uploaded file.' => 'Nu pot citi fiÈ™ierul încărcat.', + 'Database uploaded successfully.' => 'Baza de date a fost încărcată cu succes.', + 'Task sent by email successfully.' => 'Sarcina a fost trimisă prin e-mail.', + 'There is no category in this project.' => 'Nu există categorii în acest proiect.', + 'Send by email' => 'Trimis prin e-mail', + 'Create and send a comment by email' => 'Creat È™i trimis un comentariu prin e-mail', + 'Subject' => 'Subiect', + 'Upload the database' => 'ÃŽncarcă baza de date', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Ai putea încărca baza de date Sqlite descărcată anterior (format Gzip).', + 'Database file' => 'FiÈ™ierul bazei de date', + 'Upload' => 'ÃŽncarcă', + 'Your project must have at least one active swimlane.' => 'Proiectul tău trebuie să aibă măcar un culoar activ.', + 'Project: %s' => 'Proiect: %s', + 'Automatic action not found: "%s"' => 'AcÈ›iunea automatizată nu a fost gasită: "%s"', + '%d projects' => '%d proiecte', + '%d project' => '%d proiect', + 'There is no project.' => 'Nu există proiect.', + 'Sort' => 'Sortare', + 'Project ID' => 'ID-ul proiectului', + 'Project name' => 'Numele proiectului', + 'Public' => 'Public', + 'Private' => 'Privat', + '%d tasks' => '%d sarcini', + '%d task' => '%d sarcină', + 'Task ID' => 'ID-ul sarcinii', + 'Assign automatically a color when due date is expired' => 'Atribuie automat o culoare când data scadentă a expirat', + 'Total score in this column across all swimlanes' => 'Scorul total în această coloană pe toate culoarele', + 'HRK - Kuna' => 'HRK - Kuna croată', + 'ARS - Argentine Peso' => 'ARS - Peso argentinian', + 'COP - Colombian Peso' => 'COP - Peso columbian', + '%d groups' => '%d grupuri', + '%d group' => '%d grup', + 'Group ID' => 'ID-ul grupului', + 'External ID' => 'ID extern', + '%d users' => '%d utilizatori', + '%d user' => '%d utilizator', + 'Hide subtasks' => 'Ascunde sub-sarcinile', + 'Show subtasks' => 'Arată sub-sarcinile', + 'Authentication Parameters' => 'Parametri de autentificare', + 'API Access' => 'Acces API', + 'No users found.' => 'Nici un utilizator găsit.', + 'User ID' => 'ID utilizator', + 'Notifications are activated' => 'Notificările sunt activate', + 'Notifications are disabled' => 'Notificările sunt dezactivate', + 'User disabled' => 'Utilizator dezactivat', + '%d notifications' => '%d notificări', + '%d notification' => '%d notificare', + 'There is no external integration installed.' => 'Nu este instalată vreo integrare externă.', + 'You are not allowed to update tasks assigned to someone else.' => 'Nu ai voie să actualizezi sarcini atribuite altcuiva.', + 'You are not allowed to change the assignee.' => 'Nu ai voie să schimbi persoana atribuită.', + 'Task suppression is not permitted' => 'Suprimarea sarcinii nu este permisă', + 'Changing assignee is not permitted' => 'Schimbarea persoanei atribuite nu este permisă', + 'Update only assigned tasks is permitted' => 'Numai actualizarea sarcinilor atribuite este permisă', + 'Only for tasks assigned to the current user' => 'Numai pentru sarcini atribuite utilizatorului curent', + 'My projects' => 'Proiectele mele', + 'Your are not member of any project.' => 'Nu faci parte din vreun proiect', + 'My subtasks' => 'Sub-sarcinile mele', + '%d subtasks' => '%d sub-sarcini', + '%d subtask' => '%d sub-sarcină', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Este permisă mutarea sarcinii între acele coloane pentru sarcinile atribuite utilizatorului curent', + '[DUPLICATE]' => '[DUPLICAT]', + 'DKK - Danish Krona' => 'DKK - Coroană daneză', + 'Remove user from group' => 'Șterge utilizatorul din grup', + 'Assign the task to its creator' => 'Atribuie sarcina creatorului ei', + 'This task was sent by email to "%s" with subject "%s".' => 'Această sarcină a fost trimisă prin e-mail lui "%s" cu subiectul "%s".', + 'Predefined Email Subjects' => 'Subiecte predefinite in e-mail', + 'Write one subject by line.' => 'Scrie un singur subiect per linie.', + 'Create another link' => 'Creează altă legătură', + 'BRL - Brazilian Real' => 'BRL - Real brazilian', + 'Add a new Kanboard task' => 'Adaugă o nouă sarcină Kanboard', + 'Subtask not started' => 'Sub-sarcina nu este pornită', + 'Subtask currently in progress' => 'Sub-sarcină în derulare', + 'Subtask completed' => 'Sub-sarcină finalizată', + 'Subtask added successfully.' => 'Sub-sarcină adăugată.', + '%d subtasks added successfully.' => '%d sub-sarcini au fost adăugate.', + 'Enter one subtask by line.' => 'Introdu o sub-sarcină pe linie.', + 'Predefined Contents' => 'ConÈ›inut predefinit', + 'Predefined contents' => 'ConÈ›inut predefinit', + 'Predefined Task Description' => 'Descrierea predefinită a sarcinii', + 'Do you really want to remove this template? "%s"' => 'Vrei să È™tergi acest model? "%s"', + 'Add predefined task description' => 'Adaugă descriere predefinită a sarcinii', + 'Predefined Task Descriptions' => 'Descrieri predefinite ale sarcinilor', + 'Template created successfully.' => 'Model creat.', + 'Unable to create this template.' => 'Nu am putut crea modelul.', + 'Template updated successfully.' => 'Model actualizat.', + 'Unable to update this template.' => 'Nu am putut actualiza modelul.', + 'Template removed successfully.' => 'Model È™ters.', + 'Unable to remove this template.' => 'Nu am putut È™terge modelul.', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', +); diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php index 8217a086..eb991f38 100644 --- a/app/Locale/ru_RU/translations.php +++ b/app/Locale/ru_RU/translations.php @@ -39,7 +39,6 @@ return array( 'Administrator' => 'ÐдминиÑтратор', 'Sign in' => 'Войти', 'Users' => 'Пользователи', - 'No user' => 'Ðет пользователÑ', 'Forbidden' => 'Запрещено', 'Access Forbidden' => 'ДоÑтуп запрещён', 'Edit user' => 'Изменить пользователÑ', @@ -135,7 +134,7 @@ return array( 'User removed successfully.' => 'Пользователь удалён.', 'Unable to remove this user.' => 'Ðе удалоÑÑŒ удалить пользователÑ.', 'Board updated successfully.' => 'ДоÑка уÑпешно обновлена.', - 'Ready' => 'Готовые', + 'Ready' => 'СоглаÑованные', 'Backlog' => 'Ожидающие', 'Work in progress' => 'Ð’ процеÑÑе', 'Done' => 'Выполнено', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'Подзадача обновлена.', 'Unable to update your sub-task.' => 'Ðе удалоÑÑŒ обновить подзадачу.', 'Unable to create your sub-task.' => 'Ðе удалоÑÑŒ Ñоздать подзадачу.', - 'Sub-task added successfully.' => 'Подзадача добавлена.', 'Maximum size: ' => 'МакÑимальный размер: ', 'Display another project' => 'Показать другой проект', 'Created by %s' => 'Создано %s', @@ -396,22 +394,17 @@ return array( 'Activity stream' => 'Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð¾Ñть', 'Dashboard' => 'Панель управлениÑ', 'Confirmation' => 'Подтверждение паролÑ', - 'Allow everybody to access to this project' => 'Разрешить любому', - 'Everybody have access to this project.' => 'Любой может получить доÑтуп к Ñтому проекту.', 'Webhooks' => 'Webhooks', 'API' => 'API', 'Create a comment from an external provider' => 'Создать комментарий из внешнего иÑточника', - 'Project management' => 'Управление проектом', - 'My projects' => 'Мои проекты', + 'Project management' => 'Управление проектами', 'Columns' => 'Колонки', 'Task' => 'Задача', - 'Your are not member of any project.' => 'Ð’Ñ‹ не ÑоÑтоите ни в одном проекте.', 'Percentage' => 'Процент', 'Number of tasks' => 'КоличеÑтво задач', 'Task distribution' => 'РаÑпределение задач', 'Analytics' => 'Ðналитика', 'Subtask' => 'Подзадача', - 'My subtasks' => 'Мои подзадачи', 'User repartition' => 'ПерераÑпределение пользователей', 'Clone this project' => 'Клонировать проект', 'Column removed successfully.' => 'Колонка уÑпешно удалена.', @@ -459,7 +452,6 @@ return array( 'Language:' => 'Язык:', 'Timezone:' => 'Ð’Ñ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð·Ð¾Ð½Ð°:', 'All columns' => 'Ð’Ñе колонки', - 'Calendar' => 'Календарь', 'Next' => 'Следующий', '#%d' => '#%d', 'All swimlanes' => 'Ð’Ñе дорожки', @@ -556,8 +548,7 @@ return array( 'The currency rate have been added successfully.' => 'ÐšÑƒÑ€Ñ Ð²Ð°Ð»ÑŽÑ‚Ñ‹ был уÑпешно добавлен.', 'Unable to add this currency rate.' => 'Ðевозможно добавить Ñтот ÐºÑƒÑ€Ñ Ð²Ð°Ð»ÑŽÑ‚Ñ‹.', 'Webhook URL' => 'Webhook URL', - '%s removed the assignee of the task %s' => '%s удалить назначенную задачу %s', - 'Enable Gravatar images' => 'Включить Gravatar изображениÑ', + '%s removed the assignee of the task %s' => '%s удалил назначенного на задачу %s', 'Information' => 'ИнформациÑ', 'Check two factor authentication code' => 'Проверка кода двухфакторной авторизации', 'The two factor authentication code is not valid.' => 'Код двухфакторной авторизации не валиден', @@ -592,33 +583,26 @@ return array( 'Edit recurrence' => 'Редактировать повторы', 'Generate recurrent task' => 'Создать повторÑющуюÑÑ Ð·Ð°Ð´Ð°Ñ‡Ñƒ', 'Trigger to generate recurrent task' => 'Триггер Ð´Ð»Ñ Ð³ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ð¸ периодичеÑкой задачи', - 'Factor to calculate new due date' => 'КоÑффициент Ð´Ð»Ñ Ñ€Ð°Ñчёта новой даты', - 'Timeframe to calculate new due date' => 'ВычиÑление Ð´Ð»Ñ Ñ€Ð°Ñчёта новой даты', - 'Base date to calculate new due date' => 'Ð‘Ð°Ð·Ð¾Ð²Ð°Ñ Ð´Ð°Ñ‚Ð° вычиÑÐ»ÐµÐ½Ð¸Ñ Ð½Ð¾Ð²Ð¾Ð¹ даты', + 'Factor to calculate new due date' => 'КоÑффициент Ð´Ð»Ñ Ñ€Ð°Ñчёта новой даты завершениÑ', + 'Timeframe to calculate new due date' => 'Временные рамки Ð´Ð»Ñ Ñ€Ð°Ñчёта новой даты завершениÑ', + 'Base date to calculate new due date' => 'Ð‘Ð°Ð·Ð¾Ð²Ð°Ñ Ð´Ð°Ñ‚Ð° вычиÑÐ»ÐµÐ½Ð¸Ñ Ð½Ð¾Ð²Ð¾Ð¹ даты завершениÑ', 'Action date' => 'Дата дейÑтвиÑ', - 'Base date to calculate new due date: ' => 'Ð‘Ð°Ð·Ð¾Ð²Ð°Ñ Ð´Ð°Ñ‚Ð° вычиÑÐ»ÐµÐ½Ð¸Ñ Ð½Ð¾Ð²Ð¾Ð¹ даты: ', + 'Base date to calculate new due date: ' => 'Ð‘Ð°Ð·Ð¾Ð²Ð°Ñ Ð´Ð°Ñ‚Ð° вычиÑÐ»ÐµÐ½Ð¸Ñ Ð½Ð¾Ð²Ð¾Ð¹ даты завершениÑ: ', 'This task has created this child task: ' => 'Ðта задача Ñоздала Ñту дочернюю задачу:', 'Day(s)' => 'День(й)', - 'Existing due date' => 'СущеÑтвующий Ñрок', - 'Factor to calculate new due date: ' => 'КоÑффициент Ð´Ð»Ñ Ñ€Ð°Ñчёта новой даты: ', + 'Existing due date' => 'СущеÑÑ‚Ð²ÑƒÑŽÑ‰Ð°Ñ Ð´Ð°Ñ‚Ð° завершениÑ', + 'Factor to calculate new due date: ' => 'КоÑффициент Ð´Ð»Ñ Ñ€Ð°Ñчёта новой даты завершениÑ: ', 'Month(s)' => 'МеÑÑц(а)', 'Recurrence' => 'Повторение', 'This task has been created by: ' => 'Ðта задача была Ñоздана: ', 'Recurrent task has been generated:' => 'ПериодичеÑÐºÐ°Ñ Ð·Ð°Ð´Ð°Ñ‡Ð° была Ñформирована:', - 'Timeframe to calculate new due date: ' => 'ВычиÑление Ð´Ð»Ñ Ñ€Ð°Ñчёта новой даты: ', + 'Timeframe to calculate new due date: ' => 'Временные рамки Ð´Ð»Ñ Ñ€Ð°Ñчёта новой даты завершениÑ: ', 'Trigger to generate recurrent task: ' => 'Триггер Ð´Ð»Ñ Ð³ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ð¸ периодичеÑкой задачи: ', 'When task is closed' => 'Когда задача закрываетÑÑ', 'When task is moved from first column' => 'Когда задача перемещаетÑÑ Ð¸Ð· первой колонки', 'When task is moved to last column' => 'Когда задача перемещаетÑÑ Ð² поÑледнюю колонку', 'Year(s)' => 'Год(а)', - 'Calendar settings' => 'ÐаÑтройки календарÑ', - 'Project calendar view' => 'Вид ÐºÐ°Ð»ÐµÐ½Ð´Ð°Ñ€Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð°', 'Project settings' => 'ÐаÑтройки проекта', - 'Show subtasks based on the time tracking' => 'Показать подзадачи, оÑнованные на отÑлеживании времени', - 'Show tasks based on the creation date' => 'Показать задачи в завиÑимоÑти от даты ÑозданиÑ', - 'Show tasks based on the start date' => 'Показать задачи в завиÑимоÑти от даты начала', - 'Subtasks time tracking' => 'ОтÑлеживание времени подзадач', - 'User calendar view' => 'ПроÑмотреть календарь пользователÑ', 'Automatically update the start date' => 'ÐвтоматичеÑкое обновление даты начала', 'iCal feed' => 'iCal данные', 'Preferences' => 'ПредпочтениÑ', @@ -637,7 +621,6 @@ return array( 'Notification' => 'УведомлениÑ', '%s moved the task #%d to the first swimlane' => '%s перемеÑтил задачу #%d на первую дорожку', 'Swimlane' => 'Дорожки', - 'Gravatar' => 'Граватар', '%s moved the task %s to the first swimlane' => '%s перемеÑтил задачу %s на первую дорожку', '%s moved the task %s to the swimlane "%s"' => '%s перемеÑтил задачу %s на дорожку "%s"', 'This report contains all subtasks information for the given date range.' => 'Ðтот отчёт Ñодержит вÑÑŽ информацию подзадач в заданном диапазоне дат.', @@ -674,7 +657,6 @@ return array( 'Stop timer' => 'ОÑтановить таймер', 'Start timer' => 'ЗапуÑтить таймер', 'My activity stream' => 'Лента моей активноÑти', - 'My calendar' => 'Мой календарь', 'Search tasks' => 'ПоиÑк задачи', 'Reset filters' => 'СброÑить фильтры', 'My tasks due tomorrow' => 'Мои задачи на завтра', @@ -688,7 +670,6 @@ return array( 'Overview' => 'Обзор', 'Board/Calendar/List view' => 'ПроÑмотр ДоÑка/Календарь/СпиÑок', 'Switch to the board view' => 'ПереключитьÑÑ Ð² режим доÑки', - 'Switch to the calendar view' => 'ПереключитьÑÑ Ð² режим календарÑ', 'Switch to the list view' => 'ПереключитьÑÑ Ð² режим ÑпиÑка', 'Go to the search/filter box' => 'Перейти в поиÑк/фильтр', 'There is no activity yet.' => 'ÐктивноÑти еще не было', @@ -743,17 +724,10 @@ return array( 'License:' => 'ЛицензиÑ:', 'License' => 'ЛицензиÑ', 'Enter the text below' => 'Введите текÑÑ‚ ниже', - 'Sort by position' => 'Сортировать по позиции', - 'Sort by date' => 'Сортировать по дате', - 'Add task' => 'Добавить задачу', 'Start date:' => 'Дата начала:', 'Due date:' => 'Дата завершениÑ:', - 'There is no start date or due date for this task.' => 'Ð”Ð»Ñ Ñтой задачи нет даты начала или завершениÑ.', - 'Moving or resizing a task will change the start and due date of the task.' => 'Изменение или перемещение задачи повлечет изменение даты начала и Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ð·Ð°Ð´Ð°Ñ‡Ð¸.', - 'There is no task in your project.' => 'Ð’ Вашем проекте задач нет.', - 'Gantt chart' => 'Диаграмма Ганта', - 'People who are project managers' => 'Люди, которые менеджеры проекта', - 'People who are project members' => 'Люди, которые учаÑтники проекта', + 'People who are project managers' => 'Люди, которые ÑвлÑÑŽÑ‚ÑÑ Ð¼ÐµÐ½ÐµÐ´Ð¶ÐµÑ€Ð°Ð¼Ð¸ проекта', + 'People who are project members' => 'Люди, которые ÑвлÑÑŽÑ‚ÑÑ ÑƒÑ‡Ð°Ñтниками проекта', 'NOK - Norwegian Krone' => 'ÐК - ÐорвежÑÐºÐ°Ñ ÐºÑ€Ð¾Ð½Ð°', 'Show this column' => 'Показать Ñту колонку', 'Hide this column' => 'СпрÑтать Ñту колонку', @@ -763,22 +737,15 @@ return array( 'Members' => 'УчаÑтники', 'Shared project' => 'Общие/публичные проекты', 'Project managers' => 'Менеджер проекта', - 'Gantt chart for all projects' => 'Диаграмма Ганта Ð´Ð»Ñ Ð²Ñех проектов', 'Projects list' => 'СпиÑок проектов', - 'Gantt chart for this project' => 'Диаграмма Ганта Ð´Ð»Ñ Ñтого проекта', - 'Project board' => 'ДоÑка проекта', 'End date:' => 'Дата завершениÑ:', - 'There is no start date or end date for this project.' => 'Ð’ проекте не указаны дата начала или завершениÑ.', - 'Projects Gantt chart' => 'Диаграмма Ганта проектов', 'Change task color when using a specific task link' => 'Изменение цвета задач при иÑпользовании ÑÑылки на определенные задачи', 'Task link creation or modification' => 'СÑылка на Ñоздание или модификацию задачи', 'Milestone' => 'Веха', 'Documentation: %s' => 'ДокументациÑ: %s', - 'Switch to the Gantt chart view' => 'ПереключитьÑÑ Ð² режим диаграммы Ганта', 'Reset the search/filter box' => 'СброÑить поиÑк/фильтр', 'Documentation' => 'ДокументациÑ', 'Table of contents' => 'Содержание', - 'Gantt' => 'Гант', 'Author' => 'Ðвтор', 'Version' => 'ВерÑиÑ', 'Plugins' => 'Плагины', @@ -848,7 +815,7 @@ return array( 'Your file must be encoded in UTF-8' => 'Ваш файл должен иметь кодировку UTF-8', 'The first row must be the header' => 'Ð’ первой Ñтроке должны быть заголовки Ñтолбцов', 'Duplicates are not verified for you' => 'Проверка на дубликаты не оÑущеÑтвлÑетÑÑ', - 'The due date must use the ISO format: YYYY-MM-DD' => 'Дата проÑрочки должна быть в формате ISO: ГГГГ-ММ-ДД', + 'The due date must use the ISO format: YYYY-MM-DD' => 'Дата Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ð´Ð¾Ð»Ð¶Ð½Ð° быть в формате ISO: ГГГГ-ММ-ДД', 'Download CSV template' => 'Скачать шаблон CSV-файла', 'No external integration registered.' => 'Ðет зарегиÑтрированных внешних интеграций.', 'Duplicates are not imported' => 'Дубликаты не импортируютÑÑ', @@ -889,7 +856,6 @@ return array( 'There is no user available.' => 'Ðет доÑтупных пользователей.', 'Do you really want to remove the user "%s" from the group "%s"?' => 'Ð’Ñ‹ дейÑтвительно хотите удалить Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ "%s" из группы "%s"?', 'There is no group.' => 'Ðет Ñозданных групп.', - 'External Id' => 'Внешний Id', 'Add group member' => 'Добавить учаÑтника в группу', 'Do you really want to remove this group: "%s"?' => 'Ð’Ñ‹ дейÑтвительно хотите удалить группу "%s"?', 'There is no user in this group.' => 'Ð’ Ñтой группе нет учаÑтников.', @@ -955,7 +921,6 @@ return array( 'Default priority' => 'Приоритет по-умолчанию', 'Lowest priority' => 'Ðаименьший приоритет', 'Highest priority' => 'ÐаивыÑший приоритет', - 'If you put zero to the low and high priority, this feature will be disabled.' => 'ЕÑли Ð’Ñ‹ введете 0 Ð´Ð»Ñ Ð½Ð°Ð¸Ð¼ÐµÐ½ÑŒÑˆÐµÐ³Ð¾ и наивыÑшего приоритета, Ñтот функционал будет отключен.', 'Close a task when there is no activity' => 'Закрывать задачу, когда нет активноÑти', 'Duration in days' => 'ДлительноÑть в днÑÑ…', 'Send email when there is no activity on a task' => 'ОтправлÑть email, когда активноÑть по задаче отÑутÑтвует', @@ -1003,9 +968,9 @@ return array( 'Moved:' => 'Перемещена:', 'Task #%d' => 'Задача #%d', 'Time format' => 'Формат времени', - 'Start date: ' => 'Дата начала:', - 'End date: ' => 'Дата окончаниÑ:', - 'New due date: ' => 'ÐÐ¾Ð²Ð°Ñ Ð´Ð°Ñ‚Ð° иÑполнениÑ:', + 'Start date: ' => 'Дата начала: ', + 'End date: ' => 'Дата окончаниÑ: ', + 'New due date: ' => 'ÐÐ¾Ð²Ð°Ñ Ð´Ð°Ñ‚Ð° завершениÑ: ', 'Start date changed: ' => 'Дата начала изменена:', 'Disable private projects' => 'Выключить приватные проекты', 'Do you really want to remove this custom filter: "%s"?' => 'Ð’Ñ‹ точно ходите удалить Ñтот пользовательÑкий фильтр: "%s"?', @@ -1161,7 +1126,7 @@ return array( 'Internal link removed for the task #%d' => 'ВнутреннÑÑ ÑÑылка удалена Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ñ‡Ð¸ #%d', '%s set a new internal link for the task %s' => '%s добавил внутреннюю ÑÑылку Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ñ‡Ð¸ %s', '%s removed an internal link for the task %s' => '%s удалил внутреннюю ÑÑылку Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ñ‡Ð¸ %s', - 'Automatically set the due date on task creation' => 'ÐвтоматичеÑÐºÐ°Ñ ÑƒÑтановка Ñрока задачи при Ñоздании', + 'Automatically set the due date on task creation' => 'ÐвтоматичеÑки уÑтанавливать дату Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ð·Ð°Ð´Ð°Ñ‡Ð¸ при её Ñоздании', 'Move the task to another column when closed' => 'ПеремеÑтить задачу в другую колонку при закрытии', 'Move the task to another column when not moved during a given period' => 'ПеремеÑтить задачу в другую колонку еÑли она не был перемещен в указанный период', 'Dashboard for %s' => 'Панель ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð´Ð»Ñ %s', @@ -1169,8 +1134,6 @@ return array( 'Subtasks overview for %s' => 'Обзор подзадач Ð´Ð»Ñ %s', 'Projects overview for %s' => 'Обзор проектов Ð´Ð»Ñ %s', 'Activity stream for %s' => 'Лента активноÑти Ð´Ð»Ñ %s', - 'Calendar for %s' => 'Календарь Ð´Ð»Ñ %s', - 'Notifications for %s' => 'Ð£Ð²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð´Ð»Ñ %s', 'Assign a color when the task is moved to a specific swimlane' => 'Ðазначить цвет, когда задача будет перемещена в указанную дорожку', 'Assign a priority when the task is moved to a specific swimlane' => 'Ðазначить приоритет, когда задача будет перемещена в указанную дорожку', 'User unlocked successfully.' => 'Пользователь уÑпешно разблокирован.', @@ -1241,7 +1204,6 @@ return array( 'Preview' => 'ПредпроÑмотр', 'Write' => 'Редактирование', 'Write your text in Markdown' => 'Добавьте Ваше опиÑание в формате Markdown', - 'New External Task: %s' => 'ÐÐ¾Ð²Ð°Ñ Ð²Ð½ÐµÑˆÐ½ÑÑ Ð·Ð°Ð´Ð°Ñ‡Ð°: %s', 'No personal API access token registered.' => 'ПерÑональные токены доÑтупа к API не Ñозданы.', 'Your personal API access token is "%s"' => 'Ваш перÑональный токен доÑтупа к API: "%s"', 'Remove your token' => 'Удалить токен', @@ -1316,7 +1278,6 @@ return array( 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Ð’Ñ‹ можете импортировать предварительно Ñозданный файл выгрузки базы данных SQLite (формат Gzip).', 'Database file' => 'Файл выгрузки БД', 'Upload' => 'Импорт', - 'Remove this user from group' => 'Удалить Ñтого Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¸Ð· группы', 'Your project must have at least one active swimlane.' => 'Ваш проект должен иметь Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ одну активную дорожку.', 'Project: %s' => 'Проект: %s', 'Automatic action not found: "%s"' => 'ÐвтоматичеÑкое дейÑтвие не найдено: "%s"', @@ -1331,7 +1292,78 @@ return array( '%d tasks' => '%d задач', '%d task' => '%d задачу', 'Task ID' => 'ID задачи', - // 'Assign automatically a color when due date is expired' => '', - // 'Total score in this column across all swimlanes' => '', - // 'HRK - Kuna' => '', + 'Assign automatically a color when due date is expired' => 'ÐвтоматичеÑки назначать цвет поÑле иÑÑ‚ÐµÑ‡ÐµÐ½Ð¸Ñ Ñрока задачи', + 'Total score in this column across all swimlanes' => 'ÐžÐ±Ñ‰Ð°Ñ Ð¾Ñ†ÐµÐ½ÐºÐ° в Ñтой колонке Ñреди вÑех дорожек', + 'HRK - Kuna' => 'HRK - ХорватÑÐºÐ°Ñ ÐºÑƒÐ½Ð°', + 'ARS - Argentine Peso' => 'ARS - ÐргентинÑкий пеÑо', + 'COP - Colombian Peso' => 'COP - КолумбийÑкий пеÑо', + '%d groups' => '%d групп', + '%d group' => '%d группа', + 'Group ID' => 'ID группы', + 'External ID' => 'Внешний ID', + '%d users' => '%d пользователей', + '%d user' => '%d пользователь', + 'Hide subtasks' => 'Скрыть подзадачи', + 'Show subtasks' => 'Показать подзадачи', + 'Authentication Parameters' => 'Параметры аутентификации', + 'API Access' => 'ДоÑтуп к API', + 'No users found.' => 'Пользователи не найдены.', + 'User ID' => 'ID пользователÑ', + 'Notifications are activated' => 'Ð£Ð²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ñ‹', + 'Notifications are disabled' => 'Ð£Ð²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð²Ñ‹ÐºÐ»ÑŽÑ‡ÐµÐ½Ñ‹', + 'User disabled' => 'Пользователь выключен', + '%d notifications' => '%d уведомлений', + '%d notification' => '%d уведомление', + 'There is no external integration installed.' => 'Внешние интеграции не уÑтановлены.', + 'You are not allowed to update tasks assigned to someone else.' => 'Ð’Ñ‹ не можете обновлÑть задачи назначенные другому пользователю.', + 'You are not allowed to change the assignee.' => 'Ð’Ñ‹ не можете изменить назначенного на Ñту задачу.', + 'Task suppression is not permitted' => 'Удаление задач не разрешено', + 'Changing assignee is not permitted' => 'Изменение назначенного не разрешено', + 'Update only assigned tasks is permitted' => 'Разрешено обновление только назначенных задач', + 'Only for tasks assigned to the current user' => 'Только Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ñ‡ назначенных текущему пользователю', + 'My projects' => 'Мои проекты', + 'Your are not member of any project.' => 'Ð’Ñ‹ не ÑвлÑетеÑÑŒ учаÑтником какого-либо проекта.', + 'My subtasks' => 'Мои подзадачи', + '%d subtasks' => '%d подзадач', + '%d subtask' => '%d подзадача', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Текущий пользователь может перемещать назначенные ему задачи только между Ñтими колонками', + '[DUPLICATE]' => '[КОПИЯ]', + 'DKK - Danish Krona' => 'DKK - ДатÑÐºÐ°Ñ ÐºÑ€Ð¾Ð½Ð°', + 'Remove user from group' => 'Удалить Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¸Ð· группы', + 'Assign the task to its creator' => 'Ðазначать задачу её Ñоздателю', + 'This task was sent by email to "%s" with subject "%s".' => 'Ðта задача была отправлена по e-mail на "%s" Ñ Ñ‚ÐµÐ¼Ð¾Ð¹ "%s".', + 'Predefined Email Subjects' => 'ПредуÑтановленные темы Ð´Ð»Ñ e-mail', + 'Write one subject by line.' => 'ЗапиÑываютÑÑ Ð¿Ð¾ одной теме на Ñтроку.', + 'Create another link' => 'Создать другую ÑÑылку', + 'BRL - Brazilian Real' => 'BRL - бразильÑкий реал', + 'Add a new Kanboard task' => 'Добавить новую задачу на Kanboard', + 'Subtask not started' => 'Подзадача не начата', + 'Subtask currently in progress' => 'Подзадача в процеÑÑе выполнениÑ', + 'Subtask completed' => 'Подзадача завершена', + 'Subtask added successfully.' => 'Подзадача уÑпешно добавлена.', + '%d subtasks added successfully.' => '%d подзадач(а) уÑпешно добавлено.', + 'Enter one subtask by line.' => 'ЗапиÑывайте по одной подзадаче на Ñтроку.', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/sr_Latn_RS/translations.php b/app/Locale/sr_Latn_RS/translations.php index 9c3f51ae..6c288acb 100644 --- a/app/Locale/sr_Latn_RS/translations.php +++ b/app/Locale/sr_Latn_RS/translations.php @@ -39,7 +39,6 @@ return array( 'Administrator' => 'Administrator', 'Sign in' => 'Odjava', 'Users' => 'Korisnik', - 'No user' => 'Ne', 'Forbidden' => 'Zabranjeno', 'Access Forbidden' => 'Zabranjen prostup', 'Edit user' => 'Izmeni korisnika', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'Pod-zadatak zaktualizowane pomyÅ›lnie.', 'Unable to update your sub-task.' => 'Nie można zaktalizować tego pod-zadania.', 'Unable to create your sub-task.' => 'Nie można utworzyć tego pod-zadania.', - 'Sub-task added successfully.' => 'Pod-zadatak utworzone pomyÅ›lnie', 'Maximum size: ' => 'Maksimalna veliÄina: ', 'Display another project' => 'Prikaži drugi projekat', 'Created by %s' => 'Kreirao %s', @@ -396,22 +394,17 @@ return array( 'Activity stream' => 'Spisak aktinosti', 'Dashboard' => 'Panel', 'Confirmation' => 'Potvrda', - 'Allow everybody to access to this project' => 'Dozvoli svima pristup projektu', - 'Everybody have access to this project.' => 'Svima je dozvoljen pristup.', // 'Webhooks' => '', // 'API' => '', // 'Create a comment from an external provider' => '', 'Project management' => 'UreÄ‘ivanje projekata', - 'My projects' => 'Moji projekti', 'Columns' => 'Kolone', 'Task' => 'Zadaci', - 'Your are not member of any project.' => 'Nisi Älan ni jednog projekta', 'Percentage' => 'Procenat', 'Number of tasks' => 'Broj zadataka', 'Task distribution' => 'Podela zadataka', 'Analytics' => 'Analiza', 'Subtask' => 'Pod-zadatak', - 'My subtasks' => 'Moji pod-zadaci', 'User repartition' => 'Zaduženja korisnika', 'Clone this project' => 'Kopiraj projekat', 'Column removed successfully.' => 'Kolumna usuniÄ™ta pomyslnie.', @@ -459,7 +452,6 @@ return array( 'Language:' => 'Jezik:', 'Timezone:' => 'Vremenska zona:', 'All columns' => 'Sve kolone', - 'Calendar' => 'Kalendar', 'Next' => 'Sledeći', // '#%d' => '', 'All swimlanes' => 'Svi razdelniki', @@ -557,7 +549,6 @@ return array( // 'Unable to add this currency rate.' => '', // 'Webhook URL' => '', // '%s removed the assignee of the task %s' => '', - // 'Enable Gravatar images' => '', // 'Information' => '', // 'Check two factor authentication code' => '', // 'The two factor authentication code is not valid.' => '', @@ -611,14 +602,7 @@ return array( // 'When task is moved from first column' => '', // 'When task is moved to last column' => '', // 'Year(s)' => '', - // 'Calendar settings' => '', - // 'Project calendar view' => '', // 'Project settings' => '', - // 'Show subtasks based on the time tracking' => '', - // 'Show tasks based on the creation date' => '', - // 'Show tasks based on the start date' => '', - // 'Subtasks time tracking' => '', - // 'User calendar view' => '', // 'Automatically update the start date' => '', // 'iCal feed' => '', // 'Preferences' => '', @@ -637,7 +621,6 @@ return array( // 'Notification' => '', // '%s moved the task #%d to the first swimlane' => '', // 'Swimlane' => '', - // 'Gravatar' => '', // '%s moved the task %s to the first swimlane' => '', // '%s moved the task %s to the swimlane "%s"' => '', // 'This report contains all subtasks information for the given date range.' => '', @@ -674,7 +657,6 @@ return array( // 'Stop timer' => '', // 'Start timer' => '', // 'My activity stream' => '', - // 'My calendar' => '', // 'Search tasks' => '', // 'Reset filters' => '', // 'My tasks due tomorrow' => '', @@ -688,7 +670,6 @@ return array( // 'Overview' => '', // 'Board/Calendar/List view' => '', // 'Switch to the board view' => '', - // 'Switch to the calendar view' => '', // 'Switch to the list view' => '', // 'Go to the search/filter box' => '', // 'There is no activity yet.' => '', @@ -743,15 +724,8 @@ return array( // 'License:' => '', // 'License' => '', // 'Enter the text below' => '', - // 'Sort by position' => '', - // 'Sort by date' => '', - // 'Add task' => '', // 'Start date:' => '', // 'Due date:' => '', - // 'There is no start date or due date for this task.' => '', - // 'Moving or resizing a task will change the start and due date of the task.' => '', - // 'There is no task in your project.' => '', - // 'Gantt chart' => '', // 'People who are project managers' => '', // 'People who are project members' => '', // 'NOK - Norwegian Krone' => '', @@ -763,22 +737,15 @@ return array( // 'Members' => '', // 'Shared project' => '', // 'Project managers' => '', - // 'Gantt chart for all projects' => '', // 'Projects list' => '', - // 'Gantt chart for this project' => '', - // 'Project board' => '', // 'End date:' => '', - // 'There is no start date or end date for this project.' => '', - // 'Projects Gantt chart' => '', // 'Change task color when using a specific task link' => '', // 'Task link creation or modification' => '', // 'Milestone' => '', // 'Documentation: %s' => '', - // 'Switch to the Gantt chart view' => '', // 'Reset the search/filter box' => '', // 'Documentation' => '', // 'Table of contents' => '', - // 'Gantt' => '', // 'Author' => '', // 'Version' => '', // 'Plugins' => '', @@ -889,7 +856,6 @@ return array( // 'There is no user available.' => '', // 'Do you really want to remove the user "%s" from the group "%s"?' => '', // 'There is no group.' => '', - // 'External Id' => '', // 'Add group member' => '', // 'Do you really want to remove this group: "%s"?' => '', // 'There is no user in this group.' => '', @@ -955,7 +921,6 @@ return array( // 'Default priority' => '', // 'Lowest priority' => '', // 'Highest priority' => '', - // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Close a task when there is no activity' => '', // 'Duration in days' => '', // 'Send email when there is no activity on a task' => '', @@ -1169,8 +1134,6 @@ return array( // 'Subtasks overview for %s' => '', // 'Projects overview for %s' => '', // 'Activity stream for %s' => '', - // 'Calendar for %s' => '', - // 'Notifications for %s' => '', // 'Assign a color when the task is moved to a specific swimlane' => '', // 'Assign a priority when the task is moved to a specific swimlane' => '', // 'User unlocked successfully.' => '', @@ -1241,7 +1204,6 @@ return array( // 'Preview' => '', // 'Write' => '', // 'Write your text in Markdown' => '', - // 'New External Task: %s' => '', // 'No personal API access token registered.' => '', // 'Your personal API access token is "%s"' => '', // 'Remove your token' => '', @@ -1316,7 +1278,6 @@ return array( // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', // 'Database file' => '', // 'Upload' => '', - // 'Remove this user from group' => '', // 'Your project must have at least one active swimlane.' => '', // 'Project: %s' => '', // 'Automatic action not found: "%s"' => '', @@ -1334,4 +1295,75 @@ return array( // 'Assign automatically a color when due date is expired' => '', // 'Total score in this column across all swimlanes' => '', // 'HRK - Kuna' => '', + // 'ARS - Argentine Peso' => '', + // 'COP - Colombian Peso' => '', + // '%d groups' => '', + // '%d group' => '', + // 'Group ID' => '', + // 'External ID' => '', + // '%d users' => '', + // '%d user' => '', + // 'Hide subtasks' => '', + // 'Show subtasks' => '', + // 'Authentication Parameters' => '', + // 'API Access' => '', + // 'No users found.' => '', + // 'User ID' => '', + // 'Notifications are activated' => '', + // 'Notifications are disabled' => '', + // 'User disabled' => '', + // '%d notifications' => '', + // '%d notification' => '', + // 'There is no external integration installed.' => '', + // 'You are not allowed to update tasks assigned to someone else.' => '', + // 'You are not allowed to change the assignee.' => '', + // 'Task suppression is not permitted' => '', + // 'Changing assignee is not permitted' => '', + // 'Update only assigned tasks is permitted' => '', + // 'Only for tasks assigned to the current user' => '', + // 'My projects' => '', + // 'Your are not member of any project.' => '', + // 'My subtasks' => '', + // '%d subtasks' => '', + // '%d subtask' => '', + // 'Only moving task between those columns is permitted for tasks assigned to the current user' => '', + // '[DUPLICATE]' => '', + // 'DKK - Danish Krona' => '', + // 'Remove user from group' => '', + // 'Assign the task to its creator' => '', + // 'This task was sent by email to "%s" with subject "%s".' => '', + // 'Predefined Email Subjects' => '', + // 'Write one subject by line.' => '', + // 'Create another link' => '', + // 'BRL - Brazilian Real' => '', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/sv_SE/translations.php b/app/Locale/sv_SE/translations.php index 420c57d1..97b4142d 100644 --- a/app/Locale/sv_SE/translations.php +++ b/app/Locale/sv_SE/translations.php @@ -39,7 +39,6 @@ return array( 'Administrator' => 'Administratör', 'Sign in' => 'Logga in', 'Users' => 'Användare', - 'No user' => 'Ingen användare', 'Forbidden' => 'Ej tillÃ¥ten', 'Access Forbidden' => 'Ej tillÃ¥ten', 'Edit user' => 'Ändra användare', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'Deluppgiften har uppdaterats.', 'Unable to update your sub-task.' => 'Kunde inte uppdatera din deluppgift.', 'Unable to create your sub-task.' => 'Kunde inte skapa din deluppgift.', - 'Sub-task added successfully.' => 'Deluppgiften har lagts till.', 'Maximum size: ' => 'Maxstorlek: ', 'Display another project' => 'Visa ett annat projekt', 'Created by %s' => 'Skapad av %s', @@ -396,22 +394,17 @@ return array( 'Activity stream' => 'Aktivitetsström', 'Dashboard' => 'Instrumentpanel', 'Confirmation' => 'Bekräftelse', - 'Allow everybody to access to this project' => 'Ge alla tillgÃ¥ng till projektet', - 'Everybody have access to this project.' => 'Alla har tillgÃ¥ng till projektet', 'Webhooks' => 'Webhooks', 'API' => 'API', 'Create a comment from an external provider' => 'Skapa en kommentar frÃ¥n en extern leverantör', 'Project management' => 'Projekthantering', - 'My projects' => 'Mina projekt', 'Columns' => 'Kolumner', 'Task' => 'Uppgift', - 'Your are not member of any project.' => 'Du är inte medlem i nÃ¥got projekt', 'Percentage' => 'Procent', 'Number of tasks' => 'Antal uppgifter', 'Task distribution' => 'Uppgiftsfördelning', 'Analytics' => 'Analyser', 'Subtask' => 'Deluppgift', - 'My subtasks' => 'Mina deluppgifter', 'User repartition' => 'Användardeltagande', 'Clone this project' => 'Klona projektet', 'Column removed successfully.' => 'Kolumnen togs bort', @@ -459,7 +452,6 @@ return array( 'Language:' => 'SprÃ¥k', 'Timezone:' => 'Tidszon', 'All columns' => 'Alla kolumner', - 'Calendar' => 'Kalender', 'Next' => 'Nästa', '#%d' => '#%d', 'All swimlanes' => 'Alla swimlanes', @@ -557,7 +549,6 @@ return array( 'Unable to add this currency rate.' => 'Kunde inte lägga till valutakursen.', 'Webhook URL' => 'Webhook URL', '%s removed the assignee of the task %s' => '%s ta bort tilldelningen av uppgiften %s', - 'Enable Gravatar images' => 'Aktivera Gravatar bilder', 'Information' => 'Information', 'Check two factor authentication code' => 'Kolla tvÃ¥faktorsverifieringskod', 'The two factor authentication code is not valid.' => 'TvÃ¥faktorsverifieringskoden är inte giltig.', @@ -611,14 +602,7 @@ return array( 'When task is moved from first column' => 'När uppgiften flyttas frÃ¥n första kolumnen', 'When task is moved to last column' => 'När uppgiften flyttas till sista kolumnen', 'Year(s)' => 'Ã…r', - 'Calendar settings' => 'Inställningar för kalendern', - 'Project calendar view' => 'Projektkalendervy', 'Project settings' => 'Projektinställningar', - 'Show subtasks based on the time tracking' => 'Visa deluppgifter baserade pÃ¥ tidsspÃ¥rning', - 'Show tasks based on the creation date' => 'Visa uppgifter baserade pÃ¥ skapat datum', - 'Show tasks based on the start date' => 'Visa uppgifter baserade pÃ¥ startdatum', - 'Subtasks time tracking' => 'Deluppgifter tidsspÃ¥rning', - 'User calendar view' => 'Användarkalendervy', 'Automatically update the start date' => 'Automatisk uppdatering av startdatum', 'iCal feed' => 'iCal flöde', 'Preferences' => 'Preferenser', @@ -637,7 +621,6 @@ return array( 'Notification' => 'Notis', '%s moved the task #%d to the first swimlane' => '%s flyttade uppgiften #%d till första swimlane', 'Swimlane' => 'Swimlane', - 'Gravatar' => 'Gravatar', '%s moved the task %s to the first swimlane' => '%s flyttade uppgiften %s till första swimlane', '%s moved the task %s to the swimlane "%s"' => '%s flyttade uppgiften %s till swimlane "%s"', 'This report contains all subtasks information for the given date range.' => 'Denna rapport innehÃ¥ller all deluppgiftsinformation för det givna datumintervallet.', @@ -674,7 +657,6 @@ return array( 'Stop timer' => 'Stoppa timer', 'Start timer' => 'Starta timer', 'My activity stream' => 'Min aktivitetsström', - 'My calendar' => 'Min kalender', 'Search tasks' => 'Sök uppgifter', 'Reset filters' => 'Ã…terställ filter', 'My tasks due tomorrow' => 'Mina uppgifter förfaller imorgon', @@ -688,7 +670,6 @@ return array( 'Overview' => 'Översikts', 'Board/Calendar/List view' => 'Tavla/Kalender/Listvy', 'Switch to the board view' => 'Växla till tavelvy', - 'Switch to the calendar view' => 'Växla till kalendervy', 'Switch to the list view' => 'Växla till listvy', 'Go to the search/filter box' => 'GÃ¥ till sök/filter box', 'There is no activity yet.' => 'Det finns ingen aktivitet ännu.', @@ -743,15 +724,8 @@ return array( 'License:' => 'Licens:', 'License' => 'Licens', 'Enter the text below' => 'Fyll i texten nedan', - 'Sort by position' => 'Sortera efter position', - 'Sort by date' => 'Sortera efter datum', - 'Add task' => 'Lägg till uppgift', 'Start date:' => 'Startdatum:', 'Due date:' => 'Slutdatum:', - 'There is no start date or due date for this task.' => 'Det finns inget startdatum eller slutdatum för uppgiften.', - 'Moving or resizing a task will change the start and due date of the task.' => 'Flytt eller storleksändring av uppgiften ändrar start och slutdatum för uppgiften', - 'There is no task in your project.' => 'Det finns ingen uppgift i ditt projekt.', - 'Gantt chart' => 'Gantt-schema', // 'People who are project managers' => '', // 'People who are project members' => '', // 'NOK - Norwegian Krone' => '', @@ -763,22 +737,15 @@ return array( // 'Members' => '', // 'Shared project' => '', // 'Project managers' => '', - // 'Gantt chart for all projects' => '', // 'Projects list' => '', - // 'Gantt chart for this project' => '', - // 'Project board' => '', // 'End date:' => '', - // 'There is no start date or end date for this project.' => '', - // 'Projects Gantt chart' => '', // 'Change task color when using a specific task link' => '', // 'Task link creation or modification' => '', // 'Milestone' => '', // 'Documentation: %s' => '', - // 'Switch to the Gantt chart view' => '', // 'Reset the search/filter box' => '', // 'Documentation' => '', // 'Table of contents' => '', - // 'Gantt' => '', // 'Author' => '', // 'Version' => '', // 'Plugins' => '', @@ -889,7 +856,6 @@ return array( // 'There is no user available.' => '', // 'Do you really want to remove the user "%s" from the group "%s"?' => '', // 'There is no group.' => '', - // 'External Id' => '', // 'Add group member' => '', // 'Do you really want to remove this group: "%s"?' => '', // 'There is no user in this group.' => '', @@ -955,7 +921,6 @@ return array( // 'Default priority' => '', // 'Lowest priority' => '', // 'Highest priority' => '', - // 'If you put zero to the low and high priority, this feature will be disabled.' => '', // 'Close a task when there is no activity' => '', // 'Duration in days' => '', // 'Send email when there is no activity on a task' => '', @@ -1169,8 +1134,6 @@ return array( // 'Subtasks overview for %s' => '', // 'Projects overview for %s' => '', // 'Activity stream for %s' => '', - // 'Calendar for %s' => '', - // 'Notifications for %s' => '', // 'Assign a color when the task is moved to a specific swimlane' => '', // 'Assign a priority when the task is moved to a specific swimlane' => '', // 'User unlocked successfully.' => '', @@ -1241,7 +1204,6 @@ return array( // 'Preview' => '', // 'Write' => '', // 'Write your text in Markdown' => '', - // 'New External Task: %s' => '', // 'No personal API access token registered.' => '', // 'Your personal API access token is "%s"' => '', // 'Remove your token' => '', @@ -1316,7 +1278,6 @@ return array( // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', // 'Database file' => '', // 'Upload' => '', - // 'Remove this user from group' => '', // 'Your project must have at least one active swimlane.' => '', // 'Project: %s' => '', // 'Automatic action not found: "%s"' => '', @@ -1334,4 +1295,75 @@ return array( // 'Assign automatically a color when due date is expired' => '', // 'Total score in this column across all swimlanes' => '', // 'HRK - Kuna' => '', + // 'ARS - Argentine Peso' => '', + // 'COP - Colombian Peso' => '', + // '%d groups' => '', + // '%d group' => '', + // 'Group ID' => '', + // 'External ID' => '', + // '%d users' => '', + // '%d user' => '', + // 'Hide subtasks' => '', + // 'Show subtasks' => '', + // 'Authentication Parameters' => '', + // 'API Access' => '', + // 'No users found.' => '', + // 'User ID' => '', + // 'Notifications are activated' => '', + // 'Notifications are disabled' => '', + // 'User disabled' => '', + // '%d notifications' => '', + // '%d notification' => '', + // 'There is no external integration installed.' => '', + // 'You are not allowed to update tasks assigned to someone else.' => '', + // 'You are not allowed to change the assignee.' => '', + // 'Task suppression is not permitted' => '', + // 'Changing assignee is not permitted' => '', + // 'Update only assigned tasks is permitted' => '', + // 'Only for tasks assigned to the current user' => '', + // 'My projects' => '', + // 'Your are not member of any project.' => '', + // 'My subtasks' => '', + // '%d subtasks' => '', + // '%d subtask' => '', + // 'Only moving task between those columns is permitted for tasks assigned to the current user' => '', + // '[DUPLICATE]' => '', + // 'DKK - Danish Krona' => '', + // 'Remove user from group' => '', + // 'Assign the task to its creator' => '', + // 'This task was sent by email to "%s" with subject "%s".' => '', + // 'Predefined Email Subjects' => '', + // 'Write one subject by line.' => '', + // 'Create another link' => '', + // 'BRL - Brazilian Real' => '', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/th_TH/translations.php b/app/Locale/th_TH/translations.php index fe14588e..fb2e0cbe 100644 --- a/app/Locale/th_TH/translations.php +++ b/app/Locale/th_TH/translations.php @@ -39,7 +39,6 @@ return array( 'Administrator' => 'ผู้ดูà¹à¸¥à¸£à¸°à¸šà¸š', 'Sign in' => 'เข้าสู่ระบบ', 'Users' => 'ผู้ใช้', - 'No user' => 'ไม่มีผู้ใช้', 'Forbidden' => 'ไม่à¸à¸™à¸¸à¸à¸²à¸•', 'Access Forbidden' => 'ไม่à¸à¸™à¸¸à¸à¸²à¸•ให้เข้า', 'Edit user' => 'à¹à¸à¹‰à¹„ขผู้ใช้', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'ปรับปรุงงานย่à¸à¸¢à¹ˆà¹ˆà¹€à¸£à¸µà¸¢à¸šà¸£à¹‰à¸à¸¢à¹à¸¥à¹‰à¸§', 'Unable to update your sub-task.' => 'ไม่สามารถปรับปรุงานย่à¸à¸¢à¹„ด้', 'Unable to create your sub-task.' => 'ไม่สามารถสร้างงานย่à¸à¸¢à¹„ด้', - 'Sub-task added successfully.' => 'เพิ่มงานย่à¸à¸¢à¹€à¸£à¸µà¸¢à¸šà¸£à¹‰à¸à¸¢à¹à¸¥à¹‰à¸§', 'Maximum size: ' => 'ขนาดไฟล์สูงสุด:', 'Display another project' => 'à¹à¸ªà¸”งโปรเจคà¸à¸·à¹ˆà¸™', 'Created by %s' => 'สร้างโดย %s', @@ -396,22 +394,17 @@ return array( 'Activity stream' => 'à¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¸—ี่เà¸à¸´à¸”ขึ้น', 'Dashboard' => 'à¹à¸”ชบà¸à¸£à¹Œà¸”', 'Confirmation' => 'ยืนยันรหัสผ่าน', - 'Allow everybody to access to this project' => 'à¸à¸™à¸¸à¸à¸²à¸•ให้ทุà¸à¸„นเข้าถึงโปรเจคนี้', - 'Everybody have access to this project.' => 'ทุà¸à¸„นสามารถเข้าถึงโปรเจคนี้', // 'Webhooks' => '', // 'API' => '', 'Create a comment from an external provider' => 'สร้างความคิดเห็นจาà¸à¸šà¸£à¸´à¸à¸²à¸£à¸ ายนà¸à¸', 'Project management' => 'à¸à¸²à¸£à¸ˆà¸±à¸”à¸à¸²à¸£à¹‚ปรเจค', - 'My projects' => 'โปรเจคขà¸à¸‡à¸‰à¸±à¸™', 'Columns' => 'คà¸à¸¥à¸±à¸¡à¸™à¹Œ', 'Task' => 'งาน', - 'Your are not member of any project.' => 'คุณไม่ได้เป็นสมาชิà¸à¸‚à¸à¸‡à¹‚ปรเจคใดๆ', 'Percentage' => 'เปà¸à¸£à¹Œà¹€à¸‹à¹‡à¸™à¸•์', 'Number of tasks' => 'จำนวนงาน', 'Task distribution' => 'à¸à¸²à¸£à¸à¸£à¸°à¸ˆà¸²à¸¢à¸‡à¸²à¸™', 'Analytics' => 'à¸à¸²à¸£à¸§à¸´à¹€à¸„ราะห์', 'Subtask' => 'งานย่à¸à¸¢', - 'My subtasks' => 'งานย่à¸à¸¢à¸‚à¸à¸‡à¸‰à¸±à¸™', 'User repartition' => 'à¸à¸²à¸£à¹à¸šà¹ˆà¸‡à¸‡à¸²à¸™à¸‚à¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰', 'Clone this project' => 'สำเนาโปรเจคนี้', 'Column removed successfully.' => 'ลบคà¸à¸¥à¸±à¸¡à¸™à¹Œà¸ªà¸³à¹€à¸£à¹‡à¸ˆ', @@ -459,7 +452,6 @@ return array( 'Language:' => 'ภาษา:', 'Timezone:' => 'เขตเวลา:', 'All columns' => 'คà¸à¸¥à¸±à¸¡à¸™à¹Œà¸—ั้งหมด', - 'Calendar' => 'ปà¸à¸´à¸—ิน', 'Next' => 'ต่à¸à¹„ป', '#%d' => '#%d', 'All swimlanes' => 'สวิมเลนทั้งหมด', @@ -557,7 +549,6 @@ return array( 'Unable to add this currency rate.' => 'ไม่สามารถเพิ่มค่าเงินนี้', // 'Webhook URL' => '', '%s removed the assignee of the task %s' => '%s เà¸à¸²à¸œà¸¹à¹‰à¸£à¸±à¸šà¸œà¸´à¸”ชà¸à¸šà¸à¸à¸à¸ˆà¸²à¸à¸‡à¸²à¸™ %s', - 'Enable Gravatar images' => 'สามารถใช้งานภาพ Gravatar', 'Information' => 'ข้à¸à¸¡à¸¹à¸¥à¸ªà¸²à¸£à¸ªà¸™à¹€à¸—ศ', // 'Check two factor authentication code' => '', // 'The two factor authentication code is not valid.' => '', @@ -611,14 +602,7 @@ return array( 'When task is moved from first column' => 'เมื่à¸à¸‡à¸²à¸™à¸–ูà¸à¸¢à¹‰à¸²à¸¢à¸ˆà¸²à¸à¸„à¸à¸¥à¸±à¸¡à¸™à¹Œà¹à¸£à¸', 'When task is moved to last column' => 'เมื่à¸à¸‡à¸²à¸™à¸–ูà¸à¸¢à¹‰à¸²à¸¢à¹„ปคà¸à¸¥à¸±à¸¡à¸™à¹Œà¸ªà¸¸à¸”ท้าย', 'Year(s)' => 'ปี', - 'Calendar settings' => 'ตั้งค่าปà¸à¸´à¸—ิน', - 'Project calendar view' => 'มุมมà¸à¸‡à¸›à¸à¸´à¸—ินขà¸à¸‡à¹‚ปรเจค', 'Project settings' => 'ตั้งค่าโปรเจค', - 'Show subtasks based on the time tracking' => 'à¹à¸ªà¸”งงานย่à¸à¸¢à¹ƒà¸™à¸à¸²à¸£à¸•ิดตามเวลา', - 'Show tasks based on the creation date' => 'à¹à¸ªà¸”งงานจาà¸à¸§à¸±à¸™à¸—ี่สร้าง', - 'Show tasks based on the start date' => 'à¹à¸ªà¸”งงานจาà¸à¸§à¸±à¸™à¸—ี่เริ่ม', - 'Subtasks time tracking' => 'à¸à¸²à¸£à¸•ิดตามเวลางานย่à¸à¸¢', - 'User calendar view' => 'มุมมà¸à¸‡à¸›à¸à¸´à¸—ินขà¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰', 'Automatically update the start date' => 'ปรับปรุงวันที่เริ่มà¸à¸±à¸•โนมมัติ', // 'iCal feed' => '', // 'Preferences' => '', @@ -637,7 +621,6 @@ return array( 'Notification' => 'à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™', '%s moved the task #%d to the first swimlane' => '%s ย้ายงาน #%d ไปสวินเลนà¹à¸£à¸', 'Swimlane' => 'สวิมเลน', - 'Gravatar' => 'รูปà¹à¸—นตัว', '%s moved the task %s to the first swimlane' => '%s ย้ายงาน %s ไปสวินเลนà¹à¸£à¸', '%s moved the task %s to the swimlane "%s"' => '%s ย้ายงาน %s ไปสวินเลนไปสวินเลน "%s"', 'This report contains all subtasks information for the given date range.' => 'รายงานนี้มีข้à¸à¸¡à¸¹à¸¥à¸‡à¸²à¸™à¸¢à¹ˆà¸à¸¢à¸—ั้งหมดในช่วงวันที่à¸à¸³à¸«à¸™à¸”', @@ -674,7 +657,6 @@ return array( 'Stop timer' => 'หยุดจับเวลา', 'Start timer' => 'เริ่มจับเวลา', 'My activity stream' => 'à¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¸—ี่เà¸à¸´à¸”ขึ้นขà¸à¸‡à¸‰à¸±à¸™', - 'My calendar' => 'ปฎิทินขà¸à¸‡à¸‰à¸±à¸™', 'Search tasks' => 'ค้นหางาน', 'Reset filters' => 'ล้างตัวà¸à¸£à¸à¸‡', 'My tasks due tomorrow' => 'งานถึงà¸à¸³à¸«à¸™à¸”ขà¸à¸‡à¸‰à¸±à¸™à¸§à¸±à¸™à¸žà¸£à¸¸à¹ˆà¸‡à¸™à¸µà¹‰', @@ -688,7 +670,6 @@ return array( 'Overview' => 'ภาพรวม', 'Board/Calendar/List view' => 'มุมมà¸à¸‡ บà¸à¸£à¹Œà¸”/ปฎิทิน/ลิสต์', 'Switch to the board view' => 'เปลี่ยนเป็นมุมมà¸à¸‡à¸šà¸à¸£à¹Œà¸”', - 'Switch to the calendar view' => 'เปลี่ยนเป็นมุมมà¸à¸‡à¸›à¸Žà¸´à¸—ิน', 'Switch to the list view' => 'เปลี่ยนเป็นมุมมà¸à¸‡à¸¥à¸´à¸ªà¸•์', 'Go to the search/filter box' => 'ไปที่à¸à¸¥à¹ˆà¸à¸‡à¸„้นหา/ตัวà¸à¸£à¸à¸‡', 'There is no activity yet.' => 'ตà¸à¸™à¸™à¸µà¹‰à¹„ม่มีà¸à¸´à¸ˆà¸à¸£à¸£à¸¡', @@ -743,15 +724,8 @@ return array( 'License:' => 'สัà¸à¸à¸²à¸à¸™à¸¸à¸à¸²à¸•:', 'License' => 'สัà¸à¸à¸²à¸à¸™à¸¸à¸à¸²à¸•', 'Enter the text below' => 'พิมพ์ข้à¸à¸„วามด้านล่าง', - 'Sort by position' => 'เรียงตามตำà¹à¸«à¸™à¹ˆà¸‡', - 'Sort by date' => 'เรียงตามวัน', - 'Add task' => 'เพิ่มงาน', 'Start date:' => 'วันที่เริ่ม:', 'Due date:' => 'วันครบà¸à¸³à¸«à¸™à¸”:', - 'There is no start date or due date for this task.' => 'งานนี้ไม่มีวันที่เริ่มหรืà¸à¸§à¸±à¸™à¸„รบà¸à¸³à¸«à¸™à¸”', - 'Moving or resizing a task will change the start and due date of the task.' => 'à¸à¸²à¸£à¸¢à¹‰à¸²à¸¢à¸«à¸£à¸·à¸à¸›à¸£à¸±à¸šà¸‚นาดงานจะมีà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¸—ี่วันเริ่มต้นà¹à¸¥à¸°à¸§à¸±à¸™à¸—ี่ครบà¸à¸³à¸«à¸™à¸”ขà¸à¸‡à¸‡à¸²à¸™', - 'There is no task in your project.' => 'โปรเจคนี้ไม่มีงาน', - 'Gantt chart' => 'à¹à¸œà¸™à¸ ูมิà¹à¸à¸£à¸™à¸—์', 'People who are project managers' => 'คนที่เป็นผู้จัดà¸à¸²à¸£à¹‚ปรเจค', 'People who are project members' => 'คนที่เป็นสมาชิà¸à¹‚ปรเจค', 'NOK - Norwegian Krone' => 'NOK - โครนนà¸à¸£à¹Œà¹€à¸§à¸¢à¹Œ', @@ -763,22 +737,15 @@ return array( 'Members' => 'สมาชิà¸', 'Shared project' => 'à¹à¸Šà¸£à¹Œà¹‚ปรเจค', 'Project managers' => 'ผู้จัดà¸à¸²à¸£à¹‚ปรเจค', - 'Gantt chart for all projects' => 'à¹à¸œà¸™à¸ ูมิà¹à¸à¸£à¸™à¸—์สำหรับทุà¸à¹‚ปรเจค', 'Projects list' => 'รายà¸à¸²à¸£à¹‚ปรเจค', - 'Gantt chart for this project' => 'à¹à¸œà¸™à¸ ูมิà¹à¸à¸£à¸™à¸—์สำหรับโปรเจคนี้', - 'Project board' => 'บà¸à¸£à¹Œà¸”โปรเจค', 'End date:' => 'วันที่จบ:', - 'There is no start date or end date for this project.' => 'ไม่มีวันที่เริ่มหรืà¸à¸§à¸±à¸™à¸—ี่จบขà¸à¸‡à¹‚ปรเจคนี้', - 'Projects Gantt chart' => 'à¹à¸œà¸™à¸ ูมิà¹à¸à¸£à¸™à¹Œà¸‚à¸à¸‡à¹‚ปรเจค', 'Change task color when using a specific task link' => 'เปลี่ยนสีงานเมื่à¸à¸¡à¸µà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¹‚ยงงาน', 'Task link creation or modification' => 'à¸à¸²à¸£à¸ªà¸£à¹‰à¸²à¸‡à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¹‚ยงงานหรืà¸à¸à¸²à¸£à¸›à¸£à¸±à¸šà¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™', 'Milestone' => 'ขั้น', 'Documentation: %s' => 'เà¸à¸à¸ªà¸²à¸£: %s', - 'Switch to the Gantt chart view' => 'เปลี่ยนเป็นมุมมà¸à¸‡à¹à¸œà¸™à¸ ูมิà¹à¸à¸£à¸™à¸—์', 'Reset the search/filter box' => 'รีเซตà¸à¸¥à¹ˆà¸à¸‡à¸„้นหา/ตัวà¸à¸£à¸à¸‡', 'Documentation' => 'เà¸à¸à¸ªà¸²à¸£', 'Table of contents' => 'สารบัà¸', - 'Gantt' => 'à¹à¸à¸£à¸™à¸—์', 'Author' => 'ผู้à¹à¸•่ง', 'Version' => 'เวà¸à¸£à¹Œà¸Šà¸±à¸™', 'Plugins' => 'ปลั๊à¸à¸à¸´à¸™', @@ -889,7 +856,6 @@ return array( // 'There is no user available.' => '', 'Do you really want to remove the user "%s" from the group "%s"?' => 'คุณต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰ "%s" à¸à¸à¸à¸ˆà¸²à¸à¸à¸¥à¸¸à¹ˆà¸¡ "%s"?', 'There is no group.' => 'ไม่มีà¸à¸¥à¸¸à¹ˆà¸¡', - 'External Id' => 'ไà¸à¸”ีภายนà¸à¸', 'Add group member' => 'เพิ่มสมาชิà¸à¸à¸¥à¸¸à¹ˆà¸¡', 'Do you really want to remove this group: "%s"?' => 'คุณต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¸à¸¥à¸¸à¹ˆà¸¡à¸™à¸µà¹‰: "%s"?', 'There is no user in this group.' => 'ไม่มีผู้ใช้ในà¸à¸¥à¸¸à¹ˆà¸¡à¸™à¸µà¹‰', @@ -955,7 +921,6 @@ return array( 'Default priority' => 'ความสำคัà¸à¹€à¸£à¸´à¹ˆà¸¡à¸•้น', 'Lowest priority' => 'ความสำคัà¸à¸•่ำสุด', 'Highest priority' => 'ความสำคัà¸à¸ªà¸¹à¸‡à¸ªà¸¸à¸”', - 'If you put zero to the low and high priority, this feature will be disabled.' => 'ถ้าคุณใส่เลขศูนย์ทั้งความสำคัà¸à¸•่ำà¹à¸¥à¸°à¸„วามสำคัà¸à¸ªà¸¹à¸‡ จะเป็นà¸à¸²à¸£à¸›à¸´à¸”à¸à¸²à¸£à¸—ำงานคุณลัà¸à¸©à¸“ะนี้', 'Close a task when there is no activity' => 'ปิดงานเมื่à¸à¹„ม่มีà¸à¸´à¸ˆà¸à¸à¸£à¸¡à¹€à¸à¸´à¸”ขึ้น', 'Duration in days' => 'ระยะเวลาวันที่', 'Send email when there is no activity on a task' => 'ส่งà¸à¸µà¹€à¸¡à¸¥à¹€à¸¡à¸·à¹ˆà¸à¹„ม่มีà¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¹€à¸à¸´à¸”ขึ้นในงาน', @@ -1169,8 +1134,6 @@ return array( // 'Subtasks overview for %s' => '', // 'Projects overview for %s' => '', // 'Activity stream for %s' => '', - // 'Calendar for %s' => '', - // 'Notifications for %s' => '', // 'Assign a color when the task is moved to a specific swimlane' => '', // 'Assign a priority when the task is moved to a specific swimlane' => '', // 'User unlocked successfully.' => '', @@ -1241,7 +1204,6 @@ return array( // 'Preview' => '', // 'Write' => '', // 'Write your text in Markdown' => '', - // 'New External Task: %s' => '', // 'No personal API access token registered.' => '', // 'Your personal API access token is "%s"' => '', // 'Remove your token' => '', @@ -1316,7 +1278,6 @@ return array( // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', // 'Database file' => '', // 'Upload' => '', - // 'Remove this user from group' => '', // 'Your project must have at least one active swimlane.' => '', // 'Project: %s' => '', // 'Automatic action not found: "%s"' => '', @@ -1334,4 +1295,75 @@ return array( // 'Assign automatically a color when due date is expired' => '', // 'Total score in this column across all swimlanes' => '', // 'HRK - Kuna' => '', + // 'ARS - Argentine Peso' => '', + // 'COP - Colombian Peso' => '', + // '%d groups' => '', + // '%d group' => '', + // 'Group ID' => '', + // 'External ID' => '', + // '%d users' => '', + // '%d user' => '', + // 'Hide subtasks' => '', + // 'Show subtasks' => '', + // 'Authentication Parameters' => '', + // 'API Access' => '', + // 'No users found.' => '', + // 'User ID' => '', + // 'Notifications are activated' => '', + // 'Notifications are disabled' => '', + // 'User disabled' => '', + // '%d notifications' => '', + // '%d notification' => '', + // 'There is no external integration installed.' => '', + // 'You are not allowed to update tasks assigned to someone else.' => '', + // 'You are not allowed to change the assignee.' => '', + // 'Task suppression is not permitted' => '', + // 'Changing assignee is not permitted' => '', + // 'Update only assigned tasks is permitted' => '', + // 'Only for tasks assigned to the current user' => '', + // 'My projects' => '', + // 'Your are not member of any project.' => '', + // 'My subtasks' => '', + // '%d subtasks' => '', + // '%d subtask' => '', + // 'Only moving task between those columns is permitted for tasks assigned to the current user' => '', + // '[DUPLICATE]' => '', + // 'DKK - Danish Krona' => '', + // 'Remove user from group' => '', + // 'Assign the task to its creator' => '', + // 'This task was sent by email to "%s" with subject "%s".' => '', + // 'Predefined Email Subjects' => '', + // 'Write one subject by line.' => '', + // 'Create another link' => '', + // 'BRL - Brazilian Real' => '', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/tr_TR/translations.php b/app/Locale/tr_TR/translations.php index 4368609f..c16d48f2 100644 --- a/app/Locale/tr_TR/translations.php +++ b/app/Locale/tr_TR/translations.php @@ -1,8 +1,8 @@ <?php return array( - // 'number.decimals_separator' => '', - // 'number.thousands_separator' => '', + 'number.decimals_separator' => '.', + 'number.thousands_separator' => ',', 'None' => 'Hiçbiri', 'Edit' => 'Düzenle', 'Remove' => 'Sil', @@ -39,7 +39,6 @@ return array( 'Administrator' => 'Yönetici', 'Sign in' => 'GiriÅŸ yap', 'Users' => 'Kullanıcılar', - 'No user' => 'Kullanıcı yok', 'Forbidden' => 'Yasak', 'Access Forbidden' => 'EriÅŸim yasak', 'Edit user' => 'Kullanıcıyı düzenle', @@ -72,8 +71,8 @@ return array( 'Settings' => 'Ayarlar', 'Application settings' => 'Uygulama ayarları', 'Language' => 'Dil', - // 'Webhook token:' => '', - 'API token:' => 'API belirteci:', + 'Webhook token:' => 'Web kancası anahtarı:', + 'API token:' => 'API anahtarı:', 'Database size:' => 'Veritabanı boyutu :', 'Download the database' => 'Veritabanını indir', 'Optimize the database' => 'Veritabanını optimize et', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'Alt görev güncellendi.', 'Unable to update your sub-task.' => 'Alt görev güncellenemiyor.', 'Unable to create your sub-task.' => 'Alt görev oluÅŸturulamadı.', - 'Sub-task added successfully.' => 'Alt görev baÅŸarıyla eklendi.', 'Maximum size: ' => 'Maksimum boyutu', 'Display another project' => 'BaÅŸka bir proje göster', 'Created by %s' => '%s tarafından oluÅŸturuldu', @@ -374,7 +372,7 @@ return array( 'Database driver:' => 'Veritabanı sürücüsü:', 'Board settings' => 'Pano ayarları', 'Webhook settings' => 'Webhook ayarları', - 'Reset token' => 'Belirteci sıfırla', + 'Reset token' => 'Anahtarı sıfırla', 'API endpoint:' => 'API bitiÅŸ noktası:', 'Refresh interval for private board' => 'Özel panolar için yenileme sıklığı', 'Refresh interval for public board' => 'Dışa açık panolar için yenileme sıklığı', @@ -383,7 +381,7 @@ return array( 'Frequency in second (60 seconds by default)' => 'Saniye olarak frekans (varsayılan 60 saniye)', 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Saniye olarak frekans (Bu özelliÄŸi iptal etmek için 0, varsayılan deÄŸer 10 saniye)', 'Application URL' => 'Uygulama URL', - 'Token regenerated.' => 'Beliteç yeniden oluÅŸturuldu.', + 'Token regenerated.' => 'Anahtar yeniden oluÅŸturuldu.', 'Date format' => 'Tarih formatı', 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO formatı her zaman kabul edilir, örneÄŸin: "%s" ve "%s"', 'New private project' => 'Yeni özel proje', @@ -394,24 +392,19 @@ return array( 'There is nothing assigned to you.' => 'Size atanan hiçbir ÅŸey yok.', 'My tasks' => 'Görevlerim', 'Activity stream' => 'Güncel olay akışı', - 'Dashboard' => 'Anasayfa', + 'Dashboard' => 'Pano', 'Confirmation' => 'Onay', - 'Allow everybody to access to this project' => 'Bu projeye herkesin eriÅŸimine izin ver', - 'Everybody have access to this project.' => 'Bu projeye herkesin eriÅŸimi var.', - 'Webhooks' => 'Webhooks', + 'Webhooks' => 'Web Kancaları', 'API' => 'API', 'Create a comment from an external provider' => 'Dış saÄŸlayıcı ile bir yorum oluÅŸtur', 'Project management' => 'Proje yönetimi', - 'My projects' => 'Projelerim', 'Columns' => 'Sütunlar', 'Task' => 'Görev', - 'Your are not member of any project.' => 'Hiç bir projenin üyesi deÄŸilsiniz.', 'Percentage' => 'Yüzde', 'Number of tasks' => 'Görev sayısı', 'Task distribution' => 'Görev dağılımı', 'Analytics' => 'Analiz', 'Subtask' => 'Alt görev', - 'My subtasks' => 'Alt görevlerim', 'User repartition' => 'Kullanıcı dağılımı', 'Clone this project' => 'Projenin kopyasını oluÅŸtur', 'Column removed successfully.' => 'Sütun baÅŸarıyla kaldırıldı.', @@ -459,7 +452,6 @@ return array( 'Language:' => 'Dil:', 'Timezone:' => 'Saat dilimi:', 'All columns' => 'Tüm sütunlar', - 'Calendar' => 'Takvim', 'Next' => 'Sonraki', '#%d' => '#%d', 'All swimlanes' => 'Tüm Kulvarlar', @@ -531,14 +523,14 @@ return array( 'CHF - Swiss Francs' => 'CHF - İsviçre Frangı', 'Custom Stylesheet' => 'Özel Sitil Css', 'download' => 'indir', - // 'EUR - Euro' => '', + 'EUR - Euro' => 'EUR - Euro', 'GBP - British Pound' => 'GBP - İngiliz Paund', 'INR - Indian Rupee' => 'INR - Hint Rupesi', 'JPY - Japanese Yen' => 'JPY - Japon Yeni', 'NZD - New Zealand Dollar' => 'NZD - Yeni Zelanda Doları', - // 'RSD - Serbian dinar' => '', - // 'CNY - Chinese Yuan' => '', - 'USD - US Dollar' => 'USD$ Amerikan Doları', + 'RSD - Serbian dinar' => 'Sırp Dinarı', + 'CNY - Chinese Yuan' => 'Çin Yuanı', + 'USD - US Dollar' => 'USD Amerikan Doları', 'Destination column' => 'Hedef Sütun', 'Move the task to another column when assigned to a user' => 'Bir kullanıcıya atandığında görevi baÅŸka bir sütuna taşı', 'Move the task to another column when assignee is cleared' => 'Atanmış kullanıcı kaldırıldığında görevi baÅŸka bir sütuna taşı', @@ -555,9 +547,8 @@ return array( 'Reference currency' => 'Referans kur', 'The currency rate have been added successfully.' => 'Kur baÅŸarıyla eklendi', 'Unable to add this currency rate.' => 'Bu kur eklenemedi', - // 'Webhook URL' => '', + 'Webhook URL' => 'Web kancası URL', '%s removed the assignee of the task %s' => '%s, %s görevinin atanan bilgisini kaldırdı', - 'Enable Gravatar images' => 'Gravatar resimlerini kullanıma aç', 'Information' => 'Bilgi', 'Check two factor authentication code' => 'Çift-Kademeli doÄŸrulama kodunu kontrol et', 'The two factor authentication code is not valid.' => 'Çift-Kademeli doÄŸrulama kodu geçersiz', @@ -611,14 +602,7 @@ return array( 'When task is moved from first column' => 'Görev ilk sütundan taşındığı zaman', 'When task is moved to last column' => 'Görev son sütuna taşındığı zaman', 'Year(s)' => 'Yıl(lar)', - 'Calendar settings' => 'Takvim ayarları', - 'Project calendar view' => 'Proje takvim görünümü', 'Project settings' => 'Proje ayarları', - 'Show subtasks based on the time tracking' => 'Zaman takibi bazında alt görevleri göster', - 'Show tasks based on the creation date' => 'OluÅŸturulma zamanına göre görevleri göster', - 'Show tasks based on the start date' => 'BaÅŸlangıç zamanına göre görevleri göster', - 'Subtasks time tracking' => 'Alt görevler zaman takibi', - 'User calendar view' => 'Kullanıcı takvim görünümü', 'Automatically update the start date' => 'BaÅŸlangıç tarihini otomatik olarak güncelle', 'iCal feed' => 'iCal akışı', 'Preferences' => 'Ayarlar', @@ -637,7 +621,6 @@ return array( 'Notification' => 'Uyarılar', '%s moved the task #%d to the first swimlane' => '%s, #%d görevini birinci kulvara taşıdı', 'Swimlane' => 'Kulvar', - 'Gravatar' => 'Gravatar', '%s moved the task %s to the first swimlane' => '%s, %s görevini ilk kulvara taşıdı', '%s moved the task %s to the swimlane "%s"' => '%s, %s görevini "%s" kulvarına taşıdı', 'This report contains all subtasks information for the given date range.' => 'Bu rapor belirtilen tarih aralığında tüm alt görev bilgilerini içerir.', @@ -674,7 +657,6 @@ return array( 'Stop timer' => 'Zamanlayıcıyı durdur', 'Start timer' => 'Zamanlayıcıyı baÅŸlat', 'My activity stream' => 'Olay akışım', - 'My calendar' => 'Takvimim', 'Search tasks' => 'Görevleri ara', 'Reset filters' => 'Filtreleri sıfırla', 'My tasks due tomorrow' => 'Yarına tamamlanması gereken görevlerim', @@ -688,7 +670,6 @@ return array( 'Overview' => 'Özet Görünüm', 'Board/Calendar/List view' => 'Pano/Takvim/Liste görünümü', 'Switch to the board view' => 'Pano görünümüne geç', - 'Switch to the calendar view' => 'Takvim görünümüne geç', 'Switch to the list view' => 'Liste görünümüne geç', 'Go to the search/filter box' => 'Arama/Filtreleme kutusuna git', 'There is no activity yet.' => 'Henüz bir aktivite yok.', @@ -743,15 +724,8 @@ return array( 'License:' => 'Lisans:', 'License' => 'Lisans', 'Enter the text below' => 'AÅŸağıdaki metni girin', - 'Sort by position' => 'Pozisyona göre sırala', - 'Sort by date' => 'Tarihe göre sırala', - 'Add task' => 'Görev ekle', 'Start date:' => 'BaÅŸlangıç tarihi:', 'Due date:' => 'Tamamlanması gereken tarih:', - 'There is no start date or due date for this task.' => 'Bu görev için baÅŸlangıç veya tamamlanması gereken tarih yok.', - 'Moving or resizing a task will change the start and due date of the task.' => 'Bir görevin boyutunu deÄŸiÅŸtirmek, görevin baÅŸlangıç ve tamamlanması gereken tarihlerini deÄŸiÅŸtirir.', - 'There is no task in your project.' => 'Projenizde hiç görev yok.', - 'Gantt chart' => 'Gantt diyagramı', 'People who are project managers' => 'Proje müdürü olan kiÅŸiler', 'People who are project members' => 'Proje üyesi olan kiÅŸiler', 'NOK - Norwegian Krone' => ' NOK - Norveç Kronu', @@ -763,22 +737,15 @@ return array( 'Members' => 'Üyeler', 'Shared project' => 'Paylaşılan proje', 'Project managers' => 'Proje müdürleri', - 'Gantt chart for all projects' => 'Tüm projeler için Gantt diyagramı', 'Projects list' => 'Proje listesi', - 'Gantt chart for this project' => 'Bu proje için Gantt diyagramı', - 'Project board' => 'Proje Panosu', 'End date:' => 'BitiÅŸ tarihi:', - 'There is no start date or end date for this project.' => 'Bu proje için baÅŸlangıç veya bitiÅŸ tarihi yok.', - 'Projects Gantt chart' => 'Projeler Gantt diyagramı', 'Change task color when using a specific task link' => 'Belirli bir görev baÄŸlantısı kullanıldığında görevin rengini deÄŸiÅŸtir', 'Task link creation or modification' => 'Görev baÄŸlantısı oluÅŸturulması veya deÄŸiÅŸtirilmesi', 'Milestone' => 'Kilometre taşı', 'Documentation: %s' => 'Dokümantasyon: %s', - 'Switch to the Gantt chart view' => 'Gantt diyagramı görünümüne geç', 'Reset the search/filter box' => 'Arama/Filtre kutusunu sıfırla', 'Documentation' => 'Dokümantasyon', 'Table of contents' => 'İçindekiler', - 'Gantt' => 'Gantt', 'Author' => 'Yazar', 'Version' => 'Versiyon', 'Plugins' => 'Eklentiler', @@ -889,7 +856,6 @@ return array( 'There is no user available.' => 'Uygun üye yok', 'Do you really want to remove the user "%s" from the group "%s"?' => '"%s" kullanıcısını "%s" grubundan çıkarmak istediÄŸinize emin misiniz?', 'There is no group.' => 'Hiç grup yok.', - 'External Id' => 'Harici Kimlik', 'Add group member' => 'Grup üyesi ekle', 'Do you really want to remove this group: "%s"?' => '"%s" grubunu silmek istediÄŸinize emin misiniz?', 'There is no user in this group.' => 'Bu grupta hiç kullanıcı yok.', @@ -941,9 +907,9 @@ return array( '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '"%s" sütunu ve "%s" kulvarındaki %d görev kapatılacak.', 'Close all tasks of this column' => 'Bu sütundaki tüm görevleri kapat', 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Proje bildirimleri için hiçbir plugin kaydedilmedi. Yine de profil sayfanızdan bildirim ayarları yapabilirsiniz.', - 'My dashboard' => 'Dashboard um', + 'My dashboard' => 'Panom', 'My profile' => 'Profilim', - 'Project owner: ' => 'Proje sahibi', + 'Project owner: ' => 'Proje sahibi: ', 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Proje kodu opsiyoneldir ve alfanümerik olmalıdır, örneÄŸin: PROJE1', 'Project owner' => 'Proje sahibi', 'Private projects do not have users and groups management.' => '(KiÅŸiye) Özel projelerde kullanıcı ve grup yönetimi yoktur.', @@ -955,7 +921,6 @@ return array( 'Default priority' => 'Varsayılan öncelik', 'Lowest priority' => 'En düşük öncelik', 'Highest priority' => 'En yüksek öncelik', - 'If you put zero to the low and high priority, this feature will be disabled.' => 'EÄŸer en düşük ve en yüksek önceliÄŸe sıfır girerseniz, bu özellik devre dışı bırakılacak.', 'Close a task when there is no activity' => 'Bir aktivite olmadığında görevi kapat', 'Duration in days' => 'Süre (gün olarak)', 'Send email when there is no activity on a task' => 'Bir görevde aktivite olmadığında e-posta gönder', @@ -1044,7 +1009,7 @@ return array( 'Change subtask position' => 'Alt görev sırasını deÄŸiÅŸtir', 'This value must be greater than %d' => 'Bu deÄŸer %d den büyük olmalı', 'Another swimlane with the same name exists in the project' => 'Projede aynı isimli baÅŸka bir kulvar var', - 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'ÖrneÄŸin: http://ornek.dediteknoloji.com/ (sabit URLler oluÅŸturmak için)', + 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'ÖrneÄŸin: http://ornek.kanboard.net/ (sabit URLler oluÅŸturmak için)', 'Actions duplicated successfully.' => 'İşlemler baÅŸarıyla çoklandı.', 'Unable to duplicate actions.' => 'İşlemler çoklanamıyor.', 'Add a new action' => 'Yeni bir iÅŸlem ekle', @@ -1066,7 +1031,7 @@ return array( 'Display list of keyboard shortcuts' => 'Kısayol tuÅŸları listesini göster', 'Menu' => 'Menü', 'Set start date' => 'BaÅŸlangıç tarihi belirle', - // 'Avatar' => '', + 'Avatar' => 'Simge', 'Upload my avatar image' => 'Avatar resmimi yükle', 'Remove my image' => 'Resmimi kaldır', 'The OAuth2 state parameter is invalid' => 'OAuth2 durum parametresi geçersiz', @@ -1087,29 +1052,29 @@ return array( 'Assign automatically a color based on a priority' => 'ÖnceliÄŸe baÄŸlı olarak bir renk belirle', 'Overdue tasks for the project(s) "%s"' => '%s proje(leri) için süresi geçen görevler', 'Upload files' => 'Dosyaları yükle', - 'Installed Plugins' => 'YüklenmiÅŸ Pluginler', - 'Plugin Directory' => 'Plugin Klasörü', - 'Plugin installed successfully.' => 'Plugin baÅŸarıyla kuruldu.', - 'Plugin updated successfully.' => 'Plugin baÅŸarıyla güncellendi.', - 'Plugin removed successfully.' => 'Plugin baÅŸarıyla kaldırıldı.', + 'Installed Plugins' => 'YüklenmiÅŸ Eklentiler', + 'Plugin Directory' => 'Eklenti Klasörü', + 'Plugin installed successfully.' => 'Eklenti baÅŸarıyla kuruldu.', + 'Plugin updated successfully.' => 'Eklenti baÅŸarıyla güncellendi.', + 'Plugin removed successfully.' => 'Eklenti baÅŸarıyla kaldırıldı.', 'Subtask converted to task successfully.' => 'Alt görev baÅŸarıyla göreve dönüştürüldü.', 'Unable to convert the subtask.' => 'Alt görev dönüştürülemedi', 'Unable to extract plugin archive.' => 'Plugin (sıkıştırılmış) dosyası açılamadı.', - 'Plugin not found.' => 'Plugin bulunamadı', + 'Plugin not found.' => 'Eklenti bulunamadı', 'You don\'t have the permission to remove this plugin.' => 'Bu plugin\'i kaldırmak için yetkiniz yok.', - 'Unable to download plugin archive.' => 'Plugin dosyası indirilemedi.', - 'Unable to write temporary file for plugin.' => 'Plugin için geçici dosya oluÅŸturulamadı.', - 'Unable to open plugin archive.' => 'Plugin dosyası açılamadı.', - 'There is no file in the plugin archive.' => 'Plugin (sıkıştırılmış) dosyasında, dosya bulunmuyor.', + 'Unable to download plugin archive.' => 'Eklenti dosyası indirilemedi.', + 'Unable to write temporary file for plugin.' => 'Eklenti için geçici dosya oluÅŸturulamadı.', + 'Unable to open plugin archive.' => 'Eklenti dosyası açılamadı.', + 'There is no file in the plugin archive.' => 'Eklenti (sıkıştırılmış) dosyasında, dosya bulunmuyor.', 'Create tasks in bulk' => 'Toplu olarak görev oluÅŸtur', - 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Kanbord sisteminiz arayüzden plugin kurulacak ÅŸekilde ayarlanmamış.', - 'There is no plugin available.' => 'Uygun plugin yok.', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Kanbord sisteminiz arayüzden eklenti kurulacak ÅŸekilde ayarlanmamış.', + 'There is no plugin available.' => 'Uygun eklenti yok.', 'Install' => 'Kur', 'Update' => 'Güncelle', 'Up to date' => 'Güncel', 'Not available' => 'Uygun deÄŸil', - 'Remove plugin' => 'Plugini kaldır', - 'Do you really want to remove this plugin: "%s"?' => '"%s" plugin i gerçekten kaldırmak istiyor musunuz?', + 'Remove plugin' => 'Eklentiyi kaldır', + 'Do you really want to remove this plugin: "%s"?' => '"%s" eklentiyi gerçekten kaldırmak istiyor musunuz?', 'Uninstall' => 'Kaldır', 'Listing' => 'Listeliyor', 'Metadata' => 'Meta veri', @@ -1124,7 +1089,7 @@ return array( 'Email settings' => 'E-posta ayarları', 'Email sender address' => 'E-posta gönderen adresi', 'Email transport' => 'E-posta taşıma', - 'Webhook token' => 'Webhook token', + 'Webhook token' => 'Web kancası anahtarı', 'Project tags management' => 'Proje etiket yönetimi', 'Tag created successfully.' => 'Etiket baÅŸarıyla oluturuldu.', 'Unable to create this tag.' => 'Etiket oluÅŸturulamıyor.', @@ -1169,8 +1134,6 @@ return array( 'Subtasks overview for %s' => '%s için alt görev özeti', 'Projects overview for %s' => '%s için proje özeti', 'Activity stream for %s' => '%s için akış', - 'Calendar for %s' => '%s için takvim', - 'Notifications for %s' => '%s için bildirimler', 'Assign a color when the task is moved to a specific swimlane' => 'Görev bir kulvara taşındığında rengini deÄŸiÅŸtir', 'Assign a priority when the task is moved to a specific swimlane' => 'Görev bir kulvara taşındığında önceliÄŸini deÄŸiÅŸtir', 'User unlocked successfully.' => 'Kullanıcı kilidi baÅŸarıyla kaldırıldı.', @@ -1241,97 +1204,166 @@ return array( 'Preview' => 'Öngörünüm', 'Write' => 'Yaz', 'Write your text in Markdown' => 'Metninizi Markdown a yazın', - 'New External Task: %s' => 'Yeni Harici Görev: %s', - 'No personal API access token registered.' => 'KiÅŸisel API eriÅŸim belirteç-token ınız kaydedilmedi.', - 'Your personal API access token is "%s"' => 'KiÅŸisel API eriÅŸim belirteç-token ınız"%s"', - 'Remove your token' => 'Belirteç-token ınızı kaldır', - 'Generate a new token' => 'Yeni bir belirteç-token oluÅŸtur', - // 'Showing %d-%d of %d' => '', - // 'Outgoing Emails' => '', + 'No personal API access token registered.' => 'KiÅŸisel API eriÅŸim anahtarınız kaydedilmedi.', + 'Your personal API access token is "%s"' => 'KiÅŸisel API eriÅŸim anahtarınız "%s"', + 'Remove your token' => 'Anahtarı kaldır', + 'Generate a new token' => 'Yeni bir anahtar oluÅŸtur', + 'Showing %d-%d of %d' => 'Gösterilen %d-%d / %d', + 'Outgoing Emails' => 'Gönderilen ePostalar', 'Add or change currency rate' => 'Kur Oranını ekle veya deÄŸiÅŸtir', - // 'Reference currency: %s' => '', - // 'Add custom filters' => '', - // 'Export' => '', - // 'Add link label' => '', - // 'Incompatible Plugins' => '', - // 'Compatibility' => '', - // 'Permissions and ownership' => '', - // 'Priorities' => '', - // 'Close this window' => '', - // 'Unable to upload this file.' => '', - // 'Import tasks' => '', - // 'Choose a project' => '', - // 'Profile' => '', - // 'Application role' => '', - // '%d invitations were sent.' => '', - // '%d invitation was sent.' => '', - // 'Unable to create this user.' => '', - // 'Kanboard Invitation' => '', - // 'Visible on dashboard' => '', - // 'Created at:' => '', - // 'Updated at:' => '', - // 'There is no custom filter.' => '', - // 'New User' => '', - // 'Authentication' => '', - // 'If checked, this user will use a third-party system for authentication.' => '', - // 'The password is necessary only for local users.' => '', - // 'You have been invited to register on Kanboard.' => '', - // 'Click here to join your team' => '', - // 'Invite people' => '', - // 'Emails' => '', - // 'Enter one email address by line.' => '', - // 'Add these people to this project' => '', - // 'Add this person to this project' => '', - // 'Sign-up' => '', - // 'Credentials' => '', - // 'New user' => '', - // 'This username is already taken' => '', - // 'A link to reset your password has been sent by email.' => '', - // 'Your profile must have a valid email address.' => '', - // 'Unfortunately, we are unable to reset your password. Did you entered a valid username? Do you have an email address in your profile?' => '', - // 'TRL - Turkish Lira' => '', - // 'The project email is optional and could be used by several plugins.' => '', - // 'The project email must be unique across all projects' => '', - // 'The email configuration has been disabled by the administrator.' => '', - // 'Close this project' => '', - // 'Open this project' => '', - // 'Close a project' => '', - // 'Do you really want to close this project: "%s"?' => '', - // 'Reopen a project' => '', - // 'Do you really want to reopen this project: "%s"?' => '', - // 'This project is open' => '', - // 'This project is closed' => '', - // 'Unable to upload files, check the permissions of your data folder.' => '', - // 'Another category with the same name exists in this project' => '', - // 'Comment sent by email successfully.' => '', - // 'Sent by email to [%s](mailto:%s) (%s)' => '', - // 'Unable to read uploaded file.' => '', - // 'Database uploaded successfully.' => '', - // 'Task sent by email successfully.' => '', - // 'There is no category in this project.' => '', - // 'Send by email' => '', - // 'Create and send a comment by email' => '', - // 'Subject' => '', - // 'Upload the database' => '', - // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', - // 'Database file' => '', - // 'Upload' => '', - // 'Remove this user from group' => '', - // 'Your project must have at least one active swimlane.' => '', - // 'Project: %s' => '', - // 'Automatic action not found: "%s"' => '', - // '%d projects' => '', - // '%d project' => '', - // 'There is no project.' => '', - // 'Sort' => '', - // 'Project ID' => '', - // 'Project name' => '', - // 'Public' => '', - // 'Private' => '', - // '%d tasks' => '', - // '%d task' => '', - // 'Task ID' => '', - // 'Assign automatically a color when due date is expired' => '', - // 'Total score in this column across all swimlanes' => '', - // 'HRK - Kuna' => '', + 'Reference currency: %s' => 'Referans parabirimi: %s', + 'Add custom filters' => 'Özel filtre ekle', + 'Export' => 'Dışa aktar', + 'Add link label' => 'BaÄŸlantı etiketi ekle', + 'Incompatible Plugins' => 'Uyumsuz Eklentiler', + 'Compatibility' => 'Uyumluluk', + 'Permissions and ownership' => 'İzinler ve sahiplik', + 'Priorities' => 'Öncelikler', + 'Close this window' => 'Bu pencereyi kapat', + 'Unable to upload this file.' => 'Bu dosya yüklenemiyor', + 'Import tasks' => 'Görevleri içeri aktar', + 'Choose a project' => 'Proje seçin', + 'Profile' => 'Profil', + 'Application role' => 'Uygulama rolü', + '%d invitations were sent.' => '%d Davetiye gönderildi', + '%d invitation was sent.' => '%d Davetiye gönderildi', + 'Unable to create this user.' => 'Bu kullanıcı oluÅŸturulamadı.', + 'Kanboard Invitation' => 'Kanboard Daveti', + 'Visible on dashboard' => 'Panoda görünür', + 'Created at:' => 'OluÅŸturulma: ', + 'Updated at:' => 'Güncellenme: ', + 'There is no custom filter.' => 'Uygulanmış özel filtre yok', + 'New User' => 'Yeni Kullanıcı', + 'Authentication' => 'Yetkilendirme', + 'If checked, this user will use a third-party system for authentication.' => 'İşaretlenirse, bu kullanıcı kimlik doÄŸrulama için üçüncü parti bir sistem kullanacaktır.', + 'The password is necessary only for local users.' => 'Parola yalnızca yerel kullanıcılar için gereklidir', + 'You have been invited to register on Kanboard.' => 'Kanboarda kayıt yaptırmak için davet edildiniz.', + 'Click here to join your team' => 'Takımınıza katılmak için buraya tıklayın', + 'Invite people' => 'İnsanları davet et', + 'Emails' => 'ePostalar', + 'Enter one email address by line.' => 'Her satıra bir adet email girin.', + 'Add these people to this project' => 'KiÅŸileri bu projeye ekle', + 'Add this person to this project' => 'KiÅŸiyi bu projeye ekle', + 'Sign-up' => 'Kayıt', + 'Credentials' => 'Kimlik bilgileri', + 'New user' => 'Yeni kullanıcı', + 'This username is already taken' => 'Bu kullanıcı adı zaten alınmış', + 'A link to reset your password has been sent by email.' => 'Parola sıfırlama linkini içeren mesaj ePosta adresinize gönderildi.', + 'Your profile must have a valid email address.' => 'Profiliniz için geçerli bir ePosta adresi gerekiyor.', + 'Unfortunately, we are unable to reset your password. Did you entered a valid username? Do you have an email address in your profile?' => 'Malesef parolanızı sıfırlayamıyoruz. Kullanıcı adınızı doÄŸru girdiniz mi? Profilinize kayıtlı ePosta adresiniz var mı?', + 'TRL - Turkish Lira' => 'TRL - Türk Lirası', + 'The project email is optional and could be used by several plugins.' => 'Proje ePostası isteÄŸe baÄŸlıdır ve eklentiler tarafından kullanılabilir', + 'The project email must be unique across all projects' => 'Proje e-postası tüm projelerde benzersiz olmalıdır.', + 'The email configuration has been disabled by the administrator.' => 'ePosta ayarları yönetici tarafından devre dışı bırakılmış.', + 'Close this project' => 'Bu projeyi kapat', + 'Open this project' => 'Bu projeyi aç', + 'Close a project' => 'Proje kapat', + 'Do you really want to close this project: "%s"?' => 'Bu projeyi kapatmak istediÄŸinize emin misiniz? : "%s"', + 'Reopen a project' => 'Projeyi tekrar aç', + 'Do you really want to reopen this project: "%s"?' => 'Bu projeyi tekrar açmak istediÄŸinizi onaylıyor musunuz? : "%s"', + 'This project is open' => 'Bu proje açıktır', + 'This project is closed' => 'Bu proje kapatılmış', + 'Unable to upload files, check the permissions of your data folder.' => 'Dosya yüklenemedi. Lütfen yükleme dizine yazma izninizi kontrol ediniz.', + 'Another category with the same name exists in this project' => 'Bu projede aynı ada sahip baÅŸka bir kategori var', + 'Comment sent by email successfully.' => 'Yorum ePosta ile baÅŸarıyla gönderildi.', + 'Sent by email to [%s](mailto:%s) (%s)' => 'ePostayı gönder [%s](mailto:%s) (%s)', + 'Unable to read uploaded file.' => 'Yüklenen dosya okunamadı.', + 'Database uploaded successfully.' => 'Veritabanı baÅŸarıyla yüklendi.', + 'Task sent by email successfully.' => 'Görev ePosta ile baÅŸarıyla gönderildi.', + 'There is no category in this project.' => 'Bu projenin kategorisi yok', + 'Send by email' => 'ePosta ile gönder', + 'Create and send a comment by email' => 'Yorum oluÅŸtur ve ePosta ile gönder', + 'Subject' => 'Konu', + 'Upload the database' => 'Veritabanına yükle', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Daha önce indirdiÄŸiniz Sqlite Veritabanı dosyasını yükleyebilirsiniz (Gzip formatında)', + 'Database file' => 'Veritabanı dosyası', + 'Upload' => 'Yükle', + 'Your project must have at least one active swimlane.' => 'Projenizin en az bir aktif kulvara sahip olması gerekir', + 'Project: %s' => 'Proje: %s', + 'Automatic action not found: "%s"' => 'Otomatik iÅŸlem bulunamadı: "%s"', + '%d projects' => '%d proje', + '%d project' => '%d proje', + 'There is no project.' => 'Proje yok.', + 'Sort' => 'Sırala', + 'Project ID' => 'Proje No', + 'Project name' => 'Proje Adı', + 'Public' => 'Herkese açık', + 'Private' => 'Gizli', + '%d tasks' => '%d görev', + '%d task' => '%d görev', + 'Task ID' => 'Görev No', + 'Assign automatically a color when due date is expired' => 'Son günü geçtiÄŸinde otomatik olarak bir renk ata', + 'Total score in this column across all swimlanes' => 'Tüm kulvarlara göre bu kulvarın toplam puanı', + 'HRK - Kuna' => 'HRK - Kuna', + 'ARS - Argentine Peso' => 'ARS - Arjantin Pezosu', + 'COP - Colombian Peso' => 'COP - Kolombiya Pezosu', + '%d groups' => '%d grup', + '%d group' => '%d grup', + 'Group ID' => 'Grup No', + 'External ID' => 'Harici No', + '%d users' => '%d kullanıcı', + '%d user' => '%d kullanıcı', + 'Hide subtasks' => 'Alt görevleri gizler', + 'Show subtasks' => 'Alt görevleri göster', + 'Authentication Parameters' => 'Yetkilendirme Parametreleri', + 'API Access' => 'API EriÅŸimi', + 'No users found.' => 'Kullanıcı yok', + 'User ID' => 'Kullanıcı No', + 'Notifications are activated' => 'Bildirimler etkinleÅŸtirildi', + 'Notifications are disabled' => 'Bildirimler devre dışı', + 'User disabled' => 'Kullanıcı devre dışı', + '%d notifications' => '%d bildirim', + '%d notification' => '%d bildirim', + 'There is no external integration installed.' => 'YüklenmiÅŸ harici entegrasyon yok.', + 'You are not allowed to update tasks assigned to someone else.' => 'BaÅŸkasına atanmış görevi güncelleme izniniz yok.', + 'You are not allowed to change the assignee.' => 'Atanmış kiÅŸiyi deÄŸiÅŸtirme yetkiniz yok.', + 'Task suppression is not permitted' => 'Görev silmeye izniniz yok', + 'Changing assignee is not permitted' => 'Atanmışı deÄŸiÅŸtirme izniniz yok', + 'Update only assigned tasks is permitted' => 'Yalnızca atandığınız görevleri güncelleyebilirsiniz', + 'Only for tasks assigned to the current user' => 'Yalnızca geçerli kullanıcıya atanan görevler için', + 'My projects' => 'Projelerim', + 'Your are not member of any project.' => 'Herhangi bir projenin üyesi deÄŸilsiniz.', + 'My subtasks' => 'Altgörevlerim', + '%d subtasks' => '%d alt görev', + '%d subtask' => '%d alt görev', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Geçerli kullanıcıya atanan görevler için yalnızca o sütunlar arasında hareket eden bir göreve izin verilir.', + '[DUPLICATE]' => '[ÇİFT]', + 'DKK - Danish Krona' => 'DKK - Danimarka Kronu', + 'Remove user from group' => 'Kullanıcıyı gruptan çıkar', + 'Assign the task to its creator' => 'OluÅŸturucusuna görev ata', + 'This task was sent by email to "%s" with subject "%s".' => 'Bu görev "%s" konusuyla "%s" ePosta adresine gönderildi.', + 'Predefined Email Subjects' => 'Öntanımlı ePosta konuları', + 'Write one subject by line.' => 'Her satıra bir konu yazınız', + 'Create another link' => 'BaÅŸka baÄŸlantı oluÅŸtur', + 'BRL - Brazilian Real' => 'BRL - Brezilya Reali', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Locale/vi_VN/translations.php b/app/Locale/vi_VN/translations.php new file mode 100644 index 00000000..fad538a4 --- /dev/null +++ b/app/Locale/vi_VN/translations.php @@ -0,0 +1,1375 @@ +<?php + +return array( + 'number.decimals_separator' => ',', + 'number.thousands_separator' => ' ', + 'None' => 'Chưa có', + 'Edit' => 'Chỉnh sá»a', + 'Remove' => 'Xóa bá»', + 'Yes' => 'Có', + 'No' => 'Không', + 'cancel' => 'há»§y bá»', + 'or' => 'hoặc là ', + 'Yellow' => 'Mà u và ng', + 'Blue' => 'Mà u xanh da trá»i', + 'Green' => 'Mà u xanh lá', + 'Purple' => 'Mà u tÃm', + 'Red' => 'Äá»', + 'Orange' => 'Trái cam', + 'Grey' => 'Xám', + 'Brown' => 'Nâu', + 'Deep Orange' => 'Deep Orange', + 'Dark Grey' => 'Mà u xám Ä‘en', + 'Pink' => 'Hồng', + 'Teal' => 'Teal', + 'Cyan' => ' Cyan', + 'Lime' => 'Vôi', + 'Light Green' => 'Mà u xanh lợt', + 'Amber' => 'Amber', + 'Save' => 'Lưu', + 'Login' => 'Äăng nháºp', + 'Official website:' => 'Trang web chinh thưc:', + 'Unassigned' => 'Chưa được gán', + 'View this task' => 'Xem công việc nà y', + 'Remove user' => 'Xóa ngưá»i dùng', + 'Do you really want to remove this user: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» ngưá»i dùng nà y: "%s"?', + 'All users' => 'Tất cả ngưá»i dùng', + 'Username' => 'Tên đăng nháºp', + 'Password' => 'Máºt khẩu', + 'Administrator' => 'Ngưá»i quản lý', + 'Sign in' => 'Äăng nháºp', + 'Users' => 'Ngưá»i dùng', + 'Forbidden' => 'Forbidden', + 'Access Forbidden' => 'Truy cáºp bị cấm', + 'Edit user' => 'Chỉnh sá»a ngưá»i dùng', + 'Logout' => 'Äăng xuất', + 'Bad username or password' => 'Tên đăng nhập hoặc mật khẩu xâu', + 'Edit project' => 'Chỉnh sá»a dá»± án', + 'Name' => 'Tên', + 'Projects' => 'Dá»± án', + 'No project' => 'Không có dá»± án', + 'Project' => 'Dá»± án', + 'Status' => 'Trạng thái', + 'Tasks' => 'Nhiệm vụ', + 'Board' => 'Bảng', + 'Actions' => 'Hà nh động', + 'Inactive' => 'Không hoạt động', + 'Active' => 'Hoạt động', + 'Unable to update this board.' => 'Không thể cáºp nháºt bảng nà y.', + 'Disable' => 'Vô hiệu hóa', + 'Enable' => 'Báºt', + 'New project' => 'Dá»± án má»›i', + 'Do you really want to remove this project: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» dá»± án nà y: "%s"?', + 'Remove project' => 'Loại bá» dá»± án', + 'Edit the board for "%s"' => 'Chỉnh sá»a bảng cho "%s"', + 'Add a new column' => 'Thêm cá»™t má»›i', + 'Title' => 'Chức vụ', + 'Assigned to %s' => 'ÄÆ°á»£c giao cho %s', + 'Remove a column' => 'Loại bá» má»™t cá»™t', + 'Unable to remove this column.' => 'Không thể xoá cá»™t nà y.', + 'Do you really want to remove this column: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» cá»™t nà y: "%s"?', + 'Settings' => 'Cà i đặt', + 'Application settings' => 'Cà i đặt ứng dụng', + 'Language' => 'Ngôn ngữ', + 'Webhook token:' => 'Mã thông báo Webhook:', + 'API token:' => 'Mã thông báo API:', + 'Database size:' => 'KÃch thước cÆ¡ sở dữ liệu:', + 'Download the database' => 'Tải vá» cÆ¡ sở dữ liệu', + 'Optimize the database' => 'Tối ưu hóa cÆ¡ sở dữ liệu', + '(VACUUM command)' => '(Lệnh VACUUM)', + '(Gzip compressed Sqlite file)' => '(Gzip nén táºp tin Sqlite)', + 'Close a task' => 'Äóng má»™t nhiệm vụ', + 'Column' => 'Cá»™t', + 'Color' => 'Mà u', + 'Assignee' => 'Ngưá»i được chuyển nhượng', + 'Create another task' => 'Tạo má»™t nhiệm vụ khác', + 'New task' => 'Nhiệm vụ má»›i', + 'Open a task' => 'Mở má»™t nhiệm vụ', + 'Do you really want to open this task: "%s"?' => 'Bạn có thá»±c sá»± muốn mở công việc nà y: "%s"?', + 'Back to the board' => 'Trở lại bảng', + 'There is nobody assigned' => 'Không ai được chỉ định', + 'Column on the board:' => 'Cá»™t trên bảng:', + 'Close this task' => 'Äóng nhiệm vụ nà y', + 'Open this task' => 'Mở nhiệm vụ nà y', + 'There is no description.' => 'Không có mô tả.', + 'Add a new task' => 'Thêm má»™t nhiệm vụ má»›i', + 'The username is required' => 'Tên ngưá»i dùng được yêu cầu', + 'The maximum length is %d characters' => 'Chiá»u dà i tối Ä‘a là %d ký tá»±', + 'The minimum length is %d characters' => 'Chiá»u dà i tối thiểu là %d ký tá»±', + 'The password is required' => 'Máºt khẩu là bắt buá»™c', + 'This value must be an integer' => 'Giá trị nà y phải là má»™t số nguyên', + 'The username must be unique' => 'Tên ngưá»i dùng phải là duy nhất', + 'The user id is required' => 'Ngưá»i sá» dụng id là bắt buá»™c', + 'Passwords don\'t match' => 'Máºt khẩu don \'t match', + 'The confirmation is required' => 'Cần xác nháºn', + 'The project is required' => 'Dá»± án được yêu cầu', + 'The id is required' => 'Id là bắt buá»™c', + 'The project id is required' => 'Id dá»± án được yêu cầu', + 'The project name is required' => 'Tên cá»§a dá»± án được yêu cầu', + 'The title is required' => 'Tiêu đỠlà bắt buá»™c', + 'Settings saved successfully.' => 'Cà i đặt đã lưu thà nh công.', + 'Unable to save your settings.' => 'Không thể lưu cà i đặt cá»§a bạn.', + 'Database optimization done.' => 'Tối ưu hóa cÆ¡ sở dữ liệu được thá»±c hiện.', + 'Your project have been created successfully.' => 'Dá»± án cá»§a bạn đã được tạo thà nh công.', + 'Unable to create your project.' => 'Không thể tạo dá»± án cá»§a bạn.', + 'Project updated successfully.' => 'Dá»± án được cáºp nháºt thà nh công.', + 'Unable to update this project.' => 'Không thể cáºp nháºt dá»± án nà y.', + 'Unable to remove this project.' => 'Không thể xóa dá»± án nà y.', + 'Project removed successfully.' => 'Dá»± án đã được loại bá» thà nh công.', + 'Project activated successfully.' => 'Dá»± án được kÃch hoạt thà nh công.', + 'Unable to activate this project.' => 'Không thể kÃch hoạt dá»± án nà y.', + 'Project disabled successfully.' => 'Dá»± án bị vô hiệu thà nh công.', + 'Unable to disable this project.' => 'Không thể vô hiệu hoá dá»± án nà y.', + 'Unable to open this task.' => 'Không thể mở nhiệm vụ nà y.', + 'Task opened successfully.' => 'Nhiệm vụ thà nh công.', + 'Unable to close this task.' => 'Không thể đóng nhiệm vụ nà y.', + 'Task closed successfully.' => 'Nhiệm vụ đóng thà nh công.', + 'Unable to update your task.' => 'Không thể cáºp nháºt công việc cá»§a bạn.', + 'Task updated successfully.' => 'Nhiệm vụ được cáºp nháºt thà nh công.', + 'Unable to create your task.' => 'Không thể tạo ra nhiệm vụ cá»§a bạn.', + 'Task created successfully.' => 'Nhiệm vụ thà nh công.', + 'User created successfully.' => 'Ngưá»i dùng đã thà nh công.', + 'Unable to create your user.' => 'Không thể tạo ngưá»i dùng cá»§a bạn.', + 'User updated successfully.' => 'Ngưá»i dùng cáºp nháºt thà nh công.', + 'User removed successfully.' => 'Ngưá»i dùng đã xoá thà nh công.', + 'Unable to remove this user.' => 'Không thể xóa ngưá»i dùng nà y.', + 'Board updated successfully.' => 'Bảng đã được cáºp nháºt thà nh công.', + 'Ready' => 'Sẳn sà ng', + 'Backlog' => 'Backlog', + 'Work in progress' => 'Công việc Ä‘ang tiến hà nh', + 'Done' => 'Là m xong', + 'Application version:' => 'Phiên bản ứng dụng:', + 'Id' => 'ID', + 'Public link' => 'Liên kết công cá»™ng', + 'Timezone' => 'Múi giá»', + 'Sorry, I didn\'t find this information in my database!' => 'Xin lá»—i, tôi không tìm thấy thông tin nà y trong cÆ¡ sở dữ liệu cá»§a tôi!', + 'Page not found' => 'Trang không tìm thấy', + 'Complexity' => 'Sá»± phức tạp', + 'Task limit' => 'Nhiệm vụ giá»›i hạn', + 'Task count' => 'Nhiệm vụ', + 'User' => 'Ngưá»i dùng', + 'Comments' => 'Bình luáºn', + 'Comment is required' => 'Bình luáºn là bắt buá»™c', + 'Comment added successfully.' => 'Bình luáºn được thêm thà nh công.', + 'Unable to create your comment.' => 'Không thể tạo bình luáºn cá»§a bạn.', + 'Due Date' => 'Ngà y đáo hạn', + 'Invalid date' => 'Ngà y không hợp lệ', + 'Automatic actions' => 'Hà nh động tá»± động', + 'Your automatic action have been created successfully.' => 'Hà nh động tá»± động cá»§a bạn đã được tạo thà nh công.', + 'Unable to create your automatic action.' => 'Không thể tạo hà nh động tá»± động cá»§a bạn.', + 'Remove an action' => 'Loại bá» má»™t hà nh động', + 'Unable to remove this action.' => 'Không thể loại bá» hà nh động nà y.', + 'Action removed successfully.' => 'Hà nh động đã được xoá thà nh công.', + 'Automatic actions for the project "%s"' => 'Hà nh động tá»± động cho dá»± án "%s"', + 'Add an action' => 'Thêm má»™t hà nh động', + 'Event name' => 'Tên sá»± kiện', + 'Action' => 'Hoạt động', + 'Event' => 'Biến cố', + 'When the selected event occurs execute the corresponding action.' => 'Khi sá»± kiện đã chá»n xảy ra, hãy thá»±c hiện hà nh động tương ứng.', + 'Next step' => 'Bước tiếp theo', + 'Define action parameters' => 'Xác định các tham số hà nh động', + 'Do you really want to remove this action: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» hà nh động nà y: "%s"?', + 'Remove an automatic action' => 'Há»§y bá» má»™t hà nh động tá»± động', + 'Assign the task to a specific user' => 'Chỉ định nhiệm vụ cho má»™t ngưá»i dùng cụ thể', + 'Assign the task to the person who does the action' => 'Chỉ định nhiệm vụ cho ngưá»i thá»±c hiện hà nh động', + 'Duplicate the task to another project' => 'Nhân đôi công việc vá»›i má»™t dá»± án khác', + 'Move a task to another column' => 'Di chuyển má»™t nhiệm vụ tá»›i má»™t cá»™t khác', + 'Task modification' => 'Sá»a đổi tác vụ', + 'Task creation' => 'Nhiệm vụ sáng tạo', + 'Closing a task' => 'Äóng má»™t nhiệm vụ', + 'Assign a color to a specific user' => 'Chỉ định mà u cho má»™t ngưá»i dùng cụ thể', + 'Position' => 'Chức vụ', + 'Duplicate to another project' => 'Nhân bản vá»›i má»™t dá»± án khác', + 'Duplicate' => 'Bản sao', + 'Link' => 'Liên kết', + 'Comment updated successfully.' => 'Bình luáºn cáºp nháºt thà nh công.', + 'Unable to update your comment.' => 'Không thể cáºp nháºt nháºn xét cá»§a bạn.', + 'Remove a comment' => 'Xóa má»™t bình luáºn', + 'Comment removed successfully.' => 'Äã xóa nháºn xét thà nh công.', + 'Unable to remove this comment.' => 'Không thể xóa nháºn xét nà y.', + 'Do you really want to remove this comment?' => 'Bạn có tháºt sá»± muốn xóa nháºn xét nà y?', + 'Current password for the user "%s"' => 'Máºt khẩu hiện tại cho ngưá»i dùng "%s"', + 'The current password is required' => 'Máºt khẩu hiện tại là bắt buá»™c', + 'Wrong password' => 'Sai máºt khẩu', + 'Unknown' => 'Không xác định', + 'Last logins' => 'Äăng nháºp lần cuối', + 'Login date' => 'Ngà y đăng nháºp', + 'Authentication method' => 'Phương pháp xác thá»±c', + 'IP address' => 'Äịa chỉ IP', + 'User agent' => 'Äại lý ngưá»i dùng', + 'Persistent connections' => 'Kết nối liên tục', + 'No session.' => 'Không có phiên.', + 'Expiration date' => 'Ngà y hết hạn', + 'Remember Me' => 'Nhá»› tôi', + 'Creation date' => 'Ngà y thà nh láºp', + 'Everybody' => 'Má»i ngưá»i', + 'Open' => 'Mở', + 'Closed' => 'Äóng', + 'Search' => 'Tìm kiếm', + 'Nothing found.' => 'Không kết quả.', + 'Due date' => 'Ngà y đáo hạn', + 'Description' => 'Mô tả', + '%d comments' => '%d nháºn xét', + '%d comment' => '%d nháºn xét', + 'Email address invalid' => 'Äịa chỉ email không hợp lệ', + 'Your external account is not linked anymore to your profile.' => 'Tà i khoản bên ngoà i cá»§a bạn không được liên kết vá»›i hồ sÆ¡ cá»§a bạn nữa.', + 'Unable to unlink your external account.' => 'Không thể há»§y liên kết tà i khoản bên ngoà i cá»§a bạn.', + 'External authentication failed' => 'Xác thá»±c bên ngoà i không thà nh công', + 'Your external account is linked to your profile successfully.' => 'Tà i khoản bên ngoà i cá»§a bạn được liên kết vá»›i hồ sÆ¡ cá»§a bạn thà nh công.', + 'Email' => 'E-mail', + 'Task removed successfully.' => 'Nhiệm vụ đã được gỡ bá» thà nh công.', + 'Unable to remove this task.' => 'Không thể xóa tác vụ nà y.', + 'Remove a task' => 'Loại bá» má»™t nhiệm vụ', + 'Do you really want to remove this task: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» công việc nà y: "%s"?', + 'Assign automatically a color based on a category' => 'Chỉ định tá»± động má»™t mà u dá»±a trên má»™t loại', + 'Assign automatically a category based on a color' => 'Chỉ định tá»± động má»™t thể loại dá»±a trên mà u', + 'Task creation or modification' => 'Tạo tác hoặc sá»a đổi nhiệm vụ', + 'Category' => 'Thể loại', + 'Category:' => 'Thể loại:', + 'Categories' => 'Thể loại', + 'Your category have been created successfully.' => 'Danh mục cá»§a bạn đã được tạo thà nh công.', + 'This category has been updated successfully.' => 'Danh mục nà y đã được cáºp nháºt thà nh công.', + 'Unable to update this category.' => 'Không thể cáºp nháºt danh mục nà y.', + 'Remove a category' => 'Loại bá» má»™t danh mục', + 'Category removed successfully.' => 'Loại bá» thà nh công.', + 'Unable to remove this category.' => 'Không thể xóa loại nà y.', + 'Category modification for the project "%s"' => 'Sá»a đổi loại cho dá»± án "%s"', + 'Category Name' => 'Tên danh mục', + 'Add a new category' => 'Thêm má»™t loại má»›i', + 'Do you really want to remove this category: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» loại nà y: "%s"?', + 'All categories' => 'Tất cả danh mục', + 'No category' => 'Không có loại', + 'The name is required' => 'Tên là bắt buá»™c', + 'Remove a file' => 'Há»§y bá» má»™t táºp tin', + 'Unable to remove this file.' => 'Không thể xóa tệp nà y.', + 'File removed successfully.' => 'Äã xoá tệp thà nh công.', + 'Attach a document' => 'ÄÃnh kèm má»™t tà i liệu', + 'Do you really want to remove this file: "%s"?' => 'Bạn có thá»±c sá»± muốn xóa tệp nà y: "%s"?', + 'Attachments' => 'File Ä‘Ãnh kèm', + 'Edit the task' => 'Chỉnh sá»a nhiệm vụ', + 'Add a comment' => 'Thêm nháºn xét', + 'Edit a comment' => 'Chỉnh sá»a má»™t nháºn xét', + 'Summary' => 'Tóm lược', + 'Time tracking' => 'Theo dõi thá»i gian', + 'Estimate:' => 'Ước tÃnh:', + 'Spent:' => 'Spent:', + 'Do you really want to remove this sub-task?' => 'Bạn có tháºt sá»± muốn loại bá» nhiệm vụ phụ nà y?', + 'Remaining:' => 'Còn lại:', + 'hours' => 'giá»', + 'spent' => 'chi tiêu', + 'estimated' => 'ước tÃnh', + 'Sub-Tasks' => 'Nhiệm vụ phụ', + 'Add a sub-task' => 'Thêm má»™t nhiệm vụ phụ', + 'Original estimate' => 'Ước tÃnh ban đầu', + 'Create another sub-task' => 'Tạo má»™t nhiệm vụ phụ khác', + 'Time spent' => 'Thá»i gian đã bá» ra', + 'Edit a sub-task' => 'Chỉnh sá»a má»™t tiểu nhiệm vụ', + 'Remove a sub-task' => 'Loại bá» má»™t nhiệm vụ phụ', + 'The time must be a numeric value' => 'Thá»i gian phải là má»™t giá trị số', + 'Todo' => 'Là m', + 'In progress' => 'Trong tiến trình', + 'Sub-task removed successfully.' => 'Nhiệm vụ phụ được xoá thà nh công.', + 'Unable to remove this sub-task.' => 'Không thể loại bá» nhiệm vụ phụ nà y.', + 'Sub-task updated successfully.' => 'Tiểu nhiệm vụ được cáºp nháºt thà nh công.', + 'Unable to update your sub-task.' => 'Không thể cáºp nháºt tiểu nhiệm vụ cá»§a bạn.', + 'Unable to create your sub-task.' => 'Không thể tạo ra nhiệm vụ phụ cá»§a bạn.', + 'Maximum size: ' => 'KÃch thước tối Ä‘a: ', + 'Display another project' => 'Hiển thị má»™t dá»± án khác', + 'Created by %s' => 'Äã tạo bởi %s', + 'Tasks Export' => 'Nhiệm vụ xuất khẩu', + 'Start Date' => 'Ngà y bắt đầu', + 'Execute' => 'Thá»±c hiện', + 'Task Id' => 'Nhiệm vụ Id', + 'Creator' => 'Ngưá»i sáng tạo', + 'Modification date' => 'Ngà y sá»a đổi', + 'Completion date' => 'Ngà y kết thúc', + 'Clone' => 'Clone', + 'Project cloned successfully.' => 'Dá»± án nhân bản thà nh công.', + 'Unable to clone this project.' => 'Không thể nhân bản dá»± án nà y.', + 'Enable email notifications' => 'Báºt thông báo qua email', + 'Task position:' => 'Nhiệm vụ:', + 'The task #%d have been opened.' => 'Nhiệm vụ #%d đã được mở ra.', + 'The task #%d have been closed.' => 'Nhiệm vụ #%d đã được đóng lại.', + 'Sub-task updated' => 'Tiểu nhiệm vụ cáºp nháºt', + 'Title:' => 'Chức vụ:', + 'Status:' => 'Trạng thái:', + 'Assignee:' => 'Ngưá»i được chuyển nhượng:', + 'Time tracking:' => 'Theo dõi thá»i gian:', + 'New sub-task' => 'Nhiệm vụ phụ má»›i', + 'New attachment added "%s"' => 'Táºp tin Ä‘Ãnh kèm má»›i được thêm và o "%s"', + 'New comment posted by %s' => 'Bình luáºn má»›i được đăng bởi %s', + 'New comment' => 'Bình luáºn má»›i', + 'Comment updated' => 'Bình luáºn cáºp nháºt', + 'New subtask' => 'Bổ sung phụ', + 'I want to receive notifications only for those projects:' => 'Tôi chỉ muốn nháºn thông báo cho các dá»± án đó:', + 'view the task on Kanboard' => 'xem công việc trên Kanboard', + 'Public access' => 'Truy cáºp công cá»™ng', + 'Disable public access' => 'Vô hiệu hoá quyá»n truy cáºp công cá»™ng', + 'Enable public access' => 'Báºt quyá»n truy cáºp công cá»™ng', + 'Public access disabled' => 'Truy cáºp công cá»™ng bị vô hiệu hóa', + 'Move the task to another project' => 'Di chuyển công việc sang dá»± án khác', + 'Move to another project' => 'Chuyển sang dá»± án khác', + 'Do you really want to duplicate this task?' => 'Bạn có thá»±c sá»± muốn lặp lại nhiệm vụ nà y?', + 'Duplicate a task' => 'Nhân đôi má»™t nhiệm vụ', + 'External accounts' => 'Tà i khoản bên ngoà i', + 'Account type' => 'Kiểu tà i khoản', + 'Local' => 'Äịa phương', + 'Remote' => 'Xa', + 'Enabled' => 'Báºt', + 'Disabled' => 'Vô hiệu', + 'Login:' => 'Äăng nháºp:', + 'Full Name:' => 'Há» và tên:', + 'Email:' => 'E-mail:', + 'Notifications:' => 'Thông báo:', + 'Notifications' => 'Thông báo', + 'Account type:' => 'Kiểu tà i khoản:', + 'Edit profile' => 'Chỉnh sá»a hồ sÆ¡', + 'Change password' => 'Äổi máºt khẩu', + 'Password modification' => 'Sá»a đổi máºt khẩu', + 'External authentications' => 'Xác thá»±c bên ngoà i', + 'Never connected.' => 'Không bao giá» kết nối.', + 'No external authentication enabled.' => 'Không báºt xác thá»±c bên ngoà i.', + 'Password modified successfully.' => 'Äã sá»a đổi máºt khẩu thà nh công.', + 'Unable to change the password.' => 'Không thể thay đổi máºt khẩu.', + 'Change category' => 'Thay đổi loại', + '%s updated the task %s' => ' %s đã cáºp nháºt công việc %s', + '%s opened the task %s' => ' %s mở nhiệm vụ %s', + '%s moved the task %s to the position #%d in the column "%s"' => ' %s di chuyển nhiệm vụ %s đến vị trà #%d trong cá»™t "%s"', + '%s moved the task %s to the column "%s"' => ' %s di chuyển công việc %s đến cá»™t "%s"', + '%s created the task %s' => ' %s đã tạo ra nhiệm vụ %s', + '%s closed the task %s' => ' %s đã đóng công việc %s', + '%s created a subtask for the task %s' => ' %s tạo ra má»™t phụ cho nhiệm vụ %s', + '%s updated a subtask for the task %s' => ' %s cáºp nháºt má»™t công việc phụ cho nhiệm vụ %s', + 'Assigned to %s with an estimate of %s/%sh' => 'ÄÆ°á»£c giao cho %s vá»›i ước tÃnh %s / %sh', + 'Not assigned, estimate of %sh' => 'Không được chỉ định, ước tÃnh %sh', + '%s updated a comment on the task %s' => ' %s đã cáºp nháºt má»™t bình luáºn vá» nhiệm vụ %s', + '%s commented the task %s' => ' %s bình luáºn vá» nhiệm vụ %s', + '%s\'s activity' => 'Hoạt động cá»§a %s', + 'RSS feed' => 'Nguồn cấp dữ liệu RSS', + '%s updated a comment on the task #%d' => ' %s đã cáºp nháºt má»™t bình luáºn vá» nhiệm vụ #%d', + '%s commented on the task #%d' => ' %s bình luáºn vá» nhiệm vụ #%d', + '%s updated a subtask for the task #%d' => ' %s đã cáºp nháºt má»™t công việc phụ cho nhiệm vụ #%d', + '%s created a subtask for the task #%d' => ' %s tạo ra má»™t phụ cho nhiệm vụ #%d', + '%s updated the task #%d' => ' %s cáºp nháºt công việc #%d', + '%s created the task #%d' => ' %s đã tạo ra nhiệm vụ #%d', + '%s closed the task #%d' => ' %s đã đóng công việc #%d', + '%s opened the task #%d' => ' %s mở nhiệm vụ #%d', + 'Activity' => 'Hoạt động', + 'Default values are "%s"' => 'Giá trị mặc định là "%s"', + 'Default columns for new projects (Comma-separated)' => 'Cá»™t mặc định cho các dá»± án má»›i (tách bằng dấu phẩy)', + 'Task assignee change' => 'Thay đổi nhiệm vụ chuyển nhượng', + '%s changed the assignee of the task #%d to %s' => ' %s đã thay đổi ngưá»i được giao nhiệm vụ #%d thà nh %s', + '%s changed the assignee of the task %s to %s' => ' %s đã thay đổi ngưá»i được giao nhiệm vụ %s sang %s', + 'New password for the user "%s"' => 'Máºt khẩu má»›i cho ngưá»i dùng "%s"', + 'Choose an event' => 'Chá»n má»™t sá»± kiện', + 'Create a task from an external provider' => 'Tạo má»™t nhiệm vụ từ má»™t nhà cung cấp bên ngoà i', + 'Change the assignee based on an external username' => 'Thay đổi ngưá»i được chuyển nhượng dá»±a trên tên ngưá»i dùng bên ngoà i', + 'Change the category based on an external label' => 'Thay đổi thể loại dá»±a trên má»™t nhãn bên ngoà i', + 'Reference' => 'Tà i liệu tham khảo', + 'Label' => 'Nhãn', + 'Database' => 'CÆ¡ sở dữ liệu', + 'About' => 'Vá»', + 'Database driver:' => 'Trình Ä‘iá»u khiển cÆ¡ sở dữ liệu:', + 'Board settings' => 'Cà i đặt Bảng', + 'Webhook settings' => 'Cà i đặt Webhook', + 'Reset token' => 'Äặt lại mã thông báo', + 'API endpoint:' => 'Äiểm cuối API:', + 'Refresh interval for private board' => 'Khoảng thá»i gian là m má»›i cho bảng cá nhân', + 'Refresh interval for public board' => 'Khoảng thá»i gian là m má»›i cho há»™i đồng quản trị', + 'Task highlight period' => 'Nhiệm vụ thá»i gian nổi báºt', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Thá»i gian (thứ hai) để xem xét má»™t tác vụ đã được sá»a đổi gần đây (0 để vô hiệu hoá, 2 ngà y theo mặc định)', + 'Frequency in second (60 seconds by default)' => 'Tần số trong giây (mặc định 60 giây)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Tần số trong giây (0 để tắt tÃnh năng nà y, mặc định là 10 giây)', + 'Application URL' => 'URL ứng dụng', + 'Token regenerated.' => 'Token tái tạo.', + 'Date format' => 'Äịnh dạng ngà y tháng', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Äịnh dạng ISO luôn được chấp nháºn, và dụ: "%s" và "%s"', + 'New private project' => 'Dá»± án riêng tư má»›i', + 'This project is private' => 'Dá»± án nà y là riêng tư', + 'Add' => 'Thêm và o', + 'Start date' => 'Ngà y bắt đầu', + 'Time estimated' => 'Thá»i gian ước tÃnh', + 'There is nothing assigned to you.' => 'Không có gì được giao cho bạn.', + 'My tasks' => 'Nhiệm vụ cá»§a tôi', + 'Activity stream' => 'Luồng hoạt động', + 'Dashboard' => 'Bảng Ä‘iá»u khiển', + 'Confirmation' => 'Xác nháºn', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Tạo má»™t bình luáºn từ má»™t nhà cung cấp bên ngoà i', + 'Project management' => 'Quản lý dá»± án', + 'Columns' => 'Cá»™t', + 'Task' => 'Bà i táºp', + 'Percentage' => 'Phần trăm', + 'Number of tasks' => 'Số nhiệm vụ', + 'Task distribution' => 'Phân phát nhiệm vụ', + 'Analytics' => 'Phân tÃch', + 'Subtask' => 'Subtask', + 'User repartition' => 'Phân vùng ngưá»i dùng', + 'Clone this project' => 'Nhân bản dá»± án nà y', + 'Column removed successfully.' => 'Bá» cá»™t thà nh công.', + 'Not enough data to show the graph.' => 'Không đủ dữ liệu để hiển thị đồ thị.', + 'Previous' => 'Trước', + 'The id must be an integer' => 'Id phải là má»™t số nguyên', + 'The project id must be an integer' => 'Id dá»± án phải là má»™t số nguyên', + 'The status must be an integer' => 'Trạng thái phải là má»™t số nguyên', + 'The subtask id is required' => 'Cần phải có id phụ Ä‘á»', + 'The subtask id must be an integer' => 'Số thứ tá»± subtask phải là má»™t số nguyên', + 'The task id is required' => 'ID công việc được yêu cầu', + 'The task id must be an integer' => 'ID công việc phải là má»™t số nguyên', + 'The user id must be an integer' => 'ID ngưá»i dùng phải là má»™t số nguyên', + 'This value is required' => 'Giá trị nà y là bắt buá»™c', + 'This value must be numeric' => 'Giá trị nà y phải là số', + 'Unable to create this task.' => 'Không thể tạo ra nhiệm vụ nà y.', + 'Cumulative flow diagram' => 'SÆ¡ đồ luồng tÃch luỹ', + 'Daily project summary' => 'Tóm tắt dá»± án hà ng ngà y', + 'Daily project summary export' => 'Tóm tắt dá»± án hà ng ngà y xuất khẩu', + 'Exports' => 'Xuất khẩu', + 'This export contains the number of tasks per column grouped per day.' => 'Xuất nà y có chứa số nhiệm vụ cho má»—i cá»™t được nhóm trong má»™t ngà y.', + 'Active swimlanes' => 'ÄÆ°á»ng phố hoạt động', + 'Add a new swimlane' => 'Thêm má»™t đưá»ng swimlane má»›i', + 'Default swimlane' => 'Mặc định swimlane', + 'Do you really want to remove this swimlane: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» swimlane nà y: "%s"?', + 'Inactive swimlanes' => 'Bể swimlane không hoạt động', + 'Remove a swimlane' => 'Há»§y bá» swimlane lá»™i', + 'Swimlane modification for the project "%s"' => 'Sá»a đổi Swimlane cho dá»± án %s', + 'Swimlane removed successfully.' => 'Swimlane đã được gỡ bá» thà nh công.', + 'Swimlanes' => 'Swimlanes', + 'Swimlane updated successfully.' => 'Swimlane được cáºp nháºt thà nh công.', + 'Unable to remove this swimlane.' => 'Không thể tháo dải swimlane ra.', + 'Unable to update this swimlane.' => 'Không thể cáºp nháºt swimlane lá»™i nà y.', + 'Your swimlane have been created successfully.' => 'Là n sóng cá»§a bạn đã được tạo ra thà nh công.', + 'Example: "Bug, Feature Request, Improvement"' => 'Và dụ: "Lá»—i, yêu cầu tÃnh năng, cải tiến"', + 'Default categories for new projects (Comma-separated)' => 'Danh mục mặc định cho các dá»± án má»›i (Phân cách bằng dấu phẩy)', + 'Integrations' => 'TÃch hợp', + 'Integration with third-party services' => 'TÃch hợp vá»›i các dịch vụ cá»§a bên thứ ba', + 'Subtask Id' => 'Id phụ Ä‘á»', + 'Subtasks' => 'Công việc', + 'Subtasks Export' => 'Nhiệm vụ xuất khẩu', + 'Task Title' => 'Nhiệm vụ', + 'Untitled' => 'Không có tiêu Ä‘á»', + 'Application default' => 'Mặc định ứng dụng', + 'Language:' => 'Ngôn ngữ:', + 'Timezone:' => 'Múi giá»:', + 'All columns' => 'Tất cả các cá»™t', + 'Next' => 'Kế tiếp', + '#%d' => '#%d', + 'All swimlanes' => 'Tất cả swimlane', + 'All colors' => 'Äá»§ mà u sắc', + 'Moved to column %s' => 'Äã chuyển đến cá»™t %s', + 'User dashboard' => 'Trang tổng quan cá»§a ngưá»i dùng', + 'Allow only one subtask in progress at the same time for a user' => 'Cho phép chỉ có má»™t phụ đỠđang được tiến hà nh đồng thá»i cho má»™t ngưá»i dùng', + 'Edit column "%s"' => 'Chỉnh sá»a cá»™t %s ', + 'Select the new status of the subtask: "%s"' => 'Chá»n trạng thái má»›i cá»§a phụ Ä‘á»: "%s"', + 'Subtask timesheet' => 'Lịch biểu phụ Ä‘á»', + 'There is nothing to show.' => 'Không có gì để hiển thị.', + 'Time Tracking' => 'Theo dõi Thá»i gian', + 'You already have one subtask in progress' => 'Bạn đã có má»™t phụ trong tiến trình', + 'Which parts of the project do you want to duplicate?' => 'Bạn muốn nhân bản phần nà o cá»§a dá»± án?', + 'Disallow login form' => 'Disallow form đăng nháºp', + 'Start' => 'Khởi đầu', + 'End' => 'Kết thúc', + 'Task age in days' => 'Nhiệm vụ tuổi tác trong ngà y', + 'Days in this column' => 'Ngà y trong cá»™t nà y', + '%dd' => '%dd', + 'Add a new link' => 'Thêm má»™t liên kết má»›i', + 'Do you really want to remove this link: "%s"?' => 'Bạn có thá»±c sá»± muốn xoá liên kết nà y: "%s"?', + 'Do you really want to remove this link with task #%d?' => 'Bạn có thá»±c sá»± muốn loại bá» liên kết nà y vá»›i công việc #%d?', + 'Field required' => 'Trưá»ng bắt buá»™c', + 'Link added successfully.' => 'Liên kết thà nh công.', + 'Link updated successfully.' => 'Liên kết được cáºp nháºt thà nh công.', + 'Link removed successfully.' => 'Liên kết đã xoá thà nh công.', + 'Link labels' => 'Liên kết nhãn', + 'Link modification' => 'Liên kết sá»a đổi', + 'Links' => 'Liên kết', + 'Opposite label' => 'Ngược lại nhãn', + 'Remove a link' => 'Há»§y liên kết', + 'The labels must be different' => 'Các nhãn phải khác nhau', + 'There is no link.' => 'Không có liên kết.', + 'This label must be unique' => 'Nhãn nà y phải là duy nhất', + 'Unable to create your link.' => 'Không thể tạo liên kết cá»§a bạn.', + 'Unable to update your link.' => 'Không thể cáºp nháºt liên kết cá»§a bạn.', + 'Unable to remove this link.' => 'Không thể xóa liên kết nà y.', + 'relates to' => 'liên quan tá»›i', + 'blocks' => 'khối', + 'is blocked by' => 'bị chặn bởi', + 'duplicates' => 'bản sao', + 'is duplicated by' => 'được nhân đôi bởi', + 'is a child of' => 'là má»™t đứa trẻ', + 'is a parent of' => 'là cha mẹ cá»§a', + 'targets milestone' => 'mục tiêu cá»™t mốc', + 'is a milestone of' => 'là má»™t mốc quan trá»ng cá»§a', + 'fixes' => 'sá»a lá»—i', + 'is fixed by' => 'được sá»a bởi', + 'This task' => 'Nhiệm vụ nà y', + '<1h' => '<1 giá»', + '%dh' => '%dh', + 'Expand tasks' => 'Mở rá»™ng các nhiệm vụ', + 'Collapse tasks' => 'Thu hẹp nhiệm vụ', + 'Expand/collapse tasks' => 'Mở rá»™ng / thu gá»n nhiệm vụ', + 'Close dialog box' => 'Äóng há»™p thoại', + 'Submit a form' => 'Gá»i má»™t mẫu', + 'Board view' => 'Quan Ä‘iểm cá»§a há»™i đồng quản trị', + 'Keyboard shortcuts' => 'Các phÃm tắt bà n phÃm', + 'Open board switcher' => 'Open board switcher', + 'Application' => 'Ứng dụng', + 'Compact view' => 'Chế độ xem nhá» gá»n', + 'Horizontal scrolling' => 'Cuá»™n ngang', + 'Compact/wide view' => 'Chế độ xem nhá» gá»n / rá»™ng', + 'Currency' => 'Tiá»n tệ', + 'Private project' => 'Dá»± án riêng tư', + 'AUD - Australian Dollar' => 'AUD - Äô la Úc', + 'CAD - Canadian Dollar' => 'CAD - Äô la Canada', + 'CHF - Swiss Francs' => 'CHF - Swiss Francs', + 'Custom Stylesheet' => 'Biểu định kiểu Tuỳ chỉnh', + 'download' => 'tải vá»', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Bảng Anh', + 'INR - Indian Rupee' => 'INR - Rupi Ấn Äá»™', + 'JPY - Japanese Yen' => 'JPY - Yên Nháºt', + 'NZD - New Zealand Dollar' => 'NZD - Äô la New Zealand', + 'RSD - Serbian dinar' => 'RSD - dinar Serbia', + 'CNY - Chinese Yuan' => 'CNY - Yuan Trung Quốc', + 'USD - US Dollar' => 'Äô la Mỹ - Äô la Mỹ', + 'Destination column' => 'Cá»™t Ä‘Ãch', + 'Move the task to another column when assigned to a user' => 'Chuyển nhiệm vụ sang cá»™t khác khi được chỉ định cho ngưá»i dùng', + 'Move the task to another column when assignee is cleared' => 'Di chuyển nhiệm vụ sang cá»™t khác khi ngưá»i nháºn chuyển giao bị xóa', + 'Source column' => 'Cá»™t nguồn', + 'Transitions' => 'Chuyển tiếp', + 'Executer' => 'Executer', + 'Time spent in the column' => 'Thá»i gian trong cá»™t', + 'Task transitions' => 'Nhiệm vụ chuyển tiếp', + 'Task transitions export' => 'Nhiệm vụ chuyển tiếp xuất khẩu', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Báo cáo nà y chứa tất cả các cá»™t di chuyển cho má»—i nhiệm vụ vá»›i ngà y, ngưá»i sá» dụng và thá»i gian dà nh cho má»—i quá trình chuyển đổi.', + 'Currency rates' => 'Tá»· giá tiá»n tệ', + 'Rate' => 'Tá»· lệ', + 'Change reference currency' => 'Thay đổi đồng tiá»n tham chiếu', + 'Reference currency' => 'Tiá»n tệ tham khảo', + 'The currency rate have been added successfully.' => 'Tá»· giá đã được thêm thà nh công.', + 'Unable to add this currency rate.' => 'Không thể thêm tá»· giá tiá»n tệ nà y.', + 'Webhook URL' => 'Webhook URL', + '%s removed the assignee of the task %s' => ' %s loại bá» ngưá»i nháºn chuyển nhượng cá»§a nhiệm vụ %s', + 'Information' => 'Thông tin', + 'Check two factor authentication code' => 'Kiểm tra mã xác thá»±c hai lá»›p', + 'The two factor authentication code is not valid.' => 'Mã xác thá»±c hai lá»›p không hợp lệ.', + 'The two factor authentication code is valid.' => 'Mã xác thá»±c hai lá»›p là hợp lệ.', + 'Code' => 'Mã', + 'Two factor authentication' => 'Xác thá»±c hai lá»›p', + 'This QR code contains the key URI: ' => 'Mã QR nà y có chứa URI chÃnh:', + 'Check my code' => 'Kiểm tra mã cá»§a tôi', + 'Secret key: ' => 'Chìa khoá bà máºt: ', + 'Test your device' => 'Kiểm tra thiết bị cá»§a bạn', + 'Assign a color when the task is moved to a specific column' => 'Chỉ định má»™t mà u khi công việc được chuyển đến má»™t cá»™t cụ thể', + '%s via Kanboard' => ' %s qua Kanboard', + 'Burndown chart' => 'Biểu đồ Burndown', + 'This chart show the task complexity over the time (Work Remaining).' => 'Biểu đồ nà y cho thấy sá»± phức tạp cá»§a công việc qua thá»i gian (Work Remaining).', + 'Screenshot taken %s' => 'Äã chụp ảnh chụp mà n hình %s', + 'Add a screenshot' => 'Thêm ảnh chụp mà n hình', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Chụp ảnh mà n hình và nhấn CTRL + V hoặc ⌘ + V để dán và o đây.', + 'Screenshot uploaded successfully.' => 'Ảnh chụp mà n hình được tải lên thà nh công.', + 'SEK - Swedish Krona' => 'SEK - Krona Thu Swedish Äiển', + 'Identifier' => 'Bá»™ nháºn dạng', + 'Disable two factor authentication' => 'Vô hiệu hoá hai lá»›p xác thá»±c', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Bạn có thá»±c sá»± muốn vô hiệu hóa xác thá»±c hai lá»›p cho ngưá»i dùng nà y: "%s"?', + 'Edit link' => 'Chỉnh sá»a liên kết', + 'Start to type task title...' => 'Bắt đầu gõ tên tác vụ ...', + 'A task cannot be linked to itself' => 'Má»™t nhiệm vụ không thể được liên kết vá»›i chÃnh nó', + 'The exact same link already exists' => 'Liên kết chÃnh xác đã tồn tại', + 'Recurrent task is scheduled to be generated' => 'Nhiệm vụ lặp lại được dá»± kiến ​​sẽ được tạo ra', + 'Score' => 'Ghi bà n', + 'The identifier must be unique' => 'Mã nháºn diện phải là duy nhất', + 'This linked task id doesn\'t exists' => 'Không có id công việc liên kết nà o tồn tại', + 'This value must be alphanumeric' => 'Giá trị nà y phải là chữ số', + 'Edit recurrence' => 'Chỉnh sá»a sá»± tái phát', + 'Generate recurrent task' => 'Tạo ra công việc lặp Ä‘i lặp lại', + 'Trigger to generate recurrent task' => 'Trigger để tạo ra công việc lặp Ä‘i lặp lại', + 'Factor to calculate new due date' => 'Yếu tố tÃnh ngà y đáo hạn má»›i', + 'Timeframe to calculate new due date' => 'Khung thá»i gian để tÃnh toán ngà y đáo hạn má»›i', + 'Base date to calculate new due date' => 'Base date để tÃnh ngà y đáo hạn má»›i', + 'Action date' => 'Ngà y hà nh động', + 'Base date to calculate new due date: ' => 'Căn cứ để tÃnh ngà y đáo hạn má»›i:', + 'This task has created this child task: ' => 'Nhiệm vụ nà y đã tạo ra nhiệm vụ con nà y:', + 'Day(s)' => 'Ngà y', + 'Existing due date' => 'Ngà y hết hạn', + 'Factor to calculate new due date: ' => 'Yếu tố tÃnh ngà y đáo hạn má»›i:', + 'Month(s)' => 'Tháng)', + 'Recurrence' => 'Sá»± tái xuất', + 'This task has been created by: ' => 'Nhiệm vụ nà y đã được tạo ra bằng cách:', + 'Recurrent task has been generated:' => 'Nhiệm vụ lặp lại đã được tạo ra:', + 'Timeframe to calculate new due date: ' => 'Khung thá»i gian tÃnh ngà y đáo hạn má»›i:', + 'Trigger to generate recurrent task: ' => 'KÃch hoạt để tạo ra công việc lặp Ä‘i lặp lại:', + 'When task is closed' => 'Khi công việc được đóng lại', + 'When task is moved from first column' => 'Khi công việc được di chuyển từ cá»™t đầu tiên', + 'When task is moved to last column' => 'Khi công việc được chuyển đến cá»™t cuối cùng', + 'Year(s)' => 'Năm (s)', + 'Project settings' => 'Cà i đặt dá»± án', + 'Automatically update the start date' => 'Tá»± động cáºp nháºt ngà y bắt đầu', + 'iCal feed' => 'nguồn cấp dữ liệu iCal', + 'Preferences' => 'Sở thÃch', + 'Security' => 'An ninh', + 'Two factor authentication disabled' => 'Xác thá»±c hai lá»›p bị vô hiệu', + 'Two factor authentication enabled' => 'KÃch hoạt chứng thá»±c hai lá»›p', + 'Unable to update this user.' => 'Không thể cáºp nháºt ngưá»i dùng nà y.', + 'There is no user management for private projects.' => 'Không có ngưá»i quản lý ngưá»i dùng cho các dá»± án riêng tư.', + 'User that will receive the email' => 'Ngưá»i dùng sẽ nháºn được email', + 'Email subject' => 'Chá»§ đỠemail', + 'Date' => 'Ngà y', + 'Add a comment log when moving the task between columns' => 'Thêm nháºt ký nháºn xét khi di chuyển nhiệm vụ giữa các cá»™t', + 'Move the task to another column when the category is changed' => 'Di chuyển công việc sang cá»™t khác khi danh mục được thay đổi', + 'Send a task by email to someone' => 'Gá»i má»™t nhiệm vụ qua email cho ai đó', + 'Reopen a task' => 'Mở lại má»™t nhiệm vụ', + 'Notification' => 'Thông báo', + '%s moved the task #%d to the first swimlane' => ' %s di chuyển nhiệm vụ #%d đến ngưá»i đầu tiên swimlane', + 'Swimlane' => 'Swimlane', + '%s moved the task %s to the first swimlane' => ' %s di chuyển nhiệm vụ %s tá»›i swimlane đầu tiên', + '%s moved the task %s to the swimlane "%s"' => ' %s di chuyển nhiệm vụ %s tá»›i swimlane lá»™i "%s"', + 'This report contains all subtasks information for the given date range.' => 'Báo cáo nà y chứa tất cả các thông tin phụ cho má»™t phạm vi ngà y.', + 'This report contains all tasks information for the given date range.' => 'Báo cáo nà y chứa tất cả các nhiệm vụ thông tin cho phạm vi ngà y nhất định.', + 'Project activities for %s' => 'Các hoạt động dá»± án cho %s', + 'view the board on Kanboard' => 'xem bảng trên Kanboard', + 'The task have been moved to the first swimlane' => 'Nhiệm vụ đã được dá»i đến sân váºn động đầu tiên', + 'The task have been moved to another swimlane:' => 'Nhiệm vụ đã được chuyển sang má»™t đội swimlane khác: ', + 'New title: %s' => 'Tiêu đỠmá»›i: %s', + 'The task is not assigned anymore' => 'Nhiệm vụ không được giao nữa', + 'New assignee: %s' => 'Ngưá»i được chuyển nhượng má»›i: %s', + 'There is no category now' => 'Không có danh mục bây giá»', + 'New category: %s' => 'Thể loại má»›i: %s', + 'New color: %s' => 'Mà u má»›i: %s', + 'New complexity: %d' => 'Sá»± phức tạp má»›i: %d', + 'The due date have been removed' => 'Ngà y đáo hạn đã bị xoá', + 'There is no description anymore' => 'Không còn mô tả nữa', + 'Recurrence settings have been modified' => 'Cà i đặt tái láºp đã được sá»a đổi', + 'Time spent changed: %sh' => 'Thá»i gian thay đổi: %sh', + 'Time estimated changed: %sh' => 'Thá»i gian ước tÃnh thay đổi: %sh', + 'The field "%s" have been updated' => 'Trưá»ng "%s" đã được cáºp nháºt', + 'The description has been modified:' => 'Mô tả đã được sá»a đổi:', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Bạn có thá»±c sá»± muốn đóng nhiệm vụ "%s" cÅ©ng như tất cả các nhiệm vụ phụ?', + 'I want to receive notifications for:' => 'Tôi muốn nháºn thông báo cho:', + 'All tasks' => 'Tất cả các nhiệm vụ', + 'Only for tasks assigned to me' => 'Chỉ đối vá»›i những nhiệm vụ được giao cho tôi', + 'Only for tasks created by me' => 'Chỉ cho những công việc do tôi tạo ra', + 'Only for tasks created by me and assigned to me' => 'Chỉ cho những công việc mà tôi tạo ra và được giao cho tôi', + '%%Y-%%m-%%d' => '%%Y - %%m - %%d', + 'Total for all columns' => 'Tổng cho tất cả các cá»™t', + 'You need at least 2 days of data to show the chart.' => 'Bạn cần Ãt nhất 2 ngà y dữ liệu để hiển thị biểu đồ.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Ngừng hẹn giá»', + 'Start timer' => 'Bắt đầu hẹn giá»', + 'My activity stream' => 'Dòng hoạt động cá»§a tôi', + 'Search tasks' => 'Các nhiệm vụ tìm kiếm', + 'Reset filters' => 'Äặt lại bá»™ lá»c', + 'My tasks due tomorrow' => 'Nhiệm vụ cá»§a tôi và o ngà y mai', + 'Tasks due today' => 'Nhiệm vụ hôm nay', + 'Tasks due tomorrow' => 'Nhiệm vụ và o ngà y mai', + 'Tasks due yesterday' => 'Nhiệm vụ do ngà y hôm qua', + 'Closed tasks' => 'Các nhiệm vụ đã đóng', + 'Open tasks' => 'Mở nhiệm vụ', + 'Not assigned' => 'Không được chỉ định', + 'View advanced search syntax' => 'Xem cú pháp tìm kiếm nâng cao', + 'Overview' => 'Tổng quan', + 'Board/Calendar/List view' => 'Chế độ xem há»™i đồng quản trị / lịch / danh sách', + 'Switch to the board view' => 'Chuyển sang chế độ xem bảng', + 'Switch to the list view' => 'Chuyển sang chế độ xem danh sách', + 'Go to the search/filter box' => 'Äi tá»›i há»™p tìm kiếm / bá»™ lá»c', + 'There is no activity yet.' => 'Không có hoạt động nà o.', + 'No tasks found.' => 'Không tìm thấy công việc nà o.', + 'Keyboard shortcut: "%s"' => 'Các phÃm tắt bà n phÃm "%s"', + 'List' => 'Danh sách', + 'Filter' => 'Lá»c', + 'Advanced search' => 'Tìm kiếm nâng cao', + 'Example of query: ' => 'Và dụ vá» truy vấn:', + 'Search by project: ' => 'Tìm kiếm theo dá»± án:', + 'Search by column: ' => 'Tìm kiếm theo cá»™t:', + 'Search by assignee: ' => 'Tìm kiếm bởi ngưá»i được chuyển nhượng:', + 'Search by color: ' => 'Tìm kiếm theo mà u sắc:', + 'Search by category: ' => 'Tìm kiếm theo thể loại:', + 'Search by description: ' => 'Tìm theo mô tả:', + 'Search by due date: ' => 'Tìm kiếm theo ngà y đáo hạn:', + 'Average time spent into each column' => 'Thá»i gian trung bình dà nh cho từng cá»™t', + 'Average time spent' => 'Thá»i gian trung bình dà nh', + 'This chart show the average time spent into each column for the last %d tasks.' => 'Biểu đồ nà y cho thấy thá»i gian trung bình dà nh cho má»—i cá»™t cho các tác vụ %d cuối cùng.', + 'Average Lead and Cycle time' => 'Thá»i gian trung bình và thá»i gian chu kỳ', + 'Average lead time: ' => 'Thá»i gian dẫn trung bình:', + 'Average cycle time: ' => 'Thá»i gian chu kỳ trung bình:', + 'Cycle Time' => 'Thá»i gian chu kỳ', + 'Lead Time' => 'Chì Thá»i gian', + 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Biểu đồ nà y cho thấy thá»i gian dẫn và thá»i gian trung bình cho các tác vụ %d cuối cùng qua thá»i gian.', + 'Average time into each column' => 'Thá»i gian trung bình và o má»—i cá»™t', + 'Lead and cycle time' => 'Chì và thá»i gian chu kỳ', + 'Lead time: ' => 'Chì thá»i gian:', + 'Cycle time: ' => 'Thá»i gian chu kỳ: ', + 'Time spent into each column' => 'Thá»i gian dà nh cho từng cá»™t', + 'The lead time is the duration between the task creation and the completion.' => 'Thá»i gian dẫn là khoảng thá»i gian giữa nhiệm vụ sáng tạo và hoà n thà nh.', + 'The cycle time is the duration between the start date and the completion.' => 'Thá»i gian chu kỳ là khoảng thá»i gian giữa ngà y bắt đầu và ngà y hoà n thà nh.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Nếu nhiệm vụ không đóng thì thá»i gian hiện tại được sá» dụng thay vì ngà y hoà n thà nh.', + 'Set automatically the start date' => 'Äặt tá»± động ngà y bắt đầu', + 'Edit Authentication' => 'Chỉnh sá»a Xác thá»±c', + 'Remote user' => 'Ngưá»i dùng từ xa', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Ngưá»i dùng từ xa không lưu máºt khẩu cá»§a há» trong cÆ¡ sở dữ liệu Kanboard, và dụ: LDAP, Google và Github.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Nếu bạn đánh dấu ô "Disallow form đăng nháºp", các thông tin đăng nháºp và o mẫu đăng nháºp sẽ bị bá» qua.', + 'Default task color' => 'Mà u nhiệm vụ mặc định', + 'This feature does not work with all browsers.' => 'TÃnh năng nà y không hoạt động vá»›i tất cả các trình duyệt.', + 'There is no destination project available.' => 'Không có dá»± án Ä‘iểm đến có sẵn.', + 'Trigger automatically subtask time tracking' => 'Trigger tá»± động theo dõi thá»i gian theo dõi', + 'Include closed tasks in the cumulative flow diagram' => 'Bao gồm các nhiệm vụ đã đóng trong sÆ¡ đồ luồng tÃch lÅ©y', + 'Current swimlane: %s' => 'Dòng swimlane hiện tại: %s', + 'Current column: %s' => 'Cá»™t hiện tại: %s', + 'Current category: %s' => 'Thể loại hiện tại: %s', + 'no category' => 'không có loại', + 'Current assignee: %s' => 'Äang chuyển nhượng hiện tại: %s', + 'not assigned' => 'không được chỉ định', + 'Author:' => 'Tác giả:', + 'contributors' => 'ngưá»i đóng góp', + 'License:' => 'Giấy phép:', + 'License' => 'Giấy phép', + 'Enter the text below' => 'Nháºp văn bản dưới đây', + 'Start date:' => 'Ngà y bắt đầu:', + 'Due date:' => 'Ngà y đáo hạn:', + 'People who are project managers' => 'Những ngưá»i quản lý dá»± án', + 'People who are project members' => 'Những ngưá»i là thà nh viên cá»§a dá»± án', + 'NOK - Norwegian Krone' => 'NOK - Krone Na Uy', + 'Show this column' => 'Hiển thị cá»™t nà y', + 'Hide this column' => 'Ẩn cá»™t nà y', + 'open file' => 'mở táºp tin', + 'End date' => 'Ngà y cuối', + 'Users overview' => 'Tổng quan ngưá»i dùng', + 'Members' => 'Các thà nh viên', + 'Shared project' => 'Dá»± án được chia sẻ', + 'Project managers' => 'Quản lý dá»± án', + 'Projects list' => 'Danh sách các dá»± án', + 'End date:' => 'Ngà y cuối:', + 'Change task color when using a specific task link' => 'Thay đổi mà u nhiệm vụ khi sá» dụng liên kết nhiệm vụ cụ thể', + 'Task link creation or modification' => 'Tạo hoặc sá»a đổi liên kết nhiệm vụ', + 'Milestone' => 'Milestone', + 'Documentation: %s' => 'Tà i liệu: %s', + 'Reset the search/filter box' => 'Äặt lại há»™p tìm kiếm / bá»™ lá»c', + 'Documentation' => 'Tà i liệu hướng dẫn', + 'Table of contents' => 'Mục lục', + 'Author' => 'Tác giả', + 'Version' => 'Phiên bản', + 'Plugins' => 'Plugins', + 'There is no plugin loaded.' => 'Không có plugin nạp.', + 'My notifications' => 'Thông báo cá»§a tôi', + 'Custom filters' => 'Bá»™ lá»c tùy chỉnh', + 'Your custom filter have been created successfully.' => 'Bá»™ lá»c tuỳ chỉnh cá»§a bạn đã được tạo thà nh công.', + 'Unable to create your custom filter.' => 'Không thể tạo bá»™ lá»c tuỳ chỉnh cá»§a bạn.', + 'Custom filter removed successfully.' => 'Äã xoá bá»™ lá»c tùy chỉnh.', + 'Unable to remove this custom filter.' => 'Không thể xóa bá»™ lá»c tùy chỉnh nà y.', + 'Edit custom filter' => 'Chỉnh sá»a bá»™ lá»c tùy chỉnh', + 'Your custom filter have been updated successfully.' => 'Bá»™ lá»c tuỳ chỉnh cá»§a bạn đã được cáºp nháºt thà nh công.', + 'Unable to update custom filter.' => 'Không thể cáºp nháºt bá»™ lá»c tuỳ chỉnh.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Táºp tin Ä‘Ãnh kèm má»›i trên task #%d: %s', + 'New comment on task #%d' => 'à kiến ​​má»›i vá» nhiệm vụ #%d', + 'Comment updated on task #%d' => 'Bình luáºn cáºp nháºt vá» nhiệm vụ #%d', + 'New subtask on task #%d' => 'Bổ sung phụ vá» nhiệm vụ #%d', + 'Subtask updated on task #%d' => 'Không cáºp nháºt được nhiệm vụ #%d', + 'New task #%d: %s' => 'Nhiệm vụ má»›i %d: %s', + 'Task updated #%d' => 'Äã cáºp nháºt nhiệm vụ #%d', + 'Task #%d closed' => 'Nhiệm vụ #%d đóng', + 'Task #%d opened' => 'Nhiệm vụ #%d mở', + 'Column changed for task #%d' => 'Äã thay đổi cá»™t cho tác vụ #%d', + 'New position for task #%d' => 'Vị trà má»›i cho nhiệm vụ #%d', + 'Swimlane changed for task #%d' => 'Swimlane đã thay đổi cho nhiệm vụ #%d', + 'Assignee changed on task #%d' => 'Ngưá»i được chỉ định thay đổi vá» nhiệm vụ #%d', + '%d overdue tasks' => '%d quá hạn tác vụ', + 'Task #%d is overdue' => 'Công việc #%d là quá hạn', + 'No notification.' => 'Không có thông báo.', + 'Mark all as read' => 'Äánh dấu tất cả như đã Ä‘á»c', + 'Mark as read' => 'Äánh dấu là đã Ä‘á»c', + 'Total number of tasks in this column across all swimlanes' => 'Tổng số công việc trong cá»™t nà y trên tất cả các đưá»ng swimlane', + 'Collapse swimlane' => 'Thu gá»n swimlane', + 'Expand swimlane' => 'Mở rá»™ng swimlane lá»™i', + 'Add a new filter' => 'Thêm má»™t bá»™ lá»c má»›i', + 'Share with all project members' => 'Chia sẻ vá»›i tất cả các thà nh viên dá»± án', + 'Shared' => 'Chia sẻ', + 'Owner' => 'Chá»§ nhân', + 'Unread notifications' => 'Thông báo chưa Ä‘á»c', + 'Notification methods:' => 'Phương pháp thông báo:', + 'Unable to read your file' => 'Không thể Ä‘á»c táºp tin cá»§a bạn', + '%d task(s) have been imported successfully.' => '%d nhiệm vụ đã được nháºp thà nh công.', + 'Nothing have been imported!' => 'Không có gì đã được nháºp khẩu!', + 'Import users from CSV file' => 'Nháºp khẩu ngưá»i dùng từ tệp CSV', + '%d user(s) have been imported successfully.' => '%d ngưá»i dùng đã được nháºp thà nh công.', + 'Comma' => 'Comma', + 'Semi-colon' => 'Bán kết', + 'Tab' => 'Chuyển hướng', + 'Vertical bar' => 'Thanh dá»c', + 'Double Quote' => 'Double Quote', + 'Single Quote' => 'TrÃch dẫn đơn', + '%s attached a file to the task #%d' => ' %s gắn má»™t táºp tin và o tác vụ #%d', + 'There is no column or swimlane activated in your project!' => 'Không có cá»™t hoặc swimlane kÃch hoạt trong dá»± án cá»§a bạn!', + 'Append filter (instead of replacement)' => 'Thêm bá»™ lá»c (thay vì thay thế)', + 'Append/Replace' => 'Thêm / Thay thế', + 'Append' => 'Thêm', + 'Replace' => 'Thay thế', + 'Import' => 'Nháºp khẩu', + 'Change sorting' => 'Thay đổi phân loại', + 'Tasks Importation' => 'Nhiệm vụ Nháºp khẩu', + 'Delimiter' => 'Delimiter', + 'Enclosure' => 'Bao vây', + 'CSV File' => 'CSV File', + 'Instructions' => 'Hướng dẫn', + 'Your file must use the predefined CSV format' => 'Tệp cá»§a bạn phải sá» dụng định dạng CSV được xác định trước', + 'Your file must be encoded in UTF-8' => 'Táºp tin cá»§a bạn phải được mã hoá bằng UTF-8', + 'The first row must be the header' => 'Hà ng đầu tiên phải là tiêu Ä‘á»', + 'Duplicates are not verified for you' => 'Bản sao không được xác minh cho bạn', + 'The due date must use the ISO format: YYYY-MM-DD' => 'Ngà y đáo hạn phải sá» dụng định dạng ISO: YYYY-MM-DD', + 'Download CSV template' => 'Tải xuống mẫu CSV', + 'No external integration registered.' => 'Không có tÃch hợp bên ngoà i đăng ký.', + 'Duplicates are not imported' => 'Bản sao không được nháºp', + 'Usernames must be lowercase and unique' => 'Tên ngưá»i dùng phải là chữ thưá»ng và ', + 'Passwords will be encrypted if present' => 'Máºt khẩu sẽ được mã hóa nếu có', + '%s attached a new file to the task %s' => ' %s gắn má»™t táºp tin má»›i và o công việc %s', + 'Link type' => 'Loại liên kết', + 'Assign automatically a category based on a link' => 'Chỉ định tá»± động má»™t thể loại dá»±a trên liên kết', + 'BAM - Konvertible Mark' => 'BAM - Konvertible Mark', + 'Assignee Username' => 'Assignee Username', + 'Assignee Name' => 'Ngưá»i được chuyển nhượng', + 'Groups' => 'Các nhóm', + 'Members of %s' => 'Thà nh viên cá»§a %s', + 'New group' => 'Nhóm má»›i', + 'Group created successfully.' => 'Nhóm được tạo thà nh công.', + 'Unable to create your group.' => 'Không thể tạo nhóm cá»§a bạn.', + 'Edit group' => 'Chỉnh sá»a nhóm', + 'Group updated successfully.' => 'Nhóm được cáºp nháºt thà nh công.', + 'Unable to update your group.' => 'Không thể cáºp nháºt nhóm cá»§a bạn.', + 'Add group member to "%s"' => 'Thêm thà nh viên nhóm và o "%s"', + 'Group member added successfully.' => 'Thà nh viên nhóm đã thà nh công.', + 'Unable to add group member.' => 'Không thể thêm thà nh viên nhóm', + 'Remove user from group "%s"' => 'Xóa ngưá»i dùng khá»i nhóm %s', + 'User removed successfully from this group.' => 'Ngưá»i dùng đã xóa thà nh công khá»i nhóm nà y.', + 'Unable to remove this user from the group.' => 'Không thể xóa ngưá»i dùng nà y khá»i nhóm.', + 'Remove group' => 'Loại bá» nhóm', + 'Group removed successfully.' => 'Nhóm đã xoá thà nh công.', + 'Unable to remove this group.' => 'Không thể xóa nhóm nà y.', + 'Project Permissions' => 'Quyá»n dá»± án', + 'Manager' => 'Giám đốc', + 'Project Manager' => 'Quản lý dá»± án', + 'Project Member' => 'Thà nh viên Dá»± án', + 'Project Viewer' => 'Trình xem dá»± án', + 'Your account is locked for %d minutes' => 'Tà i khoản cá»§a bạn bị khóa trong %d phút', + 'Invalid captcha' => 'Captcha không hợp lệ', + 'The name must be unique' => 'Cái tên phải là duy nhất', + 'View all groups' => 'Xem tất cả các nhóm', + 'There is no user available.' => 'Không có ngưá»i dùng nà o có sẵn.', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Bạn có thá»±c sá»± muốn xóa ngưá»i dùng "%s" khá»i nhóm "%s"?', + 'There is no group.' => 'Không có nhóm.', + 'Add group member' => 'Thêm thà nh viên nhóm', + 'Do you really want to remove this group: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» nhóm nà y: "%s"?', + 'There is no user in this group.' => 'Không có ngưá»i dùng trong nhóm nà y.', + 'Permissions' => 'Quyá»n', + 'Allowed Users' => 'Ngưá»i dùng được phép', + 'No user have been allowed specifically.' => 'Không ngưá»i dùng nà o được cho phép cụ thể.', + 'Role' => 'Vai trò', + 'Enter user name...' => 'Äiá»n tên đăng nháºp...', + 'Allowed Groups' => 'Nhóm được phép', + 'No group have been allowed specifically.' => 'Không có nhóm nà o được cho phép cụ thể.', + 'Group' => 'Nhóm', + 'Group Name' => 'Tên nhóm', + 'Enter group name...' => 'Nháºp tên nhóm ...', + 'Role:' => 'Vai trò:', + 'Project members' => 'Thà nh viên dá»± án', + '%s mentioned you in the task #%d' => ' %s đỠcáºp đến bạn trong công việc #%d', + '%s mentioned you in a comment on the task #%d' => ' %s đã đỠcáºp đến bạn trong má»™t nháºn xét vá» nhiệm vụ #%d', + 'You were mentioned in the task #%d' => 'Bạn đã được đỠcáºp trong nhiệm vụ #%d', + 'You were mentioned in a comment on the task #%d' => 'Bạn đã được đỠcáºp trong má»™t nháºn xét vá» nhiệm vụ #%d', + 'Estimated hours: ' => 'GiỠước tÃnh:', + 'Actual hours: ' => 'Giá» thá»±c tế:', + 'Hours Spent' => 'Thá»i gian Nghỉ ngÆ¡i', + 'Hours Estimated' => 'Số giỠƯớc tÃnh', + 'Estimated Time' => 'ThÆ¡i gian dự Ä‘iÌ£nh', + 'Actual Time' => 'Thá»i gian Thá»±c', + 'Estimated vs actual time' => 'Ước tÃnh thá»i gian thá»±c', + 'RUB - Russian Ruble' => 'RUB - Nga Ruble', + 'Assign the task to the person who does the action when the column is changed' => 'Chỉ định nhiệm vụ cho ngưá»i thá»±c hiện hà nh động khi cá»™t được thay đổi', + 'Close a task in a specific column' => 'Äóng má»™t nhiệm vụ trong má»™t cá»™t cụ thể', + 'Time-based One-time Password Algorithm' => 'Thuáºt toán máºt khẩu má»™t lần dá»±a trên thá»i gian', + 'Two-Factor Provider: ' => 'Nhà cung cấp hai nhân tố:', + 'Disable two-factor authentication' => 'Tắt xác thá»±c hai lá»›p', + 'Enable two-factor authentication' => 'KÃch hoạt xác thá»±c hai lá»›p', + 'There is no integration registered at the moment.' => 'Hiện tại không có há»™i nháºp nà o được đăng ký.', + 'Password Reset for Kanboard' => 'Äặt lại máºt khẩu cho Kanboard', + 'Forgot password?' => 'Quên máºt khẩu?', + 'Enable "Forget Password"' => 'Báºt Quên máºt khẩu ', + 'Password Reset' => 'Äặt lại máºt khẩu', + 'New password' => 'Máºt khẩu má»›i', + 'Change Password' => 'Äổi máºt khẩu', + 'To reset your password click on this link:' => 'Äể đặt lại máºt khẩu cá»§a bạn bấm và o liên kết nà y:', + 'Last Password Reset' => 'Thiết láºp lại máºt khẩu lần cuối', + 'The password has never been reinitialized.' => 'Máºt khẩu chưa bao giỠđược khởi tạo lại.', + 'Creation' => 'Sá»± sáng tạo', + 'Expiration' => 'Hết hạn', + 'Password reset history' => 'Lịch sỠđặt lại máºt khẩu', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Tất cả nhiệm vụ cá»§a cá»™t "%s" và swimlane "%s" đã được đóng thà nh công.', + 'Do you really want to close all tasks of this column?' => 'Bạn có thá»±c sá»± muốn đóng tất cả các nhiệm vụ cá»§a cá»™t nà y?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d nhiệm vụ trong cá»™t "%s" và swimlane "%s" sẽ được đóng lại.', + 'Close all tasks of this column' => 'Äóng tất cả các tác vụ cá»§a cá»™t nà y', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Không có plugin đã đăng ký má»™t phương pháp thông báo dá»± án. Bạn vẫn có thể định cấu hình các thông báo riêng trong hồ sÆ¡ ngưá»i dùng cá»§a bạn. ', + 'My dashboard' => 'Bảng Ä‘iá»u khiển cá»§a tôi', + 'My profile' => 'Thông tin cá»§a tôi', + 'Project owner: ' => 'Chá»§ dá»± án: ', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Äịnh danh dá»± án là tùy chá»n và phải là chữ số, và dụ: MYPROJECT.', + 'Project owner' => 'Chá»§ dá»± án', + 'Private projects do not have users and groups management.' => 'Các dá»± án riêng tư không có ngưá»i dùng và quản lý nhóm.', + 'There is no project member.' => 'Không có thà nh viên dá»± án.', + 'Priority' => 'Sá»± ưu tiên', + 'Task priority' => 'Nhiệm vụ ưu tiên', + 'General' => 'Chung', + 'Dates' => 'Ngà y', + 'Default priority' => 'Mức độ ưu tiên mặc định', + 'Lowest priority' => 'Mức ưu tiên thấp nhất', + 'Highest priority' => 'Mức ưu tiên cao nhất', + 'Close a task when there is no activity' => 'Äóng má»™t nhiệm vụ khi không có hoạt động', + 'Duration in days' => 'Thá»i lượng theo ngà y', + 'Send email when there is no activity on a task' => 'Gá»i email khi không có hoạt động nà o trên má»™t tác vụ', + 'Unable to fetch link information.' => 'Không thể tìm nạp thông tin liên kết.', + 'Daily background job for tasks' => 'Công việc hà ng ngà y cho các nhiệm vụ', + 'Auto' => 'Tự động', + 'Related' => 'Liên quan', + 'Attachment' => 'Táºp tin Ä‘Ãnh kèm', + 'Title not found' => 'Tiêu đỠkhông tìm thấy', + 'Web Link' => 'Liên kết Web', + 'External links' => 'Liện kết ngoại', + 'Add external link' => 'Thêm liên kết bên ngoà i', + 'Type' => 'Kiểu', + 'Dependency' => 'Sá»± phụ thuá»™c', + 'Add internal link' => 'Thêm liên kết ná»™i bá»™', + 'Add a new external link' => 'Thêm má»™t liên kết bên ngoà i má»›i', + 'Edit external link' => 'Chỉnh sá»a liên kết bên ngoà i', + 'External link' => 'Liên kết bên ngoà i', + 'Copy and paste your link here...' => 'Sao chép và dán liên kết cá»§a bạn ở đây ...', + 'URL' => 'URL', + 'Internal links' => 'Liên kết ná»™i bá»™', + 'Assign to me' => 'Chỉ định cho tôi', + 'Me' => 'Tôi', + 'Do not duplicate anything' => 'Äừng trùng lặp bất cứ Ä‘iá»u gì', + 'Projects management' => 'Quản lý dá»± án', + 'Users management' => 'Quản lý ngưá»i dùng', + 'Groups management' => 'Quản lý nhóm', + 'Create from another project' => 'Tạo ra từ má»™t dá»± án khác', + 'open' => 'mở', + 'closed' => 'đóng cá»a', + 'Priority:' => 'Sá»± ưu tiên:', + 'Reference:' => 'Tà i liệu tham khảo:', + 'Complexity:' => 'Sá»± phức tạp:', + 'Swimlane:' => 'Swimlane:', + 'Column:' => 'Cá»™t:', + 'Position:' => 'Chức vụ:', + 'Creator:' => 'Ngưá»i sáng tạo:', + 'Time estimated:' => 'Thá»i gian ước tÃnh:', + '%s hours' => ' %s hours', + 'Time spent:' => 'Thá»i gian dà nh:', + 'Created:' => 'Tạo:', + 'Modified:' => 'Sá»a đổi:', + 'Completed:' => 'Äã hoà n thà nh:', + 'Started:' => 'Äã bắt đầu:', + 'Moved:' => 'Moved:', + 'Task #%d' => 'Nhiệm vụ #%d', + 'Time format' => 'Äịnh dạng thá»i gian', + 'Start date: ' => 'Ngà y bắt đầu:', + 'End date: ' => 'Ngà y cuối: ', + 'New due date: ' => 'Ngà y đáo hạn má»›i:', + 'Start date changed: ' => 'Ngà y bắt đầu thay đổi:', + 'Disable private projects' => 'Vô hiệu hoá các dá»± án riêng tư', + 'Do you really want to remove this custom filter: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» bá»™ lá»c tuỳ chỉnh nà y: "%s"?', + 'Remove a custom filter' => 'Xóa má»™t bá»™ lá»c tuỳ chỉnh', + 'User activated successfully.' => 'Ngưá»i dùng đã kÃch hoạt thà nh công.', + 'Unable to enable this user.' => 'Không thể kÃch hoạt ngưá»i dùng nà y.', + 'User disabled successfully.' => 'Ngưá»i dùng đã vô hiệu thà nh công.', + 'Unable to disable this user.' => 'Không thể vô hiệu hóa ngưá»i dùng nà y.', + 'All files have been uploaded successfully.' => 'Tất cả các táºp tin đã được tải lên thà nh công.', + 'The maximum allowed file size is %sB.' => 'KÃch thước táºp tin tối Ä‘a cho phép là %sB.', + 'Drag and drop your files here' => 'Kéo và thả tệp cá»§a bạn ở đây', + 'choose files' => 'chá»n táºp tin', + 'View profile' => 'Xem hồ sÆ¡', + 'Two Factor' => 'hai lá»›p', + 'Disable user' => 'Vô hiệu hoá ngưá»i dùng', + 'Do you really want to disable this user: "%s"?' => 'Bạn thá»±c sá»± muốn vô hiệu hóa ngưá»i dùng nà y: "%s"?', + 'Enable user' => 'Enable user', + 'Do you really want to enable this user: "%s"?' => 'Bạn có thá»±c sá»± muốn cho phép ngưá»i dùng nà y: "%s"?', + 'Download' => 'Tải vá»', + 'Uploaded: %s' => 'Äã tải lên: %s', + 'Size: %s' => 'KÃch thước: %s', + 'Uploaded by %s' => 'Äã tải lên bởi %s', + 'Filename' => 'Filename', + 'Size' => 'KÃch thước', + 'Column created successfully.' => 'Cá»™t được tạo thà nh công.', + 'Another column with the same name exists in the project' => 'Má»™t cá»™t có cùng tên tồn tại trong dá»± án', + 'Default filters' => 'Bá»™ lá»c mặc định', + 'Your board doesn\'t have any columns!' => 'Bảng cá»§a bạn không có bất kỳ cá»™t nà o!', + 'Change column position' => 'Thay đổi vị trà cá»™t', + 'Switch to the project overview' => 'Chuyển sang tổng quan vá» dá»± án', + 'User filters' => 'Bá»™ lá»c ngưá»i dùng', + 'Category filters' => 'Bá»™ lá»c danh mục', + 'Upload a file' => 'Tải lên má»™t tà i liệu', + 'View file' => 'Xem tà i liệu', + 'Last activity' => 'Hoạt động cuối', + 'Change subtask position' => 'Thay đổi vị trà phụ', + 'This value must be greater than %d' => 'Giá trị nà y phải lá»›n hÆ¡n %d', + 'Another swimlane with the same name exists in the project' => 'Má»™t con tà u khác có cùng tên tồn tại trong dá»± án \', + \'Example: http://example.kanboard.net/ (used to generate absolute URLs)\' => \'Và dụ: http://example.kanboard.net/ (dùng để tạo URL tuyệt đối)\', + \'Actions duplicated successfully.\' => \'Hà nh động trùng lặp thà nh công.\', + \'Unable to duplicate actions.\' => \'Không thể lặp lại hà nh động.\', + \'Add a new action\' => \'Thêm má»™t hà nh động má»›i\', + \'Import from another project\' => \'Nháºp khẩu từ má»™t dá»± án khác\', + \'There is no action at the moment.\' => Hiện tại không có hà nh động nà o. ', + // 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => '', + // 'Actions duplicated successfully.' => '', + // 'Unable to duplicate actions.' => '', + // 'Add a new action' => '', + // 'Import from another project' => '', + // 'There is no action at the moment.' => '', + 'Import actions from another project' => 'Nháºp khẩu các hà nh động từ má»™t dá»± án khác', + 'There is no available project.' => 'Không có dá»± án sẵn có.', + 'Local File' => 'Local File', + 'Configuration' => 'Cấu hình', + 'PHP version:' => 'Phiên bản PHP:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'Phiên bản hệ Ä‘iá»u hà nh:', + 'Database version:' => 'Phiên bản cÆ¡ sở dữ liệu:', + 'Browser:' => 'Trình duyệt:', + 'Task view' => 'Công việc xem', + 'Edit task' => 'Chỉnh sá»a nhiệm vụ', + 'Edit description' => 'Chỉnh sá»a mô tả', + 'New internal link' => 'Liên kết ná»™i bá»™ má»›i', + 'Display list of keyboard shortcuts' => 'Hiển thị danh sách các phÃm tắt', + 'Menu' => 'Thá»±c đơn', + 'Set start date' => 'Äặt ngà y bắt đầu', + 'Avatar' => 'Hình đại diện', + 'Upload my avatar image' => 'Tải lên hình ảnh đại diện cá»§a tôi', + 'Remove my image' => 'Há»§y bá» hình ảnh cá»§a tôi', + 'The OAuth2 state parameter is invalid' => 'Tham số trạng thái OAuth2 không hợp lệ', + 'User not found.' => 'Ngưá»i dùng không tìm thấy.', + 'Search in activity stream' => 'Tìm kiếm trong luồng hoạt động', + 'My activities' => 'Hoạt động cá»§a tôi', + 'Activity until yesterday' => 'Hoạt động cho đến ngà y hôm qua', + 'Activity until today' => 'Hoạt động cho đến ngà y hôm nay', + 'Search by creator: ' => 'Tìm kiếm theo ngưá»i sáng tạo:', + 'Search by creation date: ' => 'Tìm kiếm theo ngà y tạo:', + 'Search by task status: ' => 'Tìm theo tình trạng công việc:', + 'Search by task title: ' => 'Tìm theo tên công việc:', + 'Activity stream search' => 'Tìm kiếm luồng hoạt động', + 'Projects where "%s" is manager' => 'Dá»± án nÆ¡i "%s" là ngưá»i quản lý', + 'Projects where "%s" is member' => 'Dá»± án nÆ¡i "%s" là thà nh viên', + 'Open tasks assigned to "%s"' => 'Mở nhiệm vụ được giao cho "%s"', + 'Closed tasks assigned to "%s"' => 'Äã đóng nhiệm vụ được giao cho "%s"', + 'Assign automatically a color based on a priority' => 'Chỉ định tá»± động má»™t mà u dá»±a trên má»™t ưu tiên', + 'Overdue tasks for the project(s) "%s"' => 'Các nhiệm vụ quá hạn cho (các) dá»± án %s', + 'Upload files' => 'Tải tệp lên', + 'Installed Plugins' => 'Plugin cà i đặt', + 'Plugin Directory' => 'Plugin Directory', + 'Plugin installed successfully.' => 'Plugin được cà i đặt thà nh công.', + 'Plugin updated successfully.' => 'Plugin được cáºp nháºt thà nh công.', + 'Plugin removed successfully.' => 'Plugin được xoá thà nh công.', + 'Subtask converted to task successfully.' => 'Äã thá»±c hiện thà nh công nhiệm vụ phụ đỠđã được chuyển thà nh.', + 'Unable to convert the subtask.' => 'Không thể chuyển đổi phụ Ä‘á».', + 'Unable to extract plugin archive.' => 'Không thể trÃch xuất kho lưu trữ plugin.', + 'Plugin not found.' => 'Plugin không tìm thấy.', + 'You don\'t have the permission to remove this plugin.' => 'Bạn không có quyá»n xóa plugin nà y.', + 'Unable to download plugin archive.' => 'Không thể tải tệp lưu trữ plugin.', + 'Unable to write temporary file for plugin.' => 'Không thể ghi táºp tin tạm thá»i cho plugin.', + 'Unable to open plugin archive.' => 'Không thể mở tệp lưu trữ plugin.', + 'There is no file in the plugin archive.' => 'Không có tệp nà o trong kho lưu trữ trình cắm.', + 'Create tasks in bulk' => 'Tạo nhiệm vụ hà ng loạt', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Trưá»ng hợp Kanboard cá»§a bạn không được định cấu hình để cà i đặt plugin từ giao diện ngưá»i dùng.', + 'There is no plugin available.' => 'Không có plugin nà o sẵn có.', + 'Install' => 'Cà i đặt, dá»±ng lên', + 'Update' => 'Cáºp nháºt', + 'Up to date' => 'Cáºp nháºt', + 'Not available' => 'Không có sẵn', + 'Remove plugin' => 'Xoá plugin', + 'Do you really want to remove this plugin: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» plugin nà y: "%s"?', + 'Uninstall' => 'Gỡ cà i đặt', + 'Listing' => 'Liệt kê', + 'Metadata' => 'Siêu dữ liệu', + 'Manage projects' => 'Quản lý dá»± án', + 'Convert to task' => 'Chuyển đổi sang tác vụ', + 'Convert sub-task to task' => 'Chuyển tiểu nhiệm vụ thà nh nhiệm vụ', + 'Do you really want to convert this sub-task to a task?' => 'Bạn có thá»±c sá»± muốn chuyển đổi tiểu nhiệm vụ nà y sang má»™t nhiệm vụ?', + 'My task title' => 'Nhiệm vụ cá»§a tôi', + 'Enter one task by line.' => 'Nháºp má»™t tác vụ theo dòng.', + 'Number of failed login:' => 'Số lần đăng nháºp không thà nh công:', + 'Account locked until:' => 'Tà i khoản bị khoá cho đến khi:', + 'Email settings' => 'Cà i đặt email', + 'Email sender address' => 'Äịa chỉ ngưá»i gá»i email', + 'Email transport' => 'Váºn chuyển email', + 'Webhook token' => 'Mã thông báo cá»§a Webhook', + 'Project tags management' => 'Quản lý thẻ dá»± án', + 'Tag created successfully.' => 'Thẻ được tạo thà nh công.', + 'Unable to create this tag.' => 'Không thể tạo thẻ nà y.', + 'Tag updated successfully.' => 'Thẻ được cáºp nháºt thà nh công.', + 'Unable to update this tag.' => 'Không thể cáºp nháºt thẻ nà y.', + 'Tag removed successfully.' => 'Thẻ đã xoá thà nh công.', + 'Unable to remove this tag.' => 'Không thể xóa thẻ nà y.', + 'Global tags management' => 'Quản lý thẻ toà n cầu', + 'Tags' => 'Thẻ', + 'Tags management' => 'Quản lý thẻ', + 'Add new tag' => 'Thêm thẻ má»›i', + 'Edit a tag' => 'Chỉnh sá»a thẻ', + 'Project tags' => 'Thẻ dá»± án', + 'There is no specific tag for this project at the moment.' => 'Hiện tại không có thẻ cụ thể cho dá»± án nà y.', + 'Tag' => 'Nhãn', + 'Remove a tag' => 'Loại bá» má»™t thẻ', + 'Do you really want to remove this tag: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» thẻ nà y: "%s"?', + 'Global tags' => 'Thẻ toà n cầu', + 'There is no global tag at the moment.' => 'Hiện tại không có nhãn toà n cầu. ', + 'This field cannot be empty' => 'Trưá»ng nà y không thể để trống', + 'Close a task when there is no activity in an specific column' => 'Äóng má»™t nhiệm vụ khi không có hoạt động trong má»™t cá»™t cụ thể', + '%s removed a subtask for the task #%d' => ' %s loại bá» má»™t subtask cho nhiệm vụ #%d', + '%s removed a comment on the task #%d' => ' %s xóa má»™t nháºn xét vá» nhiệm vụ #%d', + 'Comment removed on task #%d' => 'Äã xóa nháºn xét vá» công việc #%d', + 'Subtask removed on task #%d' => 'Huá»· bá» các nhiệm vụ #%d', + 'Hide tasks in this column in the dashboard' => 'Ẩn nhiệm vụ trong cá»™t nà y trong bảng Ä‘iá»u khiển', + '%s removed a comment on the task %s' => ' %s xóa má»™t nháºn xét vá» nhiệm vụ %s', + '%s removed a subtask for the task %s' => ' %s loại bá» má»™t phụ cho nhiệm vụ %s', + 'Comment removed' => 'Äã xóa nháºn xét', + 'Subtask removed' => 'Huá»· bá» bá»', + '%s set a new internal link for the task #%d' => ' %s thiết láºp má»™t liên kết ná»™i bá»™ má»›i cho nhiệm vụ #%d', + '%s removed an internal link for the task #%d' => ' %s đã xóa liên kết ná»™i bá»™ cho tác vụ #%d', + 'A new internal link for the task #%d have been defined' => 'Má»™t liên kết ná»™i bá»™ má»›i cho nhiệm vụ #%d đã được xác định', + 'Internal link removed for the task #%d' => 'Äã loại bá» liên kết ná»™i bá»™ cho tác vụ #%d', + '%s set a new internal link for the task %s' => ' %s thiết láºp má»™t liên kết ná»™i bá»™ má»›i cho nhiệm vụ %s', + '%s removed an internal link for the task %s' => ' %s đã xóa liên kết ná»™i bá»™ cho tác vụ %s', + 'Automatically set the due date on task creation' => 'Tá»± động thiết láºp ngà y đáo hạn khi tạo tác vụ', + 'Move the task to another column when closed' => 'Di chuyển nhiệm vụ sang cá»™t khác khi đóng', + 'Move the task to another column when not moved during a given period' => 'Di chuyển công việc sang cá»™t khác khi không di chuyển trong má»™t khoảng thá»i gian nhất định', + 'Dashboard for %s' => 'Bảng Ä‘iá»u khiển cho %s', + 'Tasks overview for %s' => 'Tổng quan vá» các nhiệm vụ cho %s', + 'Subtasks overview for %s' => 'Tổng quan vá» các công việc cho %s', + 'Projects overview for %s' => 'Tổng quan vá» dá»± án cho %s', + 'Activity stream for %s' => 'Luồng hoạt động cho %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Chỉ định má»™t mà u sắc khi nhiệm vụ được chuyển đến má»™t swimlane cụ thể', + 'Assign a priority when the task is moved to a specific swimlane' => 'Chỉ định má»™t ưu tiên khi nhiệm vụ được chuyển đến má»™t con swimlane cụ thể', + 'User unlocked successfully.' => 'Ngưá»i dùng đã mở khóa thà nh công.', + 'Unable to unlock the user.' => 'Không thể mở khóa ngưá»i dùng.', + 'Move a task to another swimlane' => 'Di chuyển má»™t nhiệm vụ đến má»™t swimlane khác', + 'Creator Name' => 'Tên ngưá»i tạo', + 'Time spent and estimated' => 'Thá»i gian chi tiêu và ước tÃnh', + 'Move position' => 'Di chuyển vị trÃ', + 'Move task to another position on the board' => 'Chuyển nhiệm vụ sang vị trà khác trên bảng', + 'Insert before this task' => 'Chèn trước khi tác vụ nà y', + 'Insert after this task' => 'Chèn sau khi tác vụ nà y', + 'Unlock this user' => 'Mở khóa ngưá»i dùng nà y', + 'Custom Project Roles' => 'Vai trò dá»± án tùy chỉnh', + 'Add a new custom role' => 'Thêm má»™t vai trò tùy chỉnh má»›i', + 'Restrictions for the role "%s"' => 'Hạn chế đối vá»›i vai trò %s', + 'Add a new project restriction' => 'Thêm má»™t hạn chế dá»± án má»›i', + 'Add a new drag and drop restriction' => 'Thêm má»™t hạn chế kéo và thả má»›i', + 'Add a new column restriction' => 'Thêm má»™t cá»™t má»›i', + 'Edit this role' => 'Chỉnh sá»a vai trò nà y', + 'Remove this role' => 'Loại bá» vai trò nà y', + 'There is no restriction for this role.' => 'Không có sá»± hạn chế đối vá»›i vai trò nà y.', + 'Only moving task between those columns is permitted' => 'Chỉ di chuyển nhiệm vụ giữa các cá»™t đó được phép', + 'Close a task in a specific column when not moved during a given period' => 'Äóng má»™t nhiệm vụ trong má»™t cá»™t cụ thể khi không di chuyển trong má»™t khoảng thá»i gian nhất định', + 'Edit columns' => 'Chỉnh sá»a cá»™t', + 'The column restriction has been created successfully.' => 'Hạn chế cá»™t đã được tạo thà nh công.', + 'Unable to create this column restriction.' => 'Không thể tạo giá»›i hạn cá»™t nà y.', + 'Column restriction removed successfully.' => 'Hạn chế cá»™t đã được xoá thà nh công.', + 'Unable to remove this restriction.' => 'Không thể xóa bá» hạn chế nà y.', + 'Your custom project role has been created successfully.' => 'Vai trò dá»± án tùy chỉnh cá»§a bạn đã được tạo thà nh công.', + 'Unable to create custom project role.' => 'Không thể tạo vai trò dá»± án tùy chỉnh.', + 'Your custom project role has been updated successfully.' => 'Vai trò dá»± án tùy chỉnh cá»§a bạn đã được cáºp nháºt thà nh công.', + 'Unable to update custom project role.' => 'Không thể cáºp nháºt vai trò dá»± án tùy chỉnh.', + 'Custom project role removed successfully.' => 'Vai trò dá»± án đã được gỡ bá» thà nh công.', + 'Unable to remove this project role.' => 'Không thể xóa vai trò dá»± án nà y.', + 'The project restriction has been created successfully.' => 'Sá»± hạn chế cá»§a dá»± án đã được tạo ra thà nh công.', + 'Unable to create this project restriction.' => 'Không thể tạo ra sá»± hạn chế cá»§a dá»± án.', + 'Project restriction removed successfully.' => 'Hạn chế dá»± án đã được loại bá» thà nh công.', + 'You cannot create tasks in this column.' => 'Bạn không thể tạo ra các nhiệm vụ trong cá»™t nà y.', + 'Task creation is permitted for this column' => 'Tạo tác vụ được cho phép cho cá»™t nà y', + 'Closing or opening a task is permitted for this column' => 'Cho phép Äóng hay mở má»™t nhiệm vụ cho cá»™t nà y', + 'Task creation is blocked for this column' => 'Tạo tác vụ bị chặn cho cá»™t nà y', + 'Closing or opening a task is blocked for this column' => 'Äóng hoặc mở má»™t tác vụ bị chặn cho cá»™t nà y', + 'Task creation is not permitted' => 'Không được phép tạo công việc', + 'Closing or opening a task is not permitted' => 'Không được phép đóng hay mở má»™t nhiệm vụ', + 'New drag and drop restriction for the role "%s"' => 'Hạn chế kéo và thả má»›i cho vai trò "%s"', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Những ngưá»i thuá»™c vai trò nà y sẽ chỉ có thể di chuyển nhiệm vụ giữa cá»™t nguồn và cá»™t Ä‘Ãch.', + 'Remove a column restriction' => 'Há»§y bá» má»™t hạn chế vá» cá»™t', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» giá»›i hạn cá»™t nà y: "%s" thà nh "%s"?', + 'New column restriction for the role "%s"' => 'Hạn chế cá»™t má»›i cho vai trò %s', + 'Rule' => 'Qui định', + 'Do you really want to remove this column restriction?' => 'Bạn có thá»±c sá»± muốn loại bá» hạn chế cá»™t nà y?', + 'Custom roles' => 'Vai trò tùy chỉnh', + 'New custom project role' => 'Vai trò dá»± án má»›i tùy chỉnh', + 'Edit custom project role' => 'Chỉnh sá»a vai trò dá»± án tùy chỉnh', + 'Remove a custom role' => 'Loại bá» má»™t vai trò tùy chỉnh', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Bạn có thá»±c sá»± muốn loại bá» vai trò tùy chỉnh nà y: "%s"? Tất cả má»i ngưá»i được giao nhiệm vụ nà y sẽ trở thà nh thà nh viên cá»§a dá»± án.', + 'There is no custom role for this project.' => 'Không có vai trò tùy chỉnh cho dá»± án nà y.', + 'New project restriction for the role "%s"' => 'Hạn chế dá»± án má»›i cho vai trò "%s"', + 'Restriction' => 'Sá»± hạn chế', + 'Remove a project restriction' => 'Há»§y bá» má»™t dá»± án hạn chế', + 'Do you really want to remove this project restriction: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» hạn chế dá»± án nà y: "%s"?', + 'Duplicate to multiple projects' => 'Nhân đôi vá»›i nhiá»u dá»± án', + 'This field is required' => 'Trưá»ng nà y là bắt buá»™c', + 'Moving a task is not permitted' => 'Không được phép di chuyển má»™t nhiệm vụ', + 'This value must be in the range %d to %d' => 'Giá trị nà y phải nằm trong khoảng từ %d đến %d', + 'You are not allowed to move this task.' => 'Bạn không được phép di chuyển nhiệm vụ nà y.', + 'API User Access' => 'Truy cáºp Ngưá»i dùng API', + 'Preview' => 'Xem trước', + 'Write' => 'Viết', + 'Write your text in Markdown' => 'Viết văn bản cá»§a bạn trong Markdown', + 'No personal API access token registered.' => 'Không có đăng nháºp truy cáºp API cá nhân.', + 'Your personal API access token is "%s"' => 'Mã thông báo truy cáºp API cá nhân cá»§a bạn là "%s"', + 'Remove your token' => 'Há»§y bá» mã thông báo cá»§a bạn', + 'Generate a new token' => 'Tạo mã thông báo má»›i', + 'Showing %d-%d of %d' => 'Hiển thị %d-%d cá»§a %d', + 'Outgoing Emails' => 'Email gá»i Ä‘i', + 'Add or change currency rate' => 'Thêm hoặc thay đổi tá»· lệ tiá»n tệ', + 'Reference currency: %s' => 'Tiá»n tệ tham khảo: %s', + 'Add custom filters' => 'Thêm bá»™ lá»c tùy chỉnh', + 'Export' => 'Xuất khẩu', + 'Add link label' => 'Thêm nhãn liên kết', + 'Incompatible Plugins' => 'Plugin không tương thÃch', + 'Compatibility' => 'Khả năng tương thÃch', + 'Permissions and ownership' => 'Quyá»n và quyá»n sở hữu', + 'Priorities' => 'Ưu tiên', + 'Close this window' => 'Äóng cá»a sổ nà y', + 'Unable to upload this file.' => 'Không thể tải tệp nà y lên.', + 'Import tasks' => 'Nháºp khẩu các nhiệm vụ', + 'Choose a project' => 'Chá»n má»™t dá»± án', + 'Profile' => 'Hồ sÆ¡', + 'Application role' => 'Vai trò ứng dụng', + '%d invitations were sent.' => '%d lá»i má»i đã được gá»i.', + '%d invitation was sent.' => 'Thư má»i %d đã được gá»i.', + 'Unable to create this user.' => 'Không thể tạo ngưá»i dùng nà y.', + 'Kanboard Invitation' => 'Lá»i má»i cá»§a Kanboard', + 'Visible on dashboard' => 'Hiển thị trên bảng Ä‘iá»u khiển', + 'Created at:' => 'ÄÆ°á»£c tạo và o:', + 'Updated at:' => 'Cáºp nháºt tại:', + 'There is no custom filter.' => 'Không có bá»™ lá»c tuỳ chỉnh.', + 'New User' => 'Ngưá»i dùng má»›i', + 'Authentication' => 'Xác thá»±c', + 'If checked, this user will use a third-party system for authentication.' => 'Nếu được chá»n, ngưá»i dùng nà y sẽ sá» dụng hệ thống cá»§a bên thứ ba để xác thá»±c.', + 'The password is necessary only for local users.' => 'Máºt khẩu là cần thiết chỉ dà nh cho ngưá»i dùng cục bá»™.', + 'You have been invited to register on Kanboard.' => 'Bạn đã được má»i đăng ký trên Kanboard.', + 'Click here to join your team' => 'Nhấp và o đây để tham gia nhóm cá»§a bạn', + 'Invite people' => 'Má»i má»i ngưá»i', + 'Emails' => 'Email', + 'Enter one email address by line.' => 'Nháºp má»™t địa chỉ email theo dòng.', + 'Add these people to this project' => 'Thêm những ngưá»i nà y và o dá»± án nà y', + 'Add this person to this project' => 'Thêm ngưá»i nà y và o dá»± án nà y', + 'Sign-up' => 'Äăng ký', + 'Credentials' => 'Thông tin xác thá»±c', + 'New user' => 'Ngưá»i dùng má»›i', + 'This username is already taken' => 'Tên ngưá»i dùng nà y đã được dùng', + 'A link to reset your password has been sent by email.' => 'Má»™t liên kết để đặt lại máºt khẩu cá»§a bạn đã được gá»i bằng email.', + 'Your profile must have a valid email address.' => 'Tiểu sá» cá»§a bạn phải có địa chỉ email hợp lệ.', + 'Unfortunately, we are unable to reset your password. Did you entered a valid username? Do you have an email address in your profile?' => 'Tháºt không may, chúng tôi không thể đặt lại máºt khẩu cá»§a bạn. Bạn đã nháºp má»™t tên ngưá»i dùng hợp lệ? Bạn có địa chỉ email trong hồ sÆ¡ cá»§a bạn không? ', + 'TRL - Turkish Lira' => 'TRL - Lira Thổ NhÄ© Kỳ', + 'The project email is optional and could be used by several plugins.' => 'Email dá»± án là tùy chá»n và có thể được sá» dụng bởi má»™t số plugin.', + 'The project email must be unique across all projects' => 'Email cá»§a dá»± án phải là duy nhất trong tất cả các dá»± án', + 'The email configuration has been disabled by the administrator.' => 'Quản trị viên đã vô hiệu cấu hình email.', + 'Close this project' => 'Äóng dá»± án nà y', + 'Open this project' => 'Mở dá»± án nà y', + 'Close a project' => 'Äóng má»™t dá»± án', + 'Do you really want to close this project: "%s"?' => 'Bạn có thá»±c sá»± muốn đóng dá»± án nà y: "%s"?', + 'Reopen a project' => 'Mở lại má»™t dá»± án', + 'Do you really want to reopen this project: "%s"?' => 'Bạn có thá»±c sá»± muốn mở lại dá»± án nà y: "%s"?', + 'This project is open' => 'Dá»± án nà y Ä‘ang mở', + 'This project is closed' => 'Dá»± án nà y đã kết thúc', + 'Unable to upload files, check the permissions of your data folder.' => 'Không thể tải tệp lên, hãy kiểm tra quyá»n cá»§a thư mục dữ liệu cá»§a bạn.', + 'Another category with the same name exists in this project' => 'Má»™t thể loại khác có cùng tên tồn tại trong dá»± án nà y', + 'Comment sent by email successfully.' => 'Thảo luáºn gá»i qua email thà nh công.', + 'Sent by email to [%s](mailto:%s) (%s)' => 'Gá»i qua email tá»›i [ %s] (mailto: %s) ( %s)', + 'Unable to read uploaded file.' => 'Không thể Ä‘á»c táºp tin được tải lên.', + 'Database uploaded successfully.' => 'CÆ¡ sở dữ liệu được tải lên thà nh công.', + 'Task sent by email successfully.' => 'Nhiệm vụ gá»i qua email thà nh công.', + 'There is no category in this project.' => 'Không có hạng mục trong dá»± án nà y.', + 'Send by email' => 'Gá»i bằng thư Ä‘iện tá»', + 'Create and send a comment by email' => 'Tạo và gá»i má»™t nháºn xét qua email', + 'Subject' => 'Môn há»c', + 'Upload the database' => 'Tải lên cÆ¡ sở dữ liệu', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Bạn có thể tải lên cÆ¡ sở dữ liệu Sqlite đã tải xuống trước đây (định dạng Gzip)', + 'Database file' => 'Táºp tin cÆ¡ sở dữ liệu', + 'Upload' => 'Tải lên', + 'Your project must have at least one active swimlane.' => 'Dá»± án cá»§a bạn phải có Ãt nhất má»™t swimlane hoạt động.', + 'Project: %s' => 'Dá»± án: %s', + 'Automatic action not found: "%s"' => 'Không tìm thấy hà nh động tá»± động: "%s"', + '%d projects' => '%d dá»± án', + '%d project' => '%d dá»± án', + 'There is no project.' => 'Không có dá»± án.', + 'Sort' => 'Sắp xếp', + 'Project ID' => 'ID dá»± án', + 'Project name' => 'Tên dá»± án', + 'Public' => 'Công cá»™ng', + 'Private' => 'Riêng tư', + '%d tasks' => '%d nhiệm vụ', + '%d task' => '%d công việc', + 'Task ID' => 'ID công việc', + 'Assign automatically a color when due date is expired' => 'Chỉ định mà u tá»± động khi ngà y hết hạn hết hạn', + 'Total score in this column across all swimlanes' => 'Tổng số Ä‘iểm trong cá»™t nà y trên tất cả các đưá»ng swimlane', + 'HRK - Kuna' => 'HRK - Kuna', + 'ARS - Argentine Peso' => 'ARS - Peso Argentina', + 'COP - Colombian Peso' => 'COP - Colombia Peso', + '%d groups' => '%d nhóm', + '%d group' => '%d nhóm', + 'Group ID' => 'ID nhóm', + 'External ID' => 'ID bên ngoà i', + '%d users' => '%d ngưá»i dùng', + '%d user' => '%d ngưá»i dùng', + 'Hide subtasks' => 'Ẩn các nhiệm vụ phụ', + 'Show subtasks' => 'Hiện các nhiệm vụ phụ', + 'Authentication Parameters' => 'Thông số xác thá»±c', + 'API Access' => 'Truy cáºp API', + 'No users found.' => 'Không tìm thấy ngưá»i dùng.', + 'User ID' => 'Tên ngưá»i dùng', + 'Notifications are activated' => 'Thông báo được kÃch hoạt', + 'Notifications are disabled' => 'Thông báo bị vô hiệu hóa', + 'User disabled' => 'Ngưá»i dùng đã vô hiệu hóa', + '%d notifications' => '%d thông báo', + '%d notification' => '%d thông báo', + 'There is no external integration installed.' => 'Không có tÃch hợp bên ngoà i được cà i đặt.', + 'You are not allowed to update tasks assigned to someone else.' => 'Bạn không được phép cáºp nháºt các nhiệm vụ được giao cho ngưá»i khác.', + 'You are not allowed to change the assignee.' => 'Bạn không được phép thay đổi ngưá»i được chuyển nhượng.', + 'Task suppression is not permitted' => 'Không được phép loại bá» công việc', + 'Changing assignee is not permitted' => 'Không được phép thay đổi ngưá»i được chuyển nhượng', + 'Update only assigned tasks is permitted' => 'Chỉ được phép cáºp nháºt các nhiệm vụ được giao', + 'Only for tasks assigned to the current user' => 'Chỉ cho các nhiệm vụ được gán cho ngưá»i dùng hiện tại', + 'My projects' => 'Dá»± án cá»§a tôi', + 'Your are not member of any project.' => 'Bạn không phải là thà nh viên cá»§a bất kỳ dá»± án nà o.', + 'My subtasks' => 'Nhiệm vụ phụ cá»§a tôi', + '%d subtasks' => '%d nhiệm vụ phụ', + '%d subtask' => '%d nhiệm vụ phụ', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Chỉ di chuyển nhiệm vụ giữa các cá»™t được phép cho các nhiệm vụ được giao cho ngưá»i sá» dụng hiện tại', + '[DUPLICATE]' => '[BẢN SAO]', + 'DKK - Danish Krona' => 'DKK - Äan Mạch Äan Mạch', + 'Remove user from group' => 'Xóa ngưá»i dùng khá»i nhóm', + 'Assign the task to its creator' => 'Chỉ định nhiệm vụ cho ngưá»i sáng tạo', + 'This task was sent by email to "%s" with subject "%s".' => 'Nhiệm vụ nà y được gá»i qua email tá»›i "%s" vá»›i chá»§ đỠ"%s".', + 'Predefined Email Subjects' => 'Äối tượng Email được Xác định trước', + 'Write one subject by line.' => 'Viết má»™t chá»§ đỠtheo dòng.', + 'Create another link' => 'Tạo liên kết khác', + 'BRL - Brazilian Real' => 'BRL - Brazilian Real', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', +); diff --git a/app/Locale/zh_CN/translations.php b/app/Locale/zh_CN/translations.php index f68b59dc..46bffe6a 100644 --- a/app/Locale/zh_CN/translations.php +++ b/app/Locale/zh_CN/translations.php @@ -39,7 +39,6 @@ return array( 'Administrator' => '超级管ç†å‘˜', 'Sign in' => '登录', 'Users' => '用户', - 'No user' => '没有用户', 'Forbidden' => 'ç¦æ¢', 'Access Forbidden' => 'ç¦æ¢è¿›å…¥', 'Edit user' => '修改用户', @@ -84,7 +83,7 @@ return array( 'Color' => '颜色', 'Assignee' => '负责人', 'Create another task' => '创建å¦ä¸€ä¸ªä»»åŠ¡', - 'New task' => '新进任务', + 'New task' => '新建任务', 'Open a task' => '开一个任务', 'Do you really want to open this task: "%s"?' => 'ä½ ç¡®å®šè¦å¼€è¿™ä¸ªä»»åŠ¡å—?"%s"', 'Back to the board' => '回到看æ¿', @@ -136,7 +135,7 @@ return array( 'Unable to remove this user.' => 'æ— æ³•ç§»é™¤è¯¥ç”¨æˆ·ã€‚', 'Board updated successfully.' => 'çœ‹æ¿æˆåŠŸæ›´æ–°ã€‚', 'Ready' => '预备', - 'Backlog' => '积压', + 'Backlog' => '待办', 'Work in progress' => '工作进行ä¸', 'Done' => '完æˆ', 'Application version:' => '应用程åºç‰ˆæœ¬ï¼š', @@ -150,7 +149,7 @@ return array( 'Task count' => '任务数', 'User' => '用户', 'Comments' => '评论', - 'Comment is required' => '必须得有评论', + 'Comment is required' => '评论ä¸èƒ½ä¸ºç©º', 'Comment added successfully.' => '评论æˆåŠŸæ·»åŠ ã€‚', 'Unable to create your comment.' => 'æ— æ³•åˆ›å»ºè¯„è®ºã€‚', 'Due Date' => '到期时间', @@ -212,12 +211,12 @@ return array( 'Description' => 'æè¿°', '%d comments' => '%d个评论', '%d comment' => '%d个评论', - 'Email address invalid' => 'emailåœ°å€æ— 效', + 'Email address invalid' => 'é‚®ä»¶åœ°å€æ— 效', 'Your external account is not linked anymore to your profile.' => 'ä½ çš„å¤–éƒ¨è´¦æˆ·å…³è”已解除', 'Unable to unlink your external account.' => 'æ— æ³•å…³è”åˆ°ä½ çš„å¤–éƒ¨è´¦æˆ·', 'External authentication failed' => '外部认è¯å¤±è´¥', 'Your external account is linked to your profile successfully.' => 'ä½ å·²æˆåŠŸå…³è”åˆ°ä½ çš„å¤–éƒ¨è´¦æˆ·', - 'Email' => 'email', + 'Email' => '邮件', 'Task removed successfully.' => '任务æˆåŠŸåŽ»é™¤', 'Unable to remove this task.' => 'æ— æ³•ç§»é™¤è¯¥ä»»åŠ¡ã€‚', 'Remove a task' => '移除一个任务', @@ -274,7 +273,6 @@ return array( 'Sub-task updated successfully.' => 'æˆåŠŸåˆ›å»ºå任务', 'Unable to update your sub-task.' => 'æ— æ³•æ›´æ–°å任务', 'Unable to create your sub-task.' => 'æ— æ³•åˆ›å»ºå任务', - 'Sub-task added successfully.' => 'æˆåŠŸæ·»åŠ å任务', 'Maximum size: ' => '大å°ä¸Šé™ï¼š', 'Display another project' => '显示其它项目', 'Created by %s' => '创建者:%s', @@ -288,7 +286,7 @@ return array( 'Clone' => '克隆', 'Project cloned successfully.' => 'æˆåŠŸå¤åˆ¶é¡¹ç›®ã€‚', 'Unable to clone this project.' => 'æ— æ³•å¤åˆ¶æ¤é¡¹ç›®', - 'Enable email notifications' => 'å¯ç”¨email通知', + 'Enable email notifications' => 'å¯ç”¨é‚®ä»¶é€šçŸ¥', 'Task position:' => '任务ä½ç½®ï¼š', 'The task #%d have been opened.' => '任务#%då·²ç»è¢«å¼€å¯.', 'The task #%d have been closed.' => '任务#%då·²ç»è¢«å…³é—.', @@ -321,7 +319,7 @@ return array( 'Disabled' => 'åœç”¨', 'Login:' => '用户å:', 'Full Name:' => 'å§“å:', - 'Email:' => 'email:', + 'Email:' => '邮件:', 'Notifications:' => '通知:', 'Notifications' => '通知', 'Account type:' => '账户类型:', @@ -396,22 +394,17 @@ return array( 'Activity stream' => '动æ€è®°å½•', 'Dashboard' => '颿¿', 'Confirmation' => '确认', - 'Allow everybody to access to this project' => 'å…许所有人访问æ¤é¡¹ç›®', - 'Everybody have access to this project.' => '所有人都å¯ä»¥è®¿é—®æ¤é¡¹ç›®', 'Webhooks' => '网络钩å', 'API' => 'åº”ç”¨ç¨‹åºæŽ¥å£', 'Create a comment from an external provider' => '从外部创建一个评论', 'Project management' => '项目管ç†', - 'My projects' => '我的项目', 'Columns' => 'æ ç›®', 'Task' => '任务', - 'Your are not member of any project.' => 'æ‚¨å°šæœªåŠ å…¥ä»»ä½•é¡¹ç›®', 'Percentage' => '百分比', 'Number of tasks' => '任务数', 'Task distribution' => '任务分布', 'Analytics' => '分æž', 'Subtask' => 'å任务', - 'My subtasks' => '我的å任务', 'User repartition' => '用户分æž', 'Clone this project' => 'å¤åˆ¶æ¤é¡¹ç›®', 'Column removed successfully.' => 'æˆåŠŸåˆ é™¤äº†æ 目。', @@ -459,7 +452,6 @@ return array( 'Language:' => 'è¯è¨€ï¼š', 'Timezone:' => '时区:', 'All columns' => '全部æ ç›®', - 'Calendar' => '日程表', 'Next' => 'å‰è¿›', '#%d' => '#%d', 'All swimlanes' => '全部里程碑', @@ -557,7 +549,6 @@ return array( 'Unable to add this currency rate.' => 'æ— æ³•æ·»åŠ æ¤æ±‡çއ', 'Webhook URL' => '网络钩å URL', '%s removed the assignee of the task %s' => '%såˆ é™¤äº†ä»»åŠ¡%s的负责人', - 'Enable Gravatar images' => 'å¯ç”¨ Gravatar 图åƒ', 'Information' => 'ä¿¡æ¯', 'Check two factor authentication code' => '检查åŒé‡è®¤è¯ç ', 'The two factor authentication code is not valid.' => 'åŒé‡è®¤è¯ç 䏿£ç¡®ã€‚', @@ -611,14 +602,7 @@ return array( 'When task is moved from first column' => '当任务从第一列任务æ 移走时', 'When task is moved to last column' => '当任务移动到最åŽä¸€åˆ—ä»»åŠ¡æ æ—¶', 'Year(s)' => 'å¹´', - 'Calendar settings' => '日程设置', - 'Project calendar view' => '项目日历表', 'Project settings' => '项目设置', - 'Show subtasks based on the time tracking' => '在时间跟踪上显示å任务', - 'Show tasks based on the creation date' => '显示任务创建日期于', - 'Show tasks based on the start date' => '显示任务开始日期于', - 'Subtasks time tracking' => 'å任务时间跟踪', - 'User calendar view' => '用户日程视图', 'Automatically update the start date' => '自动更新开始日期', 'iCal feed' => '日历订阅', 'Preferences' => 'å好', @@ -628,7 +612,7 @@ return array( 'Unable to update this user.' => 'æ— æ³•æ›´æ–°æ¤ç”¨æˆ·', 'There is no user management for private projects.' => 'ç§æœ‰é¡¹ç›®ä¸‹æ— 用户å¯ç®¡ç†', 'User that will receive the email' => '用户将收到邮件', - 'Email subject' => 'email主题', + 'Email subject' => '邮件主题', 'Date' => '日期', 'Add a comment log when moving the task between columns' => 'å½“ä»»åŠ¡ç§»åŠ¨åˆ°ä»»åŠ¡æ æ—¶æ·»åŠ è¯„è®ºæ—¥å¿—', 'Move the task to another column when the category is changed' => 'å½“ä»»åŠ¡åˆ†ç±»æ”¹å˜æ—¶ç§»åŠ¨åˆ°ä»»åŠ¡æ ', @@ -637,7 +621,6 @@ return array( 'Notification' => '通知', '%s moved the task #%d to the first swimlane' => '%s将任务#%d移动到了首个里程碑', 'Swimlane' => '里程碑', - 'Gravatar' => 'Gravatar头åƒ', '%s moved the task %s to the first swimlane' => '%s将任务%s移动到了首个里程碑', '%s moved the task %s to the swimlane "%s"' => '%s将任务%s移动到了里程碑"%s"下', 'This report contains all subtasks information for the given date range.' => '该报告包å«äº†æŒ‡å®šæ—¥æœŸèŒƒå›´å†…的所有å任务信æ¯ã€‚', @@ -673,8 +656,7 @@ return array( '<30m' => 'å°äºŽ30分钟', 'Stop timer' => 'åœæ¢è®¡æ—¶å™¨', 'Start timer' => 'å¼€å¯è®¡æ—¶å™¨', - 'My activity stream' => '我的活动æµ', - 'My calendar' => '我的日程表', + 'My activity stream' => '我的动æ€', 'Search tasks' => 'æœç´¢ä»»åŠ¡', 'Reset filters' => 'é‡ç½®è¿‡æ»¤å™¨', 'My tasks due tomorrow' => '我的明天到期的任务', @@ -688,7 +670,6 @@ return array( 'Overview' => '概览', 'Board/Calendar/List view' => '看æ¿/日程/列表视图', 'Switch to the board view' => '切æ¢åˆ°çœ‹æ¿è§†å›¾', - 'Switch to the calendar view' => '切æ¢åˆ°æ—¥ç¨‹è§†å›¾', 'Switch to the list view' => '切æ¢åˆ°åˆ—表视图', 'Go to the search/filter box' => 'å‰å¾€æœç´¢/过滤箱', 'There is no activity yet.' => '当剿— 任何活动.', @@ -705,22 +686,22 @@ return array( 'Search by category: ' => '按分类:', 'Search by description: ' => '按æè¿°ï¼š', 'Search by due date: ' => '按超期时间:', - 'Average time spent into each column' => 'æ¯ä¸ªä»»åŠ¡æ å¹³å‡èŠ±è´¹æ—¶é—´', + 'Average time spent into each column' => 'æ¯ä¸ªä»»åŠ¡æ 的平å‡èŠ±è´¹æ—¶é—´', 'Average time spent' => 'å¹³å‡èŠ±è´¹æ—¶é—´', 'This chart show the average time spent into each column for the last %d tasks.' => '当剿Ÿ±çŠ¶å›¾è¡¨ç¤ºæœ€æ–°%dæ¡ä»»åŠ¡åœ¨æ¯ä¸ªä»»åŠ¡æ 下的平å‡èŠ±è´¹æ—¶é—´', - // 'Average Lead and Cycle time' => '', - // 'Average lead time: ' => '', - // 'Average cycle time: ' => '', - // 'Cycle Time' => '', - // 'Lead Time' => '', + 'Average Lead and Cycle time' => 'å¹³å‡å·¥ä½œæ—¶é—´å’Œå¹³å‡å‘¨æœŸæ—¶é—´', + 'Average lead time: ' => 'å¹³å‡å·¥ä½œæ—¶é—´', + 'Average cycle time: ' => 'å¹³å‡å‘¨æœŸæ—¶é—´: ', + 'Cycle Time' => '周期时间', + 'Lead Time' => '工作时间', // 'This chart show the average lead and cycle time for the last %d tasks over the time.' => '', - // 'Average time into each column' => '', - // 'Lead and cycle time' => '', - // 'Lead time: ' => '', - // 'Cycle time: ' => '', - // 'Time spent into each column' => '', - // 'The lead time is the duration between the task creation and the completion.' => '', - // 'The cycle time is the duration between the start date and the completion.' => '', + 'Average time into each column' => 'æ¯ä¸ªä»»åŠ¡æ çš„å¹³å‡æ—¶é—´', + 'Lead and cycle time' => '工作时间和周期时间', + 'Lead time: ' => '工作时间: ', + 'Cycle time: ' => '周期时间: ', + 'Time spent into each column' => 'æ¯ä¸ªä»»åŠ¡æ 的花费时间', + 'The lead time is the duration between the task creation and the completion.' => '工作时间是任务创建和完æˆä¹‹é—´çš„æŒç»æ—¶é—´ã€‚', + 'The cycle time is the duration between the start date and the completion.' => 'å‘¨æœŸæ—¶é—´æ˜¯å¼€å§‹æ—¥æœŸå’Œå®Œæˆæ—¶é—´ä¹‹é—´çš„æŒç»æ—¶é—´ã€‚', 'If the task is not closed the current time is used instead of the completion date.' => '如果当å‰ä»»åŠ¡æœªå…³é—æ—¶ç”¨å½“剿—¶é—´ä»£æ›¿å®Œæˆæ—¶é—´', 'Set automatically the start date' => '设置自动开始日期', 'Edit Authentication' => '编辑认è¯ä¿¡æ¯', @@ -743,15 +724,8 @@ return array( 'License:' => '授æƒè®¸å¯:', 'License' => '授æƒè®¸å¯', 'Enter the text below' => '输入下方的文本', - 'Sort by position' => '按ä½ç½®æŽ’åº', - 'Sort by date' => '按日期排åº', - 'Add task' => 'æ·»åŠ ä»»åŠ¡', 'Start date:' => '开始日期', 'Due date:' => '到期日期', - 'There is no start date or due date for this task.' => '当å‰ä»»åŠ¡æ²¡æœ‰å¼€å§‹æˆ–ç»“æŸæ—¶é—´ã€‚', - 'Moving or resizing a task will change the start and due date of the task.' => '移动或者é‡è®¾ä»»åŠ¡å°†æ”¹å˜ä»»åŠ¡å¼€å§‹å’Œç»“æŸæ—¶é—´ã€‚', - 'There is no task in your project.' => '当å‰é¡¹ç›®è¿˜æ²¡æœ‰ä»»åŠ¡', - 'Gantt chart' => '甘特图', 'People who are project managers' => '项目管ç†å‘˜', 'People who are project members' => '项目æˆå‘˜', 'NOK - Norwegian Krone' => '克朗', @@ -763,22 +737,15 @@ return array( 'Members' => 'æˆå‘˜', 'Shared project' => '公开项目', 'Project managers' => '项目管ç†å‘˜', - 'Gantt chart for all projects' => '所有项目的甘特图', 'Projects list' => '项目列表', - 'Gantt chart for this project' => 'æ¤é¡¹ç›®çš„甘特图', - 'Project board' => 'é¡¹ç›®é¢æ¿', 'End date:' => 'ç»“æŸæ—¥æœŸ', - 'There is no start date or end date for this project.' => '当å‰é¡¹ç›®æ²¡æœ‰å¼€å§‹æˆ–ç»“æŸæ—¥æœŸ', - 'Projects Gantt chart' => '项目甘特图', 'Change task color when using a specific task link' => '当任务关è”到指定任务时改å˜é¢œè‰²', 'Task link creation or modification' => '任务链接创建或更新时间', 'Milestone' => '里程碑', 'Documentation: %s' => '文档:%s', - 'Switch to the Gantt chart view' => '切æ¢åˆ°ç”˜ç‰¹å›¾', 'Reset the search/filter box' => 'é‡ç½®æœç´¢/过滤框', 'Documentation' => '文档', 'Table of contents' => '表内容', - 'Gantt' => '甘特图', 'Author' => '作者', 'Version' => '版本', 'Plugins' => 'æ’ä»¶', @@ -889,7 +856,6 @@ return array( 'There is no user available.' => '当剿²¡æœ‰æœ‰æ•ˆç”¨æˆ·', 'Do you really want to remove the user "%s" from the group "%s"?' => 'ä½ ç¡®å®šæŠŠç”¨æˆ·"%s"从"%s"ä¸ç§»é™¤ï¼Ÿ', 'There is no group.' => '当剿²¡æœ‰ç”¨æˆ·ç»„', - 'External Id' => 'å…³è”ID', 'Add group member' => 'æ·»åŠ ç”¨æˆ·ç»„æˆå‘˜', 'Do you really want to remove this group: "%s"?' => 'ä½ çœŸçš„æƒ³è¦ç§»é™¤ç”¨æˆ·ç»„:"%s"?', 'There is no user in this group.' => '当å‰ç”¨æˆ·ç»„下没有æˆå‘˜', @@ -955,7 +921,6 @@ return array( 'Default priority' => '默认优先级', 'Lowest priority' => '最低优先级', 'Highest priority' => '最高优先级', - 'If you put zero to the low and high priority, this feature will be disabled.' => 'å¦‚æžœä½ ä¸ºä¼˜å…ˆçº§å¡«0,将ç¦ç”¨æœ¬åŠŸèƒ½', 'Close a task when there is no activity' => '当任务没有活动记录时关é—任务', 'Duration in days' => 'æŒç»å¤©æ•°', 'Send email when there is no activity on a task' => '当任务没有活动记录时å‘é€é‚®ä»¶', @@ -990,7 +955,7 @@ return array( 'Reference:' => '引用:', 'Complexity:' => '夿‚度:', 'Swimlane:' => '里程碑:', - 'Column:' => '列:', + 'Column:' => 'æ 目:', 'Position:' => 'ä½ç½®ï¼š', 'Creator:' => '创建者:', 'Time estimated:' => '时间已过去:', @@ -1121,9 +1086,9 @@ return array( 'Enter one task by line.' => '写入一行任务。', 'Number of failed login:' => '登录失败次数:', 'Account locked until:' => '账户被é”定至:', - 'Email settings' => 'email设置', - 'Email sender address' => 'emailå‘é€åœ°å€', - 'Email transport' => 'email转å‘', + 'Email settings' => '邮件设置', + 'Email sender address' => '邮件å‘é€åœ°å€', + 'Email transport' => '邮件转å‘', 'Webhook token' => 'Webé’©åToken', 'Project tags management' => 'é¡¹ç›®æ ‡ç¾ç®¡ç†', 'Tag created successfully.' => 'æˆåŠŸåˆ›å»ºæ ‡ç¾ã€‚', @@ -1169,8 +1134,6 @@ return array( 'Subtasks overview for %s' => '%sçš„å任务预览', 'Projects overview for %s' => '%s的项目预览', 'Activity stream for %s' => '%s的活动足迹', - 'Calendar for %s' => '%s的日历', - 'Notifications for %s' => '%s的通知', 'Assign a color when the task is moved to a specific swimlane' => 'å½“ä»»åŠ¡ç§»åŠ¨åˆ°æŒ‡å®šé‡Œç¨‹ç¢‘æ—¶æ ‡è®°é¢œè‰²', 'Assign a priority when the task is moved to a specific swimlane' => 'å½“ä»»åŠ¡ç§»åŠ¨åˆ°æŒ‡å®šé‡Œç¨‹ç¢‘æ—¶æ ‡è®°ä¼˜å…ˆçº§', 'User unlocked successfully.' => 'ç”¨æˆ·è§£é”æˆåŠŸã€‚', @@ -1241,13 +1204,12 @@ return array( 'Preview' => '预览', 'Write' => '写入', 'Write your text in Markdown' => '用markdownæ ¼å¼å†™å…¥æ–‡æœ¬', - 'New External Task: %s' => '新的外部任务:%s', 'No personal API access token registered.' => '没有API access token 注册。', 'Your personal API access token is "%s"' => 'ä½ çš„API access token 是 “%sâ€', 'Remove your token' => '移除token', 'Generate a new token' => 'ç”Ÿæˆæ–°çš„token', 'Showing %d-%d of %d' => '本页显示 %d-%d æ¡ï¼Œå…±æœ‰: %d æ¡', - 'Outgoing Emails' => 'ç›®æ ‡email', + 'Outgoing Emails' => 'ç›®æ ‡é‚®ä»¶', 'Add or change currency rate' => 'æ·»åŠ æ›´æ”¹æ±‡çŽ‡', 'Reference currency: %s' => 'å‚考汇率:%s', 'Add custom filters' => 'æ·»åŠ è‡ªå®šä¹‰è¿‡æ»¤', @@ -1278,8 +1240,8 @@ return array( 'You have been invited to register on Kanboard.' => 'ä½ è¢«é‚€è¯·æ³¨å†Œçœ‹æ¿ã€‚', 'Click here to join your team' => 'ç‚¹å‡»è¿™é‡ŒåŠ å…¥ä½ çš„å›¢é˜Ÿ', 'Invite people' => '邀请新用户', - 'Emails' => 'Emails', - 'Enter one email address by line.' => '输入email地å€ï¼Œæ¯è¡Œä¸€ä¸ªã€‚', + 'Emails' => '邮件', + 'Enter one email address by line.' => '输入邮件地å€ï¼Œæ¯è¡Œä¸€ä¸ªã€‚', 'Add these people to this project' => 'æ·»åŠ ç”¨æˆ·åˆ°é¡¹ç›®', 'Add this person to this project' => 'æ·»åŠ ç”¨æˆ·åˆ°é¡¹ç›®', 'Sign-up' => '注册', @@ -1290,9 +1252,9 @@ return array( 'Your profile must have a valid email address.' => '个人资料必须有一个有效email地å€ã€‚', 'Unfortunately, we are unable to reset your password. Did you entered a valid username? Do you have an email address in your profile?' => '抱æ‰ï¼Œæ— 法é‡ç½®ä½ 的密ç 。请确认输入æ£ç¡®çš„ç”¨æˆ·åæˆ–email地å€ï¼Ÿ', 'TRL - Turkish Lira' => '土耳其里拉', - 'The project email is optional and could be used by several plugins.' => '项目email地å€å¯é€‰ï¼Œé€‚用于æ’件。', - 'The project email must be unique across all projects' => '项目email地å€å¿…须全局唯一', - 'The email configuration has been disabled by the administrator.' => 'emailé…置被管ç†å‘˜ç¦ç”¨ã€‚', + 'The project email is optional and could be used by several plugins.' => '项目邮件地å€å¯é€‰ï¼Œé€‚用于æ’件。', + 'The project email must be unique across all projects' => '项目邮件地å€å¿…须全局唯一', + 'The email configuration has been disabled by the administrator.' => '邮件é…置被管ç†å‘˜ç¦ç”¨ã€‚', 'Close this project' => 'å…³é—项目', 'Open this project' => '打开项目', 'Close a project' => 'å…³é—项目', @@ -1308,30 +1270,100 @@ return array( // 'Unable to read uploaded file.' => '', // 'Database uploaded successfully.' => '', // 'Task sent by email successfully.' => '', - // 'There is no category in this project.' => '', - // 'Send by email' => '', - // 'Create and send a comment by email' => '', - // 'Subject' => '', + 'There is no category in this project.' => '当å‰é¡¹ç›®æ²¡æœ‰åˆ†ç±»ã€‚', + 'Send by email' => 'å‘é€é‚®ä»¶', + 'Create and send a comment by email' => '通过电å邮件创建和å‘é€è¯„论', + 'Subject' => '主题', // 'Upload the database' => '', // 'You could upload the previously downloaded Sqlite database (Gzip format).' => '', // 'Database file' => '', // 'Upload' => '', - // 'Remove this user from group' => '', // 'Your project must have at least one active swimlane.' => '', // 'Project: %s' => '', // 'Automatic action not found: "%s"' => '', - // '%d projects' => '', - // '%d project' => '', - // 'There is no project.' => '', - // 'Sort' => '', - // 'Project ID' => '', - // 'Project name' => '', - // 'Public' => '', - // 'Private' => '', - // '%d tasks' => '', - // '%d task' => '', - // 'Task ID' => '', + '%d projects' => '%d 项目', + '%d project' => '%d 项目', + 'There is no project.' => '当剿²¡æœ‰é¡¹ç›®ã€‚', + 'Sort' => '排åº', + 'Project ID' => '项目ID', + 'Project name' => '项目å', + 'Public' => '公开', + 'Private' => 'ç§æœ‰', + '%d tasks' => '%d 任务', + '%d task' => '%d 任务', + 'Task ID' => '任务ID', // 'Assign automatically a color when due date is expired' => '', // 'Total score in this column across all swimlanes' => '', // 'HRK - Kuna' => '', + // 'ARS - Argentine Peso' => '', + // 'COP - Colombian Peso' => '', + '%d groups' => '%d 用户组', + '%d group' => '%d 用户组', + 'Group ID' => '用户组ID', + // 'External ID' => '', + '%d users' => '%d 用户', + '%d user' => '%d 用户', + 'Hide subtasks' => '显示å任务', + 'Show subtasks' => 'éšè—å任务', + // 'Authentication Parameters' => '', + // 'API Access' => '', + // 'No users found.' => '', + 'User ID' => '用户ID', + // 'Notifications are activated' => '', + // 'Notifications are disabled' => '', + 'User disabled' => '用户已ç¦ç”¨', + '%d notifications' => '%d 通知', + '%d notification' => '%d 通知', + // 'There is no external integration installed.' => '', + // 'You are not allowed to update tasks assigned to someone else.' => '', + // 'You are not allowed to change the assignee.' => '', + // 'Task suppression is not permitted' => '', + // 'Changing assignee is not permitted' => '', + // 'Update only assigned tasks is permitted' => '', + // 'Only for tasks assigned to the current user' => '', + 'My projects' => '我的项目', + 'Your are not member of any project.' => 'ä½ ä¸æ˜¯ä»»ä½•项目的æˆå‘˜ã€‚', + 'My subtasks' => '我的å任务', + '%d subtasks' => '%d å任务', + '%d subtask' => '%d å任务', + // 'Only moving task between those columns is permitted for tasks assigned to the current user' => '', + // '[DUPLICATE]' => '', + // 'DKK - Danish Krona' => '', + 'Remove user from group' => '从用户组ä¸ç§»é™¤ç”¨æˆ·', + 'Assign the task to its creator' => '将任务分é…给创建者', + // 'This task was sent by email to "%s" with subject "%s".' => '', + 'Predefined Email Subjects' => '预定义的邮件主题', + // 'Write one subject by line.' => '', + // 'Create another link' => '', + // 'BRL - Brazilian Real' => '', + // 'Add a new Kanboard task' => '', + // 'Subtask not started' => '', + // 'Subtask currently in progress' => '', + // 'Subtask completed' => '', + // 'Subtask added successfully.' => '', + // '%d subtasks added successfully.' => '', + // 'Enter one subtask by line.' => '', + // 'Predefined Contents' => '', + // 'Predefined contents' => '', + // 'Predefined Task Description' => '', + // 'Do you really want to remove this template? "%s"' => '', + // 'Add predefined task description' => '', + // 'Predefined Task Descriptions' => '', + // 'Template created successfully.' => '', + // 'Unable to create this template.' => '', + // 'Template updated successfully.' => '', + // 'Unable to update this template.' => '', + // 'Template removed successfully.' => '', + // 'Unable to remove this template.' => '', + // 'Template for the task description' => '', + // 'The start date is greater than the end date' => '', + // 'Tags must be separated by a comma' => '', + // 'Only the task title is required' => '', + // 'Creator Username' => '', + // 'Color Name' => '', + // 'Column Name' => '', + // 'Swimlane Name' => '', + // 'Time Estimated' => '', + // 'Time Spent' => '', + // 'External Link' => '', ); diff --git a/app/Model/ColumnMoveRestrictionModel.php b/app/Model/ColumnMoveRestrictionModel.php index c2603efd..9d2b2842 100644 --- a/app/Model/ColumnMoveRestrictionModel.php +++ b/app/Model/ColumnMoveRestrictionModel.php @@ -31,6 +31,7 @@ class ColumnMoveRestrictionModel extends Base self::TABLE.'.role_id', self::TABLE.'.src_column_id', self::TABLE.'.dst_column_id', + self::TABLE.'.only_assigned', 'pr.role', 'sc.title as src_column_title', 'dc.title as dst_column_title' @@ -59,6 +60,7 @@ class ColumnMoveRestrictionModel extends Base self::TABLE.'.role_id', self::TABLE.'.src_column_id', self::TABLE.'.dst_column_id', + self::TABLE.'.only_assigned', 'pr.role', 'sc.title as src_column_title', 'dc.title as dst_column_title' @@ -81,7 +83,7 @@ class ColumnMoveRestrictionModel extends Base { return $this->db ->table(self::TABLE) - ->columns(self::TABLE.'.src_column_id', self::TABLE.'.dst_column_id') + ->columns(self::TABLE.'.src_column_id', self::TABLE.'.dst_column_id', self::TABLE.'.only_assigned') ->left(ProjectRoleModel::TABLE, 'pr', 'role_id', self::TABLE, 'role_id') ->eq(self::TABLE.'.project_id', $project_id) ->eq('pr.role', $role) @@ -95,9 +97,10 @@ class ColumnMoveRestrictionModel extends Base * @param int $role_id * @param int $src_column_id * @param int $dst_column_id + * @param bool $only_assigned * @return bool|int */ - public function create($project_id, $role_id, $src_column_id, $dst_column_id) + public function create($project_id, $role_id, $src_column_id, $dst_column_id, $only_assigned = false) { return $this->db ->table(self::TABLE) @@ -106,6 +109,7 @@ class ColumnMoveRestrictionModel extends Base 'role_id' => $role_id, 'src_column_id' => $src_column_id, 'dst_column_id' => $dst_column_id, + 'only_assigned' => (int) $only_assigned, )); } diff --git a/app/Model/CurrencyModel.php b/app/Model/CurrencyModel.php index c858a6ae..6f009f61 100644 --- a/app/Model/CurrencyModel.php +++ b/app/Model/CurrencyModel.php @@ -38,6 +38,7 @@ class CurrencyModel extends Base 'INR' => t('INR - Indian Rupee'), 'JPY' => t('JPY - Japanese Yen'), 'RSD' => t('RSD - Serbian dinar'), + 'DKK' => t('DKK - Danish Krona'), 'SEK' => t('SEK - Swedish Krona'), 'NOK' => t('NOK - Norwegian Krone'), 'BAM' => t('BAM - Konvertible Mark'), @@ -45,6 +46,9 @@ class CurrencyModel extends Base 'CNY' => t('CNY - Chinese Yuan'), 'TRL' => t('TRL - Turkish Lira'), 'HRK' => t('HRK - Kuna'), + 'ARS' => t('ARS - Argentine Peso'), + 'COP' => t('COP - Colombian Peso'), + 'BRL' => t('BRL - Brazilian Real'), ); } diff --git a/app/Model/LanguageModel.php b/app/Model/LanguageModel.php index 7b12cac8..eff2191d 100644 --- a/app/Model/LanguageModel.php +++ b/app/Model/LanguageModel.php @@ -25,6 +25,7 @@ class LanguageModel extends Base return array( 'id_ID', 'bs_BA', + 'ca_ES', 'cs_CZ', 'da_DK', 'de_DE', @@ -41,6 +42,7 @@ class LanguageModel extends Base 'pl_PL', 'pt_PT', 'pt_BR', + 'ro_RO', 'ru_RU', 'sr_Latn_RS', 'fi_FI', @@ -50,6 +52,7 @@ class LanguageModel extends Base 'zh_CN', 'ja_JP', 'th_TH', + 'vi_VN', ); } @@ -80,6 +83,7 @@ class LanguageModel extends Base $languages = array( 'id_ID' => 'Bahasa Indonesia', 'bs_BA' => 'Bosanski', + 'ca_ES' => 'Català ', 'cs_CZ' => 'ÄŒeÅ¡tina', 'da_DK' => 'Dansk', 'de_DE' => 'Deutsch', @@ -96,6 +100,7 @@ class LanguageModel extends Base 'pl_PL' => 'Polski', 'pt_PT' => 'Português', 'pt_BR' => 'Português (Brasil)', + 'ro_RO' => 'Română', 'ru_RU' => 'РуÑÑкий', 'sr_Latn_RS' => 'Srpski', 'fi_FI' => 'Suomi', @@ -105,6 +110,7 @@ class LanguageModel extends Base 'zh_CN' => '䏿–‡(简体)', 'ja_JP' => '日本語', 'th_TH' => 'ไทย', + 'vi_VN' => 'Tiếng Việt', ); if ($prepend) { @@ -124,6 +130,7 @@ class LanguageModel extends Base { $languages = array( 'cs_CZ' => 'cs', + 'ca_ES' => 'ca', 'da_DK' => 'da', 'de_DE' => 'de', 'en_US' => 'en', @@ -136,14 +143,15 @@ class LanguageModel extends Base 'nb_NO' => 'nb', 'pl_PL' => 'pl', 'pt_PT' => 'pt', - 'pt_BR' => 'pt-br', + 'pt_BR' => 'pt-BR', + 'ro_RO' => 'ro', 'ru_RU' => 'ru', 'sr_Latn_RS' => 'sr', 'fi_FI' => 'fi', 'sv_SE' => 'sv', 'tr_TR' => 'tr', 'ko_KR' => 'ko', - 'zh_CN' => 'zh-cn', + 'zh_CN' => 'zh-CN', 'ja_JP' => 'ja', 'th_TH' => 'th', 'id_ID' => 'id', diff --git a/app/Model/PredefinedTaskDescriptionModel.php b/app/Model/PredefinedTaskDescriptionModel.php new file mode 100644 index 00000000..aaa4d23e --- /dev/null +++ b/app/Model/PredefinedTaskDescriptionModel.php @@ -0,0 +1,52 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +class PredefinedTaskDescriptionModel extends Base +{ + const TABLE = 'predefined_task_descriptions'; + + public function getAll($projectId) + { + return $this->db->table(self::TABLE)->eq('project_id', $projectId)->findAll(); + } + + public function getList($projectId) + { + return array('' => t('None')) + $this->db->hashtable(self::TABLE)->eq('project_id', $projectId)->getAll('id', 'title'); + } + + public function getById($projectId, $id) + { + return $this->db->table(self::TABLE)->eq('project_id', $projectId)->eq('id', $id)->findOne(); + } + + public function getDescriptionById($projectId, $id) + { + return $this->db->table(self::TABLE)->eq('project_id', $projectId)->eq('id', $id)->findOneColumn('description'); + } + + public function create($projectId, $title, $description) + { + return $this->db->table(self::TABLE)->persist(array( + 'project_id' => $projectId, + 'title' => $title, + 'description' => $description, + )); + } + + public function update($projectId, $id, $title, $description) + { + return $this->db->table(self::TABLE)->eq('project_id', $projectId)->eq('id', $id)->update(array( + 'title' => $title, + 'description' => $description, + )); + } + + public function remove($projectId, $id) + { + return $this->db->table(self::TABLE)->eq('project_id', $projectId)->eq('id', $id)->remove(); + } +} diff --git a/app/Model/ProjectActivityModel.php b/app/Model/ProjectActivityModel.php index 380ea125..17cf6485 100644 --- a/app/Model/ProjectActivityModel.php +++ b/app/Model/ProjectActivityModel.php @@ -21,13 +21,6 @@ class ProjectActivityModel extends Base const TABLE = 'project_activities'; /** - * Maximum number of events - * - * @var integer - */ - const MAX_EVENTS = 1000; - - /** * Add a new event for the project * * @access public @@ -49,7 +42,7 @@ class ProjectActivityModel extends Base 'data' => json_encode($data), ); - $this->cleanup(self::MAX_EVENTS - 1); + $this->cleanup(PROJECT_ACTIVITIES_MAX_EVENTS - 1); return $this->db->table(self::TABLE)->insert($values); } diff --git a/app/Model/ProjectGroupRoleModel.php b/app/Model/ProjectGroupRoleModel.php index 2729d5a6..2b1973f5 100644 --- a/app/Model/ProjectGroupRoleModel.php +++ b/app/Model/ProjectGroupRoleModel.php @@ -85,7 +85,13 @@ class ProjectGroupRoleModel extends Base public function getUsers($project_id) { return $this->db->table(self::TABLE) - ->columns(UserModel::TABLE.'.id', UserModel::TABLE.'.username', UserModel::TABLE.'.name', self::TABLE.'.role') + ->columns( + UserModel::TABLE.'.id', + UserModel::TABLE.'.username', + UserModel::TABLE.'.name', + UserModel::TABLE.'.email', + self::TABLE.'.role' + ) ->join(GroupMemberModel::TABLE, 'group_id', 'group_id', self::TABLE) ->join(UserModel::TABLE, 'id', 'user_id', GroupMemberModel::TABLE) ->eq(self::TABLE.'.project_id', $project_id) diff --git a/app/Model/ProjectModel.php b/app/Model/ProjectModel.php index aa7c002d..097806d8 100644 --- a/app/Model/ProjectModel.php +++ b/app/Model/ProjectModel.php @@ -202,16 +202,23 @@ class ProjectModel extends Base * Return the list of all projects * * @access public - * @param bool $prepend If true, prepend to the list the value 'None' + * @param bool $prependNone + * @param bool $noPrivateProjects * @return array */ - public function getList($prepend = true) + public function getList($prependNone = true, $noPrivateProjects = true) { - if ($prepend) { - return array(t('None')) + $this->db->hashtable(self::TABLE)->asc('name')->getAll('id', 'name'); + if ($noPrivateProjects) { + $projects = $this->db->hashtable(self::TABLE)->eq('is_private', 0)->asc('name')->getAll('id', 'name'); + } else { + $projects = $this->db->hashtable(self::TABLE)->asc('name')->getAll('id', 'name'); } - return $this->db->hashtable(self::TABLE)->asc('name')->getAll('id', 'name'); + if ($prependNone) { + return array(t('None')) + $projects; + } + + return $projects; } /** @@ -310,11 +317,11 @@ class ProjectModel extends Base } return $this->db - ->table(ProjectModel::TABLE) - ->columns(self::TABLE.'.*', UserModel::TABLE.'.username AS owner_username', UserModel::TABLE.'.name AS owner_name') - ->join(UserModel::TABLE, 'id', 'owner_id') - ->in(self::TABLE.'.id', $project_ids) - ->callback(array($this, 'applyColumnStats')); + ->table(ProjectModel::TABLE) + ->columns(self::TABLE.'.*', UserModel::TABLE.'.username AS owner_username', UserModel::TABLE.'.name AS owner_name') + ->join(UserModel::TABLE, 'id', 'owner_id') + ->in(self::TABLE.'.id', $project_ids) + ->callback(array($this, 'applyColumnStats')); } /** @@ -348,6 +355,10 @@ class ProjectModel extends Base */ public function create(array $values, $userId = 0, $addUser = false) { + if (! empty($userId) && ! $this->userModel->exists($userId)) { + return false; + } + $this->db->startTransaction(); $values['token'] = ''; @@ -440,6 +451,10 @@ class ProjectModel extends Base $values['end_date'] = $this->dateParser->getIsoDate($values['end_date']); } + if (! empty($values['owner_id']) && ! $this->userModel->exists($values['owner_id'])) { + return false; + } + $this->helper->model->convertIntegerFields($values, array('priority_default', 'priority_start', 'priority_end')); return $this->exists($values['id']) && @@ -455,7 +470,13 @@ class ProjectModel extends Base */ public function remove($project_id) { - return $this->db->table(self::TABLE)->eq('id', $project_id)->remove(); + $this->db->startTransaction(); + + $this->db->table(TagModel::TABLE)->eq('project_id', $project_id)->remove(); + $result = $this->db->table(self::TABLE)->eq('id', $project_id)->remove(); + + $this->db->closeTransaction(); + return $result; } /** diff --git a/app/Model/ProjectPermissionModel.php b/app/Model/ProjectPermissionModel.php index dabd406c..d1351a6c 100644 --- a/app/Model/ProjectPermissionModel.php +++ b/app/Model/ProjectPermissionModel.php @@ -93,20 +93,22 @@ class ProjectPermissionModel extends Base return $members; } - /** - * Return true if everybody is allowed for the project - * - * @access public - * @param integer $project_id Project id - * @return bool - */ - public function isEverybodyAllowed($project_id) + public function getMembers($project_id) + { + $userMembers = $this->projectUserRoleModel->getUsers($project_id); + $groupMembers = $this->projectGroupRoleModel->getUsers($project_id); + + $userMembers = array_column_index_unique($userMembers, 'username'); + $groupMembers = array_column_index_unique($groupMembers, 'username'); + return array_merge($userMembers, $groupMembers); + } + + public function getMembersWithEmail($project_id) { - return $this->db - ->table(ProjectModel::TABLE) - ->eq('id', $project_id) - ->eq('is_everybody_allowed', 1) - ->exists(); + $members = $this->getMembers($project_id); + return array_filter($members, function (array $user) { + return ! empty($user['email']); + }); } /** diff --git a/app/Model/ProjectRoleRestrictionModel.php b/app/Model/ProjectRoleRestrictionModel.php index 8ccdcf9c..45e7ed2f 100644 --- a/app/Model/ProjectRoleRestrictionModel.php +++ b/app/Model/ProjectRoleRestrictionModel.php @@ -14,9 +14,12 @@ class ProjectRoleRestrictionModel extends Base { const TABLE = 'project_role_has_restrictions'; - const RULE_TASK_CREATION = 'task_creation'; - const RULE_TASK_OPEN_CLOSE = 'task_open_close'; - const RULE_TASK_MOVE = 'task_move'; + const RULE_TASK_CREATION = 'task_creation'; + const RULE_TASK_SUPPRESSION = 'task_remove'; + const RULE_TASK_OPEN_CLOSE = 'task_open_close'; + const RULE_TASK_MOVE = 'task_move'; + const RULE_TASK_CHANGE_ASSIGNEE = 'task_change_assignee'; + const RULE_TASK_UPDATE_ASSIGNED = 'task_update_assigned'; /** * Get rules @@ -26,9 +29,12 @@ class ProjectRoleRestrictionModel extends Base public function getRules() { return array( - self::RULE_TASK_CREATION => t('Task creation is not permitted'), - self::RULE_TASK_OPEN_CLOSE => t('Closing or opening a task is not permitted'), - self::RULE_TASK_MOVE => t('Moving a task is not permitted'), + self::RULE_TASK_CREATION => t('Task creation is not permitted'), + self::RULE_TASK_SUPPRESSION => t('Task suppression is not permitted'), + self::RULE_TASK_OPEN_CLOSE => t('Closing or opening a task is not permitted'), + self::RULE_TASK_MOVE => t('Moving a task is not permitted'), + self::RULE_TASK_CHANGE_ASSIGNEE => t('Changing assignee is not permitted'), + self::RULE_TASK_UPDATE_ASSIGNED => t('Update only assigned tasks is permitted'), ); } diff --git a/app/Model/ProjectUserRoleModel.php b/app/Model/ProjectUserRoleModel.php index 76094431..dc4add34 100644 --- a/app/Model/ProjectUserRoleModel.php +++ b/app/Model/ProjectUserRoleModel.php @@ -44,10 +44,7 @@ class ProjectUserRoleModel extends Base { $userProjects = $this->db ->hashtable(ProjectModel::TABLE) - ->beginOr() ->eq(self::TABLE.'.user_id', $user_id) - ->eq(ProjectModel::TABLE.'.is_everybody_allowed', 1) - ->closeOr() ->in(ProjectModel::TABLE.'.is_active', $status) ->join(self::TABLE, 'project_id', 'id') ->getAll(ProjectModel::TABLE.'.id', ProjectModel::TABLE.'.name'); @@ -70,15 +67,6 @@ class ProjectUserRoleModel extends Base */ public function getUserRole($project_id, $user_id) { - $projectInfo = $this->db->table(ProjectModel::TABLE) - ->eq('id', $project_id) - ->columns('owner_id', 'is_everybody_allowed') - ->findOne(); - - if ($projectInfo['is_everybody_allowed'] == 1) { - return $projectInfo['owner_id'] == $user_id ? Role::PROJECT_MANAGER : Role::PROJECT_MEMBER; - } - $role = $this->db->table(self::TABLE)->eq('user_id', $user_id)->eq('project_id', $project_id)->findOneColumn('role'); if (empty($role)) { @@ -98,7 +86,13 @@ class ProjectUserRoleModel extends Base public function getUsers($project_id) { return $this->db->table(self::TABLE) - ->columns(UserModel::TABLE.'.id', UserModel::TABLE.'.username', UserModel::TABLE.'.name', self::TABLE.'.role') + ->columns( + UserModel::TABLE.'.id', + UserModel::TABLE.'.username', + UserModel::TABLE.'.name', + UserModel::TABLE.'.email', + self::TABLE.'.role' + ) ->join(UserModel::TABLE, 'id', 'user_id') ->eq('project_id', $project_id) ->asc(UserModel::TABLE.'.username') @@ -157,10 +151,6 @@ class ProjectUserRoleModel extends Base */ public function getAssignableUsers($project_id) { - if ($this->projectPermissionModel->isEverybodyAllowed($project_id)) { - return $this->userModel->getActiveUsersList(); - } - $userMembers = $this->db->table(self::TABLE) ->columns(UserModel::TABLE.'.id', UserModel::TABLE.'.username', UserModel::TABLE.'.name') ->join(UserModel::TABLE, 'id', 'user_id') diff --git a/app/Model/SubtaskModel.php b/app/Model/SubtaskModel.php index c62ddb53..1e652ae2 100644 --- a/app/Model/SubtaskModel.php +++ b/app/Model/SubtaskModel.php @@ -70,35 +70,6 @@ class SubtaskModel extends Base } /** - * Get the query to fetch subtasks assigned to a user - * - * @access public - * @param integer $userId - * @param array $status - * @return \PicoDb\Table - */ - public function getUserQuery($userId, array $status) - { - return $this->db->table(SubtaskModel::TABLE) - ->columns( - SubtaskModel::TABLE.'.*', - TaskModel::TABLE.'.project_id', - TaskModel::TABLE.'.color_id', - TaskModel::TABLE.'.title AS task_name', - ProjectModel::TABLE.'.name AS project_name' - ) - ->subquery($this->subtaskTimeTrackingModel->getTimerQuery($userId), 'timer_start_date') - ->eq('user_id', $userId) - ->eq(ProjectModel::TABLE.'.is_active', ProjectModel::ACTIVE) - ->eq(ColumnModel::TABLE.'.hide_in_dashboard', 0) - ->in(SubtaskModel::TABLE.'.status', $status) - ->join(TaskModel::TABLE, 'id', 'task_id') - ->join(ProjectModel::TABLE, 'id', 'project_id', TaskModel::TABLE) - ->join(ColumnModel::TABLE, 'id', 'column_id', TaskModel::TABLE) - ->callback(array($this, 'addStatusName')); - } - - /** * Get common query * * @return \PicoDb\Table @@ -117,6 +88,15 @@ class SubtaskModel extends Base ->asc(self::TABLE.'.position'); } + public function countByAssigneeAndTaskStatus($userId) + { + return $this->db->table(self::TABLE) + ->eq('user_id', $userId) + ->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN) + ->join(Taskmodel::TABLE, 'id', 'task_id') + ->count(); + } + /** * Get all subtasks for a given task * @@ -149,6 +129,24 @@ class SubtaskModel extends Base } /** + * Get subtasks for a list of tasks and a given assignee + * + * @param array $taskIds + * @param integer $userId + * @return array + */ + public function getAllByTaskIdsAndAssignee(array $taskIds, $userId) + { + if (empty($taskIds)) { + return array(); + } + + return $this->subtaskListFormatter + ->withQuery($this->getQuery()->in('task_id', $taskIds)->eq(self::TABLE.'.user_id', $userId)) + ->format(); + } + + /** * Get a subtask by the id * * @access public @@ -229,10 +227,11 @@ class SubtaskModel extends Base $result = $this->db->table(self::TABLE)->eq('id', $values['id'])->save($values); if ($result) { - $this->subtaskTimeTrackingModel->updateTaskTimeTracking($values['task_id']); + $subtask = $this->getById($values['id']); + $this->subtaskTimeTrackingModel->updateTaskTimeTracking($subtask['task_id']); if ($fireEvent) { - $this->queueManager->push($this->subtaskEventJob->withParams($values['id'], self::EVENT_UPDATE, $values)); + $this->queueManager->push($this->subtaskEventJob->withParams($subtask['id'], self::EVENT_UPDATE, $values)); } } @@ -310,24 +309,4 @@ class SubtaskModel extends Base $values['user_id'] = isset($values['user_id']) ? $values['user_id'] : 0; $this->hook->reference('model:subtask:creation:prepare', $values); } - - /** - * Add subtask status status to the resultset - * - * @access public - * @param array $subtasks Subtasks - * @return array - */ - public function addStatusName(array $subtasks) - { - $status = $this->getStatusList(); - - foreach ($subtasks as &$subtask) { - $subtask['status_name'] = $status[$subtask['status']]; - $subtask['timer_start_date'] = isset($subtask['timer_start_date']) ? $subtask['timer_start_date'] : 0; - $subtask['is_timer_started'] = ! empty($subtask['timer_start_date']); - } - - return $subtasks; - } } diff --git a/app/Model/TaskCreationModel.php b/app/Model/TaskCreationModel.php index 95f62ee5..dbec2b12 100644 --- a/app/Model/TaskCreationModel.php +++ b/app/Model/TaskCreationModel.php @@ -58,7 +58,7 @@ class TaskCreationModel extends Base */ protected function prepare(array &$values) { - $values = $this->dateParser->convert($values, array('date_due')); + $values = $this->dateParser->convert($values, array('date_due'), true); $values = $this->dateParser->convert($values, array('date_started'), true); $this->helper->model->removeFields($values, array('another_task', 'duplicate_multiple_projects')); diff --git a/app/Model/TaskDuplicationModel.php b/app/Model/TaskDuplicationModel.php index c07ebca0..eb59f555 100644 --- a/app/Model/TaskDuplicationModel.php +++ b/app/Model/TaskDuplicationModel.php @@ -47,7 +47,10 @@ class TaskDuplicationModel extends Base */ public function duplicate($task_id) { - $new_task_id = $this->save($task_id, $this->copyFields($task_id)); + $values = $this->copyFields($task_id); + $values['title'] = t('[DUPLICATE]').' '.$values['title']; + + $new_task_id = $this->save($task_id, $values); if ($new_task_id !== false) { $this->tagDuplicationModel->duplicateTaskTags($task_id, $new_task_id); diff --git a/app/Model/TaskFinderModel.php b/app/Model/TaskFinderModel.php index b610a371..e3b5e0b9 100644 --- a/app/Model/TaskFinderModel.php +++ b/app/Model/TaskFinderModel.php @@ -59,27 +59,11 @@ class TaskFinderModel extends Base */ public function getUserQuery($user_id) { - return $this->db - ->table(TaskModel::TABLE) - ->columns( - TaskModel::TABLE.'.id', - TaskModel::TABLE.'.title', - TaskModel::TABLE.'.date_due', - TaskModel::TABLE.'.date_creation', - TaskModel::TABLE.'.project_id', - TaskModel::TABLE.'.column_id', - TaskModel::TABLE.'.color_id', - TaskModel::TABLE.'.priority', - TaskModel::TABLE.'.time_spent', - TaskModel::TABLE.'.time_estimated', - TaskModel::TABLE.'.is_active', - TaskModel::TABLE.'.creator_id', - ProjectModel::TABLE.'.name AS project_name', - ColumnModel::TABLE.'.title AS column_title' - ) - ->join(ProjectModel::TABLE, 'id', 'project_id') - ->join(ColumnModel::TABLE, 'id', 'column_id') + return $this->getExtendedQuery() + ->beginOr() ->eq(TaskModel::TABLE.'.owner_id', $user_id) + ->addCondition(TaskModel::TABLE.".id IN (SELECT task_id FROM ".SubtaskModel::TABLE." WHERE ".SubtaskModel::TABLE.".user_id='$user_id')") + ->closeOr() ->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN) ->eq(ProjectModel::TABLE.'.is_active', ProjectModel::ACTIVE) ->eq(ColumnModel::TABLE.'.hide_in_dashboard', 0); @@ -213,7 +197,7 @@ class TaskFinderModel extends Base ->eq(ProjectModel::TABLE.'.is_active', 1) ->eq(TaskModel::TABLE.'.is_active', 1) ->neq(TaskModel::TABLE.'.date_due', 0) - ->lte(TaskModel::TABLE.'.date_due', mktime(23, 59, 59)); + ->lte(TaskModel::TABLE.'.date_due', time()); } /** diff --git a/app/Model/TaskModificationModel.php b/app/Model/TaskModificationModel.php index 0ae29293..b10c0aa9 100644 --- a/app/Model/TaskModificationModel.php +++ b/app/Model/TaskModificationModel.php @@ -98,7 +98,7 @@ class TaskModificationModel extends Base */ protected function prepare(array &$values) { - $values = $this->dateParser->convert($values, array('date_due')); + $values = $this->dateParser->convert($values, array('date_due'), true); $values = $this->dateParser->convert($values, array('date_started'), true); $this->helper->model->removeFields($values, array('id')); diff --git a/app/Model/TaskPositionModel.php b/app/Model/TaskPositionModel.php index 8805d57e..9f7eb983 100644 --- a/app/Model/TaskPositionModel.php +++ b/app/Model/TaskPositionModel.php @@ -243,26 +243,26 @@ class TaskPositionModel extends Base ); if ($task['swimlane_id'] != $new_swimlane_id) { - $this->queueManager->push($this->taskEventJob->withParams( + $this->taskEventJob->execute( $task['id'], array(TaskModel::EVENT_MOVE_SWIMLANE), $changes, $changes - )); + ); } elseif ($task['column_id'] != $new_column_id) { - $this->queueManager->push($this->taskEventJob->withParams( + $this->taskEventJob->execute( $task['id'], array(TaskModel::EVENT_MOVE_COLUMN), $changes, $changes - )); + ); } elseif ($task['position'] != $new_position) { - $this->queueManager->push($this->taskEventJob->withParams( + $this->taskEventJob->execute( $task['id'], array(TaskModel::EVENT_MOVE_POSITION), $changes, $changes - )); + ); } } } diff --git a/app/Model/UserModel.php b/app/Model/UserModel.php index 56b1a960..af49ce7d 100644 --- a/app/Model/UserModel.php +++ b/app/Model/UserModel.php @@ -376,4 +376,20 @@ class UserModel extends Base ->eq('id', $user_id) ->save(array('token' => '')); } + + public function getOrCreateExternalUserId($username, $name, $externalIdColumn, $externalId) + { + $userId = $this->db->table(self::TABLE)->eq($externalIdColumn, $externalId)->findOneColumn('id'); + + if (empty($userId)) { + $userId = $this->create(array( + 'username' => $username, + 'name' => $name, + 'is_ldap_user' => 1, + $externalIdColumn => $externalId, + )); + } + + return $userId; + } } diff --git a/app/Model/UserNotificationModel.php b/app/Model/UserNotificationModel.php index d77526f6..9e90cf5d 100644 --- a/app/Model/UserNotificationModel.php +++ b/app/Model/UserNotificationModel.php @@ -68,10 +68,6 @@ class UserNotificationModel extends Base */ public function getUsersWithNotificationEnabled($project_id, $exclude_user_id = 0) { - if ($this->projectPermissionModel->isEverybodyAllowed($project_id)) { - return $this->getEverybodyWithNotificationEnabled($exclude_user_id); - } - $users = array(); $members = $this->getProjectUserMembersWithNotificationEnabled($project_id, $exclude_user_id); $groups = $this->getProjectGroupMembersWithNotificationEnabled($project_id, $exclude_user_id); @@ -183,22 +179,4 @@ class UserNotificationModel extends Base ->eq(UserModel::TABLE.'.is_active', 1) ->findAll(); } - - /** - * Get a list of project members with notification enabled - * - * @access private - * @param integer $exclude_user_id User id to exclude - * @return array - */ - private function getEverybodyWithNotificationEnabled($exclude_user_id) - { - return $this->db - ->table(UserModel::TABLE) - ->columns(UserModel::TABLE.'.id', UserModel::TABLE.'.username', UserModel::TABLE.'.name', UserModel::TABLE.'.email', UserModel::TABLE.'.language', UserModel::TABLE.'.notifications_filter') - ->eq('notifications_enabled', '1') - ->neq(UserModel::TABLE.'.id', $exclude_user_id) - ->eq(UserModel::TABLE.'.is_active', 1) - ->findAll(); - } } diff --git a/app/Pagination/DashboardPagination.php b/app/Pagination/DashboardPagination.php new file mode 100644 index 00000000..b8fc4434 --- /dev/null +++ b/app/Pagination/DashboardPagination.php @@ -0,0 +1,50 @@ +<?php + +namespace Kanboard\Pagination; + +use Kanboard\Core\Base; +use Kanboard\Model\ProjectModel; +use Kanboard\Model\TaskModel; + +/** + * Class DashboardPagination + * + * @package Kanboard\Pagination + * @author Frederic Guillot + */ +class DashboardPagination extends Base +{ + /** + * Get user listing pagination + * + * @access public + * @param integer $userId + * @return array + */ + public function getOverview($userId) + { + $paginators = array(); + $projects = $this->projectUserRoleModel->getActiveProjectsByUser($userId); + + foreach ($projects as $projectId => $projectName) { + $paginator = $this->paginator + ->setUrl('DashboardController', 'show', array('user_id' => $userId)) + ->setMax(50) + ->setOrder(TaskModel::TABLE.'.priority') + ->setDirection('DESC') + ->setFormatter($this->taskListSubtaskAssigneeFormatter->withUserId($userId)) + ->setQuery($this->taskFinderModel->getUserQuery($userId)->eq(ProjectModel::TABLE.'.id', $projectId)) + ->calculate(); + + if ($paginator->getTotal() > 0) { + $paginators[] = array( + 'project_id' => $projectId, + 'project_name' => $projectName, + 'paginator' => $paginator, + ); + } + } + + return $paginators; + } +} diff --git a/app/Pagination/SubtaskPagination.php b/app/Pagination/SubtaskPagination.php index c55d0fb4..83593b5e 100644 --- a/app/Pagination/SubtaskPagination.php +++ b/app/Pagination/SubtaskPagination.php @@ -4,7 +4,6 @@ namespace Kanboard\Pagination; use Kanboard\Core\Base; use Kanboard\Core\Paginator; -use Kanboard\Model\SubtaskModel; use Kanboard\Model\TaskModel; /** @@ -19,21 +18,18 @@ class SubtaskPagination extends Base * Get dashboard pagination * * @access public - * @param integer $user_id - * @param string $method - * @param integer $max + * @param integer $userId * @return Paginator */ - public function getDashboardPaginator($user_id, $method, $max) + public function getDashboardPaginator($userId) { - $query = $this->subtaskModel->getUserQuery($user_id, array(SubtaskModel::STATUS_TODO, SubtaskModel::STATUS_INPROGRESS)); - $this->hook->reference('pagination:dashboard:subtask:query', $query); - return $this->paginator - ->setUrl('DashboardController', $method, array('pagination' => 'subtasks', 'user_id' => $user_id)) - ->setMax($max) - ->setOrder(TaskModel::TABLE.'.id') - ->setQuery($query) - ->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks'); + ->setUrl('DashboardController', 'subtasks', array('user_id' => $userId)) + ->setMax(50) + ->setOrder(TaskModel::TABLE.'.priority') + ->setDirection('DESC') + ->setFormatter($this->taskListSubtaskAssigneeFormatter->withUserId($userId)->withoutEmptyTasks()) + ->setQuery($this->taskFinderModel->getUserQuery($userId)) + ->calculate(); } } diff --git a/app/Pagination/TaskPagination.php b/app/Pagination/TaskPagination.php index 5fe986e7..53e05c13 100644 --- a/app/Pagination/TaskPagination.php +++ b/app/Pagination/TaskPagination.php @@ -18,21 +18,22 @@ class TaskPagination extends Base * Get dashboard pagination * * @access public - * @param integer $user_id + * @param integer $userId * @param string $method * @param integer $max * @return Paginator */ - public function getDashboardPaginator($user_id, $method, $max) + public function getDashboardPaginator($userId, $method, $max) { - $query = $this->taskFinderModel->getUserQuery($user_id); + $query = $this->taskFinderModel->getUserQuery($userId); $this->hook->reference('pagination:dashboard:task:query', $query); return $this->paginator - ->setUrl('DashboardController', $method, array('pagination' => 'tasks', 'user_id' => $user_id)) + ->setUrl('DashboardController', $method, array('pagination' => 'tasks', 'user_id' => $userId)) ->setMax($max) ->setOrder(TaskModel::TABLE.'.id') ->setQuery($query) + ->setFormatter($this->taskListFormatter) ->calculateOnlyIf($this->request->getStringParam('pagination') === 'tasks'); } } diff --git a/app/Pagination/UserPagination.php b/app/Pagination/UserPagination.php index 430b7d2f..87688573 100644 --- a/app/Pagination/UserPagination.php +++ b/app/Pagination/UserPagination.php @@ -15,7 +15,7 @@ use Kanboard\Model\UserModel; class UserPagination extends Base { /** - * Get user listing paginator + * Get user listing pagination * * @access public * @return Paginator diff --git a/app/Schema/Migration.php b/app/Schema/Migration.php index 654303f4..21ef4427 100644 --- a/app/Schema/Migration.php +++ b/app/Schema/Migration.php @@ -13,6 +13,13 @@ function migrate_default_swimlane(PDO $pdo) $project['default_swimlane'] = 'Default swimlane'; } + $rq = $pdo->prepare('SELECT 1 FROM swimlanes WHERE name=? AND project_id=?'); + $rq->execute(array($project['default_swimlane'], $project['id'])); + + if ($rq->fetchColumn()) { + $project['default_swimlane'] = $project['default_swimlane'].' (Default swimlane)'; + } + // Create new default swimlane $rq = $pdo->prepare('INSERT INTO swimlanes (project_id, name, is_active, position) VALUES (?, ?, ?, ?)'); $rq->execute(array( diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php index 385f63a3..5709b86d 100644 --- a/app/Schema/Mysql.php +++ b/app/Schema/Mysql.php @@ -8,7 +8,34 @@ use PDO; use Kanboard\Core\Security\Token; use Kanboard\Core\Security\Role; -const VERSION = 122; +const VERSION = 126; + +function version_126(PDO $pdo) +{ + $pdo->exec('CREATE TABLE predefined_task_descriptions ( + id INT NOT NULL AUTO_INCREMENT, + project_id INT NOT NULL, + title TEXT NOT NULL, + description TEXT NOT NULL, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + PRIMARY KEY(id) + ) ENGINE=InnoDB CHARSET=utf8'); +} + +function version_125(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects DROP COLUMN is_everybody_allowed'); +} + +function version_124(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects ADD COLUMN predefined_email_subjects TEXT'); +} + +function version_123(PDO $pdo) +{ + $pdo->exec('ALTER TABLE column_has_move_restrictions ADD COLUMN only_assigned TINYINT(1) DEFAULT 0'); +} function version_122(PDO $pdo) { diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php index cc3d9632..bf9acaa7 100644 --- a/app/Schema/Postgres.php +++ b/app/Schema/Postgres.php @@ -8,7 +8,33 @@ use PDO; use Kanboard\Core\Security\Token; use Kanboard\Core\Security\Role; -const VERSION = 101; +const VERSION = 105; + +function version_105(PDO $pdo) +{ + $pdo->exec('CREATE TABLE predefined_task_descriptions ( + id SERIAL PRIMARY KEY, + project_id INTEGER NOT NULL, + title TEXT NOT NULL, + description TEXT NOT NULL, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + )'); +} + +function version_104(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects DROP COLUMN is_everybody_allowed'); +} + +function version_103(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects ADD COLUMN predefined_email_subjects TEXT'); +} + +function version_102(PDO $pdo) +{ + $pdo->exec('ALTER TABLE column_has_move_restrictions ADD COLUMN only_assigned BOOLEAN DEFAULT FALSE'); +} function version_101(PDO $pdo) { diff --git a/app/Schema/Sql/mysql.sql b/app/Schema/Sql/mysql.sql index d861823a..08e9b365 100644 --- a/app/Schema/Sql/mysql.sql +++ b/app/Schema/Sql/mysql.sql @@ -44,6 +44,7 @@ CREATE TABLE `column_has_move_restrictions` ( `role_id` int(11) NOT NULL, `src_column_id` int(11) NOT NULL, `dst_column_id` int(11) NOT NULL, + `only_assigned` tinyint(1) DEFAULT '0', PRIMARY KEY (`restriction_id`), UNIQUE KEY `role_id` (`role_id`,`src_column_id`,`dst_column_id`), KEY `project_id` (`project_id`), @@ -751,7 +752,7 @@ CREATE TABLE `users` ( LOCK TABLES `settings` WRITE; /*!40000 ALTER TABLE `settings` DISABLE KEYS */; -INSERT INTO `settings` VALUES ('api_token','af0d1f150c4f64ad61dcfdd28d5544c767656994c05bce919ca4c78ab704',0,0),('application_currency','USD',0,0),('application_date_format','m/d/Y',0,0),('application_language','en_US',0,0),('application_stylesheet','',0,0),('application_timezone','UTC',0,0),('application_url','',0,0),('board_columns','',0,0),('board_highlight_period','172800',0,0),('board_private_refresh_interval','10',0,0),('board_public_refresh_interval','60',0,0),('calendar_project_tasks','date_started',0,0),('calendar_user_subtasks_time_tracking','0',0,0),('calendar_user_tasks','date_started',0,0),('cfd_include_closed_tasks','1',0,0),('default_color','yellow',0,0),('integration_gravatar','0',0,0),('password_reset','1',0,0),('project_categories','',0,0),('subtask_restriction','0',0,0),('subtask_time_tracking','1',0,0),('webhook_token','9dc22b7faf57e4a9978c7ee30ce7ff85763fe3ebe5d595967a283610a321',0,0),('webhook_url','',0,0); +INSERT INTO `settings` VALUES ('api_token','7b7306fb38aa10046043df6e9631f51193ef58dc14b0b62836d00b2bb03e',0,0),('application_currency','USD',0,0),('application_date_format','m/d/Y',0,0),('application_language','en_US',0,0),('application_stylesheet','',0,0),('application_timezone','UTC',0,0),('application_url','',0,0),('board_columns','',0,0),('board_highlight_period','172800',0,0),('board_private_refresh_interval','10',0,0),('board_public_refresh_interval','60',0,0),('calendar_project_tasks','date_started',0,0),('calendar_user_subtasks_time_tracking','0',0,0),('calendar_user_tasks','date_started',0,0),('cfd_include_closed_tasks','1',0,0),('default_color','yellow',0,0),('integration_gravatar','0',0,0),('password_reset','1',0,0),('project_categories','',0,0),('subtask_restriction','0',0,0),('subtask_time_tracking','1',0,0),('webhook_token','384d5b69ffd7315f2cf1eef2c170b5ed0dbf6d2a2e63462e39dca5d24db2',0,0),('webhook_url','',0,0); /*!40000 ALTER TABLE `settings` ENABLE KEYS */; UNLOCK TABLES; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; @@ -780,4 +781,4 @@ UNLOCK TABLES; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; -INSERT INTO users (username, password, role) VALUES ('admin', '$2y$10$rArU1OPUgo7QhObLvFaq1.d7FlIaC4XEsBBWLxIfifNJapk9h71uO', 'app-admin');INSERT INTO schema_version VALUES ('122'); +INSERT INTO users (username, password, role) VALUES ('admin', '$2y$10$0WR8YPAwOCrDQTRFjji6u.krMgA4PcVsmw3ypmXAkqFKFLwnFOpAG', 'app-admin');INSERT INTO schema_version VALUES ('123'); diff --git a/app/Schema/Sql/postgres.sql b/app/Schema/Sql/postgres.sql index c4310963..52ba11cb 100644 --- a/app/Schema/Sql/postgres.sql +++ b/app/Schema/Sql/postgres.sql @@ -97,7 +97,8 @@ CREATE TABLE "column_has_move_restrictions" ( "project_id" integer NOT NULL, "role_id" integer NOT NULL, "src_column_id" integer NOT NULL, - "dst_column_id" integer NOT NULL + "dst_column_id" integer NOT NULL, + "only_assigned" boolean DEFAULT false ); @@ -2570,8 +2571,8 @@ INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('board_high INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('board_public_refresh_interval', '60', 0, 0); INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('board_private_refresh_interval', '10', 0, 0); INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('board_columns', '', 0, 0); -INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('webhook_token', 'eb478afd6da3ca2b1bda2e5ef2b47d83f0ebdeef5cdebc02e01d5b568835', 0, 0); -INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('api_token', '32cf0d4f136c1adffb9475b9a4c910f3231dcac5c44777eb467f0e714e51', 0, 0); +INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('webhook_token', 'd5afda7f7444f8600138b276ae9a3d1e36781c3111ed35a55fc1a3ca3ff5', 0, 0); +INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('api_token', '8814fa59e03411e82772826d166f5cf444324efefcf334ae64b4921d53f3', 0, 0); INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('application_language', 'en_US', 0, 0); INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('application_timezone', 'UTC', 0, 0); INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('application_url', '', 0, 0); @@ -2640,4 +2641,4 @@ SELECT pg_catalog.setval('links_id_seq', 11, true); -- PostgreSQL database dump complete -- -INSERT INTO users (username, password, role) VALUES ('admin', '$2y$10$rArU1OPUgo7QhObLvFaq1.d7FlIaC4XEsBBWLxIfifNJapk9h71uO', 'app-admin');INSERT INTO schema_version VALUES ('101'); +INSERT INTO users (username, password, role) VALUES ('admin', '$2y$10$0WR8YPAwOCrDQTRFjji6u.krMgA4PcVsmw3ypmXAkqFKFLwnFOpAG', 'app-admin');INSERT INTO schema_version VALUES ('102'); diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php index 2d35b99e..70d13b98 100644 --- a/app/Schema/Sqlite.php +++ b/app/Schema/Sqlite.php @@ -8,7 +8,55 @@ use Kanboard\Core\Security\Token; use Kanboard\Core\Security\Role; use PDO; -const VERSION = 112; +const VERSION = 116; + +function version_116(PDO $pdo) +{ + $pdo->exec('CREATE TABLE predefined_task_descriptions ( + id INTEGER PRIMARY KEY, + project_id INTEGER NOT NULL, + title TEXT NOT NULL, + description TEXT NOT NULL, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + )'); +} + +function version_115(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects ADD COLUMN predefined_email_subjects TEXT'); +} + +function version_114(PDO $pdo) +{ + $pdo->exec('ALTER TABLE column_has_move_restrictions ADD COLUMN only_assigned INTEGER DEFAULT 0'); +} + +function version_113(PDO $pdo) +{ + $pdo->exec( + 'ALTER TABLE project_activities RENAME TO project_activities_bak' + ); + $pdo->exec(" + CREATE TABLE project_activities ( + id INTEGER PRIMARY KEY, + date_creation INTEGER NOT NULL, + event_name TEXT NOT NULL, + creator_id INTEGER NOT NULL, + project_id INTEGER NOT NULL, + task_id INTEGER NOT NULL, + data TEXT, + FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ) + "); + $pdo->exec( + 'INSERT INTO project_activities SELECT * FROM project_activities_bak' + ); + $pdo->exec( + 'DROP TABLE project_activities_bak' + ); +} function version_112(PDO $pdo) { @@ -951,7 +999,7 @@ function version_33(PDO $pdo) id INTEGER PRIMARY KEY, date_creation INTEGER NOT NULL, event_name TEXT NOT NULL, - creator_id INTEGE NOT NULL, + creator_id INTEGER NOT NULL, project_id INTEGER NOT NULL, task_id INTEGER NOT NULL, data TEXT, diff --git a/app/ServiceProvider/ActionProvider.php b/app/ServiceProvider/ActionProvider.php index a7e8040e..14b84c7a 100644 --- a/app/ServiceProvider/ActionProvider.php +++ b/app/ServiceProvider/ActionProvider.php @@ -19,6 +19,7 @@ use Kanboard\Action\TaskAssignColorCategory; use Kanboard\Action\TaskAssignColorColumn; use Kanboard\Action\TaskAssignColorLink; use Kanboard\Action\TaskAssignColorUser; +use Kanboard\Action\TaskAssignCreator; use Kanboard\Action\TaskAssignCurrentUser; use Kanboard\Action\TaskAssignCurrentUserColumn; use Kanboard\Action\TaskAssignSpecificUser; @@ -69,6 +70,7 @@ class ActionProvider implements ServiceProviderInterface $container['actionManager']->register(new TaskAssignColorLink($container)); $container['actionManager']->register(new TaskAssignColorUser($container)); $container['actionManager']->register(new TaskAssignColorPriority($container)); + $container['actionManager']->register(new TaskAssignCreator($container)); $container['actionManager']->register(new TaskAssignCurrentUser($container)); $container['actionManager']->register(new TaskAssignCurrentUserColumn($container)); $container['actionManager']->register(new TaskAssignSpecificUser($container)); diff --git a/app/ServiceProvider/AuthenticationProvider.php b/app/ServiceProvider/AuthenticationProvider.php index 868696e0..797e70d0 100644 --- a/app/ServiceProvider/AuthenticationProvider.php +++ b/app/ServiceProvider/AuthenticationProvider.php @@ -89,15 +89,17 @@ class AuthenticationProvider implements ServiceProviderInterface $acl->add('CustomFilterController', '*', Role::PROJECT_MEMBER); $acl->add('ExportController', '*', Role::PROJECT_MANAGER); $acl->add('TaskFileController', array('screenshot', 'create', 'save', 'remove', 'confirm'), Role::PROJECT_MEMBER); - $acl->add('TaskGanttController', '*', Role::PROJECT_MANAGER); $acl->add('ProjectViewController', array('share', 'updateSharing', 'integrations', 'updateIntegrations', 'notifications', 'updateNotifications', 'duplicate', 'doDuplication'), Role::PROJECT_MANAGER); $acl->add('ProjectPermissionController', '*', Role::PROJECT_MANAGER); $acl->add('ProjectEditController', '*', Role::PROJECT_MANAGER); + $acl->add('ProjectPredefinedContentController', '*', Role::PROJECT_MANAGER); + $acl->add('PredefinedTaskDescriptionController', '*', Role::PROJECT_MANAGER); $acl->add('ProjectFileController', '*', Role::PROJECT_MEMBER); $acl->add('ProjectUserOverviewController', '*', Role::PROJECT_MANAGER); $acl->add('ProjectStatusController', '*', Role::PROJECT_MANAGER); $acl->add('ProjectTagController', '*', Role::PROJECT_MANAGER); $acl->add('SubtaskController', '*', Role::PROJECT_MEMBER); + $acl->add('SubtaskConverterController', '*', Role::PROJECT_MEMBER); $acl->add('SubtaskRestrictionController', '*', Role::PROJECT_MEMBER); $acl->add('SubtaskStatusController', '*', Role::PROJECT_MEMBER); $acl->add('SwimlaneController', '*', Role::PROJECT_MANAGER); @@ -145,7 +147,6 @@ class AuthenticationProvider implements ServiceProviderInterface $acl->add('TagController', '*', Role::APP_ADMIN); $acl->add('PluginController', '*', Role::APP_ADMIN); $acl->add('CurrencyController', '*', Role::APP_ADMIN); - $acl->add('ProjectGanttController', '*', Role::APP_MANAGER); $acl->add('GroupListController', '*', Role::APP_ADMIN); $acl->add('GroupCreationController', '*', Role::APP_ADMIN); $acl->add('GroupModificationController', '*', Role::APP_ADMIN); diff --git a/app/ServiceProvider/AvatarProvider.php b/app/ServiceProvider/AvatarProvider.php index d17985ed..e03a047a 100644 --- a/app/ServiceProvider/AvatarProvider.php +++ b/app/ServiceProvider/AvatarProvider.php @@ -5,7 +5,6 @@ namespace Kanboard\ServiceProvider; use Pimple\Container; use Pimple\ServiceProviderInterface; use Kanboard\Core\User\Avatar\AvatarManager; -use Kanboard\User\Avatar\GravatarProvider; use Kanboard\User\Avatar\AvatarFileProvider; use Kanboard\User\Avatar\LetterAvatarProvider; @@ -28,7 +27,6 @@ class AvatarProvider implements ServiceProviderInterface { $container['avatarManager'] = new AvatarManager; $container['avatarManager']->register(new LetterAvatarProvider($container)); - $container['avatarManager']->register(new GravatarProvider($container)); $container['avatarManager']->register(new AvatarFileProvider($container)); return $container; } diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php index d66794df..7f05dfae 100644 --- a/app/ServiceProvider/ClassProvider.php +++ b/app/ServiceProvider/ClassProvider.php @@ -47,6 +47,7 @@ class ClassProvider implements ServiceProviderInterface 'LinkModel', 'NotificationModel', 'PasswordResetModel', + 'PredefinedTaskDescriptionModel', 'ProjectModel', 'ProjectFileModel', 'ProjectActivityModel', @@ -118,9 +119,9 @@ class ClassProvider implements ServiceProviderInterface 'TaskLinkValidator', 'TaskValidator', 'UserValidator', + 'PredefinedTaskDescriptionValidator', ), 'Import' => array( - 'TaskImport', 'UserImport', ), 'Export' => array( @@ -129,9 +130,10 @@ class ClassProvider implements ServiceProviderInterface 'TransitionExport', ), 'Pagination' => array( - 'TaskPagination', - 'SubtaskPagination', + 'DashboardPagination', 'ProjectPagination', + 'SubtaskPagination', + 'TaskPagination', 'UserPagination', ), 'Core' => array( diff --git a/app/ServiceProvider/FilterProvider.php b/app/ServiceProvider/FilterProvider.php index 1cc4da8a..11a29e69 100644 --- a/app/ServiceProvider/FilterProvider.php +++ b/app/ServiceProvider/FilterProvider.php @@ -27,6 +27,7 @@ use Kanboard\Filter\TaskMovedDateFilter; use Kanboard\Filter\TaskPriorityFilter; use Kanboard\Filter\TaskProjectFilter; use Kanboard\Filter\TaskReferenceFilter; +use Kanboard\Filter\TaskScoreFilter; use Kanboard\Filter\TaskStatusFilter; use Kanboard\Filter\TaskSubtaskAssigneeFilter; use Kanboard\Filter\TaskSwimlaneFilter; @@ -172,6 +173,7 @@ class FilterProvider implements ServiceProviderInterface ) ->withFilter(new TaskProjectFilter()) ->withFilter(new TaskReferenceFilter()) + ->withFilter(new TaskScoreFilter()) ->withFilter(new TaskStatusFilter()) ->withFilter(TaskSubtaskAssigneeFilter::getInstance() ->setCurrentUserId($c['userSession']->getId()) diff --git a/app/ServiceProvider/FormatterProvider.php b/app/ServiceProvider/FormatterProvider.php index feaa597f..efc85d06 100644 --- a/app/ServiceProvider/FormatterProvider.php +++ b/app/ServiceProvider/FormatterProvider.php @@ -22,15 +22,17 @@ class FormatterProvider implements ServiceProviderInterface 'BoardTaskFormatter', 'GroupAutoCompleteFormatter', 'ProjectActivityEventFormatter', - 'ProjectGanttFormatter', + 'ProjectApiFormatter', + 'ProjectsApiFormatter', 'SubtaskListFormatter', 'SubtaskTimeTrackingCalendarFormatter', + 'TaskApiFormatter', + 'TasksApiFormatter', 'TaskAutoCompleteFormatter', - 'TaskCalendarFormatter', - 'TaskGanttFormatter', 'TaskICalFormatter', 'TaskListFormatter', 'TaskListSubtaskFormatter', + 'TaskListSubtaskAssigneeFormatter', 'TaskSuggestMenuFormatter', 'UserAutoCompleteFormatter', 'UserMentionFormatter', diff --git a/app/ServiceProvider/GroupProvider.php b/app/ServiceProvider/GroupProvider.php index 08548c73..86f5d112 100644 --- a/app/ServiceProvider/GroupProvider.php +++ b/app/ServiceProvider/GroupProvider.php @@ -25,7 +25,7 @@ class GroupProvider implements ServiceProviderInterface */ public function register(Container $container) { - $container['groupManager'] = new GroupManager; + $container['groupManager'] = new GroupManager(); if (DB_GROUP_PROVIDER) { $container['groupManager']->register(new DatabaseBackendGroupProvider($container)); diff --git a/app/ServiceProvider/HelperProvider.php b/app/ServiceProvider/HelperProvider.php index 82b175cb..f0f7ac07 100644 --- a/app/ServiceProvider/HelperProvider.php +++ b/app/ServiceProvider/HelperProvider.php @@ -19,7 +19,6 @@ class HelperProvider implements ServiceProviderInterface { $container['helper'] = new Helper($container); $container['helper']->register('app', '\Kanboard\Helper\AppHelper'); - $container['helper']->register('calendar', '\Kanboard\Helper\CalendarHelper'); $container['helper']->register('asset', '\Kanboard\Helper\AssetHelper'); $container['helper']->register('board', '\Kanboard\Helper\BoardHelper'); $container['helper']->register('comment', '\Kanboard\Helper\CommentHelper'); @@ -27,7 +26,6 @@ class HelperProvider implements ServiceProviderInterface $container['helper']->register('file', '\Kanboard\Helper\FileHelper'); $container['helper']->register('form', '\Kanboard\Helper\FormHelper'); $container['helper']->register('hook', '\Kanboard\Helper\HookHelper'); - $container['helper']->register('ical', '\Kanboard\Helper\ICalHelper'); $container['helper']->register('layout', '\Kanboard\Helper\LayoutHelper'); $container['helper']->register('model', '\Kanboard\Helper\ModelHelper'); $container['helper']->register('subtask', '\Kanboard\Helper\SubtaskHelper'); diff --git a/app/ServiceProvider/RouteProvider.php b/app/ServiceProvider/RouteProvider.php index 08759b22..5b9eca98 100644 --- a/app/ServiceProvider/RouteProvider.php +++ b/app/ServiceProvider/RouteProvider.php @@ -36,7 +36,6 @@ class RouteProvider implements ServiceProviderInterface $container['route']->addRoute('dashboard/:user_id/projects', 'DashboardController', 'projects'); $container['route']->addRoute('dashboard/:user_id/tasks', 'DashboardController', 'tasks'); $container['route']->addRoute('dashboard/:user_id/subtasks', 'DashboardController', 'subtasks'); - $container['route']->addRoute('dashboard/:user_id/calendar', 'DashboardController', 'calendar'); $container['route']->addRoute('dashboard/:user_id/activity', 'DashboardController', 'activity'); $container['route']->addRoute('dashboard/:user_id/notifications', 'DashboardController', 'notifications'); @@ -119,18 +118,10 @@ class RouteProvider implements ServiceProviderInterface $container['route']->addRoute('b/:project_id', 'BoardViewController', 'show'); $container['route']->addRoute('public/board/:token', 'BoardViewController', 'readonly'); - // Calendar routes - $container['route']->addRoute('calendar/:project_id', 'CalendarController', 'show'); - $container['route']->addRoute('c/:project_id', 'CalendarController', 'show'); - // Listing routes $container['route']->addRoute('list/:project_id', 'TaskListController', 'show'); $container['route']->addRoute('l/:project_id', 'TaskListController', 'show'); - // Gantt routes - $container['route']->addRoute('gantt/:project_id', 'TaskGanttController', 'show'); - $container['route']->addRoute('gantt/:project_id/sort/:sorting', 'TaskGanttController', 'show'); - // Feed routes $container['route']->addRoute('feed/project/:token', 'FeedController', 'project'); $container['route']->addRoute('feed/user/:token', 'FeedController', 'user'); @@ -167,7 +158,6 @@ class RouteProvider implements ServiceProviderInterface $container['route']->addRoute('settings/project', 'ConfigController', 'project'); $container['route']->addRoute('settings/project', 'ConfigController', 'project'); $container['route']->addRoute('settings/board', 'ConfigController', 'board'); - $container['route']->addRoute('settings/calendar', 'ConfigController', 'calendar'); $container['route']->addRoute('settings/integrations', 'ConfigController', 'integrations'); $container['route']->addRoute('settings/webhook', 'ConfigController', 'webhook'); $container['route']->addRoute('settings/api', 'ConfigController', 'api'); diff --git a/app/ServiceProvider/UserProvider.php b/app/ServiceProvider/UserProvider.php new file mode 100644 index 00000000..c80a2aeb --- /dev/null +++ b/app/ServiceProvider/UserProvider.php @@ -0,0 +1,35 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Kanboard\Core\User\UserManager; +use Kanboard\User\DatabaseBackendUserProvider; +use Pimple\Container; +use Pimple\ServiceProviderInterface; + +/** + * User Provider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class UserProvider implements ServiceProviderInterface +{ + /** + * Register providers + * + * @access public + * @param \Pimple\Container $container + * @return \Pimple\Container + */ + public function register(Container $container) + { + $container['userManager'] = new UserManager(); + + if (DB_USER_PROVIDER) { + $container['userManager']->register(new DatabaseBackendUserProvider($container)); + } + + return $container; + } +} diff --git a/app/Template/action/index.php b/app/Template/action/index.php index a6fc70f9..29b6aca6 100644 --- a/app/Template/action/index.php +++ b/app/Template/action/index.php @@ -45,7 +45,11 @@ </li> <?php foreach ($action['params'] as $param_name => $param_value): ?> <li> - <?= $this->text->in($param_name, $available_params[$action['action_name']]) ?> = + <?php if (isset($available_params[$action['action_name']][$param_name]) && is_array($available_params[$action['action_name']][$param_name])): ?> + <?= $this->text->e(ucfirst($param_name)) ?> = + <?php else: ?> + <?= $this->text->in($param_name, $available_params[$action['action_name']]) ?> = + <?php endif ?> <strong> <?php if ($this->text->contains($param_name, 'column_id')): ?> <?= $this->text->in($param_value, $columns_list) ?> diff --git a/app/Template/action_creation/create.php b/app/Template/action_creation/create.php index 862ee474..a1169dca 100644 --- a/app/Template/action_creation/create.php +++ b/app/Template/action_creation/create.php @@ -3,7 +3,6 @@ </div> <form method="post" action="<?= $this->url->href('ActionCreationController', 'event', array('project_id' => $project['id'])) ?>"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->label(t('Action'), 'action_name') ?> <?= $this->form->select('action_name', $available_actions, $values) ?> diff --git a/app/Template/action_creation/event.php b/app/Template/action_creation/event.php index e4166548..2ea72612 100644 --- a/app/Template/action_creation/event.php +++ b/app/Template/action_creation/event.php @@ -5,7 +5,6 @@ <form method="post" action="<?= $this->url->href('ActionCreationController', 'params', array('project_id' => $project['id'])) ?>"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->hidden('action_name', $values) ?> <?= $this->form->label(t('Action'), 'action_name') ?> diff --git a/app/Template/action_creation/params.php b/app/Template/action_creation/params.php index 0cc98f50..9083b949 100644 --- a/app/Template/action_creation/params.php +++ b/app/Template/action_creation/params.php @@ -5,7 +5,6 @@ <form method="post" action="<?= $this->url->href('ActionCreationController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->hidden('event_name', $values) ?> <?= $this->form->hidden('action_name', $values) ?> @@ -43,6 +42,9 @@ <?php elseif ($this->text->contains($param_name, 'swimlane_id')): ?> <?= $this->form->label($param_desc, $param_name) ?> <?= $this->form->select('params['.$param_name.']', $swimlane_list, $values) ?> + <?php elseif (is_array($param_desc)): ?> + <?= $this->form->label(ucfirst($param_name), $param_name) ?> + <?= $this->form->select('params['.$param_name.']', $param_desc, $values) ?> <?php else: ?> <?= $this->form->label($param_desc, $param_name) ?> <?= $this->form->text('params['.$param_name.']', $values) ?> diff --git a/app/Template/board/table_column.php b/app/Template/board/table_column.php index 9d2815ff..a9652a2b 100644 --- a/app/Template/board/table_column.php +++ b/app/Template/board/table_column.php @@ -16,9 +16,7 @@ <!-- column in expanded mode --> <div class="board-column-expanded"> <?php if (! $not_editable && $this->projectRole->canCreateTaskInColumn($column['project_id'], $column['id'])): ?> - <div class="board-add-icon"> - <?= $this->modal->largeIcon('plus', t('Add a new task'), 'TaskCreationController', 'show', array('project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id'])) ?> - </div> + <?= $this->task->getNewBoardTaskButton($swimlane, $column) ?> <?php endif ?> <?php if ($swimlane['nb_swimlanes'] > 1 && ! empty($column['column_nb_tasks'])): ?> diff --git a/app/Template/board/task_footer.php b/app/Template/board/task_footer.php index 1ad1c9f1..4ea5bb31 100644 --- a/app/Template/board/task_footer.php +++ b/app/Template/board/task_footer.php @@ -32,7 +32,7 @@ <div class="task-board-icons-row"> <?php if ($task['reference']): ?> <span class="task-board-reference" title="<?= t('Reference') ?>"> - <?= $this->text->e($task['reference']) ?> + <?= $this->task->renderReference($task) ?> </span> <?php endif ?> </div> @@ -58,14 +58,14 @@ <?php if (! empty($task['date_due'])): ?> <span class="task-date - <?php if (date('Y-m-d') == date('Y-m-d', $task['date_due'])): ?> - task-date-today - <?php elseif (time() > $task['date_due']): ?> + <?php if (time() > $task['date_due']): ?> task-date-overdue + <?php elseif (date('Y-m-d') == date('Y-m-d', $task['date_due'])): ?> + task-date-today <?php endif ?> "> <i class="fa fa-calendar"></i> - <?= $this->dt->date($task['date_due']) ?> + <?= $this->dt->datetime($task['date_due']) ?> </span> <?php endif ?> </div> diff --git a/app/Template/board/task_private.php b/app/Template/board/task_private.php index c4afc0bf..ece5efbe 100644 --- a/app/Template/board/task_private.php +++ b/app/Template/board/task_private.php @@ -19,6 +19,7 @@ <div class="task-board-saving-icon" style="display: none;"><i class="fa fa-spinner fa-pulse"></i></div> <?php if ($this->user->hasProjectAccess('TaskModificationController', 'edit', $task['project_id'])): ?> <?= $this->render('task/dropdown', array('task' => $task)) ?> + <?= $this->modal->large('edit', '', 'TaskModificationController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> <?php else: ?> <strong><?= '#'.$task['id'] ?></strong> <?php endif ?> @@ -28,7 +29,7 @@ <?= $this->text->e($this->user->getInitials($task['assignee_name'] ?: $task['assignee_username'])) ?> </span> - <?php endif ?> - <?= $this->text->e($task['title']) ?> + <?= $this->url->link($this->text->e($task['title']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', $this->text->e($task['title'])) ?> </div> <?php else: ?> <div class="task-board-expanded"> @@ -36,6 +37,7 @@ <div class="task-board-header"> <?php if ($this->user->hasProjectAccess('TaskModificationController', 'edit', $task['project_id'])): ?> <?= $this->render('task/dropdown', array('task' => $task)) ?> + <?= $this->modal->large('edit', '', 'TaskModificationController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> <?php else: ?> <strong><?= '#'.$task['id'] ?></strong> <?php endif ?> @@ -51,7 +53,7 @@ <?= $this->hook->render('template:board:private:task:before-title', array('task' => $task)) ?> <div class="task-board-title"> - <?= $this->text->e($task['title']) ?> + <?= $this->url->link($this->text->e($task['title']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </div> <?= $this->hook->render('template:board:private:task:after-title', array('task' => $task)) ?> diff --git a/app/Template/board/tooltip_external_links.php b/app/Template/board/tooltip_external_links.php index a9f1fc7f..2c287adf 100644 --- a/app/Template/board/tooltip_external_links.php +++ b/app/Template/board/tooltip_external_links.php @@ -11,7 +11,7 @@ <?= $link['type'] ?> </td> <td> - <a href="<?= $link['url'] ?>" target="_blank"><?= $this->text->e($link['title']) ?></a> + <a href="<?= $link['url'] ?>" title="<?= $this->text->e($link['url']) ?>" target="_blank"><?= $this->text->e($link['title']) ?></a> </td> <td> <?= $this->text->e($link['dependency_label']) ?> diff --git a/app/Template/calendar/project.php b/app/Template/calendar/project.php deleted file mode 100644 index 769e019b..00000000 --- a/app/Template/calendar/project.php +++ /dev/null @@ -1,6 +0,0 @@ -<?= $this->projectHeader->render($project, 'CalendarController', 'show') ?> - -<?= $this->calendar->render( - $this->url->href('CalendarController', 'projectEvents', array('project_id' => $project['id'])), - $this->url->href('CalendarController', 'save', array('project_id' => $project['id'])) -) ?> diff --git a/app/Template/calendar/user.php b/app/Template/calendar/user.php deleted file mode 100644 index c68bd32d..00000000 --- a/app/Template/calendar/user.php +++ /dev/null @@ -1,4 +0,0 @@ -<?= $this->calendar->render( - $this->url->href('CalendarController', 'userEvents', array('user_id' => $user['id'])), - $this->url->href('CalendarController', 'save') -) ?> diff --git a/app/Template/category/create.php b/app/Template/category/create.php index b12ff7fa..b32a770c 100644 --- a/app/Template/category/create.php +++ b/app/Template/category/create.php @@ -3,7 +3,6 @@ </div> <form method="post" action="<?= $this->url->href('CategoryController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->label(t('Category Name'), 'name') ?> <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?> diff --git a/app/Template/category/edit.php b/app/Template/category/edit.php index 108826f3..9ad5a9e9 100644 --- a/app/Template/category/edit.php +++ b/app/Template/category/edit.php @@ -5,9 +5,6 @@ <form method="post" action="<?= $this->url->href('CategoryController', 'update', array('project_id' => $project['id'], 'category_id' => $values['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('id', $values) ?> - <?= $this->form->hidden('project_id', $values) ?> - <?= $this->form->label(t('Category Name'), 'name') ?> <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="50"', 'tabindex="1"')) ?> diff --git a/app/Template/column/create.php b/app/Template/column/create.php index aad9606b..2b0c4641 100644 --- a/app/Template/column/create.php +++ b/app/Template/column/create.php @@ -4,8 +4,6 @@ <form method="post" action="<?= $this->url->href('ColumnController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('project_id', $values) ?> - <?= $this->form->label(t('Title'), 'title') ?> <?= $this->form->text('title', $values, $errors, array('autofocus', 'required', 'maxlength="50"', 'tabindex="1"')) ?> diff --git a/app/Template/column/edit.php b/app/Template/column/edit.php index e590b5cc..25cf60c9 100644 --- a/app/Template/column/edit.php +++ b/app/Template/column/edit.php @@ -5,9 +5,6 @@ <form method="post" action="<?= $this->url->href('ColumnController', 'update', array('project_id' => $project['id'], 'column_id' => $column['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('id', $values) ?> - <?= $this->form->hidden('project_id', $values) ?> - <?= $this->form->label(t('Title'), 'title') ?> <?= $this->form->text('title', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?> diff --git a/app/Template/column_move_restriction/create.php b/app/Template/column_move_restriction/create.php index 852df971..cd9e1bf5 100644 --- a/app/Template/column_move_restriction/create.php +++ b/app/Template/column_move_restriction/create.php @@ -12,6 +12,8 @@ <?= $this->form->label(t('Destination column'), 'dst_column_id') ?> <?= $this->form->select('dst_column_id', $columns, $values, $errors) ?> + <?= $this->form->checkbox('only_assigned', t('Only for tasks assigned to the current user'), 1, isset($values['only_assigned']) && $values['only_assigned'] == 1) ?> + <?= $this->modal->submitButtons() ?> <p class="alert alert-info"><?= t('People belonging to this role will be able to move tasks only between the source and the destination column.') ?></p> diff --git a/app/Template/comment/create.php b/app/Template/comment/create.php index 0e19ac19..55e972dc 100644 --- a/app/Template/comment/create.php +++ b/app/Template/comment/create.php @@ -8,8 +8,6 @@ </div> <form method="post" action="<?= $this->url->href('CommentController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('task_id', $values) ?> - <?= $this->form->hidden('user_id', $values) ?> <?= $this->form->textEditor('comment', $values, $errors, array('autofocus' => true, 'required' => true)) ?> diff --git a/app/Template/comment/edit.php b/app/Template/comment/edit.php index 04f6ffd4..db8d2921 100644 --- a/app/Template/comment/edit.php +++ b/app/Template/comment/edit.php @@ -4,9 +4,6 @@ <form method="post" action="<?= $this->url->href('CommentController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('id', $values) ?> - <?= $this->form->hidden('task_id', $values) ?> - <?= $this->form->hidden('user_id', $values) ?> <?= $this->form->textEditor('comment', $values, $errors, array('autofocus' => true, 'required' => true)) ?> diff --git a/app/Template/comment_list/create.php b/app/Template/comment_list/create.php index a1bae5eb..4c86bc06 100644 --- a/app/Template/comment_list/create.php +++ b/app/Template/comment_list/create.php @@ -3,6 +3,6 @@ </div> <form method="post" action="<?= $this->url->href('CommentListController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->textEditor('comment', array(), array(), array('required' => true)) ?> + <?= $this->form->textEditor('comment', array('project_id' => $task['project_id']), array(), array('required' => true)) ?> <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/comment_mail/create.php b/app/Template/comment_mail/create.php index 019f8a20..8732080a 100644 --- a/app/Template/comment_mail/create.php +++ b/app/Template/comment_mail/create.php @@ -1,7 +1,7 @@ <div class="page-header"> <h2><?= t('Create and send a comment by email') ?></h2> </div> -<form method="post" action="<?= $this->url->href('CommentMailController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('CommentMailController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off" class="js-mail-form"> <?= $this->form->csrf() ?> <?= $this->form->hidden('task_id', $values) ?> <?= $this->form->hidden('user_id', $values) ?> @@ -9,9 +9,39 @@ <?= $this->form->label(t('Email'), 'email') ?> <?= $this->form->email('email', $values, $errors, array('autofocus', 'required', 'tabindex="1"')) ?> + <?php if (! empty($members)): ?> + <div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-address-card-o"></i><i class="fa fa-caret-down"></i></a> + <ul> + <?php foreach ($members as $member): ?> + <li data-email="<?= $this->text->e($member['email']) ?>" class="js-autocomplete-email"> + <?= $this->text->e($this->user->getFullname($member)) ?> + </li> + <?php endforeach ?> + </ul> + </div> + <?php endif ?> + <?= $this->form->label(t('Subject'), 'subject') ?> <?= $this->form->text('subject', $values, $errors, array('required', 'tabindex="2"')) ?> + <?php if (! empty($project['predefined_email_subjects'])): ?> + <div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-archive"></i><i class="fa fa-caret-down"></i></a> + <ul> + <?php foreach (explode("\r\n", trim($project['predefined_email_subjects'])) as $subject): ?> + <?php $subject = trim($subject); ?> + + <?php if (! empty($subject)): ?> + <li data-subject="<?= $this->text->e($subject) ?>" class="js-autocomplete-subject"> + <?= $this->text->e($subject) ?> + </li> + <?php endif ?> + <?php endforeach ?> + </ul> + </div> + <?php endif ?> + <?= $this->form->textEditor('comment', $values, $errors, array('required' => true, 'tabindex' => 3)) ?> <?= $this->modal->submitButtons(array( diff --git a/app/Template/config/about.php b/app/Template/config/about.php index 23a3e6c0..4cce5e63 100644 --- a/app/Template/config/about.php +++ b/app/Template/config/about.php @@ -79,8 +79,6 @@ </div> <?php endif ?> -<?= $this->render('config/keyboard_shortcuts') ?> - <div class="page-header"> <h2><?= t('License') ?></h2> </div> diff --git a/app/Template/config/calendar.php b/app/Template/config/calendar.php deleted file mode 100644 index 0cc3d064..00000000 --- a/app/Template/config/calendar.php +++ /dev/null @@ -1,36 +0,0 @@ -<div class="page-header"> - <h2><?= t('Calendar settings') ?></h2> -</div> -<form method="post" action="<?= $this->url->href('ConfigController', 'save', array('redirect' => 'calendar')) ?>" autocomplete="off"> - - <?= $this->form->csrf() ?> - - <fieldset> - <legend><?= t('Project calendar view') ?></legend> - <?= $this->form->radios('calendar_project_tasks', array( - 'date_creation' => t('Show tasks based on the creation date'), - 'date_started' => t('Show tasks based on the start date'), - ), - $values - ) ?> - </fieldset> - - <fieldset> - <legend><?= t('User calendar view') ?></legend> - <?= $this->form->radios('calendar_user_tasks', array( - 'date_creation' => t('Show tasks based on the creation date'), - 'date_started' => t('Show tasks based on the start date'), - ), - $values - ) ?> - </fieldset> - - <fieldset> - <legend><?= t('Subtasks time tracking') ?></legend> - <?= $this->form->checkbox('calendar_user_subtasks_time_tracking', t('Show subtasks based on the time tracking'), 1, $values['calendar_user_subtasks_time_tracking'] == 1) ?> - </fieldset> - - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - </div> -</form> diff --git a/app/Template/config/integrations.php b/app/Template/config/integrations.php index 07a90ce2..0a3f9953 100644 --- a/app/Template/config/integrations.php +++ b/app/Template/config/integrations.php @@ -4,14 +4,11 @@ <form method="post" action="<?= $this->url->href('ConfigController', 'save', array('redirect' => 'integrations')) ?>" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->hook->render('template:config:integrations', array('values' => $values)) ?> - - <h3><img src="<?= $this->url->dir() ?>assets/img/gravatar-icon.png"/> <?= t('Gravatar') ?></h3> - <div class="panel"> - <?= $this->form->checkbox('integration_gravatar', t('Enable Gravatar images'), 1, $values['integration_gravatar'] == 1) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - </div> - </div> + <?php $contents = $this->hook->render('template:config:integrations', array('values' => $values)) ?> + <?php if (empty($contents)): ?> + <p class="alert"><?= t('There is no external integration installed.') ?></p> + <?php else: ?> + <?= $contents ?> + <?php endif ?> </form> diff --git a/app/Template/config/keyboard_shortcuts.php b/app/Template/config/keyboard_shortcuts.php index 6ac71ee0..4e78dbe0 100644 --- a/app/Template/config/keyboard_shortcuts.php +++ b/app/Template/config/keyboard_shortcuts.php @@ -6,9 +6,7 @@ <ul> <li><?= t('Switch to the project overview') ?> = <strong>v o</strong></li> <li><?= t('Switch to the board view') ?> = <strong>v b</strong></li> - <li><?= t('Switch to the calendar view') ?> = <strong>v c</strong></li> <li><?= t('Switch to the list view') ?> = <strong>v l</strong></li> - <li><?= t('Switch to the Gantt chart view') ?> = <strong>v g</strong></li> </ul> <h3><?= t('Board view') ?></h3> <ul> diff --git a/app/Template/config/sidebar.php b/app/Template/config/sidebar.php index 95be963b..2f265ed2 100644 --- a/app/Template/config/sidebar.php +++ b/app/Template/config/sidebar.php @@ -15,9 +15,6 @@ <li <?= $this->app->checkMenuSelection('ConfigController', 'board') ?>> <?= $this->url->link(t('Board settings'), 'ConfigController', 'board') ?> </li> - <li <?= $this->app->checkMenuSelection('ConfigController', 'calendar') ?>> - <?= $this->url->link(t('Calendar settings'), 'ConfigController', 'calendar') ?> - </li> <li <?= $this->app->checkMenuSelection('TagController', 'index') ?>> <?= $this->url->link(t('Tags management'), 'TagController', 'index') ?> </li> diff --git a/app/Template/custom_filter/create.php b/app/Template/custom_filter/create.php index 24e896ee..724cbc85 100644 --- a/app/Template/custom_filter/create.php +++ b/app/Template/custom_filter/create.php @@ -3,7 +3,6 @@ </div> <form method="post" action="<?= $this->url->href('CustomFilterController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->label(t('Name'), 'name') ?> <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="100"')) ?> diff --git a/app/Template/custom_filter/edit.php b/app/Template/custom_filter/edit.php index b64dee53..786e0c91 100644 --- a/app/Template/custom_filter/edit.php +++ b/app/Template/custom_filter/edit.php @@ -5,9 +5,7 @@ <form method="post" action="<?= $this->url->href('CustomFilterController', 'update', array('project_id' => $filter['project_id'], 'filter_id' => $filter['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('id', $values) ?> <?= $this->form->hidden('user_id', $values) ?> - <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->label(t('Name'), 'name') ?> <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="100"')) ?> diff --git a/app/Template/dashboard/layout.php b/app/Template/dashboard/layout.php index dbd16886..45b52451 100644 --- a/app/Template/dashboard/layout.php +++ b/app/Template/dashboard/layout.php @@ -7,9 +7,9 @@ </li> <?php endif ?> <?php if ($this->app->config('disable_private_project', 0) == 0): ?> - <li> - <?= $this->modal->medium('lock', t('New private project'), 'ProjectCreationController', 'createPrivate') ?> - </li> + <li> + <?= $this->modal->medium('lock', t('New private project'), 'ProjectCreationController', 'createPrivate') ?> + </li> <?php endif ?> <li> <?= $this->url->icon('folder', t('Project management'), 'ProjectListController', 'show') ?> @@ -17,9 +17,7 @@ <li> <?= $this->modal->medium('dashboard', t('My activity stream'), 'ActivityController', 'user') ?> </li> - <li> - <?= $this->modal->medium('calendar', t('My calendar'), 'CalendarController', 'user') ?> - </li> + <?= $this->hook->render('template:dashboard:page-header:menu', array('user' => $user)) ?> </ul> </div> <section class="sidebar-container" id="dashboard"> diff --git a/app/Template/dashboard/overview.php b/app/Template/dashboard/overview.php new file mode 100644 index 00000000..e732a387 --- /dev/null +++ b/app/Template/dashboard/overview.php @@ -0,0 +1,93 @@ +<div class="filter-box margin-bottom"> + <form method="get" action="<?= $this->url->dir() ?>" class="search"> + <?= $this->form->hidden('controller', array('controller' => 'SearchController')) ?> + <?= $this->form->hidden('action', array('action' => 'index')) ?> + + <div class="input-addon"> + <?= $this->form->text('search', array(), array(), array('placeholder="'.t('Search').'"'), 'input-addon-field') ?> + <div class="input-addon-item"> + <?= $this->render('app/filters_helper') ?> + </div> + </div> + </form> +</div> + +<?php if (! $project_paginator->isEmpty()): ?> + <div class="table-list"> + <?= $this->render('project_list/header', array('paginator' => $project_paginator)) ?> + <?php foreach ($project_paginator->getCollection() as $project): ?> + <div class="table-list-row table-border-left"> + <div> + <?php if ($this->user->hasProjectAccess('ProjectViewController', 'show', $project['id'])): ?> + <?= $this->render('project/dropdown', array('project' => $project)) ?> + <?php else: ?> + <strong><?= '#'.$project['id'] ?></strong> + <?php endif ?> + + <span class="table-list-title <?= $project['is_active'] == 0 ? 'status-closed' : '' ?>"> + <?= $this->url->link($this->text->e($project['name']), 'BoardViewController', 'show', array('project_id' => $project['id'])) ?> + </span> + + <?php if ($project['is_private']): ?> + <i class="fa fa-lock fa-fw" title="<?= t('Private project') ?>"></i> + <?php endif ?> + </div> + <div class="table-list-details"> + <?php foreach ($project['columns'] as $column): ?> + <strong title="<?= t('Task count') ?>"><?= $column['nb_open_tasks'] ?></strong> + <small><?= $this->text->e($column['title']) ?></small> + <?php endforeach ?> + </div> + </div> + <?php endforeach ?> + </div> + + <?= $project_paginator ?> +<?php endif ?> + +<?php if (empty($overview_paginator)): ?> + <p class="alert"><?= t('There is nothing assigned to you.') ?></p> +<?php else: ?> + <?php foreach ($overview_paginator as $result): ?> + <?php if (! $result['paginator']->isEmpty()): ?> + <div class="page-header"> + <h2><?= $this->url->link($this->text->e($result['project_name']), 'BoardViewController', 'show', array('project_id' => $result['project_id'])) ?></h2> + </div> + + <div class="table-list"> + <?= $this->render('task_list/header', array( + 'paginator' => $result['paginator'], + )) ?> + + <?php foreach ($result['paginator']->getCollection() as $task): ?> + <div class="table-list-row color-<?= $task['color_id'] ?>"> + <?= $this->render('task_list/task_title', array( + 'task' => $task, + )) ?> + + <?= $this->render('task_list/task_details', array( + 'task' => $task, + )) ?> + + <?= $this->render('task_list/task_avatars', array( + 'task' => $task, + )) ?> + + <?= $this->render('task_list/task_icons', array( + 'task' => $task, + )) ?> + + <?= $this->render('task_list/task_subtasks', array( + 'task' => $task, + 'user_id' => $user['id'], + )) ?> + </div> + <?php endforeach ?> + </div> + + <?= $result['paginator'] ?> + <?php endif ?> + <?php endforeach ?> +<?php endif ?> + +<?= $this->hook->render('template:dashboard:show', array('user' => $user)) ?> diff --git a/app/Template/dashboard/projects.php b/app/Template/dashboard/projects.php index 7e35b059..e84a9415 100644 --- a/app/Template/dashboard/projects.php +++ b/app/Template/dashboard/projects.php @@ -4,52 +4,24 @@ <?php if ($paginator->isEmpty()): ?> <p class="alert"><?= t('Your are not member of any project.') ?></p> <?php else: ?> - <table class="table-striped table-small table-scrolling"> - <tr> - <th class="column-5"><?= $paginator->order('Id', \Kanboard\Model\ProjectModel::TABLE.'.id') ?></th> - <th class="column-3"><?= $paginator->order('<i class="fa fa-lock fa-fw" title="'.t('Private project').'"></i>', \Kanboard\Model\ProjectModel::TABLE.'.is_private') ?></th> - <th class="column-25"><?= $paginator->order(t('Project'), \Kanboard\Model\ProjectModel::TABLE.'.name') ?></th> - <th class="column-10"><?= t('Tasks') ?></th> - <th><?= t('Columns') ?></th> - </tr> + <div class="table-list"> + <?= $this->render('project_list/header', array('paginator' => $paginator)) ?> <?php foreach ($paginator->getCollection() as $project): ?> - <tr> - <td> - <?= $this->render('project/dropdown', array('project' => $project)) ?> - </td> - <td> - <?php if ($project['is_private']): ?> - <i class="fa fa-lock fa-fw" title="<?= t('Private project') ?>"></i> - <?php endif ?> - </td> - <td> - <?php if ($this->user->hasProjectAccess('TaskGanttController', 'show', $project['id'])): ?> - <?= $this->url->link('<i class="fa fa-sliders fa-fw"></i>', 'TaskGanttController', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('Gantt chart')) ?> - <?php endif ?> + <div class="table-list-row table-border-left"> + <?= $this->render('project_list/project_title', array( + 'project' => $project, + )) ?> - <?= $this->url->link('<i class="fa fa-list"></i>', 'TaskListController', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('List')) ?> - <?= $this->url->link('<i class="fa fa-calendar"></i>', 'CalendarController', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('Calendar')) ?> + <?= $this->render('project_list/project_details', array( + 'project' => $project, + )) ?> - <?= $this->url->link($this->text->e($project['name']), 'BoardViewController', 'show', array('project_id' => $project['id'])) ?> - <?php if (! empty($project['description'])): ?> - <span class="tooltip" title="<?= $this->text->markdownAttribute($project['description']) ?>"> - <i class="fa fa-info-circle"></i> - </span> - <?php endif ?> - </td> - <td> - <?= $project['nb_active_tasks'] ?> - </td> - <td class="dashboard-project-stats"> - <?php foreach ($project['columns'] as $column): ?> - <strong title="<?= t('Task count') ?>"><?= $column['nb_open_tasks'] ?></strong> - <small><?= $this->text->e($column['title']) ?></small> - <?php endforeach ?> - </td> - - </tr> + <?= $this->render('project_list/project_icons', array( + 'project' => $project, + )) ?> + </div> <?php endforeach ?> - </table> + </div> <?= $paginator ?> <?php endif ?> diff --git a/app/Template/dashboard/show.php b/app/Template/dashboard/show.php deleted file mode 100644 index b1d877cf..00000000 --- a/app/Template/dashboard/show.php +++ /dev/null @@ -1,19 +0,0 @@ -<div class="filter-box margin-bottom"> - <form method="get" action="<?= $this->url->dir() ?>" class="search"> - <?= $this->form->hidden('controller', array('controller' => 'SearchController')) ?> - <?= $this->form->hidden('action', array('action' => 'index')) ?> - - <div class="input-addon"> - <?= $this->form->text('search', array(), array(), array('placeholder="'.t('Search').'"'), 'input-addon-field') ?> - <div class="input-addon-item"> - <?= $this->render('app/filters_helper') ?> - </div> - </div> - </form> -</div> - -<?= $this->render('dashboard/projects', array('paginator' => $project_paginator, 'user' => $user)) ?> -<?= $this->render('dashboard/tasks', array('paginator' => $task_paginator, 'user' => $user)) ?> -<?= $this->render('dashboard/subtasks', array('paginator' => $subtask_paginator, 'user' => $user)) ?> - -<?= $this->hook->render('template:dashboard:show', array('user' => $user)) ?> diff --git a/app/Template/dashboard/subtasks.php b/app/Template/dashboard/subtasks.php index d86b1ef9..96bb13cc 100644 --- a/app/Template/dashboard/subtasks.php +++ b/app/Template/dashboard/subtasks.php @@ -1,45 +1,49 @@ <div class="page-header"> - <h2><?= $this->url->link(t('My subtasks'), 'DashboardController', 'subtasks', array('user_id' => $user['id'])) ?> (<?= $paginator->getTotal() ?>)</h2> + <h2><?= $this->url->link(t('My subtasks'), 'DashboardController', 'subtasks', array('user_id' => $user['id'])) ?> (<?= $nb_subtasks ?>)</h2> </div> -<?php if ($paginator->isEmpty()): ?> +<?php if ($nb_subtasks == 0): ?> <p class="alert"><?= t('There is nothing assigned to you.') ?></p> <?php else: ?> - <table class="table-striped table-small table-scrolling"> - <tr> - <th class="column-5"><?= $paginator->order('Id', \Kanboard\Model\TaskModel::TABLE.'.id') ?></th> - <th class="column-20"><?= $paginator->order(t('Project'), 'project_name') ?></th> - <th><?= $paginator->order(t('Task'), 'task_name') ?></th> - <th><?= $paginator->order(t('Subtask'), \Kanboard\Model\SubtaskModel::TABLE.'.title') ?></th> - <?= $this->hook->render('template:dashboard:subtasks:header:before-timetracking', array('paginator' => $paginator)) ?> - <th class="column-20"><?= t('Time tracking') ?></th> - </tr> - <?php foreach ($paginator->getCollection() as $subtask): ?> - <tr> - <td class="task-table color-<?= $subtask['color_id'] ?>"> - <?= $this->render('task/dropdown', array('task' => array('id' => $subtask['task_id'], 'project_id' => $subtask['project_id']))) ?> - </td> - <td> - <?= $this->url->link($this->text->e($subtask['project_name']), 'BoardViewController', 'show', array('project_id' => $subtask['project_id'])) ?> - </td> - <td> - <?= $this->url->link($this->text->e($subtask['task_name']), 'TaskViewController', 'show', array('task_id' => $subtask['task_id'], 'project_id' => $subtask['project_id'])) ?> - </td> - <td> - <?= $this->subtask->renderToggleStatus(array('project_id' => $subtask['project_id']), $subtask) ?> - </td> - <?= $this->hook->render('template:dashboard:subtasks:rows', array('subtask' => $subtask)) ?> - <td> - <?php if (! empty($subtask['time_spent'])): ?> - <strong><?= $this->text->e($subtask['time_spent']).'h' ?></strong> <?= t('spent') ?> + <div class="table-list"> + <div class="table-list-header"> + <div class="table-list-header-count"> + <?php if ($nb_subtasks > 1): ?> + <?= t('%d subtasks', $nb_subtasks) ?> + <?php else: ?> + <?= t('%d subtask', $nb_subtasks) ?> <?php endif ?> + </div> + <div class="table-list-header-menu"> + <div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon"><strong><?= t('Sort') ?> <i class="fa fa-caret-down"></i></strong></a> + <ul> + <li> + <?= $paginator->order(t('Task ID'), \Kanboard\Model\TaskModel::TABLE.'.id') ?> + </li> + <li> + <?= $paginator->order(t('Title'), \Kanboard\Model\TaskModel::TABLE.'.title') ?> + </li> + <li> + <?= $paginator->order(t('Priority'), \Kanboard\Model\TaskModel::TABLE.'.priority') ?> + </li> + </ul> + </div> + </div> + </div> - <?php if (! empty($subtask['time_estimated'])): ?> - <strong><?= $this->text->e($subtask['time_estimated']).'h' ?></strong> <?= t('estimated') ?> - <?php endif ?> - </td> - </tr> + <?php foreach ($paginator->getCollection() as $task): ?> + <div class="table-list-row color-<?= $task['color_id'] ?>"> + <?= $this->render('task_list/task_title', array( + 'task' => $task, + )) ?> + + <?= $this->render('task_list/task_subtasks', array( + 'task' => $task, + 'user_id' => $user['id'], + )) ?> + </div> <?php endforeach ?> - </table> + </div> <?= $paginator ?> <?php endif ?> diff --git a/app/Template/dashboard/tasks.php b/app/Template/dashboard/tasks.php index 427b903d..23aef990 100644 --- a/app/Template/dashboard/tasks.php +++ b/app/Template/dashboard/tasks.php @@ -4,50 +4,35 @@ <?php if ($paginator->isEmpty()): ?> <p class="alert"><?= t('There is nothing assigned to you.') ?></p> <?php else: ?> - <table class="table-striped table-small table-scrolling"> - <tr> - <th class="column-5"><?= $paginator->order('Id', \Kanboard\Model\TaskModel::TABLE.'.id') ?></th> - <th class="column-20"><?= $paginator->order(t('Project'), 'project_name') ?></th> - <th><?= $paginator->order(t('Task'), \Kanboard\Model\TaskModel::TABLE.'.title') ?></th> - <th class="column-8"><?= $paginator->order(t('Priority'), \Kanboard\Model\TaskModel::TABLE.'.priority') ?></th> - <th class="column-20"><?= t('Time tracking') ?></th> - <th class="column-10"><?= $paginator->order(t('Due date'), \Kanboard\Model\TaskModel::TABLE.'.date_due') ?></th> - <th class="column-10"><?= $paginator->order(t('Column'), 'column_title') ?></th> - </tr> + <div class="table-list"> + <?= $this->render('task_list/header', array( + 'paginator' => $paginator, + )) ?> + <?php foreach ($paginator->getCollection() as $task): ?> - <tr> - <td class="task-table color-<?= $task['color_id'] ?>"> - <?= $this->render('task/dropdown', array('task' => $task)) ?> - </td> - <td> - <?= $this->url->link($this->text->e($task['project_name']), 'BoardViewController', 'show', array('project_id' => $task['project_id'])) ?> - </td> - <td> - <?= $this->url->link($this->text->e($task['title']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - </td> - <td> - <?php if ($task['priority'] >= 0): ?> - P<?= $this->text->e($task['priority'])?> - <?php endif?> - </td> - <td> - <?php if (! empty($task['time_spent'])): ?> - <strong><?= $this->text->e($task['time_spent']).'h' ?></strong> <?= t('spent') ?> - <?php endif ?> + <div class="table-list-row color-<?= $task['color_id'] ?>"> + <?= $this->render('task_list/task_title', array( + 'task' => $task, + )) ?> + + <?= $this->render('task_list/task_details', array( + 'task' => $task, + )) ?> + + <?= $this->render('task_list/task_avatars', array( + 'task' => $task, + )) ?> + + <?= $this->render('task_list/task_icons', array( + 'task' => $task, + )) ?> - <?php if (! empty($task['time_estimated'])): ?> - <strong><?= $this->text->e($task['time_estimated']).'h' ?></strong> <?= t('estimated') ?> - <?php endif ?> - </td> - <td> - <?= $this->dt->date($task['date_due']) ?> - </td> - <td> - <?= $this->text->e($task['column_title']) ?> - </td> - </tr> + <?= $this->render('task_list/task_subtasks', array( + 'task' => $task, + )) ?> + </div> <?php endforeach ?> - </table> + </div> <?= $paginator ?> <?php endif ?> diff --git a/app/Template/group/user_dropdown.php b/app/Template/group/user_dropdown.php new file mode 100644 index 00000000..48acb955 --- /dev/null +++ b/app/Template/group/user_dropdown.php @@ -0,0 +1,11 @@ +<div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon"><strong>#<?= $user['id'] ?> <i class="fa fa-caret-down"></i></strong></a> + <ul> + <li> + <?= $this->url->icon('user', t('View profile'), 'UserViewController', 'show', array('user_id' => $user['id'])) ?> + </li> + <li> + <?= $this->modal->medium('trash', t('Remove user from group'), 'GroupListController', 'dissociate', array('group_id' => $user['group_id'], 'user_id' => $user['id'])) ?> + </li> + </ul> +</div> diff --git a/app/Template/group/users.php b/app/Template/group/users.php index 5025ca7a..2469296a 100644 --- a/app/Template/group/users.php +++ b/app/Template/group/users.php @@ -13,9 +13,20 @@ <?= $this->render('user_list/header', array('paginator' => $paginator)) ?> <?php foreach ($paginator->getCollection() as $user): ?> <div class="table-list-row table-border-left"> - <?= $this->render('user_list/user_title', array( - 'user' => $user, - )) ?> + <div> + <?= $this->render('group/user_dropdown', array('user' => $user)) ?> + <span class="table-list-title <?= $user['is_active'] == 0 ? 'status-closed' : '' ?>"> + <?= $this->avatar->small( + $user['id'], + $user['username'], + $user['name'], + $user['email'], + $user['avatar_path'], + 'avatar-inline' + ) ?> + <?= $this->url->link($this->text->e($user['name'] ?: $user['username']), 'UserViewController', 'show', array('user_id' => $user['id'])) ?> + </span> + </div> <?= $this->render('user_list/user_details', array( 'user' => $user, diff --git a/app/Template/header.php b/app/Template/header.php index 2af58d93..08523eb6 100644 --- a/app/Template/header.php +++ b/app/Template/header.php @@ -20,7 +20,7 @@ <?= $this->render('header/board_selector', array('board_selector' => $board_selector)) ?> <?php endif ?> </div> - <div class="menus-container pull-right"> + <div class="menus-container"> <?= $_top_right_corner ?> </div> </header> diff --git a/app/Template/notification/task_create.php b/app/Template/notification/task_create.php index 6e80b26a..e56c252d 100644 --- a/app/Template/notification/task_create.php +++ b/app/Template/notification/task_create.php @@ -6,7 +6,7 @@ </li> <?php if ($task['date_due']): ?> <li> - <strong><?= t('Due date:').' '.$this->dt->date($task['date_due']) ?></strong> + <strong><?= t('Due date:').' '.$this->dt->datetime($task['date_due']) ?></strong> </li> <?php endif ?> <?php if (! empty($task['creator_username'])): ?> diff --git a/app/Template/notification/task_overdue.php b/app/Template/notification/task_overdue.php index 01ad0a01..2ad4c14d 100644 --- a/app/Template/notification/task_overdue.php +++ b/app/Template/notification/task_overdue.php @@ -19,7 +19,7 @@ <?= $this->text->e($task['title']) ?> <?php endif ?> </td> - <td style="border: 1px solid #eee;"><?= $this->dt->date($task['date_due']) ?></td> + <td style="border: 1px solid #eee;"><?= $this->dt->datetime($task['date_due']) ?></td> <td style="border: 1px solid #eee;"><?= $this->text->e($task['project_name']) ?></td> <td style="border: 1px solid #eee;"> <?php if (! empty($task['assignee_username'])): ?> diff --git a/app/Template/predefined_task_description/create.php b/app/Template/predefined_task_description/create.php new file mode 100644 index 00000000..5a7a8d9f --- /dev/null +++ b/app/Template/predefined_task_description/create.php @@ -0,0 +1,14 @@ +<div class="page-header"> + <h2><?= t('Predefined Task Description') ?></h2> +</div> +<form method="post" action="<?= $this->url->href('PredefinedTaskDescriptionController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> + <?= $this->form->csrf() ?> + + <?= $this->form->label(t('Title'), 'title') ?> + <?= $this->form->text('title', $values, $errors, array('autofocus', 'required', 'tabindex="1"')) ?> + + <?= $this->form->label(t('Description'), 'description') ?> + <?= $this->form->textEditor('description', $values, $errors, array('tabindex' => 2)) ?> + + <?= $this->modal->submitButtons() ?> +</form> diff --git a/app/Template/predefined_task_description/edit.php b/app/Template/predefined_task_description/edit.php new file mode 100644 index 00000000..039d650b --- /dev/null +++ b/app/Template/predefined_task_description/edit.php @@ -0,0 +1,14 @@ +<div class="page-header"> + <h2><?= t('Predefined Task Description') ?></h2> +</div> +<form method="post" action="<?= $this->url->href('PredefinedTaskDescriptionController', 'update', array('project_id' => $project['id'], 'id' => $template['id'])) ?>" autocomplete="off"> + <?= $this->form->csrf() ?> + + <?= $this->form->label(t('Title'), 'title') ?> + <?= $this->form->text('title', $values, $errors, array('autofocus', 'required', 'tabindex="1"')) ?> + + <?= $this->form->label(t('Description'), 'description') ?> + <?= $this->form->textEditor('description', $values, $errors, array('tabindex' => 2)) ?> + + <?= $this->modal->submitButtons() ?> +</form> diff --git a/app/Template/predefined_task_description/remove.php b/app/Template/predefined_task_description/remove.php new file mode 100644 index 00000000..f60a8e75 --- /dev/null +++ b/app/Template/predefined_task_description/remove.php @@ -0,0 +1,15 @@ +<div class="page-header"> + <h2><?= t('Predefined Task Description') ?></h2> +</div> + +<div class="confirm"> + <p class="alert alert-info"> + <?= t('Do you really want to remove this template? "%s"', $template['title']) ?> + </p> + + <?= $this->modal->confirmButtons( + 'PredefinedTaskDescriptionController', + 'remove', + array('project_id' => $project['id'], 'id' => $template['id']) + ) ?> +</div> diff --git a/app/Template/project/dropdown.php b/app/Template/project/dropdown.php index 72c687a1..28fd9ba2 100644 --- a/app/Template/project/dropdown.php +++ b/app/Template/project/dropdown.php @@ -5,17 +5,8 @@ <?= $this->url->icon('th', t('Board'), 'BoardViewController', 'show', array('project_id' => $project['id'])) ?> </li> <li> - <?= $this->url->icon('calendar', t('Calendar'), 'CalendarController', 'show', array('project_id' => $project['id'])) ?> - </li> - <li> <?= $this->url->icon('list', t('Listing'), 'TaskListController', 'show', array('project_id' => $project['id'])) ?> </li> - <?php if ($this->user->hasProjectAccess('TaskGanttController', 'show', $project['id'])): ?> - <li> - <?= $this->url->icon('sliders', t('Gantt'), 'TaskGanttController', 'show', array('project_id' => $project['id'])) ?> - </li> - <?php endif ?> - <li> <?= $this->modal->medium('dashboard', t('Activity'), 'ActivityController', 'project', array('project_id' => $project['id'])) ?> </li> diff --git a/app/Template/project/sidebar.php b/app/Template/project/sidebar.php index 9775c7a1..4b39172c 100644 --- a/app/Template/project/sidebar.php +++ b/app/Template/project/sidebar.php @@ -13,6 +13,9 @@ <li <?= $this->app->checkMenuSelection('ProjectEditController') ?>> <?= $this->url->link(t('Edit project'), 'ProjectEditController', 'show', array('project_id' => $project['id'])) ?> </li> + <li <?= $this->app->checkMenuSelection('ProjectPredefinedContentController') ?>> + <?= $this->url->link(t('Predefined contents'), 'ProjectPredefinedContentController', 'show', array('project_id' => $project['id'])) ?> + </li> <li <?= $this->app->checkMenuSelection('ProjectViewController', 'share') ?>> <?= $this->url->link(t('Public access'), 'ProjectViewController', 'share', array('project_id' => $project['id'])) ?> </li> diff --git a/app/Template/project_edit/show.php b/app/Template/project_edit/show.php index 62bd9af6..96531007 100644 --- a/app/Template/project_edit/show.php +++ b/app/Template/project_edit/show.php @@ -9,7 +9,6 @@ <?php endif ?> <form method="post" action="<?= $this->url->href('ProjectEditController', 'update', array('project_id' => $project['id'], 'redirect' => 'edit')) ?>" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('id', $values) ?> <fieldset> <legend><?= t('General') ?></legend> diff --git a/app/Template/project_gantt/show.php b/app/Template/project_gantt/show.php deleted file mode 100644 index 725f348d..00000000 --- a/app/Template/project_gantt/show.php +++ /dev/null @@ -1,42 +0,0 @@ -<section id="main"> - <div class="page-header"> - <ul> - <?php if ($this->user->hasAccess('ProjectCreationController', 'create')): ?> - <li> - <?= $this->modal->medium('plus', t('New project'), 'ProjectCreationController', 'create') ?> - </li> - <?php endif ?> - <?php if ($this->app->config('disable_private_project', 0) == 0): ?> - <li> - <?= $this->modal->medium('lock', t('New private project'), 'ProjectCreationController', 'createPrivate') ?> - </li> - <?php endif ?> - <li> - <?= $this->url->icon('folder', t('Projects list'), 'ProjectListController', 'show') ?> - </li> - <?php if ($this->user->hasAccess('ProjectUserOverviewController', 'managers')): ?> - <li> - <?= $this->url->icon('user', t('Users overview'), 'ProjectUserOverviewController', 'managers') ?> - </li> - <?php endif ?> - </ul> - </div> - <section> - <?php if (empty($projects)): ?> - <p class="alert"><?= t('No project') ?></p> - <?php else: ?> - <div - id="gantt-chart" - data-records='<?= json_encode($projects, JSON_HEX_APOS) ?>' - data-save-url="<?= $this->url->href('ProjectGanttController', 'save') ?>" - data-label-project-manager="<?= t('Project managers') ?>" - data-label-project-member="<?= t('Project members') ?>" - data-label-gantt-link="<?= t('Gantt chart for this project') ?>" - data-label-board-link="<?= t('Project board') ?>" - data-label-start-date="<?= t('Start date:') ?>" - data-label-end-date="<?= t('End date:') ?>" - data-label-not-defined="<?= t('There is no start date or end date for this project.') ?>" - ></div> - <?php endif ?> - </section> -</section> diff --git a/app/Template/project_header/search.php b/app/Template/project_header/search.php index 512e88d7..75110779 100644 --- a/app/Template/project_header/search.php +++ b/app/Template/project_header/search.php @@ -2,6 +2,7 @@ <form method="get" action="<?= $this->url->dir() ?>" class="search"> <?= $this->form->hidden('controller', $filters) ?> <?= $this->form->hidden('action', $filters) ?> + <?= $this->form->hidden('plugin', $filters) ?> <?= $this->form->hidden('project_id', $filters) ?> <div class="input-addon"> diff --git a/app/Template/project_header/views.php b/app/Template/project_header/views.php index 2684c744..4203595e 100644 --- a/app/Template/project_header/views.php +++ b/app/Template/project_header/views.php @@ -5,15 +5,9 @@ <li <?= $this->app->checkMenuSelection('BoardViewController') ?>> <?= $this->url->icon('th', t('Board'), 'BoardViewController', 'show', array('project_id' => $project['id'], 'search' => $filters['search']), false, 'view-board', t('Keyboard shortcut: "%s"', 'v b')) ?> </li> - <li <?= $this->app->checkMenuSelection('CalendarController') ?>> - <?= $this->url->icon('calendar', t('Calendar'), 'CalendarController', 'project', array('project_id' => $project['id'], 'search' => $filters['search']), false, 'view-calendar', t('Keyboard shortcut: "%s"', 'v c')) ?> - </li> <li <?= $this->app->checkMenuSelection('TaskListController') ?>> <?= $this->url->icon('list', t('List'), 'TaskListController', 'show', array('project_id' => $project['id'], 'search' => $filters['search']), false, 'view-listing', t('Keyboard shortcut: "%s"', 'v l')) ?> </li> - <?php if ($this->user->hasProjectAccess('TaskGanttController', 'show', $project['id'])): ?> - <li <?= $this->app->checkMenuSelection('TaskGanttController') ?>> - <?= $this->url->icon('sliders', t('Gantt'), 'TaskGanttController', 'show', array('project_id' => $project['id'], 'search' => $filters['search']), false, 'view-gantt', t('Keyboard shortcut: "%s"', 'v g')) ?> - </li> - <?php endif ?> + + <?= $this->hook->render('template:project-header:view-switcher', array('project' => $project, 'filters' => $filters)) ?> </ul> diff --git a/app/Template/project_list/listing.php b/app/Template/project_list/listing.php index f8c46729..6f6c93c1 100644 --- a/app/Template/project_list/listing.php +++ b/app/Template/project_list/listing.php @@ -18,10 +18,6 @@ <li><?= $this->url->icon('user', t('Users overview'), 'ProjectUserOverviewController', 'managers') ?></li> <?php endif ?> - <?php if ($this->user->hasAccess('ProjectGanttController', 'show')): ?> - <li><?= $this->url->icon('sliders', t('Projects Gantt chart'), 'ProjectGanttController', 'show') ?></li> - <?php endif ?> - <?= $this->hook->render('template:project-list:menu:after') ?> </ul> </div> diff --git a/app/Template/project_list/project_icons.php b/app/Template/project_list/project_icons.php index c7e9c4a9..348db367 100644 --- a/app/Template/project_list/project_icons.php +++ b/app/Template/project_list/project_icons.php @@ -1,4 +1,6 @@ <div class="table-list-icons"> + + <?php if ($project['is_public']): ?> <i class="fa fa-share-alt fa-fw" title="<?= t('Shared project') ?>"></i> <?php endif ?> @@ -18,6 +20,6 @@ <?php endif ?> <?php if ($project['is_active'] == 0): ?> - <i class="fa fa-ban fa-fw" aria-hidden="true" title="<?= t('Closed') ?>"></i><?= t('Closed') ?> + <i class="fa fa-ban fa-fw" aria-hidden="true" title="<?= t('Closed') ?>"></i><?= t('Closed') ?> <?php endif ?> </div> diff --git a/app/Template/project_permission/groups.php b/app/Template/project_permission/groups.php index c9914344..c241302b 100644 --- a/app/Template/project_permission/groups.php +++ b/app/Template/project_permission/groups.php @@ -46,7 +46,7 @@ 'placeholder="'.t('Enter group name...').'"', 'title="'.t('Enter group name...').'"', 'data-dst-field="group_id"', - 'data-dst-extra-field="external_id"', + 'data-dst-extra-fields="external_id"', 'data-search-url="'.$this->url->href('GroupAjaxController', 'autocomplete').'"', ), 'autocomplete') ?> diff --git a/app/Template/project_permission/index.php b/app/Template/project_permission/index.php index 689966b6..52a69fb5 100644 --- a/app/Template/project_permission/index.php +++ b/app/Template/project_permission/index.php @@ -2,36 +2,18 @@ <h2><?= t('Allowed Users') ?></h2> </div> -<?php if ($project['is_everybody_allowed']): ?> - <div class="alert"><?= t('Everybody have access to this project.') ?></div> -<?php else: ?> - <?= $this->render('project_permission/users', array( - 'project' => $project, - 'roles' => $roles, - 'users' => $users, - 'errors' => $errors, - 'values' => $values, - )) ?> +<?= $this->render('project_permission/users', array( + 'project' => $project, + 'roles' => $roles, + 'users' => $users, + 'errors' => $errors, + 'values' => $values, +)) ?> - <?= $this->render('project_permission/groups', array( - 'project' => $project, - 'roles' => $roles, - 'groups' => $groups, - 'errors' => $errors, - 'values' => $values, - )) ?> -<?php endif ?> - -<?php if ($project['is_private'] == 0): ?> -<hr/> -<form method="post" action="<?= $this->url->href('ProjectPermissionController', 'allowEverybody', array('project_id' => $project['id'])) ?>"> - <?= $this->form->csrf() ?> - - <?= $this->form->hidden('id', array('id' => $project['id'])) ?> - <?= $this->form->checkbox('is_everybody_allowed', t('Allow everybody to access to this project'), 1, $project['is_everybody_allowed']) ?> - - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - </div> -</form> -<?php endif ?> +<?= $this->render('project_permission/groups', array( + 'project' => $project, + 'roles' => $roles, + 'groups' => $groups, + 'errors' => $errors, + 'values' => $values, +)) ?> diff --git a/app/Template/project_permission/users.php b/app/Template/project_permission/users.php index bc92d060..1180fe1c 100644 --- a/app/Template/project_permission/users.php +++ b/app/Template/project_permission/users.php @@ -34,15 +34,19 @@ <?= $this->form->csrf() ?> <?= $this->form->hidden('project_id', array('project_id' => $project['id'])) ?> <?= $this->form->hidden('user_id', $values) ?> + <?= $this->form->hidden('username', $values) ?> + <?= $this->form->hidden('external_id', $values) ?> + <?= $this->form->hidden('external_id_column', $values) ?> <?= $this->form->label(t('Name'), 'name') ?> <?= $this->form->text('name', $values, $errors, array( - 'required', - 'placeholder="'.t('Enter user name...').'"', - 'title="'.t('Enter user name...').'"', - 'data-dst-field="user_id"', - 'data-search-url="'.$this->url->href('UserAjaxController', 'autocomplete').'"', - ), + 'required', + 'placeholder="'.t('Enter user name...').'"', + 'title="'.t('Enter user name...').'"', + 'data-dst-field="user_id"', + 'data-dst-extra-fields="external_id,external_id_column,username"', + 'data-search-url="'.$this->url->href('UserAjaxController', 'autocomplete').'"', + ), 'autocomplete') ?> <?= $this->form->select('role', $roles, $values, $errors) ?> diff --git a/app/Template/project_predefined_content/show.php b/app/Template/project_predefined_content/show.php new file mode 100644 index 00000000..b2785ada --- /dev/null +++ b/app/Template/project_predefined_content/show.php @@ -0,0 +1,47 @@ +<div class="page-header"> + <h2><?= t('Predefined Contents') ?></h2> + <ul> + <li> + <?= $this->modal->medium('plus', t('Add predefined task description'), 'PredefinedTaskDescriptionController', 'create', array('project_id' => $project['id'])) ?> + </li> + </ul> +</div> + +<?php if (! empty($predefined_task_descriptions)): ?> + <h3><?= t('Predefined Task Descriptions') ?></h3> + <table> + <?php foreach ($predefined_task_descriptions as $template): ?> + <tr> + <td> + <div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog"></i><i class="fa fa-caret-down"></i></a> + <ul> + <li> + <?= $this->modal->medium('edit', t('Edit'), 'PredefinedTaskDescriptionController', 'edit', array('project_id' => $project['id'], 'id' => $template['id'])) ?> + </li> + <li> + <?= $this->modal->confirm('trash-o', t('Remove'), 'PredefinedTaskDescriptionController', 'confirm', array('project_id' => $project['id'], 'id' => $template['id'])) ?> + </li> + </ul> + </div> + <?= $this->text->e($template['title']) ?> + <span class="tooltip" title="<?= $this->text->markdownAttribute($template['description']) ?>"> + <i class="fa fa-info-circle"></i> + </span> + </td> + </tr> + <?php endforeach ?> + </table> +<?php endif ?> + +<form method="post" action="<?= $this->url->href('ProjectPredefinedContentController', 'update', array('project_id' => $project['id'], 'redirect' => 'edit')) ?>" autocomplete="off"> + <?= $this->form->csrf() ?> + + <fieldset> + <legend><?= t('Predefined Email Subjects') ?></legend> + <?= $this->form->textarea('predefined_email_subjects', $values, $errors, array('tabindex="1"')) ?> + <p class="form-help"><?= t('Write one subject by line.') ?></p> + </fieldset> + + <?= $this->modal->submitButtons(array('tabindex' => 2)) ?> +</form> diff --git a/app/Template/project_role/show.php b/app/Template/project_role/show.php index 5377f7bb..65c9ef11 100644 --- a/app/Template/project_role/show.php +++ b/app/Template/project_role/show.php @@ -80,7 +80,11 @@ <i class="fa fa-check-circle-o fa-fw" aria-hidden="true"></i> <strong><?= $this->text->e($restriction['src_column_title']) ?> / <?= $this->text->e($restriction['dst_column_title']) ?></strong> <i class="fa fa-arrow-right fa-fw" aria-hidden="true"></i> - <?= t('Only moving task between those columns is permitted') ?> + <?php if ($restriction['only_assigned'] == 1): ?> + <?= t('Only moving task between those columns is permitted for tasks assigned to the current user') ?> + <?php else: ?> + <?= t('Only moving task between those columns is permitted') ?> + <?php endif ?> </td> <td> <?= $this->modal->confirm('trash-o', t('Remove'), 'ColumnMoveRestrictionController', 'confirm', array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id'])) ?> diff --git a/app/Template/project_tag/create.php b/app/Template/project_tag/create.php index a0e6243b..6765e8fc 100644 --- a/app/Template/project_tag/create.php +++ b/app/Template/project_tag/create.php @@ -3,7 +3,6 @@ </div> <form method="post" action="<?= $this->url->href('ProjectTagController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->label(t('Name'), 'name') ?> <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="255"')) ?> diff --git a/app/Template/project_tag/edit.php b/app/Template/project_tag/edit.php index 8cb1e209..29290c0c 100644 --- a/app/Template/project_tag/edit.php +++ b/app/Template/project_tag/edit.php @@ -3,8 +3,6 @@ </div> <form method="post" action="<?= $this->url->href('ProjectTagController', 'update', array('tag_id' => $tag['id'], 'project_id' => $project['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('id', $values) ?> - <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->label(t('Name'), 'name') ?> <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="255"')) ?> diff --git a/app/Template/project_user_overview/layout.php b/app/Template/project_user_overview/layout.php index 9115ef3c..c97106f7 100644 --- a/app/Template/project_user_overview/layout.php +++ b/app/Template/project_user_overview/layout.php @@ -14,11 +14,6 @@ <li> <?= $this->url->icon('folder', t('Projects list'), 'ProjectListController', 'show') ?> </li> - <?php if ($this->user->hasAccess('ProjectGanttController', 'show')): ?> - <li> - <?= $this->url->icon('sliders', t('Projects Gantt chart'), 'ProjectGanttController', 'show') ?> - </li> - <?php endif ?> </ul> </div> <section class="sidebar-container"> diff --git a/app/Template/project_user_overview/roles.php b/app/Template/project_user_overview/roles.php index b8c67323..f53e7fa3 100644 --- a/app/Template/project_user_overview/roles.php +++ b/app/Template/project_user_overview/roles.php @@ -14,7 +14,6 @@ </td> <td> <?= $this->url->link('<i class="fa fa-th"></i>', 'BoardViewController', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('Board')) ?> - <?= $this->url->link('<i class="fa fa-sliders fa-fw"></i>', 'TaskGanttController', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('Gantt chart')) ?> <?= $this->url->link('<i class="fa fa-cog fa-fw"></i>', 'ProjectViewController', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('Project settings')) ?> <?= $this->text->e($project['project_name']) ?> diff --git a/app/Template/project_user_overview/tasks.php b/app/Template/project_user_overview/tasks.php index 8d682170..1e01a43f 100644 --- a/app/Template/project_user_overview/tasks.php +++ b/app/Template/project_user_overview/tasks.php @@ -36,7 +36,7 @@ <?= $this->dt->date($task['date_started']) ?> </td> <td> - <?= $this->dt->date($task['date_due']) ?> + <?= $this->dt->datetime($task['date_due']) ?> </td> </tr> <?php endforeach ?> diff --git a/app/Template/subtask/create.php b/app/Template/subtask/create.php index 96ad7a46..1ed36d47 100644 --- a/app/Template/subtask/create.php +++ b/app/Template/subtask/create.php @@ -2,11 +2,20 @@ <h2><?= t('Add a sub-task') ?></h2> </div> -<form method="post" action="<?= $this->url->href('SubtaskController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> +<?php if (isset($values['subtasks_added']) && $values['subtasks_added'] > 0): ?> + <p class="alert alert-success"> + <?php if ($values['subtasks_added'] == 1): ?> + <?= t('Subtask added successfully.') ?> + <?php else: ?> + <?= t('%d subtasks added successfully.', $values['subtasks_added']) ?> + <?php endif ?> + </p> +<?php endif ?> +<form method="post" action="<?= $this->url->href('SubtaskController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('task_id', $values) ?> - <?= $this->subtask->renderTitleField($values, $errors, array('autofocus')) ?> + + <?= $this->subtask->renderBulkTitleField($values, $errors, array('autofocus')) ?> <?= $this->subtask->renderAssigneeField($users_list, $values, $errors) ?> <?= $this->subtask->renderTimeEstimatedField($values, $errors) ?> diff --git a/app/Template/subtask/edit.php b/app/Template/subtask/edit.php index 7c0266a8..aed57e95 100644 --- a/app/Template/subtask/edit.php +++ b/app/Template/subtask/edit.php @@ -4,8 +4,6 @@ <form method="post" action="<?= $this->url->href('SubtaskController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('id', $values) ?> - <?= $this->form->hidden('task_id', $values) ?> <?= $this->subtask->renderTitleField($values, $errors, array('autofocus')) ?> <?= $this->subtask->renderAssigneeField($users_list, $values, $errors) ?> diff --git a/app/Template/subtask/table.php b/app/Template/subtask/table.php index 5488796d..e1bca500 100644 --- a/app/Template/subtask/table.php +++ b/app/Template/subtask/table.php @@ -21,7 +21,7 @@ 'task' => $task, 'subtask' => $subtask, )) ?> - <?= $this->subtask->renderToggleStatus($task, $subtask, true) ?> + <?= $this->subtask->renderToggleStatus($task, $subtask, 'table') ?> <?php else: ?> <?= $this->subtask->renderTitle($subtask) ?> <?php endif ?> diff --git a/app/Template/swimlane/create.php b/app/Template/swimlane/create.php index 7d05e731..5ff8cbee 100644 --- a/app/Template/swimlane/create.php +++ b/app/Template/swimlane/create.php @@ -2,9 +2,7 @@ <h2><?= t('Add a new swimlane') ?></h2> </div> <form method="post" action="<?= $this->url->href('SwimlaneController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> - <?= $this->form->csrf() ?> - <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->label(t('Name'), 'name') ?> <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="50"', 'tabindex="1"')) ?> diff --git a/app/Template/swimlane/edit.php b/app/Template/swimlane/edit.php index c1c41196..b1d713ee 100644 --- a/app/Template/swimlane/edit.php +++ b/app/Template/swimlane/edit.php @@ -3,12 +3,8 @@ </div> <form method="post" action="<?= $this->url->href('SwimlaneController', 'update', array('project_id' => $project['id'], 'swimlane_id' => $values['id'])) ?>" autocomplete="off"> - <?= $this->form->csrf() ?> - <?= $this->form->hidden('id', $values) ?> - <?= $this->form->hidden('project_id', $values) ?> - <?= $this->form->label(t('Name'), 'name') ?> <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="50"', 'tabindex="1"')) ?> diff --git a/app/Template/task/changes.php b/app/Template/task/changes.php index 2c2bf267..6aa3d49b 100644 --- a/app/Template/task/changes.php +++ b/app/Template/task/changes.php @@ -31,7 +31,7 @@ if (empty($task['date_due'])) { echo '<li>'.t('The due date have been removed').'</li>'; } else { - echo '<li>'.t('New due date: ').$this->dt->date($task['date_due']).'</li>'; + echo '<li>'.t('New due date: ').$this->dt->datetime($task['date_due']).'</li>'; } break; case 'description': diff --git a/app/Template/task/details.php b/app/Template/task/details.php index 24099a3c..cf305743 100644 --- a/app/Template/task/details.php +++ b/app/Template/task/details.php @@ -22,7 +22,7 @@ </li> <?php if (! empty($task['reference'])): ?> <li> - <strong><?= t('Reference:') ?></strong> <span><?= $this->text->e($task['reference']) ?></span> + <strong><?= t('Reference:') ?></strong> <span><?= $this->task->renderReference($task) ?></span> </li> <?php endif ?> <?php if (! empty($task['score'])): ?> @@ -92,12 +92,6 @@ <span><?= $this->text->e($task['creator_name'] ?: $task['creator_username']) ?></span> </li> <?php endif ?> - <?php if ($task['date_due']): ?> - <li> - <strong><?= t('Due date:') ?></strong> - <span><?= $this->dt->date($task['date_due']) ?></span> - </li> - <?php endif ?> <?php if ($task['time_estimated']): ?> <li> <strong><?= t('Time estimated:') ?></strong> @@ -116,6 +110,18 @@ </div> <div class="task-summary-column"> <ul class="no-bullet"> + <?php if ($task['date_due']): ?> + <li> + <strong><?= t('Due date:') ?></strong> + <span><?= $this->dt->datetime($task['date_due']) ?></span> + </li> + <?php endif ?> + <?php if ($task['date_started']): ?> + <li> + <strong><?= t('Started:') ?></strong> + <span><?= $this->dt->datetime($task['date_started']) ?></span> + </li> + <?php endif ?> <li> <strong><?= t('Created:') ?></strong> <span><?= $this->dt->datetime($task['date_creation']) ?></span> @@ -130,12 +136,6 @@ <span><?= $this->dt->datetime($task['date_completed']) ?></span> </li> <?php endif ?> - <?php if ($task['date_started']): ?> - <li> - <strong><?= t('Started:') ?></strong> - <span><?= $this->dt->datetime($task['date_started']) ?></span> - </li> - <?php endif ?> <?php if ($task['date_moved']): ?> <li> <strong><?= t('Moved:') ?></strong> diff --git a/app/Template/task/dropdown.php b/app/Template/task/dropdown.php index f35abc79..5135fb77 100644 --- a/app/Template/task/dropdown.php +++ b/app/Template/task/dropdown.php @@ -1,15 +1,17 @@ <div class="dropdown"> <a href="#" class="dropdown-menu dropdown-menu-link-icon"><strong>#<?= $task['id'] ?> <i class="fa fa-caret-down"></i></strong></a> <ul> - <?php if (array_key_exists('date_started', $task) && empty($task['date_started'])): ?> - <li> - <?= $this->url->icon('play', t('Set automatically the start date'), 'TaskModificationController', 'start', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - </li> + <?php if ($this->projectRole->canUpdateTask($task)): ?> + <?php if (array_key_exists('date_started', $task) && empty($task['date_started'])): ?> + <li> + <?= $this->url->icon('play', t('Set automatically the start date'), 'TaskModificationController', 'start', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + </li> + <?php endif ?> + <li> + <?= $this->modal->large('edit', t('Edit the task'), 'TaskModificationController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + </li> <?php endif ?> <li> - <?= $this->modal->large('edit', t('Edit the task'), 'TaskModificationController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - </li> - <li> <?= $this->modal->medium('plus', t('Add a sub-task'), 'SubtaskController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> diff --git a/app/Template/task/show.php b/app/Template/task/show.php index 565f0632..892d62f2 100644 --- a/app/Template/task/show.php +++ b/app/Template/task/show.php @@ -18,7 +18,7 @@ 'task' => $task, 'subtasks' => $subtasks, 'project' => $project, - 'editable' => true, + 'editable' => $this->user->hasProjectAccess('SubtaskController', 'edit', $project['id']), )) ?> <?php endif ?> @@ -29,7 +29,7 @@ 'links' => $internal_links, 'project' => $project, 'link_label_list' => $link_label_list, - 'editable' => true, + 'editable' => $this->user->hasProjectAccess('TaskInternalLinkController', 'edit', $project['id']), 'is_public' => false, )) ?> <?php endif ?> diff --git a/app/Template/task/sidebar.php b/app/Template/task/sidebar.php index 952c3298..265c6ef0 100644 --- a/app/Template/task/sidebar.php +++ b/app/Template/task/sidebar.php @@ -29,12 +29,14 @@ <h2><?= t('Actions') ?></h2> </div> <ul> + <?php if ($this->projectRole->canUpdateTask($task)): ?> <li> <?= $this->modal->large('edit', t('Edit the task'), 'TaskModificationController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> <?= $this->modal->medium('refresh fa-rotate-90', t('Edit recurrence'), 'TaskRecurrenceController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> + <?php endif ?> <li> <?= $this->modal->medium('plus', t('Add a sub-task'), 'SubtaskController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> diff --git a/app/Template/task_bulk/show.php b/app/Template/task_bulk/show.php index acf80d8c..b13cf239 100644 --- a/app/Template/task_bulk/show.php +++ b/app/Template/task_bulk/show.php @@ -6,12 +6,16 @@ <?= $this->form->csrf() ?> <?= $this->form->hidden('column_id', $values) ?> <?= $this->form->hidden('swimlane_id', $values) ?> - <?= $this->form->hidden('project_id', $values) ?> <?= $this->task->renderColorField($values) ?> <?= $this->task->renderAssigneeField($users_list, $values, $errors) ?> <?= $this->task->renderCategoryField($categories_list, $values, $errors) ?> + <?php if (! empty($task_description_templates)): ?> + <?= $this->form->label(t('Template for the task description'), 'task_description_template_id') ?> + <?= $this->form->select('task_description_template_id', $task_description_templates, $values, $errors) ?> + <?php endif ?> + <?= $this->form->label(t('Tasks'), 'tasks') ?> <?= $this->form->textarea('tasks', $values, $errors, array('placeholder="'.t('My task title').'"')) ?> <p class="form-help"><?= t('Enter one task by line.') ?></p> diff --git a/app/Template/task_comments/show.php b/app/Template/task_comments/show.php index d34e5e95..47bb5ff2 100644 --- a/app/Template/task_comments/show.php +++ b/app/Template/task_comments/show.php @@ -25,7 +25,11 @@ <?php if ($editable): ?> <?= $this->render('task_comments/create', array( - 'values' => array(), + 'values' => array( + 'user_id' => $this->user->getId(), + 'task_id' => $task['id'], + 'project_id' => $task['project_id'], + ), 'errors' => array(), 'task' => $task, )) ?> diff --git a/app/Template/task_creation/show.php b/app/Template/task_creation/show.php index e957087f..935e0823 100644 --- a/app/Template/task_creation/show.php +++ b/app/Template/task_creation/show.php @@ -1,14 +1,14 @@ <div class="page-header"> - <h2><?= $this->text->e($project['name']) ?> > <?= t('New task') ?><?= $this->task->getNewTaskDropdown($project['id'], $values['swimlane_id'], $values['column_id']) ?></h2> + <h2><?= $this->text->e($project['name']) ?> > <?= t('New task') ?></h2> </div> <form method="post" action="<?= $this->url->href('TaskCreationController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('project_id', $values) ?> <div class="task-form-container"> <div class="task-form-main-column"> <?= $this->task->renderTitleField($values, $errors) ?> <?= $this->task->renderDescriptionField($values, $errors) ?> + <?= $this->task->renderDescriptionTemplateDropdown($project['id']) ?> <?= $this->task->renderTagField($project) ?> <?= $this->hook->render('template:task:form:first-column', array('values' => $values, 'errors' => $errors)) ?> diff --git a/app/Template/task_external_link/edit.php b/app/Template/task_external_link/edit.php index df10d444..e448b10f 100644 --- a/app/Template/task_external_link/edit.php +++ b/app/Template/task_external_link/edit.php @@ -2,7 +2,7 @@ <h2><?= t('Edit external link') ?></h2> </div> -<form action="<?= $this->url->href('TaskExternalLinkController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off"> +<form action="<?= $this->url->href('TaskExternalLinkController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'link_id' => $link['id'])) ?>" method="post" autocomplete="off"> <?= $this->render('task_external_link/form', array('task' => $task, 'dependencies' => $dependencies, 'values' => $values, 'errors' => $errors)) ?> <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/task_external_link/find.php b/app/Template/task_external_link/find.php index a3665c0d..29d85101 100644 --- a/app/Template/task_external_link/find.php +++ b/app/Template/task_external_link/find.php @@ -4,7 +4,6 @@ <form action="<?= $this->url->href('TaskExternalLinkController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?> <?= $this->form->label(t('External link'), 'text') ?> <?= $this->form->text( diff --git a/app/Template/task_external_link/form.php b/app/Template/task_external_link/form.php index 932ca521..4ad2b2e0 100644 --- a/app/Template/task_external_link/form.php +++ b/app/Template/task_external_link/form.php @@ -1,6 +1,4 @@ <?= $this->form->csrf() ?> -<?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?> -<?= $this->form->hidden('id', $values) ?> <?= $this->form->hidden('link_type', $values) ?> <?= $this->form->label(t('URL'), 'url') ?> diff --git a/app/Template/task_external_link/table.php b/app/Template/task_external_link/table.php index e345037e..5b22161a 100644 --- a/app/Template/task_external_link/table.php +++ b/app/Template/task_external_link/table.php @@ -1,12 +1,15 @@ <?php if (! empty($links)): ?> <table class="table-striped table-scrolling"> - <tr> - <th class="column-15"><?= t('Type') ?></th> - <th><?= t('Title') ?></th> - <th class="column-10"><?= t('Dependency') ?></th> - <th class="column-15"><?= t('Creator') ?></th> - <th class="column-15"><?= t('Date') ?></th> - </tr> + <thead> + <tr> + <th class="column-15"><?= t('Type') ?></th> + <th><?= t('Title') ?></th> + <th class="column-10"><?= t('Dependency') ?></th> + <th class="column-15"><?= t('Creator') ?></th> + <th class="column-15"><?= t('Date') ?></th> + </tr> + </thead> + <tbody> <?php foreach ($links as $link): ?> <tr> <td> @@ -26,7 +29,7 @@ <?= $this->text->e($link['type']) ?> </td> <td> - <a href="<?= $link['url'] ?>" target="_blank"><?= $this->text->e($link['title']) ?></a> + <a href="<?= $link['url'] ?>" title="<?= $this->text->e($link['url']) ?>" target="_blank"><?= $this->text->e($link['title']) ?></a> </td> <td> <?= $this->text->e($link['dependency_label']) ?> @@ -39,5 +42,6 @@ </td> </tr> <?php endforeach ?> + </tbody> </table> <?php endif ?> diff --git a/app/Template/task_gantt/show.php b/app/Template/task_gantt/show.php deleted file mode 100644 index 61a476b7..00000000 --- a/app/Template/task_gantt/show.php +++ /dev/null @@ -1,31 +0,0 @@ -<section id="main"> - <?= $this->projectHeader->render($project, 'TaskGanttController', 'show') ?> - <div class="menu-inline"> - <ul> - <li <?= $sorting === 'board' ? 'class="active"' : '' ?>> - <?= $this->url->icon('sort-numeric-asc', t('Sort by position'), 'TaskGanttController', 'show', array('project_id' => $project['id'], 'sorting' => 'board')) ?> - </li> - <li <?= $sorting === 'date' ? 'class="active"' : '' ?>> - <?= $this->url->icon('sort-amount-asc', t('Sort by date'), 'TaskGanttController', 'show', array('project_id' => $project['id'], 'sorting' => 'date')) ?> - </li> - <li> - <?= $this->modal->large('plus', t('Add task'), 'TaskCreationController', 'show', array('project_id' => $project['id'])) ?> - </li> - </ul> - </div> - - <?php if (! empty($tasks)): ?> - <div - id="gantt-chart" - data-records='<?= json_encode($tasks, JSON_HEX_APOS) ?>' - data-save-url="<?= $this->url->href('TaskGanttController', 'save', array('project_id' => $project['id'])) ?>" - data-label-start-date="<?= t('Start date:') ?>" - data-label-end-date="<?= t('Due date:') ?>" - data-label-assignee="<?= t('Assignee:') ?>" - data-label-not-defined="<?= t('There is no start date or due date for this task.') ?>" - ></div> - <p class="alert alert-info"><?= t('Moving or resizing a task will change the start and due date of the task.') ?></p> - <?php else: ?> - <p class="alert"><?= t('There is no task in your project.') ?></p> - <?php endif ?> -</section> diff --git a/app/Template/task_import/show.php b/app/Template/task_import/show.php index 20b020d3..342cfb76 100644 --- a/app/Template/task_import/show.php +++ b/app/Template/task_import/show.php @@ -26,6 +26,8 @@ <li><?= t('The first row must be the header') ?></li> <li><?= t('Duplicates are not verified for you') ?></li> <li><?= t('The due date must use the ISO format: YYYY-MM-DD') ?></li> + <li><?= t('Tags must be separated by a comma') ?></li> + <li><?= t('Only the task title is required') ?></li> </ul> <p class="margin-top"> <?= $this->url->icon('download', t('Download CSV template'), 'TaskImportController', 'template', array('project_id' => $project['id'])) ?> diff --git a/app/Template/task_internal_link/create.php b/app/Template/task_internal_link/create.php index 3c39b87c..bab41253 100644 --- a/app/Template/task_internal_link/create.php +++ b/app/Template/task_internal_link/create.php @@ -5,7 +5,6 @@ <form action="<?= $this->url->href('TaskInternalLinkController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?> <?= $this->form->hidden('opposite_task_id', $values) ?> <?= $this->form->label(t('Label'), 'link_id') ?> @@ -25,5 +24,7 @@ ), 'autocomplete') ?> + <?= $this->form->checkbox('another_tasklink', t('Create another link'), 1, isset($values['another_tasklink']) && $values['another_tasklink'] == 1) ?> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/task_internal_link/edit.php b/app/Template/task_internal_link/edit.php index 5abf7b65..fab84d0b 100644 --- a/app/Template/task_internal_link/edit.php +++ b/app/Template/task_internal_link/edit.php @@ -3,10 +3,8 @@ </div> <form action="<?= $this->url->href('TaskInternalLinkController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'link_id' => $task_link['id'])) ?>" method="post" autocomplete="off"> - <?= $this->form->csrf() ?> - <?= $this->form->hidden('id', $values) ?> - <?= $this->form->hidden('task_id', $values) ?> + <?= $this->form->hidden('opposite_task_id', $values) ?> <?= $this->form->label(t('Label'), 'link_id') ?> diff --git a/app/Template/task_list/listing.php b/app/Template/task_list/listing.php index b3c66aa6..97393972 100644 --- a/app/Template/task_list/listing.php +++ b/app/Template/task_list/listing.php @@ -1,40 +1,38 @@ -<section id="main"> - <?= $this->projectHeader->render($project, 'TaskListController', 'show') ?> +<?= $this->projectHeader->render($project, 'TaskListController', 'show') ?> - <?php if ($paginator->isEmpty()): ?> - <p class="alert"><?= t('No tasks found.') ?></p> - <?php elseif (! $paginator->isEmpty()): ?> - <div class="table-list"> - <?= $this->render('task_list/header', array( - 'paginator' => $paginator, - 'project' => $project, - )) ?> +<?php if ($paginator->isEmpty()): ?> + <p class="alert"><?= t('No tasks found.') ?></p> +<?php elseif (! $paginator->isEmpty()): ?> + <div class="table-list"> + <?= $this->render('task_list/header', array( + 'paginator' => $paginator, + 'project' => $project, + )) ?> - <?php foreach ($paginator->getCollection() as $task): ?> - <div class="table-list-row color-<?= $task['color_id'] ?>"> - <?= $this->render('task_list/task_title', array( - 'task' => $task, - )) ?> + <?php foreach ($paginator->getCollection() as $task): ?> + <div class="table-list-row color-<?= $task['color_id'] ?>"> + <?= $this->render('task_list/task_title', array( + 'task' => $task, + )) ?> - <?= $this->render('task_list/task_details', array( - 'task' => $task, - )) ?> + <?= $this->render('task_list/task_details', array( + 'task' => $task, + )) ?> - <?= $this->render('task_list/task_avatars', array( - 'task' => $task, - )) ?> + <?= $this->render('task_list/task_avatars', array( + 'task' => $task, + )) ?> - <?= $this->render('task_list/task_icons', array( - 'task' => $task, - )) ?> + <?= $this->render('task_list/task_icons', array( + 'task' => $task, + )) ?> - <?= $this->render('task_list/task_subtasks', array( - 'task' => $task, - )) ?> - </div> - <?php endforeach ?> - </div> + <?= $this->render('task_list/task_subtasks', array( + 'task' => $task, + )) ?> + </div> + <?php endforeach ?> + </div> - <?= $paginator ?> - <?php endif ?> -</section> + <?= $paginator ?> +<?php endif ?> diff --git a/app/Template/task_list/sort_menu.php b/app/Template/task_list/sort_menu.php index 48081fe0..a42fb46e 100644 --- a/app/Template/task_list/sort_menu.php +++ b/app/Template/task_list/sort_menu.php @@ -17,6 +17,9 @@ <?= $paginator->order(t('Priority'), \Kanboard\Model\TaskModel::TABLE.'.priority') ?> </li> <li> + <?= $paginator->order(t('Position'), \Kanboard\Model\TaskModel::TABLE.'.position') ?> + </li> + <li> <?= $paginator->order(t('Title'), \Kanboard\Model\TaskModel::TABLE.'.title') ?> </li> <li> @@ -26,6 +29,9 @@ <?= $paginator->order(t('Due date'), \Kanboard\Model\TaskModel::TABLE.'.date_due') ?> </li> <li> + <?= $paginator->order(t('Start date'), \Kanboard\Model\TaskModel::TABLE.'.date_started') ?> + </li> + <li> <?= $paginator->order(t('Status'), \Kanboard\Model\TaskModel::TABLE.'.is_active') ?> </li> </ul> diff --git a/app/Template/task_list/task_icons.php b/app/Template/task_list/task_icons.php index 8694d147..d02c9021 100644 --- a/app/Template/task_list/task_icons.php +++ b/app/Template/task_list/task_icons.php @@ -1,7 +1,7 @@ <div class="task-list-icons"> <?php if ($task['reference']): ?> <span class="task-board-reference" title="<?= t('Reference') ?>"> - <?= $this->text->e($task['reference']) ?> + <?= $this->task->renderReference($task) ?> </span> <?php endif ?> <?php if ($task['is_milestone'] == 1): ?> @@ -23,16 +23,23 @@ </span> <?php endif ?> + <?php if (! empty($task['date_started'])): ?> + <span title="<?= t('Start date') ?>" class="task-date"> + <i class="fa fa-clock-o"></i> + <?= $this->dt->date($task['date_started']) ?> + </span> + <?php endif ?> + <?php if (! empty($task['date_due'])): ?> - <span class="task-date - <?php if (date('Y-m-d') == date('Y-m-d', $task['date_due'])): ?> - task-date-today - <?php elseif (time() > $task['date_due']): ?> + <span title="<?= t('Due date') ?>" class="task-date + <?php if (time() > $task['date_due']): ?> task-date-overdue + <?php elseif (date('Y-m-d') == date('Y-m-d', $task['date_due'])): ?> + task-date-today <?php endif ?> "> <i class="fa fa-calendar"></i> - <?= $this->dt->date($task['date_due']) ?> + <?= $this->dt->datetime($task['date_due']) ?> </span> <?php endif ?> @@ -81,6 +88,8 @@ </span> <?php endif ?> + <span title="<?= t('Position') ?>">(<?= $task['position'] ?>)</span> + <?php if ($task['is_active'] == 1): ?> <div class="task-icon-age"> <span title="<?= t('Task age in days')?>" class="task-icon-age-total"><?= $this->dt->age($task['date_creation']) ?></span> diff --git a/app/Template/task_list/task_subtasks.php b/app/Template/task_list/task_subtasks.php index 716d6df2..9110b171 100644 --- a/app/Template/task_list/task_subtasks.php +++ b/app/Template/task_list/task_subtasks.php @@ -3,7 +3,7 @@ <?php foreach ($task['subtasks'] as $subtask): ?> <div class="task-list-subtask"> <span class="subtask-cell column-50"> - <?= $this->subtask->renderToggleStatus($task, $subtask) ?> + <?= $this->subtask->renderToggleStatus($task, $subtask, 'rows', isset($user_id) ? $user_id : 0) ?> </span> <span class="subtask-cell column-20 subtask-assignee"> <?php if (! empty($subtask['username'])): ?> diff --git a/app/Template/task_mail/create.php b/app/Template/task_mail/create.php index 9a1a26b3..f369ef99 100644 --- a/app/Template/task_mail/create.php +++ b/app/Template/task_mail/create.php @@ -1,15 +1,45 @@ <div class="page-header"> <h2><?= $this->text->e($task['title']) ?> > <?= t('Send by email') ?></h2> </div> -<form method="post" action="<?= $this->url->href('TaskMailController', 'send', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('TaskMailController', 'send', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off" class="js-mail-form"> <?= $this->form->csrf() ?> <?= $this->form->label(t('Email'), 'email') ?> <?= $this->form->email('email', $values, $errors, array('autofocus', 'required', 'tabindex="1"')) ?> + <?php if (! empty($members)): ?> + <div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-address-card-o"></i><i class="fa fa-caret-down"></i></a> + <ul> + <?php foreach ($members as $member): ?> + <li data-email="<?= $this->text->e($member['email']) ?>" class="js-autocomplete-email"> + <?= $this->text->e($this->user->getFullname($member)) ?> + </li> + <?php endforeach ?> + </ul> + </div> + <?php endif ?> + <?= $this->form->label(t('Subject'), 'subject') ?> <?= $this->form->text('subject', $values, $errors, array('required', 'tabindex="2"')) ?> + <?php if (! empty($project['predefined_email_subjects'])): ?> + <div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-archive"></i><i class="fa fa-caret-down"></i></a> + <ul> + <?php foreach (explode("\r\n", trim($project['predefined_email_subjects'])) as $subject): ?> + <?php $subject = trim($subject); ?> + + <?php if (! empty($subject)): ?> + <li data-subject="<?= $this->text->e($subject) ?>" class="js-autocomplete-subject"> + <?= $this->text->e($subject) ?> + </li> + <?php endif ?> + <?php endforeach ?> + </ul> + </div> + <?php endif ?> + <?= $this->modal->submitButtons(array( 'submitLabel' => t('Send by email'), 'tabindex' => 3, diff --git a/app/Template/task_mail/email.php b/app/Template/task_mail/email.php index 70db572e..ee85b1d1 100644 --- a/app/Template/task_mail/email.php +++ b/app/Template/task_mail/email.php @@ -9,7 +9,7 @@ </li> <?php if ($task['date_due']): ?> <li> - <strong><?= t('Due date:').' '.$this->dt->date($task['date_due']) ?></strong> + <strong><?= t('Due date:').' '.$this->dt->datetime($task['date_due']) ?></strong> </li> <?php endif ?> <?php if (! empty($task['creator_username'])): ?> diff --git a/app/Template/task_modification/show.php b/app/Template/task_modification/show.php index 710abedf..c7c41ddd 100644 --- a/app/Template/task_modification/show.php +++ b/app/Template/task_modification/show.php @@ -3,13 +3,12 @@ </div> <form method="post" action="<?= $this->url->href('TaskModificationController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> - <?= $this->form->hidden('id', $values) ?> - <?= $this->form->hidden('project_id', $values) ?> <div class="task-form-container"> <div class="task-form-main-column"> <?= $this->task->renderTitleField($values, $errors) ?> <?= $this->task->renderDescriptionField($values, $errors) ?> + <?= $this->task->renderDescriptionTemplateDropdown($project['id']) ?> <?= $this->task->renderTagField($project, $tags) ?> <?= $this->hook->render('template:task:form:first-column', array('values' => $values, 'errors' => $errors)) ?> diff --git a/app/User/Avatar/GravatarProvider.php b/app/User/Avatar/GravatarProvider.php deleted file mode 100644 index e066d766..00000000 --- a/app/User/Avatar/GravatarProvider.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -namespace Kanboard\User\Avatar; - -use Kanboard\Core\Base; -use Kanboard\Core\User\Avatar\AvatarProviderInterface; - -/** - * Gravatar Avatar Provider - * - * @package avatar - * @author Frederic Guillot - */ -class GravatarProvider extends Base implements AvatarProviderInterface -{ - /** - * Render avatar html - * - * @access public - * @param array $user - * @param int $size - * @return string - */ - public function render(array $user, $size) - { - $url = sprintf('https://www.gravatar.com/avatar/%s?s=%d', md5(strtolower($user['email'])), $size); - $title = $this->helper->text->e($user['name'] ?: $user['username']); - return '<img src="'.$url.'" alt="'.$title.'" title="'.$title.'">'; - } - - /** - * Determine if the provider is active - * - * @access public - * @param array $user - * @return boolean - */ - public function isActive(array $user) - { - return !empty($user['email']) && $this->configModel->get('integration_gravatar') == 1; - } -} diff --git a/app/User/DatabaseBackendUserProvider.php b/app/User/DatabaseBackendUserProvider.php new file mode 100644 index 00000000..835d90be --- /dev/null +++ b/app/User/DatabaseBackendUserProvider.php @@ -0,0 +1,43 @@ +<?php + +namespace Kanboard\User; + +use Kanboard\Core\Base; +use Kanboard\Core\User\UserBackendProviderInterface; +use Kanboard\Filter\UserNameFilter; +use Kanboard\Model\UserModel; + +/** + * Database Backend User Provider + * + * @package Kanboard\User + * @author Frederic Guillot + */ +class DatabaseBackendUserProvider extends Base implements UserBackendProviderInterface +{ + /** + * Find a group from a search query + * + * @access public + * @param string $input + * @return DatabaseUserProvider[] + */ + public function find($input) + { + $result = array(); + + $users = $this->userQuery->withFilter(new UserNameFilter($input)) + ->getQuery() + ->columns(UserModel::TABLE.'.id', UserModel::TABLE.'.username', UserModel::TABLE.'.name') + ->eq(UserModel::TABLE.'.is_active', 1) + ->asc(UserModel::TABLE.'.name') + ->asc(UserModel::TABLE.'.username') + ->findAll(); + + foreach ($users as $user) { + $result[] = new DatabaseUserProvider($user); + } + + return $result; + } +} diff --git a/app/User/DatabaseUserProvider.php b/app/User/DatabaseUserProvider.php index fc626610..3b26aedb 100644 --- a/app/User/DatabaseUserProvider.php +++ b/app/User/DatabaseUserProvider.php @@ -83,7 +83,7 @@ class DatabaseUserProvider implements UserProviderInterface */ public function getRole() { - return ''; + return empty($this->user['role']) ? '' : $this->user['role']; } /** @@ -94,7 +94,7 @@ class DatabaseUserProvider implements UserProviderInterface */ public function getUsername() { - return ''; + return empty($this->user['username']) ? '' : $this->user['username']; } /** @@ -105,7 +105,7 @@ class DatabaseUserProvider implements UserProviderInterface */ public function getName() { - return ''; + return empty($this->user['name']) ? '' : $this->user['name']; } /** @@ -116,7 +116,7 @@ class DatabaseUserProvider implements UserProviderInterface */ public function getEmail() { - return ''; + return empty($this->user['email']) ? '' : $this->user['email']; } /** diff --git a/app/Validator/PredefinedTaskDescriptionValidator.php b/app/Validator/PredefinedTaskDescriptionValidator.php new file mode 100644 index 00000000..3ff4e3cb --- /dev/null +++ b/app/Validator/PredefinedTaskDescriptionValidator.php @@ -0,0 +1,22 @@ +<?php + +namespace Kanboard\Validator; + +use SimpleValidator\Validator; +use SimpleValidator\Validators; + +class PredefinedTaskDescriptionValidator extends BaseValidator +{ + public function validate(array $values) + { + $v = new Validator($values, array( + new Validators\Required('title', t('This value is required')), + new Validators\Required('description', t('This value is required')), + )); + + return array( + $v->execute(), + $v->getErrors() + ); + } +}
\ No newline at end of file diff --git a/app/Validator/TaskValidator.php b/app/Validator/TaskValidator.php index f82e7374..3fc70317 100644 --- a/app/Validator/TaskValidator.php +++ b/app/Validator/TaskValidator.php @@ -49,6 +49,20 @@ class TaskValidator extends BaseValidator ); } + public function validateStartAndDueDate(array $values) + { + if (!empty($values['date_started']) && !empty($values['date_due'])) { + $startDate = $this->dateParser->getTimestamp($values['date_started']); + $endDate = $this->dateParser->getTimestamp($values['date_due']); + + if ($startDate > $endDate) { + return array(false, array('date_started' => array(t('The start date is greater than the end date')))); + } + } + + return array(true, array()); + } + /** * Validate task creation * @@ -64,11 +78,14 @@ class TaskValidator extends BaseValidator ); $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + $result = $v->execute(); + $errors = $v->getErrors(); - return array( - $v->execute(), - $v->getErrors() - ); + if ($result) { + list($result, $errors) = $this->validateStartAndDueDate($values); + } + + return array($result, $errors); } /** @@ -81,7 +98,6 @@ class TaskValidator extends BaseValidator public function validateBulkCreation(array $values) { $rules = array( - new Validators\Required('project_id', t('The project is required')), new Validators\Required('tasks', t('Field required')), new Validators\Required('column_id', t('Field required')), new Validators\Required('swimlane_id', t('Field required')), @@ -134,11 +150,14 @@ class TaskValidator extends BaseValidator ); $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + $result = $v->execute(); + $errors = $v->getErrors(); - return array( - $v->execute(), - $v->getErrors() - ); + if ($result) { + list($result, $errors) = $this->validateStartAndDueDate($values); + } + + return array($result, $errors); } /** @@ -155,11 +174,14 @@ class TaskValidator extends BaseValidator ); $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + $result = $v->execute(); + $errors = $v->getErrors(); - return array( - $v->execute(), - $v->getErrors() - ); + if ($result) { + list($result, $errors) = $this->validateStartAndDueDate($values); + } + + return array($result, $errors); } /** @@ -185,27 +207,6 @@ class TaskValidator extends BaseValidator } /** - * Validate time tracking modification (form) - * - * @access public - * @param array $values Form values - * @return array $valid, $errors [0] = Success or not, [1] = List of errors - */ - public function validateTimeModification(array $values) - { - $rules = array( - new Validators\Required('id', t('The id is required')), - ); - - $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); - - return array( - $v->execute(), - $v->getErrors() - ); - } - - /** * Validate task email creation * * @access public diff --git a/app/Validator/UserValidator.php b/app/Validator/UserValidator.php index fe402c47..041390a3 100644 --- a/app/Validator/UserValidator.php +++ b/app/Validator/UserValidator.php @@ -116,6 +116,10 @@ class UserValidator extends BaseValidator $v = new Validator($values, array_merge($rules, $this->commonPasswordValidationRules())); if ($v->execute()) { + if (! $this->userSession->isAdmin() && $values['id'] != $this->userSession->getId()) { + return array(false, array('current_password' => array('Invalid User ID'))); + } + if ($this->authenticationManager->passwordAuthentication($this->userSession->getUsername(), $values['current_password'], false)) { return array(true, array()); } else { diff --git a/app/check_setup.php b/app/check_setup.php index d962a6f8..314c9786 100644 --- a/app/check_setup.php +++ b/app/check_setup.php @@ -43,3 +43,8 @@ foreach (array('gd', 'mbstring', 'hash', 'openssl', 'json', 'hash', 'ctype', 'fi if (ini_get('arg_separator.output') === '&') { ini_set('arg_separator.output', '&'); } + +// Make sure we can read files with "\r", "\r\n" and "\n" +if (ini_get('auto_detect_line_endings') != 1) { + ini_set("auto_detect_line_endings", 1); +} diff --git a/app/common.php b/app/common.php index 69c56953..a1d42c33 100644 --- a/app/common.php +++ b/app/common.php @@ -42,6 +42,7 @@ $container->register(new Kanboard\ServiceProvider\NotificationProvider()); $container->register(new Kanboard\ServiceProvider\ClassProvider()); $container->register(new Kanboard\ServiceProvider\EventDispatcherProvider()); $container->register(new Kanboard\ServiceProvider\GroupProvider()); +$container->register(new Kanboard\ServiceProvider\UserProvider()); $container->register(new Kanboard\ServiceProvider\RouteProvider()); $container->register(new Kanboard\ServiceProvider\ActionProvider()); $container->register(new Kanboard\ServiceProvider\ExternalLinkProvider()); diff --git a/app/constants.php b/app/constants.php index d62bfd3a..6b260a4a 100644 --- a/app/constants.php +++ b/app/constants.php @@ -56,6 +56,7 @@ defined('DB_SSL_CA') or define('DB_SSL_CA', null); // Database backend group provider defined('DB_GROUP_PROVIDER') or define('DB_GROUP_PROVIDER', true); +defined('DB_USER_PROVIDER') or define('DB_USER_PROVIDER', true); // LDAP configuration defined('LDAP_AUTH') or define('LDAP_AUTH', false); @@ -146,3 +147,5 @@ defined('HTTP_PROXY_PASSWORD') or define('HTTP_PROXY_PASSWORD', ''); defined('HTTP_VERIFY_SSL_CERTIFICATE') or define('HTTP_VERIFY_SSL_CERTIFICATE', true); defined('TOTP_ISSUER') or define('TOTP_ISSUER', 'Kanboard'); + +defined('PROJECT_ACTIVITIES_MAX_EVENTS') or define('PROJECT_ACTIVITIES_MAX_EVENTS', 10000); diff --git a/assets/css/app.min.css b/assets/css/app.min.css index 665cd718..b9de5f4d 100644 --- a/assets/css/app.min.css +++ b/assets/css/app.min.css @@ -1 +1 @@ -h1,li,ul,ol,table,tr,td,th,p,blockquote,body{margin:0;padding:0;font-size:100%}body{margin-left:10px;margin-right:10px;padding-bottom:10px;color:#333;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;text-rendering:optimizeLegibility}small{font-size:0.8em}hr{border:0;height:0;border-top:1px solid rgba(0,0,0,0.1);border-bottom:1px solid rgba(255,255,255,0.3)}.margin-top{margin-top:20px}.margin-bottom{margin-bottom:20px}.pull-right{text-align:right}ul.no-bullet li{list-style-type:none;margin-left:0}#app-loading-icon{position:fixed;right:3px;bottom:3px}.assign-me{vertical-align:bottom}a{color:#36c;border:none}a:focus{outline:0;color:#DF5353;text-decoration:none}a:hover{color:#333;text-decoration:none}a .fa{padding-right:3px;text-decoration:none;color:#333}h1,h2,h3{font-weight:normal;color:#333}h1{font-size:1.5em}h2{font-size:1.4em;margin-bottom:10px}h3{margin-top:10px;font-size:1.2em}table{width:100%;border-collapse:collapse;border-spacing:0;margin-bottom:20px}table.table-fixed{table-layout:fixed;white-space:nowrap}table.table-fixed th{overflow:hidden}table.table-fixed td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}table.table-small{font-size:0.8em}table.table-striped tr:nth-child(odd){background:#fefefe}@media (max-width: 768px){table.table-scrolling{overflow-x:auto;display:inline-block;vertical-align:top;max-width:100%;white-space:nowrap}}table th{text-align:left;padding:0.5em 3px;border:1px solid #eee;background:#fbfbfb}table th a{text-decoration:none;color:#333}table th a:focus,table th a:hover{text-decoration:underline}table td{border:1px solid #eee;padding:0.5em 3px;vertical-align:top}table td li{margin-left:20px}.column-1{width:1%}.column-2{width:2%}.column-3{width:3%}.column-4{width:4%}.column-5{width:5%}.column-6{width:6%}.column-7{width:7%}.column-8{width:8%}.column-9{width:9%}.column-10{width:10%}.column-11{width:11%}.column-12{width:12%}.column-13{width:13%}.column-14{width:14%}.column-15{width:15%}.column-16{width:16%}.column-17{width:17%}.column-18{width:18%}.column-19{width:19%}.column-20{width:20%}.column-21{width:21%}.column-22{width:22%}.column-23{width:23%}.column-24{width:24%}.column-25{width:25%}.column-26{width:26%}.column-27{width:27%}.column-28{width:28%}.column-29{width:29%}.column-30{width:30%}.column-31{width:31%}.column-32{width:32%}.column-33{width:33%}.column-34{width:34%}.column-35{width:35%}.column-36{width:36%}.column-37{width:37%}.column-38{width:38%}.column-39{width:39%}.column-40{width:40%}.column-41{width:41%}.column-42{width:42%}.column-43{width:43%}.column-44{width:44%}.column-45{width:45%}.column-46{width:46%}.column-47{width:47%}.column-48{width:48%}.column-49{width:49%}.column-50{width:50%}.column-51{width:51%}.column-52{width:52%}.column-53{width:53%}.column-54{width:54%}.column-55{width:55%}.column-56{width:56%}.column-57{width:57%}.column-58{width:58%}.column-59{width:59%}.column-60{width:60%}.column-61{width:61%}.column-62{width:62%}.column-63{width:63%}.column-64{width:64%}.column-65{width:65%}.column-66{width:66%}.column-67{width:67%}.column-68{width:68%}.column-69{width:69%}.column-70{width:70%}.column-71{width:71%}.column-72{width:72%}.column-73{width:73%}.column-74{width:74%}.column-75{width:75%}.column-76{width:76%}.column-77{width:77%}.column-78{width:78%}.column-79{width:79%}.column-80{width:80%}.column-81{width:81%}.column-82{width:82%}.column-83{width:83%}.column-84{width:84%}.column-85{width:85%}.column-86{width:86%}.column-87{width:87%}.column-88{width:88%}.column-89{width:89%}.column-90{width:90%}.column-91{width:91%}.column-92{width:92%}.column-93{width:93%}.column-94{width:94%}.column-95{width:95%}.column-96{width:96%}.column-97{width:97%}.column-98{width:98%}.column-99{width:99%}.column-100{width:100%}.draggable-row-handle{cursor:move;color:#dedede}.draggable-row-handle:hover{color:#333}tr.draggable-item-selected{background:#fff;border:2px solid #666;box-shadow:4px 2px 10px -4px rgba(0,0,0,0.55)}tr.draggable-item-selected td{border-top:none;border-bottom:none}tr.draggable-item-selected td:first-child{border-left:none}tr.draggable-item-selected td:last-child{border-right:none}.table-stripped tr.draggable-item-hover,.table-stripped tr.draggable-item-hover{background:#FEFFF2}.table-list{font-size:0.9em;margin-bottom:20px}.table-list-header{background:#fbfbfb;border:1px solid #e5e5e5;border-radius:5px 5px 0 0;line-height:35px;padding-left:3px;padding-right:3px}.table-list-header a{color:#333;font-weight:500;text-decoration:none;margin-right:10px}.table-list-header a:hover,.table-list-header a:focus{color:#767676}.table-list-header .table-list-header-count{color:#767676;display:inline-block;float:left}.table-list-header .table-list-header-menu{text-align:right}.table-list-row{padding-left:3px;padding-right:3px;border-bottom:1px solid #e5e5e5;border-right:1px solid #e5e5e5}.table-list-row.table-border-left{border-left:1px solid #e5e5e5}.table-list-row:nth-child(odd){background:#fefefe}.table-list-row:last-child{border-radius:0 0 5px 5px}.table-list-row:hover{background:#fff8dc;border-bottom:1px solid #ffeb8e;border-right:1px solid #ffeb8e}.table-list-row .table-list-title{font-weight:500;line-height:23px}.table-list-row .table-list-title.status-closed{text-decoration:line-through;margin-right:10px}.table-list-row .table-list-title.status-closed a{font-style:italic}.table-list-row .table-list-title a{color:#333;text-decoration:none}.table-list-row .table-list-title a:hover,.table-list-row .table-list-title a:focus{text-decoration:underline}.table-list-row .table-list-details{color:#999;font-weight:300;line-height:30px}.table-list-row .table-list-details span{margin-left:5px}.table-list-row .table-list-details span:first-child{margin-left:0}.table-list-row .table-list-details li{display:inline;list-style-type:none}.table-list-row .table-list-details li:after{content:', '}.table-list-row .table-list-details li:last-child:after{content:''}.table-list-row .table-list-details-with-icons{float:left}@media (max-width: 768px){.table-list-row .table-list-details-with-icons{float:none}}.table-list-row .table-list-icons{font-size:0.8em;text-align:right;line-height:30px}@media (max-width: 768px){.table-list-row .table-list-icons{text-align:left;line-height:20px}}.table-list-row .table-list-icons span{margin-left:5px}.table-list-row .table-list-icons a{text-decoration:none}.table-list-row .table-list-icons a:hover{color:#333}.table-list-row .table-list-icons a:hover i{color:#333}.table-list-category{font-size:0.9em;font-weight:500;color:#000;padding:1px 2px 1px 2px;border-radius:3px;background:#fcfcfc;border:1px solid #ccc}.table-list-category a{text-decoration:none;color:#000}.table-list-category a:hover{color:#36c}fieldset{border:1px solid #ccc;margin-top:20px}legend{font-weight:500;font-size:1.2em}label{cursor:pointer;display:block;margin-top:10px;font-weight:400}input[type="number"],input[type="date"],input[type="email"],input[type="password"],input[type="text"]:not(.input-addon-field){color:#999;border:1px solid #ccc;width:300px;max-width:95%;font-size:1em;height:25px;padding-bottom:0;font-family:sans-serif;-webkit-appearance:none;-moz-appearance:none}input[type="number"]::-webkit-input-placeholder,input[type="date"]::-webkit-input-placeholder,input[type="email"]::-webkit-input-placeholder,input[type="password"]::-webkit-input-placeholder,input[type="text"]:not(.input-addon-field)::-webkit-input-placeholder{color:#dedede}input[type="number"]::-moz-placeholder,input[type="date"]::-moz-placeholder,input[type="email"]::-moz-placeholder,input[type="password"]::-moz-placeholder,input[type="text"]:not(.input-addon-field)::-moz-placeholder{color:#dedede}input[type="number"]:-ms-input-placeholder,input[type="date"]:-ms-input-placeholder,input[type="email"]:-ms-input-placeholder,input[type="password"]:-ms-input-placeholder,input[type="text"]:not(.input-addon-field):-ms-input-placeholder{color:#dedede}input[type="number"]:focus,input[type="date"]:focus,input[type="email"]:focus,input[type="password"]:focus,input[type="text"]:focus{color:#000;border-color:rgba(82,168,236,0.8);outline:0;box-shadow:0 0 8px rgba(82,168,236,0.6)}input[type="number"]{width:70px}input[type="text"]:not(.input-addon-field).form-numeric{width:70px}input[type="text"]:not(.input-addon-field).form-datetime,input[type="text"]:not(.input-addon-field).form-date{width:150px}input[type="text"]:not(.input-addon-field).form-input-large{width:400px}input[type="text"]:not(.input-addon-field).form-input-small{width:150px}textarea:focus{color:#000;border-color:rgba(82,168,236,0.8);outline:0;box-shadow:0 0 8px rgba(82,168,236,0.6)}textarea{padding:3px;border:1px solid #ccc;width:400px;max-width:99%;height:200px;font-family:sans-serif;font-size:1em}textarea::-webkit-input-placeholder{color:#dedede}textarea::-moz-placeholder{color:#dedede}textarea:-ms-input-placeholder{color:#dedede}select{font-size:1.0em;max-width:95%}select:focus{outline:0}select[multiple]{width:300px}.tag-autocomplete{width:400px}span.select2-container{margin-top:2px}.form-actions{padding-top:20px;clear:both}.form-required{color:red;padding-left:5px;font-weight:bold}@media (max-width: 480px){.form-required{display:none}}input.form-error,textarea.form-error{border:2px solid #b94a48}input.form-error:focus,textarea.form-error:focus{box-shadow:none;border:2px solid #b94a48}.form-errors{color:#b94a48;list-style-type:none}ul.form-errors li{margin-left:0}.form-help{font-size:0.8em;color:brown;margin-bottom:15px}.form-inline{padding:0;margin:0;border:none}.form-inline label{display:inline;padding-right:3px}.form-inline input,.form-inline select{margin:0 15px 0 0}.form-inline .form-required{display:none}.form-inline .form-actions{display:inline-block}.form-inline .js-submit-buttons-rendered{display:inline-block}.form-inline-group{display:inline}.form-columns{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:flex-start;justify-content:flex-start}.form-columns .form-column{margin-right:25px;flex-grow:1}.form-columns fieldset{margin-top:0}.form-login{max-width:350px;margin:5% auto 0}.form-login li{margin-left:25px;line-height:25px}.form-login h2{margin-bottom:30px;font-weight:bold}.reset-password{margin-top:20px;margin-bottom:20px}.reset-password a{color:#999}.input-addon{display:flex}.input-addon-field{flex:1;font-size:1em;color:#999;margin:0;-webkit-appearance:none;-moz-appearance:none}.input-addon-field:first-child{border-radius:5px 0 0 5px}.input-addon-field:last-child{border-radius:0 5px 5px 0}.input-addon-item{background-color:rgba(147,128,108,0.1);color:#666;font:inherit;font-weight:normal}.input-addon-item:first-child{border-radius:5px 0 0 5px}.input-addon-item:last-child{border-radius:0 5px 5px 0}@media (max-width: 480px){.input-addon-item .dropdown .fa-caret-down{display:none}}.input-addon-field,.input-addon-item{border:1px solid rgba(147,128,108,0.25);padding:4px 0.75em}.input-addon-field:not(:first-child),.input-addon-item:not(:first-child){border-left:0}.icon-success{color:#468847}.icon-error{color:#b94a48}.icon-fade-out{opacity:1;animation:icon-fadeout 5s linear forwards}@keyframes icon-fadeout{0%{opacity:1}100%{opacity:0}}.alert{padding:8px 35px 8px 14px;margin-top:5px;margin-bottom:5px;color:#c09853;background-color:#fcf8e3;border:1px solid #fbeed5;border-radius:4px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-normal{color:#333;background-color:#f0f0f0;border-color:#ddd}.alert ul{margin-top:10px;margin-bottom:10px}.alert li{margin-left:25px}.alert-fade-out{text-align:center;position:fixed;bottom:0;left:20%;width:60%;padding-top:5px;padding-bottom:5px;margin-bottom:0;border-width:1px 0 0;border-radius:4px 4px 0 0;z-index:9999;opacity:1;animation:fadeout 5s linear forwards}@keyframes fadeout{0%{opacity:1}100%{opacity:0}}a.btn{text-decoration:none}.btn{-webkit-appearance:none;-moz-appearance:none;font-size:1.2em;font-weight:normal;cursor:pointer;display:inline-block;border-radius:2px;padding:3px 10px;margin:0;border:1px solid #ddd;background:#f5f5f5;color:#333}.btn:hover,.btn:focus{border-color:#bbb;background:#fafafa;color:#000}.btn-red{border-color:#b0281a;background:#d14836;color:#fff}.btn-red:hover,.btn-red:focus{border-color:#b0281a;background:#c53727;color:#fff}.btn-blue{border-color:#3079ed;background:#4d90fe;color:#fff}.btn-blue:hover,.btn-blue:focus{border-color:#3079ed;background:#357ae8;color:#fff}.btn:disabled{color:#ccc;border-color:#ccc;background:#f7f7f7}.buttons-header{font-size:0.8em;margin-top:5px;margin-bottom:15px}.tooltip-arrow:after{background:#fff;border:1px solid #aaaaaa;box-shadow:0 0 5px #aaa}div.ui-tooltip{min-width:200px;max-width:600px}.tooltip-arrow{width:20px;height:10px;overflow:hidden;position:absolute}.tooltip-arrow.top{top:-10px}.tooltip-arrow.bottom{bottom:-10px}.tooltip-arrow.align-left{left:10px}.tooltip-arrow.align-right{right:10px}.tooltip-arrow:after{content:"";position:absolute;width:14px;height:14px;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.tooltip-arrow.bottom:after{top:-10px}.tooltip-arrow.top:after{bottom:-10px}.tooltip-arrow.align-left:after{left:0}.tooltip-arrow.align-right:after{right:0}.tooltip-large{width:600px}.ui-tooltip-content .markdown p{margin-bottom:0}.ui-tooltip li{list-style-type:none}.tooltip .fa-info-circle{color:#999}h2 .dropdown ul{display:none}.dropdown{display:inline;position:relative}.dropdown ul{display:none}ul.dropdown-submenu-open{display:block;position:absolute;z-index:1000;min-width:285px;list-style:none;margin:3px 0 0 1px;padding:6px 0;background-color:#fff;border:1px solid #b2b2b2;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,0.15)}.dropdown-submenu-open li{display:block;margin:0;padding:8px 10px;font-size:0.9em;border-bottom:1px solid #f8f8f8;cursor:pointer}.dropdown-submenu-open li.no-hover{cursor:default}.dropdown-submenu-open li:last-child{border:none}.dropdown-submenu-open li:not(.no-hover):hover{background:#4078C0;color:#fff}.dropdown-submenu-open li:hover a{color:#fff}.dropdown-submenu-open a{text-decoration:none;color:#333}.dropdown-submenu-open a:focus{text-decoration:underline}.dropdown-menu-link-text,.dropdown-menu-link-icon{color:#333;text-decoration:none}.dropdown-menu-link-text:hover{text-decoration:underline}td a.dropdown-menu strong{color:#333}td a.dropdown-menu strong i{color:#333}td a.dropdown-menu i{color:#dedede}td a.dropdown-menu:hover strong{color:#555}td a.dropdown-menu:hover strong i{color:#555}td a.dropdown-menu:hover i{color:#333}.accordion-title{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAADCAYAAABS3WWCAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NEQ5RDgxQzc2RjQ5MTFFMjhEMUNENzFGRUMwRjhBRTciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NEQ5RDgxQzg2RjQ5MTFFMjhEMUNENzFGRUMwRjhBRTciPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo0RDlEODFDNTZGNDkxMUUyOEQxQ0Q3MUZFQzBGOEFFNyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo0RDlEODFDNjZGNDkxMUUyOEQxQ0Q3MUZFQzBGOEFFNyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PvXFWFAAAAAYSURBVHjaYvj//z8D0/Pnz/8zgFgAAQYAS5UJscReGMIAAAAASUVORK5CYII=) repeat-x scroll 0 10px}.accordion-title h3{display:inline;padding-right:5px;background:#fff}.accordion-content{margin-top:15px;margin-bottom:25px}.accordion-toggle{color:#333;text-decoration:none}.accordion-toggle:focus{color:#333}.accordion-toggle:hover{color:#999}.accordion-toggle:before{content:"\f0d7"}.accordion-collapsed{margin-bottom:25px}.accordion-collapsed .accordion-toggle:before{content:"\f0da"}.accordion-collapsed .accordion-content{display:none}#select-dropdown-menu{position:absolute;display:block;z-index:1000;min-width:160px;padding:5px 0;background:#fff;list-style:none;border:1px solid #ccc;border-radius:3px;box-shadow:0 6px 12px rgba(0,0,0,0.175);overflow:scroll}.select-dropdown-menu-item{white-space:nowrap;overflow:hidden;padding:3px 10px;color:#555;cursor:pointer;border-bottom:1px solid #f8f8f8;line-height:1.5em;font-weight:400}.select-dropdown-menu-item.active{color:#fff;background:#428bca}.select-dropdown-menu-item:last-child{border:none}.select-dropdown-input-container{position:relative;border:1px solid #ccc;border-radius:5px}.select-dropdown-input-container input.select-dropdown-input{margin:0 0 0 5px;border:none;height:23px}.select-dropdown-input-container input.select-dropdown-input:focus{border:none;box-shadow:none}.select-dropdown-input-container .select-dropdown-chevron{color:#555;position:absolute;top:4px;right:5px;cursor:pointer}.select-dropdown-input-container .select-loading-icon{color:#555;position:absolute;top:4px;right:5px}#suggest-menu{position:absolute;display:block;z-index:1000;min-width:160px;padding:5px 0;background:#fff;list-style:none;border:1px solid #ccc;border-radius:3px;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.suggest-menu-item{white-space:nowrap;padding:3px 10px;color:#333;font-weight:bold;cursor:pointer}.suggest-menu-item.active{color:#fff;background:#428bca}.suggest-menu-item.active small{color:#fff}.suggest-menu-item small{color:#999;font-weight:normal}#modal-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.9);overflow:auto;z-index:100}#modal-box{position:fixed;max-height:calc(100% - 30px);top:2%;left:50%;transform:translateX(-50%);background:#fff;overflow:auto;border-radius:5px}#modal-content{padding:0 5px 5px}#modal-header{text-align:right;padding-right:5px}#modal-close-button{color:#333}#modal-close-button:hover{color:#b94a48}.pagination{text-align:center}.pagination-showing{margin-right:5px;padding-right:5px;border-right:1px solid #999}.pagination-next{margin-left:5px}.pagination-previous{margin-right:5px}header{box-sizing:border-box;display:flex;flex-wrap:wrap;margin-top:5px;margin-bottom:5px;border-bottom:1px solid #dedede}header>*{box-sizing:border-box}header>*{width:1%}header .menus-container{width:10%}@media (min-width: 768px) and (max-width: 1150px){header .menus-container{width:15%}}@media (max-width: 768px){header .menus-container{width:20%;order:2}header .menus-container .header-creation-menu{display:none}}header .board-selector-container{width:25%}@media (min-width: 768px) and (max-width: 1150px){header .board-selector-container{width:20%}}@media (max-width: 768px){header .board-selector-container{width:80%;order:1;margin-bottom:5px}}header .title-container{width:65%}@media (max-width: 768px){header .title-container{width:100%;order:3}}header h1{font-size:1.5em}header h1 .tooltip{opacity:0.3;font-size:0.7em}a i.web-notification-icon{color:#36c}a i.web-notification-icon:focus,a i.web-notification-icon:hover{color:#000}.logo a{opacity:0.5;color:#d40000;text-decoration:none}.logo span{color:#333}.logo a:hover{opacity:0.8;color:#333}.logo a:focus span,.logo a:hover span{color:#d40000}.page-header{margin-bottom:20px}.page-header .dropdown{padding-right:10px}.page-header h2{margin:0;padding:0;font-weight:bold;border-bottom:1px dotted #ccc}.page-header h2 a{color:#333;text-decoration:none}.page-header h2 a:focus,.page-header h2 a:hover{color:#999}.page-header ul{text-align:left;margin-top:5px;display:inline-block}.page-header li{display:inline;padding-right:15px}@media (max-width: 480px){.page-header li{display:block;line-height:1.5em}}.page-header li.active a{color:#333;text-decoration:none;font-weight:bold}.page-header li.active a:hover,.page-header li.active a:focus{text-decoration:underline}.menu-inline{margin-bottom:5px}.menu-inline li{display:inline;padding-right:15px}.menu-inline li .active a{font-weight:bold;color:#000;text-decoration:none}.sidebar-container{box-sizing:border-box;display:flex;flex-wrap:wrap}.sidebar-container>*{box-sizing:border-box}.sidebar-container>*{width:1%}.sidebar-content{padding-left:10px;width:82%}@media (max-width: 480px){.sidebar-content{width:100%}}.sidebar{max-width:240px;min-width:190px;width:18%}@media (max-width: 480px){.sidebar{width:100%;max-width:99%;min-width:0}}.sidebar h2{margin-top:0}.sidebar>ul a{text-decoration:none;color:#999;font-weight:300}.sidebar>ul a:hover{color:#333}.sidebar>ul li{list-style-type:none;line-height:35px;border-bottom:1px dotted #efefef;padding-left:13px}.sidebar>ul li:hover{border-left:5px solid #555;padding-left:8px}.sidebar>ul li.active{border-left:5px solid #333;padding-left:8px}.sidebar>ul li.active a{color:#333;font-weight:bold}.sidebar-icons>ul li{padding-left:0}.sidebar-icons>ul li:hover,.sidebar-icons>ul li.active{padding-left:0;border-left:none}.sidebar>ul li.active a:focus,.sidebar>ul li.active a:hover{color:#555}.sidebar>ul li:last-child{margin-bottom:15px}.avatar img{vertical-align:bottom}.avatar-left{float:left;margin-right:10px}.avatar-inline{display:inline-block;margin-right:3px}.avatar-48 img,.avatar-48 div{border-radius:30px}.avatar-48 .avatar-letter{line-height:48px;width:48px;font-size:25px}.avatar-20 img,.avatar-20 div{border-radius:10px}.avatar-20 .avatar-letter{line-height:20px;width:20px;font-size:11px}.avatar-letter{color:#fff;text-align:center}#file-dropzone,#screenshot-zone{position:relative;border:2px dashed #ccc;width:99%;height:250px;overflow:auto}#file-dropzone-inner,#screenshot-inner{position:absolute;left:0;bottom:48%;width:100%;text-align:center;color:#aaa}#screenshot-zone.screenshot-pasted{border:2px solid #333}#file-list{margin:20px}#file-list li{list-style-type:none;padding-top:8px;padding-bottom:8px;border-bottom:1px dotted #ddd;width:95%}#file-list li .file-error{font-weight:bold;color:#b94a48}.file-thumbnails{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:flex-start;justify-content:flex-start}.file-thumbnail{width:250px;border:1px solid #efefef;border-radius:5px;margin-bottom:20px;box-shadow:4px 2px 10px -6px rgba(0,0,0,0.55);margin-right:15px}.file-thumbnail img{cursor:pointer;border-top-left-radius:5px;border-top-right-radius:5px}.file-thumbnail img:hover{opacity:0.5}.file-thumbnail-content{padding-left:8px;padding-right:8px}.file-thumbnail-title{font-weight:700;font-size:0.9em;color:#555;overflow:hidden;text-overflow:ellipsis}.file-thumbnail-description{font-size:0.8em;color:#999;margin-top:8px;margin-bottom:5px}.file-viewer{position:relative}.file-viewer img{max-width:95%;max-height:85%;margin-top:10px}.color-picker{width:180px}.color-picker-option{height:25px}.color-picker-square{display:inline-block;width:18px;height:18px;margin-right:5px;border:1px solid #000}.color-picker-label{display:inline-block;vertical-align:bottom;padding-bottom:3px}.filter-box{max-width:800px}.action-menu{color:#333;text-decoration:none}.action-menu:hover,.action-menu:focus{text-decoration:underline}.js-project-creation-options{max-width:500px;border-left:3px dotted #efefef;margin-top:20px;padding-left:15px;padding-bottom:5px;padding-top:5px}.project-overview-columns{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center;margin-bottom:20px;font-size:1.4em}@media (max-width: 480px){.project-overview-columns{display:block}}.project-overview-column{text-align:center;margin-right:3%;margin-top:5px;padding:3px 15px 3px 15px;border:1px dashed #ddd}@media (max-width: 480px){.project-overview-column{text-align:left}}.project-overview-column small{color:#999}.project-overview-column strong{color:#555;display:block}@media (max-width: 480px){.project-overview-column strong{display:inline}}.project-header{box-sizing:border-box;display:flex;flex-wrap:wrap;margin-bottom:8px}.project-header>*{box-sizing:border-box}.project-header>*{width:1%}.project-header .dropdown-component{margin-top:4px;width:5%}@media (min-width: 768px) and (max-width: 1150px){.project-header .dropdown-component{width:8%}}@media (max-width: 768px){.project-header .dropdown-component{width:100%}}.project-header .views-switcher-component{margin-top:4px;width:38%}@media (max-width: 1300px){.project-header .views-switcher-component{width:45%}}@media (min-width: 768px) and (max-width: 1150px){.project-header .views-switcher-component{width:92%}}@media (max-width: 768px){.project-header .views-switcher-component{margin-top:0;width:100%}}.project-header .filter-box-component{margin:0;width:55%}@media (max-width: 1300px){.project-header .filter-box-component{width:50%}}@media (min-width: 768px) and (max-width: 1150px){.project-header .filter-box-component{width:100%;margin-top:10px}.project-header .filter-box-component .filter-box{max-width:100%}}@media (max-width: 768px){.project-header .filter-box-component{width:100%;margin-top:10px}.project-header .filter-box-component .filter-box{max-width:100%}}.project-header .filter-box-component form{margin:0}.views{display:inline-block;margin-right:10px;font-size:0.9em}@media (max-width: 560px){.views{width:100%}}@media (max-width: 768px){.views{margin-top:10px;font-size:1em}}@media (max-width: 480px){.views{margin-top:5px}}.views li{white-space:nowrap;background:#fafafa;border:1px solid #ddd;border-right:none;padding:4px 8px;display:inline}@media (max-width: 560px){.views li{display:block;margin-top:5px;border-radius:5px;border:1px solid #ddd}}.views li.active a{font-weight:bold;color:#000;text-decoration:none}.views li:first-child{border-top-left-radius:5px;border-bottom-left-radius:5px}.views li:last-child{border-right:1px solid #ddd;border-top-right-radius:5px;border-bottom-right-radius:5px}.views a{color:#555;text-decoration:none}.views a:hover{color:#333;text-decoration:underline}.dashboard-project-stats small{margin-right:10px;color:#999}.dashboard-table-link{font-weight:bold;color:#000;text-decoration:none}.dashboard-table-link:focus,.dashboard-table-link:hover{color:#999}.public-board{margin-top:5px}.public-task{max-width:800px;margin:5px auto 0}#board-container{overflow-x:auto}#board{table-layout:fixed;margin-bottom:0}#board th.board-column-header{width:240px}#board td{vertical-align:top}.board-container-compact{overflow-x:initial}@media all and (-ms-high-contrast: active), (-ms-high-contrast: none){.board-container-compact #board{table-layout:auto}}#board th.board-column-header.board-column-compact{width:initial}.board-column-collapsed{display:none}td.board-column-task-collapsed{font-weight:bold;background-color:#fbfbfb}#board th.board-column-header-collapsed{width:28px;min-width:28px;text-align:center;overflow:hidden}.board-rotation-wrapper{position:relative;padding:8px 4px;min-height:150px;overflow:hidden}.board-rotation{white-space:nowrap;-webkit-backface-visibility:hidden;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg);-webkit-transform-origin:0 100%;-moz-transform-origin:0 100%;-ms-transform-origin:0 100%;transform-origin:0 100%}.board-column-title .dropdown-menu{text-decoration:none}.board-add-icon{float:left;padding:0 5px}.board-add-icon i{text-decoration:none;color:#36c;font-size:1.4em}.board-add-icon i:focus,.board-add-icon i:hover{text-decoration:none;color:red}.board-column-header-task-count{color:#999;font-weight:normal}a.board-swimlane-toggle{text-decoration:none}a.board-swimlane-toggle:hover,a.board-swimlane-toggle:focus{color:#000;text-decoration:none;border:none}.board-task-list{min-height:60px}.board-task-list-limit{background-color:#DF5353}.draggable-item{cursor:pointer;user-select:none;-webkit-user-select:none;-moz-user-select:none}.draggable-placeholder{border:2px dashed #000;background:#fafafa;height:70px;margin-bottom:10px}div.draggable-item-selected{border:1px solid #000}.task-board-sort-handle{float:left;padding-right:5px}.task-board{position:relative;margin-bottom:4px;border:1px solid #000;padding:2px;word-wrap:break-word;font-size:0.9em;border-radius:6px}div.task-board-recent{border-width:2px}div.task-board-status-closed{user-select:none;border:1px dotted #555}.task-board a{color:#000;text-decoration:none}.task-board-collapsed{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.task-board-title{margin-top:5px;margin-bottom:8px}.task-board-title a:hover{text-decoration:underline}.task-board-saving-state{opacity:0.3}.task-board-saving-icon{position:absolute;margin:auto;width:100%;text-align:center;color:#000}.task-board-avatars{text-align:right;float:right}.task-board-change-assignee{cursor:pointer}.task-board-change-assignee:hover{opacity:0.6}.task-list-avatars{display:inline-block;float:left}@media (max-width: 768px){.task-list-avatars{float:none;display:block}}.task-list-avatars .task-avatar-assignee{font-weight:300;color:#999}.task-list-avatars:hover .task-avatar-assignee{font-weight:400;color:#000}.task-board-icons,.task-list-icons{font-size:0.8em;text-align:right}.task-board-icons a,.task-list-icons a{text-decoration:none}.task-board-icons a:hover,.task-list-icons a:hover{color:#333}.task-board-icons a:hover i,.task-list-icons a:hover i{color:#333}.task-board-icons .task-score,.task-list-icons .task-score{font-weight:bold}.task-board-icons .flag-milestone,.task-list-icons .flag-milestone{color:green}.task-board-icons{margin-top:7px}.task-board-icons a{opacity:0.5}.task-board-icons span{opacity:0.5;margin-left:4px}.task-board-icons a:hover{opacity:1.0;font-weight:bold}.task-board-icons .task-board-icons-row{line-height:22px}.task-list-icons{line-height:22px}.task-list-icons a,.task-list-icons span,.task-list-icons i{color:#999;opacity:1.0}.task-list-icons span{margin-left:5px}@media (max-width: 768px){.task-list-icons{text-align:left}}.task-icon-age{display:inline-block}span.task-icon-age-total{border:1px solid #e5e5e5;padding:1px 3px 1px 3px;border-top-left-radius:3px;border-bottom-left-radius:3px}span.task-icon-age-column{border:1px solid #e5e5e5;border-left:none;margin-left:-5px;padding:1px 3px 1px 3px;border-top-right-radius:3px;border-bottom-right-radius:3px}.task-board span.task-icon-age-total,.task-board span.task-icon-age-column{border-color:#666}.task-board-category-container{text-align:right;margin-top:8px;margin-bottom:8px}.task-board-category{border:1px solid #555;font-size:0.9em;font-weight:500;color:#000;padding:1px 3px 1px 2px;border-radius:3px}.task-board-category a:hover{text-decoration:underline}.task-date{font-weight:500;color:#000}span.task-date-today{opacity:1.0;color:#36c}span.task-date-overdue{opacity:1.0;color:#b94a48}.task-tags li{display:inline-block;margin:3px 3px 0 0;padding:1px 3px 1px 3px;color:#333;border:1px solid #333;border-radius:4px}.task-summary-container .task-tags{margin-top:10px}.task-list-tag{background:#ffeb8e;border-color:#333}#task-summary{margin-bottom:15px}#task-summary h2{color:#555;font-size:1.6em;margin-top:0;padding-top:0}.task-summary-container{border:2px solid #000;border-radius:8px;padding:15px}.task-summary-columns{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-justify-content:space-between;justify-content:space-between}@media (max-width: 480px){.task-summary-columns{display:block}}.task-summary-column{color:#333}.task-summary-column span{color:#555}.task-summary-column li{line-height:23px}#external-task-view{padding:10px;margin-top:10px;margin-bottom:10px;border:1px dotted #ccc}.task-form-container{box-sizing:border-box;display:flex;flex-wrap:wrap}.task-form-container>*{box-sizing:border-box}.task-form-container>*{width:1%}.task-form-main-column{width:60%}@media (max-width: 1000px){.task-form-main-column{width:100%}}.task-form-main-column input[type="text"]{width:700px;max-width:99%}.task-form-secondary-column{max-width:250px;min-width:200px;max-height:600px;padding-left:10px;overflow:auto;width:20%}@media (max-width: 1000px){.task-form-secondary-column{width:100%;max-width:99%;max-height:none}}@media (max-width: 768px){.task-form-secondary-column{padding-left:0}}.task-form-secondary-column label:first-child{margin-top:0}@media (max-width: 1000px){.task-form-secondary-column label:first-child{margin-top:10px}}.task-form-bottom{width:100%}.comment-sorting{text-align:right}.comment-sorting a{color:#555;font-weight:normal;text-decoration:none}.comment-sorting a:hover{color:#999}.comment{padding:5px;margin-bottom:15px}.comment-title{border-bottom:1px dotted #eee;margin-left:55px}.comment-date{color:#999;font-weight:200}.comment-actions{text-align:right}.comment-content{margin-left:55px}.comments .text-editor textarea{height:90px}.comments .text-editor .text-editor-preview-area{height:90px}.comments .comment-highlighted{background-color:#fff8dc;border:2px solid #ffeb8e}.comments .comment-highlighted:hover{background-color:#fff8dc}.comments .comment:hover{background:#fff8dc}.comments .comment:nth-child(even):not(.comment-highlighted){background:#fbfbfb}.comments .comment:nth-child(even):not(.comment-highlighted):hover{background:#fff8dc}.subtask-cell{padding:4px 10px;border-top:1px dotted #dedede;border-left:1px dotted #dedede;display:table-cell;vertical-align:middle}.subtask-cell a{color:#333;text-decoration:none}.subtask-cell a:hover,.subtask-cell a:focus{color:#36c}.subtask-cell:first-child{border-left:none}@media (max-width: 768px){.subtask-cell{width:90%;display:block;border-left:none}}.task-list-subtasks{display:table;width:100%}@media (max-width: 768px){.task-list-subtasks{display:block}}.task-list-subtask{display:table-row}@media (max-width: 768px){.task-list-subtask{display:block}}@media (max-width: 768px){.subtask-assignee,.subtask-time-tracking-cell{display:none}}.task-links-table td{vertical-align:middle}.task-links-task-count{color:#999;font-weight:normal}.task-link-closed{text-decoration:line-through}.text-editor{margin-top:10px}.text-editor a{font-size:1em;color:#999;text-decoration:none;margin-right:10px}.text-editor a:hover{color:#36c}.text-editor .text-editor-preview-area{border:1px solid #dedede;width:700px;max-width:99%;height:250px;overflow:auto;padding:2px}.text-editor textarea{width:700px;max-width:98%;height:250px}.markdown{line-height:1.4em}.markdown h1{margin-top:5px;margin-bottom:10px;font-weight:bold}.markdown h2{font-weight:bold}.markdown p{margin-bottom:10px}.markdown ol,.markdown ul{margin-left:25px;margin-top:10px;margin-bottom:10px}.markdown pre{background:#fbfbfb;padding:10px;border-radius:5px;border:1px solid #ddd;overflow:auto;color:#555}.markdown blockquote{font-style:italic;border-left:3px solid #ddd;padding-left:10px;margin-bottom:10px;margin-left:20px}.markdown img{display:block;max-width:80%;margin-top:10px}.documentation{margin:0 auto;padding:20px;max-width:850px;background:#fefefe;border:1px solid #ccc;border-radius:5px;color:#555}.documentation img{border:1px solid #333}.documentation h1{text-decoration:none;margin-bottom:30px}.documentation h2{text-decoration:none;border-bottom:1px solid #ccc;margin-bottom:25px}.documentation li{line-height:30px}.panel{border-radius:4px;padding:8px 35px 8px 10px;margin-top:10px;margin-bottom:15px;border:1px solid #ddd;color:#333;background-color:#fcfcfc;overflow:auto}.panel li{list-style-type:square;margin-left:20px;line-height:1.35em}.activity-event{margin-bottom:15px;padding:10px}.activity-event:hover{background:#fafafa}.activity-date{margin-left:10px;font-weight:normal;color:#999}.activity-content{margin-left:55px}.activity-title{font-weight:bold;color:#000;border-bottom:1px dotted #efefef}.activity-description{color:#555;margin-top:10px}@media (max-width: 480px){.activity-description{overflow:auto}}.activity-description li{list-style-type:circle}.activity-description ul{margin-top:10px;margin-left:20px}div.ganttview-hzheader-month,div.ganttview-hzheader-day,div.ganttview-vtheader,div.ganttview-vtheader-item-name,div.ganttview-vtheader-series,div.ganttview-grid,div.ganttview-grid-row-cell{float:left}div.ganttview-hzheader-month,div.ganttview-hzheader-day{text-align:center}div.ganttview-grid-row-cell.last,div.ganttview-hzheader-day.last,div.ganttview-hzheader-month.last{border-right:none}div.ganttview{border:1px solid #999}div.ganttview-hzheader-month{width:60px;height:20px;border-right:1px solid #d0d0d0;line-height:20px;overflow:hidden}div.ganttview-hzheader-day{width:20px;height:20px;border-right:1px solid #f0f0f0;border-top:1px solid #d0d0d0;line-height:20px;color:#555}div.ganttview-vtheader{margin-top:41px;width:400px;overflow:hidden;background-color:#fff}div.ganttview-vtheader-item{color:#555}div.ganttview-vtheader-series-name{width:400px;height:31px;line-height:31px;padding-left:3px;border-top:1px solid #d0d0d0;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}div.ganttview-vtheader-series-name a{color:#555;text-decoration:none}div.ganttview-vtheader-series-name a:hover{color:#333;text-decoration:underline}div.ganttview-vtheader-series-name a i{color:#000}div.ganttview-vtheader-series-name a:hover i{color:#555}div.ganttview-slide-container{overflow:auto;border-left:1px solid #999}div.ganttview-grid-row-cell{width:20px;height:31px;border-right:1px solid #f0f0f0;border-top:1px solid #f0f0f0}div.ganttview-grid-row-cell.ganttview-weekend{background-color:#fafafa}div.ganttview-blocks{margin-top:40px}div.ganttview-block-container{height:28px;padding-top:4px}div.ganttview-block{position:relative;height:25px;background-color:#E5ECF9;border:1px solid #c0c0c0;border-radius:3px}.ganttview-block-movable{cursor:move}div.ganttview-block-not-defined{border-color:#000;background-color:#000}div.ganttview-block-text{position:absolute;height:12px;font-size:0.7em;color:#999;padding:2px 3px}div.ganttview-block div.ui-resizable-handle.ui-resizable-s{bottom:-0}.user-mention-link{font-weight:bold;color:#000;text-decoration:none}.user-mention-link:hover{color:#555}.image-slideshow-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.95);overflow:auto;z-index:100}.image-slideshow-overlay img{display:block;margin:auto}.image-slideshow-overlay figcaption{color:#fff;opacity:0.7;position:absolute;bottom:5px;right:15px}.slideshow-icon{color:#fff;position:absolute;font-size:2.5em;opacity:0.6}.slideshow-icon:hover{opacity:0.9;cursor:pointer}.slideshow-previous-icon{left:10px;top:45%}.slideshow-next-icon{right:10px;top:45%}.slideshow-close-icon{right:10px;top:10px;font-size:1.4em}.slideshow-download-icon{left:10px;bottom:10px;font-size:1.3em} +h1,li,ul,ol,table,tr,td,th,p,blockquote,body{margin:0;padding:0;font-size:100%}body{padding-bottom:10px;color:#333;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;text-rendering:optimizeLegibility}small{font-size:0.8em}hr{border:0;height:0;border-top:1px solid rgba(0,0,0,0.1);border-bottom:1px solid rgba(255,255,255,0.3)}.page{margin-left:10px;margin-right:10px}.margin-top{margin-top:20px}.margin-bottom{margin-bottom:20px}.pull-right{text-align:right}ul.no-bullet li{list-style-type:none;margin-left:0}#app-loading-icon{position:fixed;right:3px;bottom:3px}.assign-me{vertical-align:bottom}a{color:#36c;border:none}a:focus{outline:0;color:#DF5353;text-decoration:none}a:hover{color:#333;text-decoration:none}a .fa{padding-right:3px;text-decoration:none;color:#333}h1,h2,h3{font-weight:normal;color:#333}h1{font-size:1.5em}h2{font-size:1.4em;margin-bottom:10px}h3{margin-top:10px;font-size:1.2em}table{width:100%;border-collapse:collapse;border-spacing:0;margin-bottom:20px}table.table-fixed{table-layout:fixed;white-space:nowrap}table.table-fixed th{overflow:hidden}table.table-fixed td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}table.table-small{font-size:0.8em}table.table-striped tr:nth-child(odd){background:#fefefe}@media (max-width: 768px){table.table-scrolling{overflow-x:auto;display:inline-block;vertical-align:top;max-width:100%;white-space:nowrap}}table th{text-align:left;padding:0.5em 3px;border:1px solid #eee;background:#fbfbfb}table th a{text-decoration:none;color:#333}table th a:focus,table th a:hover{text-decoration:underline}table td{border:1px solid #eee;padding:0.5em 3px;vertical-align:top}table td li{margin-left:20px}.column-1{width:1%}.column-2{width:2%}.column-3{width:3%}.column-4{width:4%}.column-5{width:5%}.column-6{width:6%}.column-7{width:7%}.column-8{width:8%}.column-9{width:9%}.column-10{width:10%}.column-11{width:11%}.column-12{width:12%}.column-13{width:13%}.column-14{width:14%}.column-15{width:15%}.column-16{width:16%}.column-17{width:17%}.column-18{width:18%}.column-19{width:19%}.column-20{width:20%}.column-21{width:21%}.column-22{width:22%}.column-23{width:23%}.column-24{width:24%}.column-25{width:25%}.column-26{width:26%}.column-27{width:27%}.column-28{width:28%}.column-29{width:29%}.column-30{width:30%}.column-31{width:31%}.column-32{width:32%}.column-33{width:33%}.column-34{width:34%}.column-35{width:35%}.column-36{width:36%}.column-37{width:37%}.column-38{width:38%}.column-39{width:39%}.column-40{width:40%}.column-41{width:41%}.column-42{width:42%}.column-43{width:43%}.column-44{width:44%}.column-45{width:45%}.column-46{width:46%}.column-47{width:47%}.column-48{width:48%}.column-49{width:49%}.column-50{width:50%}.column-51{width:51%}.column-52{width:52%}.column-53{width:53%}.column-54{width:54%}.column-55{width:55%}.column-56{width:56%}.column-57{width:57%}.column-58{width:58%}.column-59{width:59%}.column-60{width:60%}.column-61{width:61%}.column-62{width:62%}.column-63{width:63%}.column-64{width:64%}.column-65{width:65%}.column-66{width:66%}.column-67{width:67%}.column-68{width:68%}.column-69{width:69%}.column-70{width:70%}.column-71{width:71%}.column-72{width:72%}.column-73{width:73%}.column-74{width:74%}.column-75{width:75%}.column-76{width:76%}.column-77{width:77%}.column-78{width:78%}.column-79{width:79%}.column-80{width:80%}.column-81{width:81%}.column-82{width:82%}.column-83{width:83%}.column-84{width:84%}.column-85{width:85%}.column-86{width:86%}.column-87{width:87%}.column-88{width:88%}.column-89{width:89%}.column-90{width:90%}.column-91{width:91%}.column-92{width:92%}.column-93{width:93%}.column-94{width:94%}.column-95{width:95%}.column-96{width:96%}.column-97{width:97%}.column-98{width:98%}.column-99{width:99%}.column-100{width:100%}.draggable-row-handle{cursor:move;color:#dedede}.draggable-row-handle:hover{color:#333}tr.draggable-item-selected{background:#fff;border:2px solid #666;box-shadow:4px 2px 10px -4px rgba(0,0,0,0.55)}tr.draggable-item-selected td{border-top:none;border-bottom:none}tr.draggable-item-selected td:first-child{border-left:none}tr.draggable-item-selected td:last-child{border-right:none}.table-stripped tr.draggable-item-hover,.table-stripped tr.draggable-item-hover{background:#FEFFF2}.table-list{font-size:0.9em;margin-bottom:20px}.table-list-header{background:#fbfbfb;border:1px solid #e5e5e5;border-radius:5px 5px 0 0;line-height:35px;padding-left:3px;padding-right:3px}.table-list-header a{color:#333;font-weight:500;text-decoration:none;margin-right:10px}.table-list-header a:hover,.table-list-header a:focus{color:#767676}.table-list-header .table-list-header-count{color:#767676;display:inline-block;float:left}.table-list-header .table-list-header-menu{text-align:right}.table-list-row{padding-left:3px;padding-right:3px;border-bottom:1px solid #e5e5e5;border-right:1px solid #e5e5e5}.table-list-row.table-border-left{border-left:1px solid #e5e5e5}.table-list-row:nth-child(odd){background:#fefefe}.table-list-row:last-child{border-radius:0 0 5px 5px}.table-list-row:hover{background:#fff8dc;border-bottom:1px solid #ffeb8e;border-right:1px solid #ffeb8e}.table-list-row .table-list-title{font-weight:500;line-height:23px}.table-list-row .table-list-title.status-closed{text-decoration:line-through;margin-right:10px}.table-list-row .table-list-title.status-closed a{font-style:italic}.table-list-row .table-list-title a{color:#333;text-decoration:none}.table-list-row .table-list-title a:hover,.table-list-row .table-list-title a:focus{text-decoration:underline}.table-list-row .table-list-details{color:#999;font-weight:300;line-height:30px}.table-list-row .table-list-details span{margin-left:5px}.table-list-row .table-list-details span:first-child{margin-left:0}.table-list-row .table-list-details li{display:inline;list-style-type:none}.table-list-row .table-list-details li:after{content:', '}.table-list-row .table-list-details li:last-child:after{content:''}.table-list-row .table-list-details strong{font-weight:400;color:#555}.table-list-row .table-list-details-with-icons{float:left}@media (max-width: 768px){.table-list-row .table-list-details-with-icons{float:none}}.table-list-row .table-list-icons{font-size:0.8em;text-align:right;line-height:30px}@media (max-width: 768px){.table-list-row .table-list-icons{text-align:left;line-height:20px}}.table-list-row .table-list-icons span{margin-left:5px}.table-list-row .table-list-icons a{text-decoration:none}.table-list-row .table-list-icons a:hover{color:#333}.table-list-row .table-list-icons a:hover i{color:#333}.table-list-category{font-size:0.9em;font-weight:500;color:#000;padding:1px 2px 1px 2px;border-radius:3px;background:#fcfcfc;border:1px solid #ccc}.table-list-category a{text-decoration:none;color:#000}.table-list-category a:hover{color:#36c}fieldset{border:1px solid #ccc;margin-top:20px}legend{font-weight:500;font-size:1.2em}label{cursor:pointer;display:block;margin-top:10px;font-weight:400}input[type="number"],input[type="date"],input[type="email"],input[type="password"],input[type="text"]:not(.input-addon-field){color:#999;border:1px solid #ccc;width:300px;max-width:95%;font-size:1em;height:25px;padding-bottom:0;font-family:sans-serif;-webkit-appearance:none;-moz-appearance:none}input[type="number"]::-webkit-input-placeholder,input[type="date"]::-webkit-input-placeholder,input[type="email"]::-webkit-input-placeholder,input[type="password"]::-webkit-input-placeholder,input[type="text"]:not(.input-addon-field)::-webkit-input-placeholder{color:#dedede}input[type="number"]::-moz-placeholder,input[type="date"]::-moz-placeholder,input[type="email"]::-moz-placeholder,input[type="password"]::-moz-placeholder,input[type="text"]:not(.input-addon-field)::-moz-placeholder{color:#dedede}input[type="number"]:-ms-input-placeholder,input[type="date"]:-ms-input-placeholder,input[type="email"]:-ms-input-placeholder,input[type="password"]:-ms-input-placeholder,input[type="text"]:not(.input-addon-field):-ms-input-placeholder{color:#dedede}input[type="number"]:focus,input[type="date"]:focus,input[type="email"]:focus,input[type="password"]:focus,input[type="text"]:focus{color:#000;border-color:rgba(82,168,236,0.8);outline:0;box-shadow:0 0 8px rgba(82,168,236,0.6)}input[type="number"]{width:70px}input[type="text"]:not(.input-addon-field).form-numeric{width:70px}input[type="text"]:not(.input-addon-field).form-datetime,input[type="text"]:not(.input-addon-field).form-date{width:150px}input[type="text"]:not(.input-addon-field).form-input-large{width:400px}input[type="text"]:not(.input-addon-field).form-input-small{width:150px}textarea:focus{color:#000;border-color:rgba(82,168,236,0.8);outline:0;box-shadow:0 0 8px rgba(82,168,236,0.6)}textarea{padding:3px;border:1px solid #ccc;width:400px;max-width:99%;height:200px;font-family:sans-serif;font-size:1em}textarea::-webkit-input-placeholder{color:#dedede}textarea::-moz-placeholder{color:#dedede}textarea:-ms-input-placeholder{color:#dedede}select{font-size:1.0em;max-width:95%}select:focus{outline:0}select[multiple]{width:300px}.tag-autocomplete{width:400px}span.select2-container{margin-top:2px}.form-actions{padding-top:20px;clear:both}.form-required{color:red;padding-left:5px;font-weight:bold}@media (max-width: 480px){.form-required{display:none}}input.form-error,textarea.form-error{border:2px solid #b94a48}input.form-error:focus,textarea.form-error:focus{box-shadow:none;border:2px solid #b94a48}.form-errors{color:#b94a48;list-style-type:none}ul.form-errors li{margin-left:0}.form-help{font-size:0.8em;color:brown;margin-bottom:15px}.form-inline{padding:0;margin:0;border:none}.form-inline label{display:inline;padding-right:3px}.form-inline input,.form-inline select{margin:0 15px 0 0}.form-inline .form-required{display:none}.form-inline .form-actions{display:inline-block}.form-inline .js-submit-buttons-rendered{display:inline-block}.form-inline-group{display:inline}.form-columns{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:flex-start;justify-content:flex-start}.form-columns .form-column{margin-right:25px;flex-grow:1}.form-columns fieldset{margin-top:0}.form-login{max-width:350px;margin:5% auto 0}@media (max-width: 480px){.form-login{margin-left:5px}}.form-login li{margin-left:25px;line-height:25px}.form-login h2{margin-bottom:30px;font-weight:bold}.reset-password{margin-top:20px;margin-bottom:20px}.reset-password a{color:#999}.input-addon{display:flex}.input-addon-field{flex:1;font-size:1em;color:#999;margin:0;-webkit-appearance:none;-moz-appearance:none}.input-addon-field:first-child{border-radius:5px 0 0 5px}.input-addon-field:last-child{border-radius:0 5px 5px 0}.input-addon-item{background-color:rgba(147,128,108,0.1);color:#666;font:inherit;font-weight:normal}.input-addon-item:first-child{border-radius:5px 0 0 5px}.input-addon-item:last-child{border-radius:0 5px 5px 0}@media (max-width: 480px){.input-addon-item .dropdown .fa-caret-down{display:none}}.input-addon-field,.input-addon-item{border:1px solid rgba(147,128,108,0.25);padding:4px 0.75em}.input-addon-field:not(:first-child),.input-addon-item:not(:first-child){border-left:0}.icon-success{color:#468847}.icon-error{color:#b94a48}.icon-fade-out{opacity:1;animation:icon-fadeout 5s linear forwards}@keyframes icon-fadeout{0%{opacity:1}100%{opacity:0}}.alert{padding:8px 35px 8px 14px;margin-top:5px;margin-bottom:5px;color:#c09853;background-color:#fcf8e3;border:1px solid #fbeed5;border-radius:4px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-normal{color:#333;background-color:#f0f0f0;border-color:#ddd}.alert ul{margin-top:10px;margin-bottom:10px}.alert li{margin-left:25px}.alert-fade-out{text-align:center;position:fixed;bottom:0;left:20%;width:60%;padding-top:5px;padding-bottom:5px;margin-bottom:0;border-width:1px 0 0;border-radius:4px 4px 0 0;z-index:9999;opacity:1;animation:fadeout 5s linear forwards}@keyframes fadeout{0%{opacity:1}100%{opacity:0}}a.btn{text-decoration:none}.btn{-webkit-appearance:none;-moz-appearance:none;font-size:1.2em;font-weight:normal;cursor:pointer;display:inline-block;border-radius:2px;padding:3px 10px;margin:0;border:1px solid #ddd;background:#f5f5f5;color:#333}.btn:hover,.btn:focus{border-color:#bbb;background:#fafafa;color:#000}.btn-red{border-color:#b0281a;background:#d14836;color:#fff}.btn-red:hover,.btn-red:focus{border-color:#b0281a;background:#c53727;color:#fff}.btn-blue{border-color:#3079ed;background:#4d90fe;color:#fff}.btn-blue:hover,.btn-blue:focus{border-color:#3079ed;background:#357ae8;color:#fff}.btn:disabled{color:#ccc;border-color:#ccc;background:#f7f7f7}.buttons-header{font-size:0.8em;margin-top:5px;margin-bottom:15px}.tooltip-arrow:after{background:#fff;border:1px solid #aaaaaa;box-shadow:0 0 5px #aaa}div.ui-tooltip{min-width:200px;max-width:600px}.tooltip-arrow{width:20px;height:10px;overflow:hidden;position:absolute}.tooltip-arrow.top{top:-10px}.tooltip-arrow.bottom{bottom:-10px}.tooltip-arrow.align-left{left:10px}.tooltip-arrow.align-right{right:10px}.tooltip-arrow:after{content:"";position:absolute;width:14px;height:14px;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.tooltip-arrow.bottom:after{top:-10px}.tooltip-arrow.top:after{bottom:-10px}.tooltip-arrow.align-left:after{left:0}.tooltip-arrow.align-right:after{right:0}.tooltip-large{width:600px}.ui-tooltip-content .markdown p{margin-bottom:0}.tooltip .fa-info-circle{color:#999}h2 .dropdown ul{display:none}.dropdown{display:inline;position:relative}.dropdown ul{display:none}.dropdown-smaller{font-size:0.85em}ul.dropdown-submenu-open{display:block;position:absolute;z-index:1000;min-width:285px;list-style:none;margin:3px 0 0 1px;padding:6px 0;background-color:#fff;border:1px solid #b2b2b2;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,0.15)}.dropdown-submenu-open li{display:block;margin:0;padding:8px 10px;font-size:0.9em;border-bottom:1px solid #f8f8f8;cursor:pointer}.dropdown-submenu-open li.no-hover{cursor:default}.dropdown-submenu-open li:last-child{border:none}.dropdown-submenu-open li:not(.no-hover):hover{background:#4078C0;color:#fff}.dropdown-submenu-open li:hover a{color:#fff}.dropdown-submenu-open a{text-decoration:none;color:#333}.dropdown-submenu-open a:focus{text-decoration:underline}.dropdown-menu-link-text,.dropdown-menu-link-icon{color:#333;text-decoration:none}.dropdown-menu-link-text:hover{text-decoration:underline}td a.dropdown-menu strong{color:#333}td a.dropdown-menu strong i{color:#333}td a.dropdown-menu i{color:#dedede}td a.dropdown-menu:hover strong{color:#555}td a.dropdown-menu:hover strong i{color:#555}td a.dropdown-menu:hover i{color:#333}.accordion-title{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAADCAYAAABS3WWCAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NEQ5RDgxQzc2RjQ5MTFFMjhEMUNENzFGRUMwRjhBRTciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NEQ5RDgxQzg2RjQ5MTFFMjhEMUNENzFGRUMwRjhBRTciPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo0RDlEODFDNTZGNDkxMUUyOEQxQ0Q3MUZFQzBGOEFFNyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo0RDlEODFDNjZGNDkxMUUyOEQxQ0Q3MUZFQzBGOEFFNyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PvXFWFAAAAAYSURBVHjaYvj//z8D0/Pnz/8zgFgAAQYAS5UJscReGMIAAAAASUVORK5CYII=) repeat-x scroll 0 10px}.accordion-title h3{display:inline;padding-right:5px;background:#fff}.accordion-content{margin-top:15px;margin-bottom:25px}.accordion-toggle{color:#333;text-decoration:none}.accordion-toggle:focus{color:#333}.accordion-toggle:hover{color:#999}.accordion-toggle:before{content:"\f0d7"}.accordion-collapsed{margin-bottom:25px}.accordion-collapsed .accordion-toggle:before{content:"\f0da"}.accordion-collapsed .accordion-content{display:none}#select-dropdown-menu{position:absolute;display:block;z-index:1000;min-width:160px;padding:5px 0;background:#fff;list-style:none;border:1px solid #ccc;border-radius:3px;box-shadow:0 6px 12px rgba(0,0,0,0.175);overflow:scroll}.select-dropdown-menu-item{white-space:nowrap;overflow:hidden;padding:3px 10px;color:#555;cursor:pointer;border-bottom:1px solid #f8f8f8;line-height:1.5em;font-weight:400}.select-dropdown-menu-item.active{color:#fff;background:#428bca}.select-dropdown-menu-item:last-child{border:none}.select-dropdown-input-container{position:relative;border:1px solid #ccc;border-radius:5px;background-color:#fff}.select-dropdown-input-container input.select-dropdown-input{margin:0 0 0 5px;border:none;height:23px}.select-dropdown-input-container input.select-dropdown-input:focus{border:none;box-shadow:none}.select-dropdown-input-container .select-dropdown-chevron{color:#555;position:absolute;top:4px;right:5px;cursor:pointer}.select-dropdown-input-container .select-loading-icon{color:#555;position:absolute;top:4px;right:5px}#suggest-menu{position:absolute;display:block;z-index:1000;min-width:160px;padding:5px 0;background:#fff;list-style:none;border:1px solid #ccc;border-radius:3px;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.suggest-menu-item{white-space:nowrap;padding:3px 10px;color:#333;font-weight:bold;cursor:pointer}.suggest-menu-item.active{color:#fff;background:#428bca}.suggest-menu-item.active small{color:#fff}.suggest-menu-item small{color:#999;font-weight:normal}#modal-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.9);overflow:auto;z-index:100}#modal-box{position:fixed;max-height:calc(100% - 30px);top:2%;left:50%;transform:translateX(-50%);background:#fff;overflow:auto;border-radius:5px}#modal-content{padding:0 5px 5px}#modal-header{text-align:right;padding-right:5px}#modal-close-button{color:#333}#modal-close-button:hover{color:#b94a48}.pagination{text-align:center}.pagination-showing{margin-right:5px;padding-right:5px;border-right:1px solid #999}.pagination-next{margin-left:5px}.pagination-previous{margin-right:5px}header{display:flex;flex-wrap:wrap;padding:5px 10px;margin-bottom:5px;border-bottom:1px solid #dedede;background-color:#fbfbfb}header .title-container{flex:1;min-width:300px}@media (max-width: 768px){header .title-container{order:3}}header .board-selector-container{min-width:320px;display:flex;align-items:center}@media (max-width: 768px){header .board-selector-container{order:2}}header .menus-container{min-width:120px;display:flex;align-items:center;justify-content:flex-end}@media (max-width: 768px){header .menus-container{order:1;margin-bottom:5px;margin-left:auto}}header h1{font-size:1.5em}header h1 .tooltip{opacity:0.3;font-size:0.7em}a i.web-notification-icon{color:#36c}a i.web-notification-icon:focus,a i.web-notification-icon:hover{color:#000}.logo a{opacity:0.5;color:#d40000;text-decoration:none}.logo span{color:#333}.logo a:hover{opacity:0.8;color:#333}.logo a:focus span,.logo a:hover span{color:#d40000}.page-header{margin-bottom:20px}.page-header .dropdown{padding-right:10px}.page-header h2{margin:0;padding:0;font-weight:bold;border-bottom:1px dotted #ccc}.page-header h2 a{color:#333;text-decoration:none}.page-header h2 a:focus,.page-header h2 a:hover{color:#999}.page-header ul{text-align:left;margin-top:5px;display:inline-block}.page-header li{display:inline;padding-right:15px}@media (max-width: 480px){.page-header li{display:block;line-height:1.5em}}.page-header li.active a{color:#333;text-decoration:none;font-weight:bold}.page-header li.active a:hover,.page-header li.active a:focus{text-decoration:underline}.menu-inline{margin-bottom:5px}.menu-inline li{display:inline;padding-right:15px}.menu-inline li .active a{font-weight:bold;color:#000;text-decoration:none}.sidebar-container{height:100%;display:flex;flex-flow:row}@media (max-width: 768px){.sidebar-container{flex-flow:wrap}}.sidebar-content{padding-left:10px;flex:1 100%}@media (max-width: 768px){.sidebar-content{padding-left:0;order:1}}.sidebar{width:230px}@media (max-width: 768px){.sidebar{flex:1 auto;order:2}}.sidebar h2{margin-top:0}.sidebar>ul a{text-decoration:none;color:#999;font-weight:300}.sidebar>ul a:hover{color:#333}.sidebar>ul li{list-style-type:none;line-height:35px;border-bottom:1px dotted #efefef;padding-left:13px}.sidebar>ul li:hover{border-left:5px solid #555;padding-left:8px}.sidebar>ul li.active{border-left:5px solid #333;padding-left:8px}.sidebar>ul li.active a{color:#333;font-weight:bold}.sidebar-icons>ul li{padding-left:0}.sidebar-icons>ul li:hover,.sidebar-icons>ul li.active{padding-left:0;border-left:none}.sidebar>ul li.active a:focus,.sidebar>ul li.active a:hover{color:#555}.sidebar>ul li:last-child{margin-bottom:15px}.avatar img{vertical-align:bottom}.avatar-left{float:left;margin-right:10px}.avatar-inline{display:inline-block;margin-right:3px}.avatar-48 img,.avatar-48 div{border-radius:30px}.avatar-48 .avatar-letter{line-height:48px;width:48px;font-size:25px}.avatar-20 img,.avatar-20 div{border-radius:10px}.avatar-20 .avatar-letter{line-height:20px;width:20px;font-size:11px}.avatar-letter{color:#fff;text-align:center}#file-dropzone,#screenshot-zone{position:relative;border:2px dashed #ccc;width:99%;height:250px;overflow:auto}#file-dropzone-inner,#screenshot-inner{position:absolute;left:0;bottom:48%;width:100%;text-align:center;color:#aaa}#screenshot-zone.screenshot-pasted{border:2px solid #333}#file-list{margin:20px}#file-list li{list-style-type:none;padding-top:8px;padding-bottom:8px;border-bottom:1px dotted #ddd;width:95%}#file-list li .file-error{font-weight:bold;color:#b94a48}.file-thumbnails{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:flex-start;justify-content:flex-start}.file-thumbnail{width:250px;border:1px solid #efefef;border-radius:5px;margin-bottom:20px;box-shadow:4px 2px 10px -6px rgba(0,0,0,0.55);margin-right:15px}.file-thumbnail img{cursor:pointer;border-top-left-radius:5px;border-top-right-radius:5px}.file-thumbnail img:hover{opacity:0.5}.file-thumbnail-content{padding-left:8px;padding-right:8px}.file-thumbnail-title{font-weight:700;font-size:0.9em;color:#555;overflow:hidden;text-overflow:ellipsis}.file-thumbnail-description{font-size:0.8em;color:#999;margin-top:8px;margin-bottom:5px}.file-viewer{position:relative}.file-viewer img{max-width:95%;max-height:85%;margin-top:10px}.color-picker{width:180px}.color-picker-option{height:25px}.color-picker-square{display:inline-block;width:18px;height:18px;margin-right:5px;border:1px solid #000}.color-picker-label{display:inline-block;vertical-align:bottom;padding-bottom:3px}.filter-box{max-width:1024px}.action-menu{color:#333;text-decoration:none}.action-menu:hover,.action-menu:focus{text-decoration:underline}.js-project-creation-options{max-width:500px;border-left:3px dotted #efefef;margin-top:20px;padding-left:15px;padding-bottom:5px;padding-top:5px}.project-overview-columns{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center;margin-bottom:20px;font-size:1.4em}@media (max-width: 480px){.project-overview-columns{display:block}}.project-overview-column{text-align:center;margin-right:3%;margin-top:5px;padding:3px 15px 3px 15px;border:1px dashed #ddd}@media (max-width: 480px){.project-overview-column{text-align:left}}.project-overview-column small{color:#999}.project-overview-column strong{color:#555;display:block}@media (max-width: 480px){.project-overview-column strong{display:inline}}.project-header{margin-bottom:8px}.project-header .dropdown-component{margin-top:4px;margin-right:5px;float:left}@media (max-width: 768px){.project-header .dropdown-component{float:none}}.project-header .views-switcher-component{margin-top:4px;float:left}@media (max-width: 768px){.project-header .views-switcher-component{float:none;margin-bottom:10px}}.project-header .filter-box-component form{margin:0}.views{margin-right:10px;margin-top:1px;font-size:0.9em}@media (max-width: 560px){.views{width:100%}}@media (max-width: 768px){.views{margin-top:10px;font-size:1em}}@media (max-width: 480px){.views{margin-top:5px}}.views li{white-space:nowrap;background:#fafafa;border:1px solid #ddd;border-right:none;padding:4px 8px;display:inline}@media (max-width: 560px){.views li{display:block;margin-top:5px;border-radius:5px;border:1px solid #ddd}}.views li.active a{font-weight:bold;color:#000;text-decoration:none}.views li:first-child{border-top-left-radius:5px;border-bottom-left-radius:5px}.views li:last-child{border-right:1px solid #ddd;border-top-right-radius:5px;border-bottom-right-radius:5px}.views a{color:#555;text-decoration:none}.views a:hover{color:#333;text-decoration:underline}.dashboard-project-stats small{margin-right:10px;color:#999}.dashboard-table-link{font-weight:bold;color:#000;text-decoration:none}.dashboard-table-link:focus,.dashboard-table-link:hover{color:#999}.public-board{margin-top:5px}.public-task{max-width:800px;margin:5px auto 0}#board-container{overflow-x:auto}#board{table-layout:fixed;margin-bottom:0}#board th.board-column-header{width:240px}#board td{vertical-align:top}.board-container-compact{overflow-x:initial}@media all and (-ms-high-contrast: active), (-ms-high-contrast: none){.board-container-compact #board{table-layout:auto}}#board th.board-column-header.board-column-compact{width:initial}.board-column-collapsed{display:none}td.board-column-task-collapsed{font-weight:bold;background-color:#fbfbfb}#board th.board-column-header-collapsed{width:28px;min-width:28px;text-align:center;overflow:hidden}.board-rotation-wrapper{position:relative;padding:8px 4px;min-height:150px;overflow:hidden}.board-rotation{white-space:nowrap;-webkit-backface-visibility:hidden;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg);-webkit-transform-origin:0 100%;-moz-transform-origin:0 100%;-ms-transform-origin:0 100%;transform-origin:0 100%}.board-column-title .dropdown-menu{text-decoration:none}.board-add-icon{float:left;padding:0 5px}.board-add-icon i{text-decoration:none;color:#36c;font-size:1.4em}.board-add-icon i:focus,.board-add-icon i:hover{text-decoration:none;color:red}.board-column-header-task-count{color:#999;font-weight:normal}a.board-swimlane-toggle{text-decoration:none}a.board-swimlane-toggle:hover,a.board-swimlane-toggle:focus{color:#000;text-decoration:none;border:none}.board-task-list{min-height:60px}.board-task-list-limit{background-color:#DF5353}.draggable-item{cursor:pointer;user-select:none;-webkit-user-select:none;-moz-user-select:none}.draggable-placeholder{border:2px dashed #000;background:#fafafa;height:70px;margin-bottom:10px}div.draggable-item-selected{border:1px solid #000}.task-board-sort-handle{float:left;padding-right:5px}.task-board{position:relative;margin-bottom:4px;border:1px solid #000;padding:2px;word-wrap:break-word;font-size:0.9em;border-radius:6px}div.task-board-recent{border-width:2px}div.task-board-status-closed{user-select:none;border:1px dotted #555}.task-board a{color:#000;text-decoration:none}.task-board-collapsed{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.task-board-title{margin-top:5px;margin-bottom:8px}.task-board-title a:hover{text-decoration:underline}.task-board-saving-state{opacity:0.3}.task-board-saving-icon{position:absolute;margin:auto;width:100%;text-align:center;color:#000}.task-board-avatars{text-align:right;float:right}.task-board-change-assignee{cursor:pointer}.task-board-change-assignee:hover{opacity:0.6}.task-list-avatars{display:inline-block;float:left}@media (max-width: 768px){.task-list-avatars{float:none;display:block}}.task-list-avatars .task-avatar-assignee{font-weight:300;color:#999}.task-list-avatars:hover .task-avatar-assignee{font-weight:400;color:#000}.task-board-icons,.task-list-icons{font-size:0.8em;text-align:right}.task-board-icons a,.task-list-icons a{text-decoration:none}.task-board-icons a:hover,.task-list-icons a:hover{color:#333}.task-board-icons a:hover i,.task-list-icons a:hover i{color:#333}.task-board-icons .task-score,.task-list-icons .task-score{font-weight:bold}.task-board-icons .flag-milestone,.task-list-icons .flag-milestone{color:green}.task-board-icons{margin-top:7px}.task-board-icons a{opacity:0.5}.task-board-icons span{opacity:0.5;margin-left:4px}.task-board-icons a:hover{opacity:1.0;font-weight:bold}.task-board-icons .task-board-icons-row{line-height:22px}.task-list-icons{line-height:22px}.task-list-icons a,.task-list-icons span,.task-list-icons i{color:#999;opacity:1.0}.task-list-icons span{margin-left:5px}@media (max-width: 768px){.task-list-icons{text-align:left}}.task-icon-age{display:inline-block}span.task-icon-age-total{border:1px solid #e5e5e5;padding:1px 3px 1px 3px;border-top-left-radius:3px;border-bottom-left-radius:3px}span.task-icon-age-column{border:1px solid #e5e5e5;border-left:none;margin-left:-5px;padding:1px 3px 1px 3px;border-top-right-radius:3px;border-bottom-right-radius:3px}.task-board span.task-icon-age-total,.task-board span.task-icon-age-column{border-color:#666}.task-board-category-container{text-align:right;margin-top:8px;margin-bottom:8px}.task-board-category{border:1px solid #555;font-size:0.9em;font-weight:500;color:#000;padding:1px 3px 1px 2px;border-radius:3px}.task-board-category a:hover{text-decoration:underline}.task-date{font-weight:500;color:#000}span.task-date-today{opacity:1.0;color:#36c}span.task-date-overdue{opacity:1.0;color:#b94a48}.task-tags li{display:inline-block;margin:3px 3px 0 0;padding:1px 3px 1px 3px;color:#333;border:1px solid #333;border-radius:4px}.task-summary-container .task-tags{margin-top:10px}.task-list-tag{background:#ffeb8e;border-color:#333}#task-summary{margin-bottom:15px}#task-summary h2{color:#555;font-size:1.6em;margin-top:0;padding-top:0}.task-summary-container{border:2px solid #000;border-radius:8px;padding:10px}.task-summary-columns{display:flex;flex-flow:row;justify-content:space-between}@media (max-width: 768px){.task-summary-columns{flex-flow:column}}.task-summary-column{color:#333}.task-summary-column span{color:#555}.task-summary-column li{line-height:23px}#external-task-view{padding:10px;margin-top:10px;margin-bottom:10px;border:1px dotted #ccc}.task-form-container{box-sizing:border-box;display:flex;flex-wrap:wrap}.task-form-container>*{box-sizing:border-box}.task-form-container>*{width:1%}.task-form-main-column{width:60%}@media (max-width: 1000px){.task-form-main-column{width:100%}}.task-form-main-column input[type="text"]{width:700px;max-width:99%}.task-form-secondary-column{max-width:250px;min-width:200px;max-height:600px;padding-left:10px;overflow:auto;width:20%}@media (max-width: 1000px){.task-form-secondary-column{width:100%;max-width:99%;max-height:none}}@media (max-width: 768px){.task-form-secondary-column{padding-left:0}}.task-form-secondary-column label:first-child{margin-top:0}@media (max-width: 1000px){.task-form-secondary-column label:first-child{margin-top:10px}}.task-form-bottom{width:100%}.comment-sorting{text-align:right}.comment-sorting a{color:#555;font-weight:normal;text-decoration:none}.comment-sorting a:hover{color:#999}.comment{padding:5px;margin-bottom:15px}.comment-title{border-bottom:1px dotted #eee;margin-left:55px}.comment-date{color:#999;font-weight:200}.comment-actions{text-align:right}.comment-content{margin-left:55px}.comments .text-editor textarea{height:90px}.comments .text-editor .text-editor-preview-area{height:90px}.comments .comment-highlighted{background-color:#fff8dc;border:2px solid #ffeb8e}.comments .comment-highlighted:hover{background-color:#fff8dc}.comments .comment:hover{background:#fff8dc}.comments .comment:nth-child(even):not(.comment-highlighted){background:#fbfbfb}.comments .comment:nth-child(even):not(.comment-highlighted):hover{background:#fff8dc}.subtask-cell{padding:4px 10px;border-top:1px dotted #dedede;border-left:1px dotted #dedede;display:table-cell;vertical-align:middle}.subtask-cell a{color:#333;text-decoration:none}.subtask-cell a:hover,.subtask-cell a:focus{color:#36c}.subtask-cell:first-child{border-left:none}@media (max-width: 768px){.subtask-cell{width:90%;display:block;border-left:none}}.task-list-subtasks{display:table;width:100%}@media (max-width: 768px){.task-list-subtasks{display:block}}.task-list-subtask{display:table-row}@media (max-width: 768px){.task-list-subtask{display:block}}@media (max-width: 768px){.subtask-assignee,.subtask-time-tracking-cell{display:none}}.task-links-table td{vertical-align:middle}.task-links-task-count{color:#999;font-weight:normal}.task-link-closed{text-decoration:line-through}.text-editor{margin-top:10px}.text-editor a{font-size:1em;color:#999;text-decoration:none;margin-right:10px}.text-editor a:hover{color:#36c}.text-editor .text-editor-preview-area{border:1px solid #dedede;width:700px;max-width:99%;height:250px;overflow:auto;padding:2px}.text-editor textarea{width:700px;max-width:98%;height:250px}.markdown{line-height:1.4em}.markdown h1{margin-top:5px;margin-bottom:10px;font-weight:bold}.markdown h2{font-weight:bold}.markdown p{margin-bottom:10px}.markdown ol,.markdown ul{margin-left:25px;margin-top:10px;margin-bottom:10px}.markdown pre{background:#fbfbfb;padding:10px;border-radius:5px;border:1px solid #ddd;overflow:auto;color:#555}.markdown blockquote{font-style:italic;border-left:3px solid #ddd;padding-left:10px;margin-bottom:10px;margin-left:20px}.markdown img{display:block;max-width:80%;margin-top:10px}.documentation{margin:0 auto;padding:20px;max-width:850px;background:#fefefe;border:1px solid #ccc;border-radius:5px;color:#555}.documentation img{border:1px solid #333}.documentation h1{text-decoration:none;margin-bottom:30px}.documentation h2{text-decoration:none;border-bottom:1px solid #ccc;margin-bottom:25px}.documentation li{line-height:30px}.panel{border-radius:4px;padding:8px 35px 8px 10px;margin-top:10px;margin-bottom:15px;border:1px solid #ddd;color:#333;background-color:#fcfcfc;overflow:auto}.panel li{list-style-type:square;margin-left:20px;line-height:1.35em}.activity-event{margin-bottom:15px;padding:10px}.activity-event:nth-child(even){background:#fafafa}.activity-event:hover{background:#fff8dc}.activity-date{margin-left:10px;font-weight:normal;color:#999}.activity-content{margin-left:55px}.activity-title{font-weight:bold;color:#000;border-bottom:1px dotted #efefef}.activity-description{color:#555;margin-top:10px}@media (max-width: 480px){.activity-description{overflow:auto}}.activity-description li{list-style-type:circle}.activity-description ul{margin-top:10px;margin-left:20px}.user-mention-link{font-weight:bold;color:#000;text-decoration:none}.user-mention-link:hover{color:#555}.image-slideshow-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.95);overflow:auto;z-index:100}.image-slideshow-overlay img{display:block;margin:auto}.image-slideshow-overlay figcaption{color:#fff;opacity:0.7;position:absolute;bottom:5px;right:15px}.slideshow-icon{color:#fff;position:absolute;font-size:2.5em;opacity:0.6}.slideshow-icon:hover{opacity:0.9;cursor:pointer}.slideshow-previous-icon{left:10px;top:45%}.slideshow-next-icon{right:10px;top:45%}.slideshow-close-icon{right:10px;top:10px;font-size:1.4em}.slideshow-download-icon{left:10px;bottom:10px;font-size:1.3em} diff --git a/assets/css/images/ui-icons_444444_256x240.png b/assets/css/images/ui-icons_444444_256x240.png Binary files differindex 4d42e485..618770ef 100644 --- a/assets/css/images/ui-icons_444444_256x240.png +++ b/assets/css/images/ui-icons_444444_256x240.png diff --git a/assets/css/images/ui-icons_555555_256x240.png b/assets/css/images/ui-icons_555555_256x240.png Binary files differindex fefed751..fce05401 100644 --- a/assets/css/images/ui-icons_555555_256x240.png +++ b/assets/css/images/ui-icons_555555_256x240.png diff --git a/assets/css/images/ui-icons_777620_256x240.png b/assets/css/images/ui-icons_777620_256x240.png Binary files differindex 5c367e87..c896b8d1 100644 --- a/assets/css/images/ui-icons_777620_256x240.png +++ b/assets/css/images/ui-icons_777620_256x240.png diff --git a/assets/css/images/ui-icons_777777_256x240.png b/assets/css/images/ui-icons_777777_256x240.png Binary files differindex 7a9d6daf..36e7a1ca 100644 --- a/assets/css/images/ui-icons_777777_256x240.png +++ b/assets/css/images/ui-icons_777777_256x240.png diff --git a/assets/css/images/ui-icons_cc0000_256x240.png b/assets/css/images/ui-icons_cc0000_256x240.png Binary files differindex b3abecbc..5bd316c5 100644 --- a/assets/css/images/ui-icons_cc0000_256x240.png +++ b/assets/css/images/ui-icons_cc0000_256x240.png diff --git a/assets/css/images/ui-icons_ffffff_256x240.png b/assets/css/images/ui-icons_ffffff_256x240.png Binary files differindex ee392c5a..2cbe10f0 100644 --- a/assets/css/images/ui-icons_ffffff_256x240.png +++ b/assets/css/images/ui-icons_ffffff_256x240.png diff --git a/assets/css/vendor.min.css b/assets/css/vendor.min.css index ccd091ae..f28a108d 100644 --- a/assets/css/vendor.min.css +++ b/assets/css/vendor.min.css @@ -13,13 +13,8 @@ .select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single .select2-selection__clear{position:relative}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-search--inline{float:left}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;padding:0}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select2-results{display:block}.select2-results__options{list-style:none;margin:0;padding:0}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none}.select2-results__option[aria-selected]{cursor:pointer}.select2-container--open .select2-dropdown{left:0}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-search--dropdown{display:block;padding:4px}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-search--dropdown.select2-search--hide{display:none}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0)}.select2-hidden-accessible{border:0 !important;clip:rect(0 0 0 0) !important;height:1px !important;margin:-1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;width:1px !important}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text}.select2-container--default .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%}.select2-container--default .select2-selection--multiple .select2-selection__placeholder{color:#999;margin-top:5px;float:left}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#333}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline{float:right}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--default.select2-container--focus .select2-selection--multiple{border:solid black 1px;outline:0}.select2-container--default.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection__choice__remove{display:none}.select2-container--default.select2-container--open.select2-container--above .select2-selection--single,.select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple{border-top-left-radius:0;border-top-right-radius:0}.select2-container--default.select2-container--open.select2-container--below .select2-selection--single,.select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--default .select2-search--dropdown .select2-search__field{border:1px solid #aaa}.select2-container--default .select2-search--inline .select2-search__field{background:transparent;border:none;outline:0;box-shadow:none;-webkit-appearance:textfield}.select2-container--default .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--default .select2-results__option[role=group]{padding:0}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option .select2-results__option{padding-left:1em}.select2-container--default .select2-results__option .select2-results__option .select2-results__group{padding-left:0}.select2-container--default .select2-results__option .select2-results__option .select2-results__option{margin-left:-1em;padding-left:2em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-2em;padding-left:3em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-3em;padding-left:4em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-4em;padding-left:5em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-5em;padding-left:6em}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#5897fb;color:white}.select2-container--default .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic .select2-selection--single{background-color:#f7f7f7;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #fff 50%, #eee 100%);background-image:-o-linear-gradient(top, #fff 50%, #eee 100%);background-image:linear-gradient(to bottom, #fff 50%, #eee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic .select2-selection--single:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--classic .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-right:10px}.select2-container--classic .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--classic .select2-selection--single .select2-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eee 50%, #ccc 100%);background-image:-o-linear-gradient(top, #eee 50%, #ccc 100%);background-image:linear-gradient(to bottom, #eee 50%, #ccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0)}.select2-container--classic .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto}.select2-container--classic.select2-container--open .select2-selection--single{border:1px solid #5897fb}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow{background:transparent;border:none}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #fff 0%, #eee 50%);background-image:-o-linear-gradient(top, #fff 0%, #eee 50%);background-image:linear-gradient(to bottom, #fff 0%, #eee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eee 50%, #fff 100%);background-image:-o-linear-gradient(top, #eee 50%, #fff 100%);background-image:linear-gradient(to bottom, #eee 50%, #fff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0)}.select2-container--classic .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0}.select2-container--classic .select2-selection--multiple:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--multiple .select2-selection__rendered{list-style:none;margin:0;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__clear{display:none}.select2-container--classic .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover{color:#555}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{float:right}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--classic.select2-container--open .select2-selection--multiple{border:1px solid #5897fb}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--classic .select2-search--dropdown .select2-search__field{border:1px solid #aaa;outline:0}.select2-container--classic .select2-search--inline .select2-search__field{outline:0;box-shadow:none}.select2-container--classic .select2-dropdown{background-color:#fff;border:1px solid transparent}.select2-container--classic .select2-dropdown--above{border-bottom:none}.select2-container--classic .select2-dropdown--below{border-top:none}.select2-container--classic .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--classic .select2-results__option[role=group]{padding:0}.select2-container--classic .select2-results__option[aria-disabled=true]{color:grey}.select2-container--classic .select2-results__option--highlighted[aria-selected]{background-color:#3875d7;color:#fff}.select2-container--classic .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic.select2-container--open .select2-dropdown{border-color:#5897fb} /*! - * FullCalendar v3.1.0 Stylesheet - * Docs & License: http://fullcalendar.io/ - * (c) 2016 Adam Shaw - */.fc-icon,body .fc{font-size:1em}.fc-button-group,.fc-icon{display:inline-block}.fc-bg,.fc-row .fc-bgevent-skeleton,.fc-row .fc-highlight-skeleton{bottom:0}.fc-icon,.fc-unselectable{-khtml-user-select:none;-webkit-touch-callout:none}.fc{direction:ltr;text-align:left}.fc-rtl{text-align:right}.fc th,.fc-basic-view td.fc-week-number,.fc-icon,.fc-toolbar{text-align:center}.fc-unthemed .fc-content,.fc-unthemed .fc-divider,.fc-unthemed .fc-list-heading td,.fc-unthemed .fc-list-view,.fc-unthemed .fc-popover,.fc-unthemed .fc-row,.fc-unthemed tbody,.fc-unthemed td,.fc-unthemed th,.fc-unthemed thead{border-color:#ddd}.fc-unthemed .fc-popover{background-color:#fff}.fc-unthemed .fc-divider,.fc-unthemed .fc-list-heading td,.fc-unthemed .fc-popover .fc-header{background:#eee}.fc-unthemed .fc-popover .fc-header .fc-close{color:#666}.fc-unthemed td.fc-today{background:#fcf8e3}.fc-highlight{background:#bce8f1;opacity:.3}.fc-bgevent{background:#8fdf82;opacity:.3}.fc-nonbusiness{background:#d7d7d7}.fc-icon{height:1em;line-height:1em;overflow:hidden;font-family:"Courier New",Courier,monospace;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.fc-icon:after{position:relative}.fc-icon-left-single-arrow:after{content:"\02039";font-weight:700;font-size:200%;top:-7%}.fc-icon-right-single-arrow:after{content:"\0203A";font-weight:700;font-size:200%;top:-7%}.fc-icon-left-double-arrow:after{content:"\000AB";font-size:160%;top:-7%}.fc-icon-right-double-arrow:after{content:"\000BB";font-size:160%;top:-7%}.fc-icon-left-triangle:after{content:"\25C4";font-size:125%;top:3%}.fc-icon-right-triangle:after{content:"\25BA";font-size:125%;top:3%}.fc-icon-down-triangle:after{content:"\25BC";font-size:125%;top:2%}.fc-icon-x:after{content:"\000D7";font-size:200%;top:6%}.fc button{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;height:2.1em;padding:0 .6em;font-size:1em;white-space:nowrap;cursor:pointer}.fc button::-moz-focus-inner{margin:0;padding:0}.fc-state-default{border:1px solid;background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05)}.fc-state-default.fc-corner-left{border-top-left-radius:4px;border-bottom-left-radius:4px}.fc-state-default.fc-corner-right{border-top-right-radius:4px;border-bottom-right-radius:4px}.fc button .fc-icon{position:relative;top:-.05em;margin:0 .2em;vertical-align:middle}.fc-state-active,.fc-state-disabled,.fc-state-down,.fc-state-hover{color:#333;background-color:#e6e6e6}.fc-state-hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.fc-state-active,.fc-state-down{background-color:#ccc;background-image:none;box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.fc-state-disabled{cursor:default;background-image:none;opacity:.65;box-shadow:none}.fc-event.fc-draggable,.fc-event[href],.fc-popover .fc-header .fc-close,a[data-goto]{cursor:pointer}.fc .fc-button-group>*{float:left;margin:0 0 0 -1px}.fc .fc-button-group>:first-child{margin-left:0}.fc-popover{position:absolute;box-shadow:0 2px 6px rgba(0,0,0,.15)}.fc-popover .fc-header{padding:2px 4px}.fc-popover .fc-header .fc-title{margin:0 2px}.fc-ltr .fc-popover .fc-header .fc-title,.fc-rtl .fc-popover .fc-header .fc-close{float:left}.fc-ltr .fc-popover .fc-header .fc-close,.fc-rtl .fc-popover .fc-header .fc-title{float:right}.fc-unthemed .fc-popover{border-width:1px;border-style:solid}.fc-unthemed .fc-popover .fc-header .fc-close{font-size:.9em;margin-top:2px}.fc-popover>.ui-widget-header+.ui-widget-content{border-top:0}.fc-divider{border-style:solid;border-width:1px}hr.fc-divider{height:0;margin:0;padding:0 0 2px;border-width:1px 0}.fc-bg table,.fc-row .fc-bgevent-skeleton table,.fc-row .fc-highlight-skeleton table{height:100%}.fc-clear{clear:both}.fc-bg,.fc-bgevent-skeleton,.fc-helper-skeleton,.fc-highlight-skeleton{position:absolute;top:0;left:0;right:0}.fc table{width:100%;box-sizing:border-box;table-layout:fixed;border-collapse:collapse;border-spacing:0;font-size:1em}.fc td,.fc th{border-style:solid;border-width:1px;padding:0;vertical-align:top}.fc td.fc-today{border-style:double}a[data-goto]:hover{text-decoration:underline}.fc .fc-row{border-style:solid;border-width:0}.fc-row table{border-left:0 hidden transparent;border-right:0 hidden transparent;border-bottom:0 hidden transparent}.fc-row:first-child table{border-top:0 hidden transparent}.fc-row{position:relative}.fc-row .fc-bg{z-index:1}.fc-row .fc-bgevent-skeleton td,.fc-row .fc-highlight-skeleton td{border-color:transparent}.fc-row .fc-bgevent-skeleton{z-index:2}.fc-row .fc-highlight-skeleton{z-index:3}.fc-row .fc-content-skeleton{position:relative;z-index:4;padding-bottom:2px}.fc-row .fc-helper-skeleton{z-index:5}.fc-row .fc-content-skeleton td,.fc-row .fc-helper-skeleton td{background:0 0;border-color:transparent;border-bottom:0}.fc-row .fc-content-skeleton tbody td,.fc-row .fc-helper-skeleton tbody td{border-top:0}.fc-scroller{-webkit-overflow-scrolling:touch}.fc-row.fc-rigid,.fc-time-grid-event{overflow:hidden}.fc-scroller>.fc-day-grid,.fc-scroller>.fc-time-grid{position:relative;width:100%}.fc-event{position:relative;display:block;font-size:.85em;line-height:1.3;border-radius:3px;border:1px solid #3a87ad;font-weight:400}.fc-event,.fc-event-dot{background-color:#3a87ad}.fc-event,.fc-event:hover,.ui-widget .fc-event{color:#fff;text-decoration:none}.fc-not-allowed,.fc-not-allowed .fc-event{cursor:not-allowed}.fc-event .fc-bg{z-index:1;background:#fff;opacity:.25}.fc-event .fc-content{position:relative;z-index:2}.fc-event .fc-resizer{position:absolute;z-index:4;display:none}.fc-event.fc-allow-mouse-resize .fc-resizer,.fc-event.fc-selected .fc-resizer{display:block}.fc-event.fc-selected .fc-resizer:before{content:"";position:absolute;z-index:9999;top:50%;left:50%;width:40px;height:40px;margin-left:-20px;margin-top:-20px}.fc-event.fc-selected{z-index:9999!important;box-shadow:0 2px 5px rgba(0,0,0,.2)}.fc-event.fc-selected.fc-dragging{box-shadow:0 2px 7px rgba(0,0,0,.3)}.fc-h-event.fc-selected:before{content:"";position:absolute;z-index:3;top:-10px;bottom:-10px;left:0;right:0}.fc-ltr .fc-h-event.fc-not-start,.fc-rtl .fc-h-event.fc-not-end{margin-left:0;border-left-width:0;padding-left:1px;border-top-left-radius:0;border-bottom-left-radius:0}.fc-ltr .fc-h-event.fc-not-end,.fc-rtl .fc-h-event.fc-not-start{margin-right:0;border-right-width:0;padding-right:1px;border-top-right-radius:0;border-bottom-right-radius:0}.fc-ltr .fc-h-event .fc-start-resizer,.fc-rtl .fc-h-event .fc-end-resizer{cursor:w-resize;left:-1px}.fc-ltr .fc-h-event .fc-end-resizer,.fc-rtl .fc-h-event .fc-start-resizer{cursor:e-resize;right:-1px}.fc-h-event.fc-allow-mouse-resize .fc-resizer{width:7px;top:-1px;bottom:-1px}.fc-h-event.fc-selected .fc-resizer{border-radius:4px;border-width:1px;width:6px;height:6px;border-style:solid;border-color:inherit;background:#fff;top:50%;margin-top:-4px}.fc-ltr .fc-h-event.fc-selected .fc-start-resizer,.fc-rtl .fc-h-event.fc-selected .fc-end-resizer{margin-left:-4px}.fc-ltr .fc-h-event.fc-selected .fc-end-resizer,.fc-rtl .fc-h-event.fc-selected .fc-start-resizer{margin-right:-4px}.fc-day-grid-event{margin:1px 2px 0;padding:0 1px}tr:first-child>td>.fc-day-grid-event{margin-top:2px}.fc-day-grid-event.fc-selected:after{content:"";position:absolute;z-index:1;top:-1px;right:-1px;bottom:-1px;left:-1px;background:#000;opacity:.25}.fc-day-grid-event .fc-content{white-space:nowrap;overflow:hidden}.fc-day-grid-event .fc-time{font-weight:700}.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer,.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer{margin-left:-2px}.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer,.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer{margin-right:-2px}a.fc-more{margin:1px 3px;font-size:.85em;cursor:pointer;text-decoration:none}a.fc-more:hover{text-decoration:underline}.fc-limited{display:none}.fc-day-grid .fc-row{z-index:1}.fc-more-popover{z-index:2;width:220px}.fc-more-popover .fc-event-container{padding:10px}.fc-now-indicator{position:absolute;border:0 solid red}.fc-unselectable{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}.fc-toolbar.fc-header-toolbar{margin-bottom:1em}.fc-toolbar.fc-footer-toolbar{margin-top:1em}.fc-toolbar .fc-left{float:left}.fc-toolbar .fc-right{float:right}.fc-toolbar .fc-center{display:inline-block}.fc .fc-toolbar>*>*{float:left;margin-left:.75em}.fc .fc-toolbar>*>:first-child{margin-left:0}.fc-toolbar h2{margin:0}.fc-toolbar button{position:relative}.fc-toolbar .fc-state-hover,.fc-toolbar .ui-state-hover{z-index:2}.fc-toolbar .fc-state-down{z-index:3}.fc-toolbar .fc-state-active,.fc-toolbar .ui-state-active{z-index:4}.fc-toolbar button:focus{z-index:5}.fc-view-container *,.fc-view-container :after,.fc-view-container :before{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.fc-view,.fc-view>table{position:relative;z-index:1}.fc-basicDay-view .fc-content-skeleton,.fc-basicWeek-view .fc-content-skeleton{padding-bottom:1em}.fc-basic-view .fc-body .fc-row{min-height:4em}.fc-row.fc-rigid .fc-content-skeleton{position:absolute;top:0;left:0;right:0}.fc-day-top.fc-other-month{opacity:.3}.fc-basic-view .fc-day-number,.fc-basic-view .fc-week-number{padding:2px}.fc-basic-view th.fc-day-number,.fc-basic-view th.fc-week-number{padding:0 2px}.fc-ltr .fc-basic-view .fc-day-top .fc-day-number{float:right}.fc-rtl .fc-basic-view .fc-day-top .fc-day-number{float:left}.fc-ltr .fc-basic-view .fc-day-top .fc-week-number{float:left;border-radius:0 0 3px}.fc-rtl .fc-basic-view .fc-day-top .fc-week-number{float:right;border-radius:0 0 0 3px}.fc-basic-view .fc-day-top .fc-week-number{min-width:1.5em;text-align:center;background-color:#f2f2f2;color:grey}.fc-basic-view td.fc-week-number>*{display:inline-block;min-width:1.25em}.fc-agenda-view .fc-day-grid{position:relative;z-index:2}.fc-agenda-view .fc-day-grid .fc-row{min-height:3em}.fc-agenda-view .fc-day-grid .fc-row .fc-content-skeleton{padding-bottom:1em}.fc .fc-axis{vertical-align:middle;padding:0 4px;white-space:nowrap}.fc-ltr .fc-axis{text-align:right}.fc-rtl .fc-axis{text-align:left}.ui-widget td.fc-axis{font-weight:400}.fc-time-grid,.fc-time-grid-container{position:relative;z-index:1}.fc-time-grid{min-height:100%}.fc-time-grid table{border:0 hidden transparent}.fc-time-grid>.fc-bg{z-index:1}.fc-time-grid .fc-slats,.fc-time-grid>hr{position:relative;z-index:2}.fc-time-grid .fc-content-col{position:relative}.fc-time-grid .fc-content-skeleton{position:absolute;z-index:3;top:0;left:0;right:0}.fc-time-grid .fc-business-container{position:relative;z-index:1}.fc-time-grid .fc-bgevent-container{position:relative;z-index:2}.fc-time-grid .fc-highlight-container{z-index:3;position:relative}.fc-time-grid .fc-event-container{position:relative;z-index:4}.fc-time-grid .fc-now-indicator-line{z-index:5}.fc-time-grid .fc-helper-container{position:relative;z-index:6}.fc-time-grid .fc-slats td{height:1.5em;border-bottom:0}.fc-time-grid .fc-slats .fc-minor td{border-top-style:dotted}.fc-time-grid .fc-slats .ui-widget-content{background:0 0}.fc-time-grid .fc-highlight{position:absolute;left:0;right:0}.fc-ltr .fc-time-grid .fc-event-container{margin:0 2.5% 0 2px}.fc-rtl .fc-time-grid .fc-event-container{margin:0 2px 0 2.5%}.fc-time-grid .fc-bgevent,.fc-time-grid .fc-event{position:absolute;z-index:1}.fc-time-grid .fc-bgevent{left:0;right:0}.fc-v-event.fc-not-start{border-top-width:0;padding-top:1px;border-top-left-radius:0;border-top-right-radius:0}.fc-v-event.fc-not-end{border-bottom-width:0;padding-bottom:1px;border-bottom-left-radius:0;border-bottom-right-radius:0}.fc-time-grid-event.fc-selected{overflow:visible}.fc-time-grid-event.fc-selected .fc-bg{display:none}.fc-time-grid-event .fc-content{overflow:hidden}.fc-time-grid-event .fc-time,.fc-time-grid-event .fc-title{padding:0 1px}.fc-time-grid-event .fc-time{font-size:.85em;white-space:nowrap}.fc-time-grid-event.fc-short .fc-content{white-space:nowrap}.fc-time-grid-event.fc-short .fc-time,.fc-time-grid-event.fc-short .fc-title{display:inline-block;vertical-align:top}.fc-time-grid-event.fc-short .fc-time span{display:none}.fc-time-grid-event.fc-short .fc-time:before{content:attr(data-start)}.fc-time-grid-event.fc-short .fc-time:after{content:"\000A0-\000A0"}.fc-time-grid-event.fc-short .fc-title{font-size:.85em;padding:0}.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer{left:0;right:0;bottom:0;height:8px;overflow:hidden;line-height:8px;font-size:11px;font-family:monospace;text-align:center;cursor:s-resize}.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer:after{content:"="}.fc-time-grid-event.fc-selected .fc-resizer{border-radius:5px;border-width:1px;width:8px;height:8px;border-style:solid;border-color:inherit;background:#fff;left:50%;margin-left:-5px;bottom:-5px}.fc-time-grid .fc-now-indicator-line{border-top-width:1px;left:0;right:0}.fc-time-grid .fc-now-indicator-arrow{margin-top:-5px}.fc-ltr .fc-time-grid .fc-now-indicator-arrow{left:0;border-width:5px 0 5px 6px;border-top-color:transparent;border-bottom-color:transparent}.fc-rtl .fc-time-grid .fc-now-indicator-arrow{right:0;border-width:5px 6px 5px 0;border-top-color:transparent;border-bottom-color:transparent}.fc-event-dot{display:inline-block;width:10px;height:10px;border-radius:5px}.fc-rtl .fc-list-view{direction:rtl}.fc-list-view{border-width:1px;border-style:solid}.fc .fc-list-table{table-layout:auto}.fc-list-table td{border-width:1px 0 0;padding:8px 14px}.fc-list-table tr:first-child td{border-top-width:0}.fc-list-heading{border-bottom-width:1px}.fc-list-heading td{font-weight:700}.fc-ltr .fc-list-heading-main{float:left}.fc-ltr .fc-list-heading-alt,.fc-rtl .fc-list-heading-main{float:right}.fc-rtl .fc-list-heading-alt{float:left}.fc-list-item.fc-has-url{cursor:pointer}.fc-list-item:hover td{background-color:#f5f5f5}.fc-list-item-marker,.fc-list-item-time{white-space:nowrap;width:1px}.fc-ltr .fc-list-item-marker{padding-right:0}.fc-rtl .fc-list-item-marker{padding-left:0}.fc-list-item-title a{text-decoration:none;color:inherit}.fc-list-item-title a[href]:hover{text-decoration:underline}.fc-list-empty-wrap2{position:absolute;top:0;left:0;right:0;bottom:0}.fc-list-empty-wrap1{width:100%;height:100%;display:table}.fc-list-empty{display:table-cell;vertical-align:middle;text-align:center}.fc-unthemed .fc-list-empty{background-color:#eee} -/*! * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} -.c3 svg{font:10px sans-serif;-webkit-tap-highlight-color:transparent}.c3 line,.c3 path{fill:none;stroke:#000}.c3 text{-webkit-user-select:none;-moz-user-select:none;user-select:none}.c3-bars path,.c3-event-rect,.c3-legend-item-tile,.c3-xgrid-focus,.c3-ygrid{shape-rendering:crispEdges}.c3-chart-arc path{stroke:#fff}.c3-chart-arc text{fill:#fff;font-size:13px}.c3-grid line{stroke:#aaa}.c3-grid text{fill:#aaa}.c3-xgrid,.c3-ygrid{stroke-dasharray:3 3}.c3-text.c3-empty{fill:gray;font-size:2em}.c3-line{stroke-width:1px}.c3-circle._expanded_{stroke-width:1px;stroke:#fff}.c3-selected-circle{fill:#fff;stroke-width:2px}.c3-bar{stroke-width:0}.c3-bar._expanded_{fill-opacity:.75}.c3-target.c3-focused{opacity:1}.c3-target.c3-focused path.c3-line,.c3-target.c3-focused path.c3-step{stroke-width:2px}.c3-target.c3-defocused{opacity:.3!important}.c3-region{fill:#4682b4;fill-opacity:.1}.c3-brush .extent{fill-opacity:.1}.c3-legend-item{font-size:12px}.c3-legend-item-hidden{opacity:.15}.c3-legend-background{opacity:.75;fill:#fff;stroke:#d3d3d3;stroke-width:1}.c3-title{font:14px sans-serif}.c3-tooltip-container{z-index:10}.c3-tooltip{border-collapse:collapse;border-spacing:0;background-color:#fff;empty-cells:show;-webkit-box-shadow:7px 7px 12px -9px #777;-moz-box-shadow:7px 7px 12px -9px #777;box-shadow:7px 7px 12px -9px #777;opacity:.9}.c3-tooltip tr{border:1px solid #CCC}.c3-tooltip th{background-color:#aaa;font-size:14px;padding:2px 5px;text-align:left;color:#FFF}.c3-tooltip td{font-size:13px;padding:3px 6px;background-color:#fff;border-left:1px dotted #999}.c3-tooltip td>span{display:inline-block;width:10px;height:10px;margin-right:6px}.c3-tooltip td.value{text-align:right}.c3-area{stroke-width:0;opacity:.2}.c3-chart-arcs-title{dominant-baseline:middle;font-size:1.3em}.c3-chart-arcs .c3-chart-arcs-background{fill:#e0e0e0;stroke:none}.c3-chart-arcs .c3-chart-arcs-gauge-unit{fill:#000;font-size:16px}.c3-chart-arcs .c3-chart-arcs-gauge-max,.c3-chart-arcs .c3-chart-arcs-gauge-min{fill:#777}.c3-chart-arc .c3-gauge-value{fill:#000}
\ No newline at end of file +.c3 svg{font:10px sans-serif;-webkit-tap-highlight-color:transparent}.c3 line,.c3 path{fill:none;stroke:#000}.c3 text{-webkit-user-select:none;-moz-user-select:none;user-select:none}.c3-bars path,.c3-event-rect,.c3-legend-item-tile,.c3-xgrid-focus,.c3-ygrid{shape-rendering:crispEdges}.c3-chart-arc path{stroke:#fff}.c3-chart-arc text{fill:#fff;font-size:13px}.c3-grid line{stroke:#aaa}.c3-grid text{fill:#aaa}.c3-xgrid,.c3-ygrid{stroke-dasharray:3 3}.c3-text.c3-empty{fill:grey;font-size:2em}.c3-line{stroke-width:1px}.c3-circle._expanded_{stroke-width:1px;stroke:#fff}.c3-selected-circle{fill:#fff;stroke-width:2px}.c3-bar{stroke-width:0}.c3-bar._expanded_{fill-opacity:.75}.c3-target.c3-focused{opacity:1}.c3-target.c3-focused path.c3-line,.c3-target.c3-focused path.c3-step{stroke-width:2px}.c3-target.c3-defocused{opacity:.3!important}.c3-region{fill:#4682b4;fill-opacity:.1}.c3-brush .extent{fill-opacity:.1}.c3-legend-item{font-size:12px}.c3-legend-item-hidden{opacity:.15}.c3-legend-background{opacity:.75;fill:#fff;stroke:#d3d3d3;stroke-width:1}.c3-title{font:14px sans-serif}.c3-tooltip-container{z-index:10}.c3-tooltip{border-collapse:collapse;border-spacing:0;background-color:#fff;empty-cells:show;-webkit-box-shadow:7px 7px 12px -9px #777;-moz-box-shadow:7px 7px 12px -9px #777;box-shadow:7px 7px 12px -9px #777;opacity:.9}.c3-tooltip tr{border:1px solid #ccc}.c3-tooltip th{background-color:#aaa;font-size:14px;padding:2px 5px;text-align:left;color:#fff}.c3-tooltip td{font-size:13px;padding:3px 6px;background-color:#fff;border-left:1px dotted #999}.c3-tooltip td>span{display:inline-block;width:10px;height:10px;margin-right:6px}.c3-tooltip td.value{text-align:right}.c3-area{stroke-width:0;opacity:.2}.c3-chart-arcs-title{dominant-baseline:middle;font-size:1.3em}.c3-chart-arcs .c3-chart-arcs-background{fill:#e0e0e0;stroke:none}.c3-chart-arcs .c3-chart-arcs-gauge-unit{fill:#000;font-size:16px}.c3-chart-arcs .c3-chart-arcs-gauge-max{fill:#777}.c3-chart-arcs .c3-chart-arcs-gauge-min{fill:#777}.c3-chart-arc .c3-gauge-value{fill:#000}
\ No newline at end of file diff --git a/assets/img/gravatar-icon.png b/assets/img/gravatar-icon.png Binary files differdeleted file mode 100644 index 2d1f8396..00000000 --- a/assets/img/gravatar-icon.png +++ /dev/null diff --git a/assets/js/app.min.js b/assets/js/app.min.js index 8ee7ddc9..bb0c7ac2 100644 --- a/assets/js/app.min.js +++ b/assets/js/app.min.js @@ -1,3 +1,2 @@ -!function(){function t(t,a,i){if(!n)throw new Error("textarea-caret-position#getCaretCoordinates should only be called in a browser");var r=i&&i.debug||!1;if(r){var d=document.querySelector("#input-textarea-caret-position-mirror-div");d&&d.parentNode.removeChild(d)}var s=document.createElement("div");s.id="input-textarea-caret-position-mirror-div",document.body.appendChild(s);var l=s.style,c=window.getComputedStyle?getComputedStyle(t):t.currentStyle;l.whiteSpace="pre-wrap","INPUT"!==t.nodeName&&(l.wordWrap="break-word"),l.position="absolute",r||(l.visibility="hidden"),e.forEach(function(t){l[t]=c[t]}),o?t.scrollHeight>parseInt(c.height)&&(l.overflowY="scroll"):l.overflow="hidden",s.textContent=t.value.substring(0,a),"INPUT"===t.nodeName&&(s.textContent=s.textContent.replace(/\s/g,"Â "));var u=document.createElement("span");u.textContent=t.value.substring(a)||".",s.appendChild(u);var p={top:u.offsetTop+parseInt(c.borderTopWidth),left:u.offsetLeft+parseInt(c.borderLeftWidth)};return r?u.style.backgroundColor="#aaa":document.body.removeChild(s),p}var e=["direction","boxSizing","width","height","overflowX","overflowY","borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth","borderStyle","paddingTop","paddingRight","paddingBottom","paddingLeft","fontStyle","fontVariant","fontWeight","fontStretch","fontSize","fontSizeAdjust","lineHeight","fontFamily","textAlign","textTransform","textIndent","textDecoration","letterSpacing","wordSpacing","tabSize","MozTabSize"],n="undefined"!=typeof window,o=n&&null!=window.mozInnerScreenX;"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports=t:n&&(window.getCaretCoordinates=t)}(),function(){function t(){if(!("KeyboardEvent"in window)||"key"in KeyboardEvent.prototype)return!1;var t={get:function(t){var e=n.keys[this.which||this.keyCode];return Array.isArray(e)&&(e=e[+this.shiftKey]),e}};return Object.defineProperty(KeyboardEvent.prototype,"key",t),t}var e,n={polyfill:t,keys:{3:"Cancel",6:"Help",8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",28:"Convert",29:"NonConvert",30:"Accept",31:"ModeChange",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",41:"Select",42:"Print",43:"Execute",44:"PrintScreen",45:"Insert",46:"Delete",48:["0",")"],49:["1","!"],50:["2","@"],51:["3","#"],52:["4","$"],53:["5","%"],54:["6","^"],55:["7","&"],56:["8","*"],57:["9","("],91:"OS",93:"ContextMenu",144:"NumLock",145:"ScrollLock",181:"VolumeMute",182:"VolumeDown",183:"VolumeUp",186:[";",":"],187:["=","+"],188:[",","<"],189:["-","_"],190:[".",">"],191:["/","?"],192:["`","~"],219:["[","{"],220:["\\","|"],221:["]","}"],222:["'",'"'],224:"Meta",225:"AltGraph",246:"Attn",247:"CrSel",248:"ExSel",249:"EraseEof",250:"Play",251:"ZoomOut"}};for(e=1;e<25;e++)n.keys[111+e]="F"+e;var o="";for(e=65;e<91;e++)o=String.fromCharCode(e),n.keys[e]=[o.toLowerCase(),o.toUpperCase()];n.polyfill()}(),Element.prototype.matches||(Element.prototype.matches=Element.prototype.matchesSelector||Element.prototype.mozMatchesSelector||Element.prototype.msMatchesSelector||Element.prototype.oMatchesSelector||Element.prototype.webkitMatchesSelector||function(t){for(var e=(this.document||this.ownerDocument).querySelectorAll(t),n=e.length;--n>=0&&e.item(n)!==this;);return n>-1});var KB={components:{},utils:{},html:{},http:{},listeners:{clicks:{},changes:{},keys:[],internals:{}}};KB.on=function(t,e){this.listeners.internals.hasOwnProperty(t)||(this.listeners.internals[t]=[]),this.listeners.internals[t].push(e)},KB.trigger=function(t,e){if(this.listeners.internals.hasOwnProperty(t))for(var n=0;n<this.listeners.internals[t].length;n++)this.listeners.internals[t][n](e)},KB.removeListener=function(t,e){if(this.listeners.internals.hasOwnProperty(t))for(var n=0;n<this.listeners.internals[t].length;n++)this.listeners.internals[t][n]===e&&this.listeners.internals[t].splice(n,1)},KB.onClick=function(t,e){this.listeners.clicks[t]=e},KB.onChange=function(t,e){this.listeners.changes[t]=e},KB.onKey=function(t,e,n,o,a){this.listeners.keys.push({combination:t,callback:e,ignoreInputField:n||!1,ctrlKey:o||!1,metaKey:a||!1})},KB.listen=function(){function t(t){for(var e in o.listeners.clicks)o.listeners.clicks.hasOwnProperty(e)&&t.target.matches(e)&&(t.preventDefault(),o.listeners.clicks[e](t))}function e(t){for(var e in o.listeners.changes)o.listeners.changes.hasOwnProperty(e)&&t.target.matches(e)&&o.listeners.changes[e](t.target)}function n(t){var e=KB.utils.getKey(t),n=KB.utils.isInputField(t);if(n&&["Escape","Enter"].indexOf(e)===-1||a.push(e),a.length>0){for(var i=!0,r=0;r<o.listeners.keys.length;r++){var d=o.listeners.keys[r],s=d.combination,l=s.split("+");if(KB.utils.arraysIdentical(a,l)&&t.ctrlKey===d.ctrlKey&&t.metaKey===d.metaKey){if(n&&!d.ignoreInputField)return void(a=[]);t.preventDefault(),t.stopPropagation(),a=[],d.callback(t);break}KB.utils.arraysStartsWith(a,l)&&a.length<l.length&&(i=!1)}i&&(a=[])}}var o=this,a=[];document.addEventListener("click",t,!1),document.addEventListener("change",e,!1),window.addEventListener("keydown",n,!1)},KB.component=function(t,e){this.components[t]=e},KB.getComponent=function(t,e,n){var o=this.components[t];return new o(e,n)},KB.render=function(){for(var t in this.components)for(var e=document.querySelectorAll(".js-"+t),n=0;n<e.length;n++)if(this.components.hasOwnProperty(t)){var o;e[n].dataset.params&&(o=JSON.parse(e[n].dataset.params));var a=KB.getComponent(t,e[n],o);a.render(),e[n].className=e[n].className+"-rendered"}},KB.interval=function(t,e){setInterval(e,1e3*t)},KB.dom=function(t){function e(t){var e="string"==typeof t?document.createElement(t):t;this.attr=function(t,n){return null!==n&&"undefined"!=typeof n?(e.setAttribute(t,n),this):e.getAttribute(t)},this.data=function(t,n){return 1===arguments.length?e.dataset[t]:(e.dataset[t]=n,this)},this.hide=function(){return e.style.display="none",this},this.show=function(){return e.style.display="block",this},this.toggle=function(){return"none"===e.style.display?this.show():this.hide(),this},this.style=function(t,n){return e.style[t]=n,this},this.on=function(t,n,o){return e.addEventListener(t,function(t){o||t.preventDefault(),n(t.target)}),this},this.click=function(t){return this.on("click",t)},this.mouseover=function(t){return this.on("mouseover",t)},this.change=function(t){return this.on("change",t)},this.add=function(t){return e.appendChild(t),this},this.replace=function(t){return e.parentNode.replaceChild(t,e),this},this.html=function(t){return e.innerHTML=t,this},this.text=function(t){return e.appendChild(document.createTextNode(t)),this},this.replaceText=function(t){return e.textContent=t,this},this.addClass=function(t){return e.classList.add(t),this},this.removeClass=function(t){return e.classList.remove(t),this},this.toggleClass=function(t){return e.classList.toggle(t),this},this.hasClass=function(t){return e.classList.contains(t)},this.disable=function(){return e.disabled=!0,this},this.enable=function(){return e.disabled=!1,this},this.remove=function(){return e.parentNode.removeChild(e),this},this.empty=function(){for(;e.firstChild;)e.removeChild(e.firstChild);return this},this.parent=function(t){for(;e&&e!==document;e=e.parentNode)if(e.matches(t))return e;return null},this.find=function(t){return e.querySelector(t)},this.for=function(t,n){for(var o=0;o<n.length;o++){var a=n[o];if("object"!=typeof a)e.appendChild(KB.dom(t).text(a).build());else{var i=KB.dom(t);for(var r in a)a.hasOwnProperty(r)&&r in this&&"function"==typeof this[r]?i[r](a[r]):i.attr(r,a[r]);e.appendChild(i.build())}}return this},this.build=function(){return e}}return new e(t)},KB.find=function(t){var e=document.querySelector(t);return e?KB.dom(e):null},KB.exists=function(t){return!!document.querySelector(t)},KB.focus=function(t){var e=document.querySelector(t);if(e)return e.focus()},KB.html.label=function(t,e){return KB.dom("label").attr("for",e).text(t).build()},KB.html.radio=function(t,e,n){return KB.dom("label").add(KB.dom("input").attr("type","radio").attr("name",e).attr("value",n).build()).text(t).build()},KB.html.radios=function(t){var e=KB.dom("div");for(var n in t)t.hasOwnProperty(n)&&e.add(KB.html.radio(n.label,n.name,n.value))},KB.http.request=function(t,e,n,o){function a(t){var e=t.getResponseHeader("X-Ajax-Redirect"),n=t.getResponseHeader("Location");if("self"===e)window.location.reload();else if(e&&e.indexOf("#")>-1)window.location=e.split("#")[0];else if(e)window.location=e;else if(n)window.location=n;else if("application/json"===t.getResponseHeader("Content-Type"))try{return JSON.parse(t.responseText)}catch(t){}return t.responseText}var i=function(){},r=function(){};this.execute=function(){var d=new XMLHttpRequest;d.open(t,e,!0),d.setRequestHeader("X-Requested-With","XMLHttpRequest");for(var s in n)n.hasOwnProperty(s)&&d.setRequestHeader(s,n[s]);return d.onerror=function(){r()},d.onreadystatechange=function(){if(d.readyState===XMLHttpRequest.DONE){var t=a(d);200===d.status?i(t):r(t)}},d.send(o),this},this.success=function(t){return i=t,this},this.error=function(t){return r=t,this}},KB.http.get=function(t){return new KB.http.request("GET",t).execute()},KB.http.postJson=function(t,e){var n={"Content-Type":"application/json",Accept:"application/json"};return new KB.http.request("POST",t,n,JSON.stringify(e)).execute()},KB.http.postForm=function(t,e){var n=new FormData(e);return new KB.http.request("POST",t,{},n).execute()},KB.http.uploadFile=function(t,e,n,o,a,i){var r=new FormData;r.append("files[]",e);var d=new XMLHttpRequest;d.upload.addEventListener("progress",n),d.upload.addEventListener("error",a),d.open("POST",t,!0),d.setRequestHeader("X-Requested-With","XMLHttpRequest"),d.onreadystatechange=function(){d.readyState===XMLHttpRequest.DONE&&(200===d.status?o():"undefined"!=typeof i&&i(JSON.parse(d.responseText)))},d.send(r)},function(){function t(t){t.target.matches("#modal-overlay")&&(t.stopPropagation(),t.preventDefault(),s())}function e(){KB.trigger("modal.close")}function n(){KB.trigger("modal.loading"),a()}function o(){return document.querySelector("#modal-content form:not(.js-modal-ignore-form)")}function a(){var t=o();if(t){var e=t.getAttribute("action");e&&KB.http.postForm(e,t).success(function(t){KB.trigger("modal.stop"),t?r(t):s()})}}function i(){var t=KB.find("#modal-content form");t&&t.on("submit",n,!1);var e=document.querySelector("#modal-content input[autofocus]");e&&e.focus(),KB.render(),_KB.datePicker(),_KB.autoComplete(),_KB.tagAutoComplete(),_KB.get("Task").onPopoverOpened()}function r(t){var e=KB.find("#modal-content");e&&(e.replace(KB.dom("div").attr("id","modal-content").html(t).build()),i())}function d(n,o,a){var r=KB.dom("a").attr("href","#").attr("id","modal-close-button").html('<i class="fa fa-times"></i>').click(e).build(),d=KB.dom("div").attr("id","modal-header").add(r).build(),s=KB.dom("div").attr("id","modal-content").html(n).build(),l=KB.dom("div").attr("id","modal-box").style("width",o).add(d).add(s).build(),c=KB.dom("div").attr("id","modal-overlay").add(l).build();a&&c.addEventListener("click",t,!1),document.body.appendChild(c),i()}function s(){c=!1;var t=KB.find("#modal-overlay");t&&t.remove()}function l(t){var e=KB.utils.getViewportSize();if(e.width<700)return"99%";switch(t){case"large":return e.width<1350?"98%":"1350px";case"medium":return e.width<1024?"70%":"1024px"}return e.width<800?"75%":"800px"}var c=!1;KB.on("modal.close",function(){s()}),KB.on("modal.submit",function(){a()}),KB.modal={open:function(t,e,n){_KB.get("Dropdown").close(),s(),"undefined"==typeof n&&(n=!0),KB.http.get(t).success(function(t){c=!0,d(t,l(e),n)})},close:function(){s()},isOpen:function(){return c},replace:function(t){KB.http.get(t).success(function(t){r(t)})},getForm:o,submitForm:a}}(),KB.utils.formatDuration=function(t){return t>=86400?Math.round(t/86400)+"d":t>=3600?Math.round(t/3600)+"h":t>=60?Math.round(t/60)+"m":t+"s"},KB.utils.getSelectionPosition=function(t){var e,n;return e=t.value.length<t.selectionStart?t.value.length:t.selectionStart,n=t.selectionStart===t.selectionEnd?e:t.selectionEnd,{selectionStart:e,selectionEnd:n}},KB.utils.arraysIdentical=function(t,e){var n=t.length;if(n!==e.length)return!1;for(;n--;)if(t[n]!==e[n])return!1;return!0},KB.utils.arraysStartsWith=function(t,e){for(var n=Math.min(t.length,e.length),o=0;o<n;o++)if(t[o]!==e[o])return!1;return!0},KB.utils.isInputField=function(t){var e=t.target;return!("INPUT"!==e.tagName&&"SELECT"!==e.tagName&&"TEXTAREA"!==e.tagName&&!e.isContentEditable)},KB.utils.getKey=function(t){var e={Esc:"Escape",Up:"ArrowUp",Down:"ArrowDown",Left:"ArrowLeft",Right:"ArrowRight"};for(var n in e)if(e.hasOwnProperty(n)&&n===t.key)return e[n];return t.key},KB.utils.getViewportSize=function(){return{width:Math.max(document.documentElement.clientWidth,window.innerWidth||0),height:Math.max(document.documentElement.clientHeight,window.innerHeight||0)}},KB.utils.isVisible=function(){var t="";return"undefined"!=typeof document.hidden?t="visibilityState":"undefined"!=typeof document.mozHidden?t="mozVisibilityState":"undefined"!=typeof document.msHidden?t="msVisibilityState":"undefined"!=typeof document.webkitHidden&&(t="webkitVisibilityState"),""===t||"visible"===document[t]},KB.onClick(".accordion-toggle",function(t){var e=KB.dom(t.target).parent(".accordion-section");e&&KB.dom(e).toggleClass("accordion-collapsed")}),function(){function t(t){var e=KB.dom(t.target).parent("a, .task-board-change-assignee");if(!e){var n=KB.dom(t.target).parent(".task-board");if(n){var o=KB.dom(n).data("taskUrl");o&&(window.location=o)}}}function e(t){var e=KB.dom(t.target).parent(".task-board-change-assignee"),n=KB.dom(e).data("url");n&&KB.modal.open(n,"medium",!1)}KB.onClick(".task-board *",t),KB.onClick(".task-board-change-assignee *",e)}(),KB.component("calendar",function(t,e){var n={month:"month",week:"agendaWeek",day:"agendaDay"};this.render=function(){var o=$(t),a="month";if(window.location.hash){var i=window.location.hash.substr(1);a=n[i]||a}o.fullCalendar({locale:$("body").data("js-lang"),editable:!0,eventLimit:!0,defaultView:a,header:{left:"prev,next today",center:"title",right:"month,agendaWeek,agendaDay"},eventDrop:function(t){$.ajax({cache:!1,url:e.saveUrl,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({task_id:t.id,date_due:t.start.format()})})},viewRender:function(t){for(var a in n)if(n[a]===t.name){window.location.hash=a;break}var i=e.checkUrl,r={start:o.fullCalendar("getView").start.format(),end:o.fullCalendar("getView").end.format()};for(var d in r)i+="&"+d+"="+r[d];$.getJSON(i,function(t){o.fullCalendar("removeEvents"),o.fullCalendar("addEventSource",t),o.fullCalendar("rerenderEvents")})}})}}),KB.component("chart-project-avg-time-column",function(t,e){this.render=function(){var n=e.metrics,o=[e.label],a=[];for(var i in n)o.push(n[i].average),a.push(n[i].title);KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:[o],type:"bar"},bar:{width:{ratio:.5}},axis:{x:{type:"category",categories:a},y:{tick:{format:KB.utils.formatDuration}}},legend:{show:!1}})}}),KB.component("chart-project-burndown",function(t,e){this.render=function(){for(var n=e.metrics,o=[[e.labelTotal]],a=[],i=d3.time.format("%Y-%m-%d"),r=d3.time.format(e.dateFormat),d=0;d<n.length;d++)for(var s=0;s<n[d].length;s++){var l=n[d][s];0===d?s>0&&o.push([l]):s>0?(o[s].push(l),"undefined"==typeof o[0][d]&&o[0].push(0),o[0][d]+=l):a.push(r(i.parse(l)))}KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:o},axis:{x:{type:"category",categories:a}}})}}),KB.component("chart-project-cumulative-flow",function(t,e){this.render=function(){for(var n=e.metrics,o=[],a=[],i=[],r=d3.time.format("%Y-%m-%d"),d=d3.time.format(e.dateFormat),s=0;s<n.length;s++)for(var l=0;l<n[s].length;l++){var c=n[s][l];0===s?l>0&&(a.push(c),o.push([c])):l>0?o[l-1].push(c):i.push(d(r.parse(c)))}KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:o.reverse(),type:"area-spline",groups:[a],order:null},axis:{x:{type:"category",categories:i}}})}}),KB.component("chart-project-lead-cycle-time",function(t,e){this.render=function(){var n=e.metrics,o=[e.labelCycle],a=[e.labelLead],i=[],r={};r[e.labelCycle]="area",r[e.labelLead]="area-spline";var d={};d[e.labelLead]="#afb42b",d[e.labelCycle]="#4e342e";for(var s=0;s<n.length;s++)o.push(parseInt(n[s].avg_cycle_time)),a.push(parseInt(n[s].avg_lead_time)),i.push(n[s].day);KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:[a,o],types:r,colors:d},axis:{x:{type:"category",categories:i},y:{tick:{format:KB.utils.formatDuration}}}})}}),KB.component("chart-project-task-distribution",function(t,e){this.render=function(){for(var n=[],o=0;o<e.metrics.length;o++)n.push([e.metrics[o].column_title,e.metrics[o].nb_tasks]);KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:n,type:"donut"}})}}),KB.component("chart-project-time-comparison",function(t,e){this.render=function(){var n=[e.labelSpent],o=[e.labelEstimated],a=[];for(var i in e.metrics)n.push(e.metrics[i].time_spent),o.push(e.metrics[i].time_estimated),a.push("open"===i?e.labelOpen:e.labelClosed);KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:[n,o],type:"bar"},bar:{width:{ratio:.2}},axis:{x:{type:"category",categories:a}},legend:{show:!0}})}}),KB.component("chart-project-user-distribution",function(t,e){this.render=function(){for(var n=[],o=0;o<e.metrics.length;o++)n.push([e.metrics[o].user,e.metrics[o].nb_tasks]);KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:n,type:"donut"}})}}),KB.component("chart-task-time-column",function(t,e){this.render=function(){for(var n=e.metrics,o=[e.label],a=[],i=0;i<n.length;i++)o.push(n[i].time_spent),a.push(n[i].title);KB.dom(t).add(KB.dom("div").attr("id","chart-task-time-column").build()),c3.generate({bindto:"#chart-task-time-column",data:{columns:[o],type:"bar"},bar:{width:{ratio:.5}},axis:{x:{type:"category",categories:a},y:{tick:{format:KB.utils.formatDuration}}},legend:{show:!1}})}}),KB.on("dom.ready",function(){function t(){if(0===window.location.hash.indexOf("#comment-")){var t=KB.find(window.location.hash);if(t){var e=document.querySelectorAll(".comment");e.forEach(function(t){KB.dom(t).removeClass("comment-highlighted")}),t.addClass("comment-highlighted")}}}window.addEventListener("hashchange",t),t()}),KB.component("confirm-buttons",function(t,e){function n(){r=!0,KB.find("#modal-confirm-button").replace(i()),KB.http.get(e.url)}function o(){KB.trigger("modal.close")}function a(){r=!1,KB.find("#modal-confirm-button").replace(i())}function i(){var t=KB.dom("button").click(n).attr("id","modal-confirm-button").attr("type","button").attr("class","btn btn-red");return r&&t.disable().add(KB.dom("i").attr("class","fa fa-spinner fa-pulse").build()).text(" "),e.tabindex&&t.attr("tabindex",e.tabindex),t.text(e.submitLabel).build()}var r=!1;this.render=function(){KB.on("modal.stop",a),KB.on("modal.close",function(){KB.removeListener("modal.stop",a)});var n=KB.dom("div").attr("class","form-actions").add(i()).text(" "+e.orLabel+" ").add(KB.dom("a").attr("href","#").click(o).text(e.cancelLabel).build()).build();t.appendChild(n)}}),KB.component("external-task-view",function(t,e){this.render=function(){$.ajax({cache:!1,url:e.url,success:function(e){KB.dom(t).html('<div id="external-task-view">'+e+"</div>")}})}}),KB.component("file-upload",function(t,e){function n(t){if(t.lengthComputable){var e=t.loaded/t.total,n=Math.floor(100*e);KB.find("#file-progress-"+w).attr("value",e),KB.find("#file-percentage-"+w).replaceText("("+n+"%)")}}function o(){var t=KB.dom("div").addClass("file-error").text(e.labelUploadError).build();KB.find("#file-item-"+w).add(t)}function a(t){var e=KB.dom("div").addClass("file-error").text(t.message).build();KB.find("#file-item-"+w).add(e),KB.trigger("modal.stop")}function i(){if(w++,w<y.length)KB.http.uploadFile(e.url,y[w],n,i,o,a);else{KB.trigger("modal.stop"),KB.trigger("modal.hide");var t=KB.dom("div").addClass("alert").addClass("alert-success").text(e.labelSuccess).build(),d=KB.dom("button").attr("type","button").addClass("btn").addClass("btn-blue").click(r).text(e.labelCloseSuccess).build();KB.dom(B).replace(KB.dom("div").add(t).add(d).build())}}function r(){window.location.reload()}function d(){w=0,p()}function s(){y=K.files,f()}function l(){y=[],w=0,K.click()}function c(t){t.stopPropagation(),t.preventDefault()}function u(t){t.stopPropagation(),t.preventDefault(),y=t.dataTransfer.files,f()}function p(){y.length>0&&KB.http.uploadFile(e.url,y[w],n,i,o,a)}function f(){y.length>0?(KB.trigger("modal.enable"),KB.dom(B).empty().add(b())):(KB.trigger("modal.disable"),KB.dom(B).empty().add(h()))}function m(){return KB.dom("input").attr("id","file-input-element").attr("type","file").attr("name","files[]").attr("multiple",!0).on("change",s).hide().build()}function h(){var t=KB.dom("a").attr("href","#").text(e.labelChooseFiles).click(l).build();return KB.dom("div").attr("id","file-dropzone-inner").text(e.labelDropzone+" "+e.labelOr+" ").add(t).build()}function g(){var t=KB.dom("div").attr("id","file-dropzone").add(h()).build();return t.ondragover=c,t.ondrop=u,t.ondragover=c,t}function v(t){var n=!1,o=KB.dom("progress").attr("id","file-progress-"+t).attr("value",0).build(),a=KB.dom("span").attr("id","file-percentage-"+t).text("(0%)").build(),i=KB.dom("li").attr("id","file-item-"+t).add(o).text(" "+y[t].name+" ").add(a);return y[t].size>e.maxSize&&(i.add(KB.dom("div").addClass("file-error").text(e.labelOversize).build()),n=!0),n&&KB.trigger("modal.disable"),i.build()}function b(){for(var t=KB.dom("ul").attr("id","file-list").build(),e=0;e<y.length;e++)t.appendChild(v(e));return t}var K=null,B=null,y=[],w=0;this.render=function(){KB.on("modal.submit",d),KB.on("modal.close",function(){KB.removeListener("modal.submit",d)}),K=m(),B=g(),t.appendChild(K),t.appendChild(B)}}),KB.onClick(".js-form-export",function(t){var e=document.querySelector("#modal-content form"),n=e.querySelector("#form-from"),o=e.querySelector("#form-to");""!==n.value&&""!==o.value&&e.submit()}),KB.component("image-slideshow",function(t,e){function n(t){switch(KB.utils.getKey(t)){case"Escape":s();break;case"ArrowRight":i();break;case"ArrowLeft":r()}}function o(t){t.matches(".slideshow-next-icon")?i():t.matches(".slideshow-previous-icon")?r():t.matches(".slideshow-download-icon")?window.location.href=t.href:s()}function a(t){var e=KB.dom(t).data("imageId"),n=l(e);d(n)}function i(){s();for(var t=0;t<e.images.length;t++)if(e.images[t].id===p.id){var n=t+1;n>=e.images.length&&(n=0),p=e.images[n];break}d()}function r(){s();for(var t=0;t<e.images.length;t++)if(e.images[t].id===p.id){var n=t-1;n<0&&(n=e.images.length-1),p=e.images[n];break}d()}function d(){var t=KB.dom("div").attr("class","fa fa-window-close slideshow-icon slideshow-close-icon").build(),e=KB.dom("a").attr("class","fa fa-download slideshow-icon slideshow-download-icon").attr("href",c(p,"download")).build(),a=KB.dom("div").attr("class","fa fa-chevron-circle-left slideshow-icon slideshow-previous-icon").build(),i=KB.dom("div").attr("class","fa fa-chevron-circle-right slideshow-icon slideshow-next-icon").build(),r=KB.dom("img").attr("src",c(p,"image")).attr("alt",p.name).attr("title",p.name).style("maxHeight",window.innerHeight-50+"px").build(),d=KB.dom("figcaption").text(p.name).build(),s=KB.dom("figure").add(r).add(d).build(),l=KB.dom("div").addClass("image-slideshow-overlay").add(t).add(e).add(a).add(i).add(s).click(o).build();document.body.appendChild(l),document.addEventListener("keydown",n,!1)}function s(){var t=KB.find(".image-slideshow-overlay");null!==t&&(document.removeEventListener("keydown",n,!1),t.remove())}function l(t){for(var n=0;n<e.images.length;n++)if(e.images[n].id===t)return e.images[n];return null}function c(t,n){var o=new RegExp(e.regex,"g");return e.url[n].replace(o,t.id)}function u(t){return KB.dom("img").attr("src",c(t,"thumbnail")).attr("alt",t.name).attr("title",t.name).data("imageId",t.id).click(a).build()}var p;this.render=function(){p=e.image,t.appendChild(u(p))}}),KB.keyboardShortcuts=function(){function t(t){if(!KB.modal.isOpen()){var e=KB.find(t);null!==e&&(window.location=e.attr("href"))}}function e(){if(KB.modal.isOpen())KB.modal.submitForm();else{var t=$("form");1==t.length?t.submit():t.length>1&&("INPUT"!==document.activeElement.tagName&&"TEXTAREA"!==document.activeElement.tagName||$(document.activeElement).parents("form").submit())}}KB.onKey("?",function(){KB.modal.isOpen()||KB.modal.open(KB.find("body").data("keyboardShortcutUrl"))}),KB.onKey("Escape",function(){KB.exists("#suggest-menu")||(KB.trigger("modal.close"),_KB.get("Dropdown").close())}),KB.onKey("Enter",e,!0,!0),KB.onKey("Enter",e,!0,!1,!0),KB.onKey("b",function(){KB.modal.isOpen()||KB.trigger("board.selector.open")}),KB.exists("#board")&&(KB.onKey("c",function(){KB.modal.isOpen()||_KB.get("BoardHorizontalScrolling").toggle()}),KB.onKey("s",function(){KB.modal.isOpen()||_KB.get("BoardCollapsedMode").toggle()}),KB.onKey("n",function(){KB.modal.isOpen()||KB.modal.open(KB.find("#board").data("taskCreationUrl"),"large",!1)})),KB.exists("#task-view")&&(KB.onKey("e",function(){KB.modal.isOpen()||KB.modal.open(KB.find("#task-view").data("editUrl"),"large",!1)}),KB.onKey("c",function(){KB.modal.isOpen()||KB.modal.open(KB.find("#task-view").data("commentUrl"),"medium",!1)}),KB.onKey("s",function(){KB.modal.isOpen()||KB.modal.open(KB.find("#task-view").data("subtaskUrl"),"medium",!1)}),KB.onKey("l",function(){KB.modal.isOpen()||KB.modal.open(KB.find("#task-view").data("internalLinkUrl"),"medium",!1)})),KB.onKey("f",function(){KB.modal.isOpen()||KB.focus("#form-search")}),KB.onKey("r",function(){if(!KB.modal.isOpen()){var t=$(".filter-reset").data("filter"),e=$("#form-search");e.val(t),$("form.search").submit()}}),KB.onKey("v+o",function(){t("a.view-overview")}),KB.onKey("v+b",function(){t("a.view-board")}),KB.onKey("v+c",function(){t("a.view-calendar")}),KB.onKey("v+l",function(){t("a.view-listing")}),KB.onKey("v+g",function(){t("a.view-gantt")})},function(){function t(t){return"I"===t.target.tagName?t.target.parentNode.getAttribute("href"):t.target.getAttribute("href")}KB.onClick(".js-modal-large",function(e){KB.modal.open(t(e),"large",!1)}),KB.onClick(".js-modal-medium",function(e){KB.modal.isOpen()?KB.modal.replace(t(e)):KB.modal.open(t(e),"medium",!1)}),KB.onClick(".js-modal-small",function(e){KB.modal.open(t(e),"small",!1)}),KB.onClick(".js-modal-confirm",function(e){KB.modal.open(t(e),"small")}),KB.onClick(".js-modal-close",function(){KB.modal.close()}),KB.onClick(".js-modal-replace",function(e){var n=t(e);KB.modal.isOpen()?KB.modal.replace(n):window.location.href=n})}(),KB.onChange(".js-project-creation-select-options",function(t){var e=t.value;"0"===e?KB.find(".js-project-creation-options").hide():KB.find(".js-project-creation-options").show()}),KB.component("project-select-role",function(t,e){function n(t){d=!0,e.role=t.value,a(),o()}function o(){KB.http.postJson(e.url,{id:e.id,role:e.role}).success(function(){d=!1,s=!0,a()}).error(function(){d=!1,s=!1,l=!0,a()})}function a(){KB.dom(r).remove(),r=i(),t.appendChild(r)}function i(){var t=[],o=KB.dom("div");for(var a in e.roles)if(e.roles.hasOwnProperty(a)){var i={value:a,text:e.roles[a]};e.role===a&&(i.selected="selected"),t.push(i)}return o.add(KB.dom("select").change(n).for("option",t).build()),d?(o.text(" "),o.add(KB.dom("i").attr("class","fa fa-spinner fa-pulse fa-fw").build())):s?(o.text(" "),o.add(KB.dom("i").attr("class","fa fa-check fa-fw icon-fade-out icon-success").build())):l&&(o.text(" "),o.add(KB.dom("i").attr("class","fa fa-check fa-fw icon-fade-out icon-error").build())),o.build()}var r,d=!1,s=!1,l=!1;this.render=function(){r=i(),t.appendChild(r)}}),KB.component("screenshot",function(t){function e(t){d(t.target.result)}function n(t){if(t.clipboardData&&t.clipboardData.items){var n=t.clipboardData.items;if(n)for(var o=0;o<n.length;o++)if(n[o].type.indexOf("image")!==-1){var a=n[o].getAsFile(),i=new FileReader;i.onload=e,i.readAsDataURL(a)}}else setTimeout(r,100)}function o(){a(),window.Clipboard||(s=document.createElement("div"),s.id="screenshot-pastezone",s.contentEditable=!0,s.style.opacity=0,s.style.position="fixed",s.style.top=0,s.style.right=0,s.style.width=0,document.body.insertBefore(s,document.body.firstChild),s.focus(),document.addEventListener("click",i),document.getElementById("screenshot-zone").addEventListener("click",i)),window.addEventListener("paste",n,!1)}function a(){KB.exists("#screenshot-pastezone")&&KB.find("#screenshot-pastezone").remove(),document.removeEventListener("click",i),s=null}function i(){null!==s&&s.focus()}function r(){var t=s.childNodes[0];t&&"IMG"===t.tagName&&d(t.src),s.innerHTML=""}function d(t){var e=new Image;e.src=t,e.onload=function(){var e=t.split("base64,");l.value=e[1]};var n=document.getElementById("screenshot-zone");n.innerHTML="",n.className="screenshot-pasted",n.appendChild(e),a(),o()}var s=null,l=null;KB.on("modal.close",function(){a()}),this.render=function(){l=KB.dom("input").attr("type","hidden").attr("name","screenshot").build(),t.appendChild(l),o()}}),KB.component("select-dropdown-autocomplete",function(t,e){function n(){KB.dom(C).show(),KB.dom(x).hide()}function o(){KB.dom(C).hide(),KB.dom(x).show()}function a(){var t=KB.find("#select-dropdown-menu");if(t){var e=y.getBoundingClientRect();t.style("top",document.body.scrollTop+e.bottom+"px")}}function i(t){switch(KB.utils.getKey(t)){case"Escape":w.value="",b();break;case"ArrowUp":t.preventDefault(),t.stopImmediatePropagation(),f();break;case"ArrowDown":t.preventDefault(),t.stopImmediatePropagation(),m();break;case"Enter":t.preventDefault(),t.stopImmediatePropagation(),u()}}function r(){b(),K()}function d(t){KB.dom(t).hasClass("select-dropdown-menu-item")&&(KB.find(".select-dropdown-menu-item.active").removeClass("active"),KB.dom(t).addClass("active"))}function s(){u()}function l(e){t.contains(e.target)||(w.value="",b())}function c(){var t=KB.find("#select-dropdown-menu");null===t?K():b()}function u(){var t=KB.find(".select-dropdown-menu-item.active"),o=t.data("value");k.value=o,w.value=e.items[o],b(),e.redirect?window.location=e.redirect.url.replace(new RegExp(e.redirect.regex,"g"),o):e.replace&&(n(),KB.modal.replace(e.replace.url.replace(new RegExp(e.replace.regex,"g"),o)))}function p(){for(var t=document.querySelectorAll(".select-dropdown-menu-item"),e=0;e<t.length;e++)if(KB.dom(t[e]).hasClass("active")){KB.dom(t[e]).removeClass("active");break}return{items:t,index:e}}function f(){var t=p();t.index>0&&(t.index=t.index-1),KB.dom(t.items[t.index]).addClass("active")}function m(){var t=p();t.index<t.items.length-1&&t.index++,KB.dom(t.items[t.index]).addClass("active")}function h(t){var n=[];for(var o in t)t.hasOwnProperty(o)&&n.push({class:"select-dropdown-menu-item",text:t[o],"data-label":t[o],"data-value":o});return e.sortByKeys?n.sort(function(t,e){var n=t["data-value"].toLowerCase(),o=e["data-value"].toLowerCase();return n<o?-1:n>o?1:0}):n.sort(function(t,e){var n=t["data-label"].toLowerCase(),o=e["data-label"].toLowerCase();return n<o?-1:n>o?1:0}),n}function g(t,n){for(var o=[],a=!1,i=0;i<n.length;i++)if(0===t.length||n[i]["data-label"].toLowerCase().indexOf(t.toLowerCase())>-1){var r=n[i];"undefined"!=typeof e.defaultValue&&String(e.defaultValue)===r["data-value"]&&(r.class+=" active",a=!0),o.push(r)}return!a&&o.length>0&&(o[0].class+=" active"),o}function v(){var t=g(w.value,h(e.items)),n=y.getBoundingClientRect();return 0===t.length?null:KB.dom("ul").attr("id","select-dropdown-menu").style("top",document.body.scrollTop+n.bottom+"px").style("left",n.left+"px").style("width",n.width+"px").style("maxHeight",window.innerHeight-n.bottom-20+"px").mouseover(d).click(s).for("li",t).build()}function b(){var t=KB.find("#select-dropdown-menu");null!==t&&t.remove(), -document.removeEventListener("keydown",i,!1),document.removeEventListener("click",l,!1)}function K(){var t=v();null!==t&&document.body.appendChild(t),document.addEventListener("keydown",i,!1),document.addEventListener("click",l,!1)}function B(){return e.defaultValue&&e.defaultValue in e.items?e.items[e.defaultValue]:e.placeholder?e.placeholder:""}var y,w,k,x,C;this.render=function(){KB.on("select.dropdown.loading.start",n),KB.on("select.dropdown.loading.stop",o),KB.on("modal.close",function(){KB.removeListener("select.dropdown.loading.start",n),KB.removeListener("select.dropdown.loading.stop",o)}),x=KB.dom("i").attr("class","fa fa-chevron-down select-dropdown-chevron").click(c).build(),C=KB.dom("span").hide().addClass("select-loading-icon").add(KB.dom("i").attr("class","fa fa-spinner fa-pulse").build()).build(),k=KB.dom("input").attr("type","hidden").attr("name",e.name).attr("value",e.defaultValue||"").build(),w=KB.dom("input").attr("type","text").attr("placeholder",B()).addClass("select-dropdown-input").style("width",t.offsetWidth-30+"px").on("focus",c).on("input",r,!0).build(),y=KB.dom("div").addClass("select-dropdown-input-container").add(k).add(w).add(x).add(C).build(),t.appendChild(y),e.onFocus&&e.onFocus.forEach(function(t){KB.on(t,function(){w.focus()})}),window.addEventListener("scroll",a,!1)}}),KB.interval(60,function(){var t=KB.find("body").data("statusUrl"),e=KB.find("body").data("loginUrl");null===KB.find(".form-login")&&KB.http.get(t).error(function(){window.location=e})}),KB.component("submit-buttons",function(t,e){function n(){u=!0,c(),KB.trigger("modal.submit")}function o(){KB.trigger("modal.close")}function a(){u=!1,c()}function i(){u=!1,p=!0,c()}function r(){u=!1,p=!1,c()}function d(){KB.dom(m).hide()}function s(t){f=t.submitLabel,c()}function l(){var t=KB.dom("button").attr("type","submit").attr("class","btn btn-"+(e.color||"blue"));return KB.modal.isOpen()&&t.click(n),e.tabindex&&t.attr("tabindex",e.tabindex),u&&t.disable().add(KB.dom("i").attr("class","fa fa-spinner fa-pulse").build()).text(" "),p&&t.disable(),t.text(f).build()}function c(){var t=l();KB.dom(h).replace(t),h=t}var u=!1,p=e.disabled||!1,f=e.submitLabel,m=null,h=null;this.render=function(){KB.on("modal.stop",a),KB.on("modal.disable",i),KB.on("modal.enable",r),KB.on("modal.hide",d),KB.on("modal.submit.label",s),KB.on("modal.close",function(){KB.removeListener("modal.stop",a),KB.removeListener("modal.disable",i),KB.removeListener("modal.enable",r),KB.removeListener("modal.hide",d),KB.removeListener("modal.submit.label",s)}),h=l();var n=KB.dom("div").attr("class","form-actions").add(h);KB.modal.isOpen()&&n.text(" "+e.orLabel+" ").add(KB.dom("a").attr("href","#").click(o).text(e.cancelLabel).build()),m=n.build(),t.appendChild(m)}}),KB.component("suggest-menu",function(t,e){function n(t){switch(KB.utils.getKey(t)){case"Escape":u();break;case"ArrowUp":t.preventDefault(),t.stopImmediatePropagation(),l();break;case"ArrowDown":t.preventDefault(),t.stopImmediatePropagation(),c();break;case"Enter":t.preventDefault(),t.stopImmediatePropagation(),i()}}function o(){i()}function a(t){KB.dom(t).hasClass("suggest-menu-item")&&(KB.find(".suggest-menu-item.active").removeClass("active"),KB.dom(t).addClass("active"))}function i(){t.focus();var e=KB.find(".suggest-menu-item.active"),n=e.data("value"),o=e.data("trigger"),a=t.value,i=r(t),d=o+n+" ",s=KB.utils.getSelectionPosition(t),l=a.substring(0,s.selectionStart-i.length),c=a.substring(s.selectionEnd),p=l.length+d.length;t.value=l+d+c,t.setSelectionRange(p,p),u()}function r(t){var e=t.value.substring(0,t.selectionEnd).split("\n"),n=e[e.length-1],o=n.split(" ");return o[o.length-1]}function d(){for(var t=["#modal-content form","#modal-content","body"],e=0;e<t.length;e++){var n=document.querySelector(t[e]);if(null!==n)return n}return null}function s(){for(var t=document.querySelectorAll(".suggest-menu-item"),e=0;e<t.length;e++)if(KB.dom(t[e]).hasClass("active")){KB.dom(t[e]).removeClass("active");break}return{items:t,index:e}}function l(){var t=s();t.index>0&&(t.index=t.index-1),KB.dom(t.items[t.index]).addClass("active")}function c(){var t=s();t.index<t.items.length-1&&t.index++,KB.dom(t.items[t.index]).addClass("active")}function u(){var t=KB.find("#suggest-menu");null!==t&&t.remove(),document.removeEventListener("keydown",n,!1)}function p(t){var n=r(t),o=f(n,e.triggers);u(),null!==o&&m(o,n.substring(o.length),e.triggers[o])}function f(t,e){for(var n in e)if(e.hasOwnProperty(n)&&0===t.indexOf(n))return n;return null}function m(t,e,n){if("string"==typeof n){var o=new RegExp("SEARCH_TERM","g"),a=n.replace(o,e);KB.http.get(a).success(function(n){h(t,e,n)})}else h(t,e,n)}function h(t,e,n){n=g(e,n),n.length>0&&b(v(t,n))}function g(t,e){var n=[];if(0===t.length)return e;for(var o=0;o<e.length;o++)0===e[o].value.toLowerCase().indexOf(t.toLowerCase())&&n.push(e[o]);return n}function v(t,e){for(var n=[],o=0;o<e.length;o++){var a="suggest-menu-item";0===o&&(a+=" active"),n.push({class:a,html:e[o].html,"data-value":e[o].value,"data-trigger":t})}return n}function b(e){var i=d(),r=getCaretCoordinates(t,t.selectionEnd),s=r.left+t.offsetLeft-t.scrollLeft,l=r.top+t.offsetTop-t.scrollTop+16;document.addEventListener("keydown",n,!1);var c=KB.dom("ul").attr("id","suggest-menu").click(o).mouseover(a).style("left",s+"px").style("top",l+"px").for("li",e).build();i.appendChild(c)}this.render=function(){t.addEventListener("input",function(){p(this)})}}),KB.component("task-move-position",function(t,e){function n(t){var e=KB.dom(document).find("#"+t);return e?parseInt(e.options[e.selectedIndex].value):null}function o(){var t=n("form-swimlanes");return null===t?e.board[0].id:t}function a(){var t=n("form-columns");return null===t?e.board[0].columns[0].id:t}function i(){var t=n("form-position");return null===t?1:t}function r(){var t=KB.find("input[name=positionChoice]:checked");return t?t.value:"before"}function d(){var t=KB.dom(document).find("#form-columns");KB.dom(t).replace(p());var e=KB.dom(document).find("#form-tasks");KB.dom(e).replace(f())}function s(){var t=KB.dom(document).find("#form-tasks");KB.dom(t).replace(f())}function l(t){KB.trigger("modal.stop"),KB.find("#message-container").replace(KB.dom("div").attr("id","message-container").attr("class","alert alert-error").text(t).build())}function c(){var t=i(),n=r();"after"===n&&t++,KB.find("#message-container").replace(KB.dom("div").attr("id","message-container").build()),KB.http.postJson(e.saveUrl,{column_id:a(),swimlane_id:o(),position:t}).error(function(t){t&&l(t.message)})}function u(){var t=[];return e.board.forEach(function(e){t.push({value:e.id,text:e.name})}),KB.dom("select").attr("id","form-swimlanes").change(d).for("option",t).build()}function p(){var t=[],n=o();return e.board.forEach(function(e){n===e.id&&e.columns.forEach(function(e){t.push({value:e.id,text:e.title})})}),KB.dom("select").attr("id","form-columns").change(s).for("option",t).build()}function f(){var t=[],n=o(),i=a(),r=KB.dom("div").attr("id","form-tasks");return e.board.forEach(function(e){n===e.id&&e.columns.forEach(function(e){i===e.id&&e.tasks.forEach(function(e){t.push({value:e.position,text:"#"+e.id+" - "+e.title})})})}),t.length>0&&r.add(KB.html.label(e.positionLabel,"form-position")).add(KB.dom("select").attr("id","form-position").for("option",t).build()).add(KB.html.radio(e.beforeLabel,"positionChoice","before")).add(KB.html.radio(e.afterLabel,"positionChoice","after")),r.build()}this.render=function(){KB.on("modal.submit",c),KB.on("modal.close",function(){KB.removeListener("modal.submit",c)});var n=KB.dom("div").add(KB.dom("div").attr("id","message-container").build()).add(KB.html.label(e.swimlaneLabel,"form-swimlanes")).add(u()).add(KB.html.label(e.columnLabel,"form-columns")).add(p()).add(f()).build();t.appendChild(n)}}),KB.component("text-editor",function(t,e){function n(){var t=KB.dom("div").attr("class","text-editor-toolbar").for("a",[{href:"#",html:'<i class="fa fa-pencil-square-o fa-fw"></i> '+e.labelWrite,click:function(){a()}}]).build();return h=KB.dom("div").attr("class","text-editor-preview-area markdown").build(),KB.dom("div").attr("class","text-editor-view-mode").add(t).add(h).hide().build()}function o(){var t=KB.dom("div").attr("class","text-editor-toolbar").for("a",[{href:"#",html:'<i class="fa fa-eye fa-fw"></i> '+e.labelPreview,click:function(){a()}},{href:"#",html:'<i class="fa fa-bold fa-fw"></i>',click:function(){d("**")}},{href:"#",html:'<i class="fa fa-italic fa-fw"></i>',click:function(){d("_")}},{href:"#",html:'<i class="fa fa-strikethrough fa-fw"></i>',click:function(){d("~~")}},{href:"#",html:'<i class="fa fa-quote-right fa-fw"></i>',click:function(){l("> ")}},{href:"#",html:'<i class="fa fa-list-ul fa-fw"></i>',click:function(){l("* ")}},{href:"#",html:'<i class="fa fa-code fa-fw"></i>',click:function(){s("```")}}]).build(),n=KB.dom("textarea");return n.attr("name",e.name),e.tabindex&&n.attr("tabindex",e.tabindex),e.required&&n.attr("required","required"),n.text(e.text),e.placeholder&&n.attr("placeholder",e.placeholder),p=n.build(),e.suggestOptions&&KB.getComponent("suggest-menu",p,e.suggestOptions).render(),KB.dom("div").attr("class","text-editor-write-mode").add(t).add(p).build()}function a(){KB.dom(h).html(marked(p.value,{sanitize:!0})),KB.dom(f).toggle(),KB.dom(m).toggle()}function i(){return p.value.substring(p.selectionStart,p.selectionEnd)}function r(t,e,n,o){return t.substring(0,e)+o+t.substring(n)}function d(t){var e=i();c(t+e+t),u(t)}function s(t){var e=i();c("\n"+t+"\n"+e+"\n"+t),u(t,2)}function l(t){var e=i();if(e.indexOf("\n")===-1)c("\n"+t+e);else{for(var n=e.split("\n"),o=0;o<n.length;o++)n[o].indexOf(t)===-1&&(n[o]=t+n[o]);c(n.join("\n"))}u(t,1)}function c(t){p.focus();var e=!1,n=KB.utils.getSelectionPosition(p);if(g=n.selectionStart,v=n.selectionEnd,document.queryCommandSupported("insertText")&&(e=document.execCommand("insertText",!1,t)),!e){try{document.execCommand("ms-beginUndoUnit")}catch(t){}p.value=r(p.value,g,v,t);try{document.execCommand("ms-endUndoUnit")}catch(t){}}}function u(t,e){e=e||0;var n=v+t.length+e;p.setSelectionRange(n,n)}var p,f,m,h,g,v;this.render=function(){m=o(),f=n(),t.appendChild(KB.dom("div").attr("class","text-editor").add(f).add(m).build()),e.autofocus&&p.focus()}}),document.addEventListener("DOMContentLoaded",function(){KB.render(),KB.listen(),KB.keyboardShortcuts(),KB.trigger("dom.ready")});var Kanboard={};Kanboard.App=function(){this.controllers={}},Kanboard.App.prototype.get=function(t){return this.controllers[t]},Kanboard.App.prototype.execute=function(){for(var t in Kanboard)if("App"!==t){var e=new Kanboard[t](this);this.controllers[t]=e,"function"==typeof e.execute&&e.execute(),"function"==typeof e.listen&&e.listen(),"function"==typeof e.focus&&e.focus()}this.focus(),this.datePicker(),this.autoComplete(),this.tagAutoComplete()},Kanboard.App.prototype.focus=function(){$(document).on("focus",".auto-select",function(){$(this).select()}),$(document).on("mouseup",".auto-select",function(t){t.preventDefault()})},Kanboard.App.prototype.datePicker=function(){var t=$("body"),e=t.data("js-date-format"),n=t.data("js-time-format"),o=t.data("js-lang");$.datepicker.setDefaults($.datepicker.regional[o]),$.timepicker.setDefaults($.timepicker.regional[o]),$(".form-date").datepicker({showOtherMonths:!0,selectOtherMonths:!0,dateFormat:e,constrainInput:!1}),$(".form-datetime").datetimepicker({dateFormat:e,timeFormat:n,constrainInput:!1})},Kanboard.App.prototype.tagAutoComplete=function(){$(".tag-autocomplete").select2({tags:!0})},Kanboard.App.prototype.autoComplete=function(){$(".autocomplete").each(function(){var t=$(this),e=t.data("dst-field"),n=t.data("dst-extra-field");""===$("#form-"+e).val()&&t.parent().find("button[type=submit]").attr("disabled","disabled"),t.autocomplete({source:t.data("search-url"),minLength:1,select:function(o,a){$("input[name="+e+"]").val(a.item.id),n&&$("input[name="+n+"]").val(a.item[n]),t.parent().find("button[type=submit]").removeAttr("disabled")}})})},Kanboard.App.prototype.hasId=function(t){return!!document.getElementById(t)},Kanboard.App.prototype.showLoadingIcon=function(){$("body").append('<span id="app-loading-icon"> <i class="fa fa-spinner fa-spin"></i></span>')},Kanboard.App.prototype.hideLoadingIcon=function(){$("#app-loading-icon").remove()},Kanboard.BoardCollapsedMode=function(t){this.app=t},Kanboard.BoardCollapsedMode.prototype.toggle=function(){var t=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:$('.filter-display-mode:not([style="display: none;"]) a').attr("href"),success:function(e){$(".filter-display-mode").toggle(),t.app.get("BoardDragAndDrop").refresh(e)}})},Kanboard.BoardColumnView=function(t){this.app=t},Kanboard.BoardColumnView.prototype.execute=function(){this.app.hasId("board")&&this.render()},Kanboard.BoardColumnView.prototype.listen=function(){var t=this;$(document).on("click",".board-toggle-column-view",function(){t.toggle($(this).data("column-id"))})},Kanboard.BoardColumnView.prototype.onBoardRendered=function(){this.render()},Kanboard.BoardColumnView.prototype.render=function(){var t=this;$(".board-column-header").each(function(){var e=$(this).data("column-id");localStorage.getItem("hidden_column_"+e)&&t.hideColumn(e)})},Kanboard.BoardColumnView.prototype.toggle=function(t){localStorage.getItem("hidden_column_"+t)?this.showColumn(t):this.hideColumn(t),this.app.get("BoardDragAndDrop").dragAndDrop()},Kanboard.BoardColumnView.prototype.hideColumn=function(t){$(".board-column-"+t+" .board-column-expanded").hide(),$(".board-column-"+t+" .board-column-collapsed").show(),$(".board-column-header-"+t+" .board-column-expanded").hide(),$(".board-column-header-"+t+" .board-column-collapsed").show(),$(".board-column-header-"+t).each(function(){$(this).removeClass("board-column-compact"),$(this).addClass("board-column-header-collapsed")}),$(".board-column-"+t).each(function(){$(this).addClass("board-column-task-collapsed")}),$(".board-column-"+t+" .board-rotation").each(function(){$(this).css("width",$(".board-column-"+t).height())}),localStorage.setItem("hidden_column_"+t,1)},Kanboard.BoardColumnView.prototype.showColumn=function(t){$(".board-column-"+t+" .board-column-expanded").show(),$(".board-column-"+t+" .board-column-collapsed").hide(),$(".board-column-header-"+t+" .board-column-expanded").show(),$(".board-column-header-"+t+" .board-column-collapsed").hide(),$(".board-column-header-"+t).removeClass("board-column-header-collapsed"),$(".board-column-"+t).removeClass("board-column-task-collapsed"),0==localStorage.getItem("horizontal_scroll")&&$(".board-column-header-"+t).addClass("board-column-compact"),localStorage.removeItem("hidden_column_"+t)},Kanboard.BoardHorizontalScrolling=function(t){this.app=t},Kanboard.BoardHorizontalScrolling.prototype.execute=function(){this.app.hasId("board")&&this.render()},Kanboard.BoardHorizontalScrolling.prototype.listen=function(){var t=this;$(document).on("click",".filter-toggle-scrolling",function(e){e.preventDefault(),t.toggle()})},Kanboard.BoardHorizontalScrolling.prototype.onBoardRendered=function(){this.render()},Kanboard.BoardHorizontalScrolling.prototype.toggle=function(){var t=localStorage.getItem("horizontal_scroll")||1;localStorage.setItem("horizontal_scroll",0==t?1:0),this.render()},Kanboard.BoardHorizontalScrolling.prototype.render=function(){0==localStorage.getItem("horizontal_scroll")?($(".filter-wide").show(),$(".filter-compact").hide(),$("#board-container").addClass("board-container-compact"),$("#board th:not(.board-column-header-collapsed)").addClass("board-column-compact")):($(".filter-wide").hide(),$(".filter-compact").show(),$("#board-container").removeClass("board-container-compact"),$("#board th").removeClass("board-column-compact"))},Kanboard.BoardPolling=function(t){this.app=t},Kanboard.BoardPolling.prototype.execute=function(){if(this.app.hasId("board")){var t=parseInt($("#board").attr("data-check-interval"));t>0&&window.setInterval(this.check.bind(this),1e3*t)}},Kanboard.BoardPolling.prototype.check=function(){if(KB.utils.isVisible()&&!this.app.get("BoardDragAndDrop").savingInProgress){var t=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:$("#board").data("check-url"),statusCode:{200:function(e){t.app.get("BoardDragAndDrop").refresh(e)},304:function(){t.app.hideLoadingIcon()}}})}},Kanboard.Column=function(t){this.app=t},Kanboard.Column.prototype.listen=function(){this.dragAndDrop()},Kanboard.Column.prototype.dragAndDrop=function(){var t=this;$(".draggable-row-handle").mouseenter(function(){$(this).parent().parent().addClass("draggable-item-hover")}).mouseleave(function(){$(this).parent().parent().removeClass("draggable-item-hover")}),$(".columns-table tbody").sortable({forcePlaceholderSize:!0,handle:"td:first i",helper:function(t,e){return e.children().each(function(){$(this).width($(this).width())}),e},stop:function(e,n){var o=n.item;o.removeClass("draggable-item-selected"),t.savePosition(o.data("column-id"),o.index()+1)},start:function(t,e){e.item.addClass("draggable-item-selected")}}).disableSelection()},Kanboard.Column.prototype.savePosition=function(t,e){var n=$(".columns-table").data("save-position-url"),o=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:n,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({column_id:t,position:e}),complete:function(){o.app.hideLoadingIcon()}})},Kanboard.Dropdown=function(t){this.app=t},Kanboard.Dropdown.prototype.listen=function(){var t=this;$(document).on("click",function(){t.close()}),$(document).on("click",".dropdown-menu",function(e){e.preventDefault(),e.stopImmediatePropagation(),t.close();var n=$(this).next("ul"),o=$(this).offset();$("body").append(jQuery("<div>",{id:"dropdown"})),n.clone().appendTo("#dropdown");var a=$("#dropdown ul");a.addClass("dropdown-submenu-open");var i=a.outerHeight(),r=a.outerWidth();o.top+i-$(window).scrollTop()<$(window).height()||$(window).scrollTop()+o.top<i?a.css("top",o.top+$(this).height()):a.css("top",o.top-i-5),o.left+r>$(window).width()?a.css("left",o.left-r+$(this).outerWidth()):a.css("left",o.left)}),$(document).on("click",".dropdown-submenu-open li",function(t){$(t.target).is("li")&&$(this).find("a:visible")[0].click()})},Kanboard.Dropdown.prototype.close=function(){$("#dropdown").remove()},Kanboard.Gantt=function(t){this.app=t,this.data=[],this.options={container:"#gantt-chart",showWeekends:!0,allowMoves:!0,allowResizes:!0,cellWidth:21,cellHeight:31,slideWidth:1e3,vHeaderWidth:200}},Kanboard.Gantt.prototype.execute=function(){this.app.hasId("gantt-chart")&&this.show()},Kanboard.Gantt.prototype.saveRecord=function(t){this.app.showLoadingIcon(),$.ajax({cache:!1,url:$(this.options.container).data("save-url"),contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify(t),complete:this.app.hideLoadingIcon.bind(this)})},Kanboard.Gantt.prototype.show=function(){this.data=this.prepareData($(this.options.container).data("records"));var t=Math.floor(this.options.slideWidth/this.options.cellWidth+5),e=this.getDateRange(t),n=e[0],o=e[1],a=$(this.options.container),i=jQuery("<div>",{class:"ganttview"});i.append(this.renderVerticalHeader()),i.append(this.renderSlider(n,o)),a.append(i),jQuery("div.ganttview-grid-row div.ganttview-grid-row-cell:last-child",a).addClass("last"),jQuery("div.ganttview-hzheader-days div.ganttview-hzheader-day:last-child",a).addClass("last"),jQuery("div.ganttview-hzheader-months div.ganttview-hzheader-month:last-child",a).addClass("last"),$(this.options.container).data("readonly")?(this.options.allowResizes=!1,this.options.allowMoves=!1):(this.listenForBlockResize(n),this.listenForBlockMove(n))},Kanboard.Gantt.prototype.renderVerticalHeader=function(){for(var t=jQuery("<div>",{class:"ganttview-vtheader"}),e=jQuery("<div>",{class:"ganttview-vtheader-item"}),n=jQuery("<div>",{class:"ganttview-vtheader-series"}),o=0;o<this.data.length;o++){var a=jQuery("<span>").append(jQuery("<i>",{class:"fa fa-info-circle tooltip",title:this.getVerticalHeaderTooltip(this.data[o])})).append(" ");"task"==this.data[o].type?a.append(jQuery("<a>",{href:this.data[o].link,title:this.data[o].title}).text(this.data[o].title)):a.append(jQuery("<a>",{href:this.data[o].board_link,title:$(this.options.container).data("label-board-link")}).append('<i class="fa fa-th"></i>')).append(" ").append(jQuery("<a>",{href:this.data[o].gantt_link,title:$(this.options.container).data("label-gantt-link")}).append('<i class="fa fa-sliders"></i>')).append(" ").append(jQuery("<a>",{href:this.data[o].link}).text(this.data[o].title)),n.append(jQuery("<div>",{class:"ganttview-vtheader-series-name"}).append(a))}return e.append(n),t.append(e),t},Kanboard.Gantt.prototype.renderSlider=function(t,e){var n=jQuery("<div>",{class:"ganttview-slide-container"}),o=this.getDates(t,e);return n.append(this.renderHorizontalHeader(o)),n.append(this.renderGrid(o)),n.append(this.addBlockContainers()),this.addBlocks(n,t),n},Kanboard.Gantt.prototype.renderHorizontalHeader=function(t){var e=jQuery("<div>",{class:"ganttview-hzheader"}),n=jQuery("<div>",{class:"ganttview-hzheader-months"}),o=jQuery("<div>",{class:"ganttview-hzheader-days"}),a=0;for(var i in t)for(var r in t[i]){var d=t[i][r].length*this.options.cellWidth;a+=d,n.append(jQuery("<div>",{class:"ganttview-hzheader-month",css:{width:d-1+"px"}}).append($.datepicker.regional[$("body").data("js-lang")].monthNames[r]+" "+i));for(var s in t[i][r])o.append(jQuery("<div>",{class:"ganttview-hzheader-day"}).append(t[i][r][s].getDate()))}return n.css("width",a+"px"),o.css("width",a+"px"),e.append(n).append(o),e},Kanboard.Gantt.prototype.renderGrid=function(t){var e=jQuery("<div>",{class:"ganttview-grid"}),n=jQuery("<div>",{class:"ganttview-grid-row"});for(var o in t)for(var a in t[o])for(var i in t[o][a]){var r=jQuery("<div>",{class:"ganttview-grid-row-cell"});this.options.showWeekends&&this.isWeekend(t[o][a][i])&&r.addClass("ganttview-weekend"),n.append(r)}var d=jQuery("div.ganttview-grid-row-cell",n).length*this.options.cellWidth;n.css("width",d+"px"),e.css("width",d+"px");for(var s=0;s<this.data.length;s++)e.append(n.clone());return e},Kanboard.Gantt.prototype.addBlockContainers=function(){for(var t=jQuery("<div>",{class:"ganttview-blocks"}),e=0;e<this.data.length;e++)t.append(jQuery("<div>",{class:"ganttview-block-container"}));return t},Kanboard.Gantt.prototype.addBlocks=function(t,e){for(var n=jQuery("div.ganttview-blocks div.ganttview-block-container",t),o=0,a=0;a<this.data.length;a++){var i=this.data[a],r=this.daysBetween(i.start,i.end)+1,d=this.daysBetween(e,i.start),s=jQuery("<div>",{class:"ganttview-block-text"}),l=jQuery("<div>",{class:"ganttview-block tooltip"+(this.options.allowMoves?" ganttview-block-movable":""),title:this.getBarTooltip(i),css:{width:r*this.options.cellWidth-9+"px","margin-left":d*this.options.cellWidth+"px"}}).append(s);r>=2&&s.append(i.progress),l.data("record",i),this.setBarColor(l,i),jQuery(n[o]).append(l),o+=1}},Kanboard.Gantt.prototype.getVerticalHeaderTooltip=function(t){var e="";if("task"==t.type)e=jQuery("<span>").append(jQuery("<strong>").text(t.column_title)).append(document.createTextNode(" ("+t.progress+")")).append(jQuery("<br>")).append(document.createTextNode(t.title)).prop("outerHTML");else{var n=["project-manager","project-member"];for(var o in n){var a=n[o];if(!jQuery.isEmptyObject(t.users[a])){var i=jQuery("<ul>");for(var r in t.users[a])r&&i.append(jQuery("<li>").text(t.users[a][r]));e+="<p><strong>"+$(this.options.container).data("label-"+a)+"</strong></p>"+i.prop("outerHTML")}}}return e},Kanboard.Gantt.prototype.getBarTooltip=function(t){var e="";if(t.not_defined)e=$(this.options.container).data("label-not-defined");else{if("task"==t.type){var n=$(this.options.container).data("label-assignee");e+=jQuery("<strong>").text(t.progress).prop("outerHTML"),e+="<br>",e+=jQuery("<span>").append(document.createTextNode(n+" "+(t.assignee?t.assignee:""))).prop("outerHTML"),e+="<br>"}e+=$(this.options.container).data("label-start-date")+" "+$.datepicker.formatDate("yy-mm-dd",t.start)+"<br/>",e+=$(this.options.container).data("label-end-date")+" "+$.datepicker.formatDate("yy-mm-dd",t.end)}return e},Kanboard.Gantt.prototype.setBarColor=function(t,e){e.not_defined?t.addClass("ganttview-block-not-defined"):(t.css("background-color",e.color.background),t.css("border-color",e.color.border),"0%"!=e.progress&&t.append(jQuery("<div>",{css:{"z-index":0,position:"absolute",top:0,bottom:0,"background-color":e.color.border,width:e.progress,opacity:.4}})))},Kanboard.Gantt.prototype.listenForBlockResize=function(t){var e=this;jQuery("div.ganttview-block",this.options.container).resizable({grid:this.options.cellWidth,handles:"e,w",delay:300,stop:function(){var n=jQuery(this);e.updateDataAndPosition(n,t),e.saveRecord(n.data("record"))}})},Kanboard.Gantt.prototype.listenForBlockMove=function(t){var e=this;jQuery("div.ganttview-block",this.options.container).draggable({axis:"x",delay:300,grid:[this.options.cellWidth,this.options.cellWidth],stop:function(){var n=jQuery(this);e.updateDataAndPosition(n,t),e.saveRecord(n.data("record"))}})},Kanboard.Gantt.prototype.updateDataAndPosition=function(t,e){var n=jQuery("div.ganttview-slide-container",this.options.container),o=n.scrollLeft(),a=t.offset().left-n.offset().left-1+o,i=t.data("record");i.not_defined=!1,this.setBarColor(t,i);var r=Math.round(a/this.options.cellWidth),d=this.addDays(this.cloneDate(e),r);i.start=d;var s=t.outerWidth(),l=Math.round(s/this.options.cellWidth)-1;i.end=this.addDays(this.cloneDate(d),l),"task"===i.type&&l>0&&jQuery("div.ganttview-block-text",t).text(i.progress),t.attr("title",this.getBarTooltip(i)),t.data("record",i),t.css("top","").css("left","").css("position","relative").css("margin-left",a+"px")},Kanboard.Gantt.prototype.getDates=function(t,e){var n=[];n[t.getFullYear()]=[],n[t.getFullYear()][t.getMonth()]=[t];for(var o=t;this.compareDate(o,e)==-1;){var a=this.addDays(this.cloneDate(o),1);n[a.getFullYear()]||(n[a.getFullYear()]=[]),n[a.getFullYear()][a.getMonth()]||(n[a.getFullYear()][a.getMonth()]=[]),n[a.getFullYear()][a.getMonth()].push(a),o=a}return n},Kanboard.Gantt.prototype.prepareData=function(t){for(var e=0;e<t.length;e++){var n=new Date(t[e].start[0],t[e].start[1]-1,t[e].start[2],0,0,0,0);t[e].start=n;var o=new Date(t[e].end[0],t[e].end[1]-1,t[e].end[2],0,0,0,0);t[e].end=o}return t},Kanboard.Gantt.prototype.getDateRange=function(t){for(var e=new Date,n=new Date,o=0;o<this.data.length;o++){var a=new Date;a.setTime(Date.parse(this.data[o].start));var i=new Date;i.setTime(Date.parse(this.data[o].end)),0==o&&(e=a,n=i),1==this.compareDate(e,a)&&(e=a),this.compareDate(n,i)==-1&&(n=i)}return this.daysBetween(e,n)<t&&(n=this.addDays(this.cloneDate(e),t)),e.setDate(e.getDate()-1),[e,n]},Kanboard.Gantt.prototype.daysBetween=function(t,e){if(!t||!e)return 0;for(var n=0,o=this.cloneDate(t);this.compareDate(o,e)==-1;)n+=1,this.addDays(o,1);return n},Kanboard.Gantt.prototype.isWeekend=function(t){return t.getDay()%6==0},Kanboard.Gantt.prototype.cloneDate=function(t){return new Date(t.getTime())},Kanboard.Gantt.prototype.addDays=function(t,e){return t.setDate(t.getDate()+1*e),t},Kanboard.Gantt.prototype.compareDate=function(t,e){if(isNaN(t)||isNaN(e))throw new Error(t+" - "+e);if(t instanceof Date&&e instanceof Date)return t<e?-1:t>e?1:0;throw new TypeError(t+" - "+e)},Kanboard.Search=function(t){this.app=t},Kanboard.Search.prototype.focus=function(){$(document).on("focus","#form-search",function(){var t=$("#form-search");if(t[0].setSelectionRange){var e=2*t.val().length;t[0].setSelectionRange(e,e)}})},Kanboard.Search.prototype.listen=function(){$(document).on("click",".filter-helper",function(t){t.preventDefault();var e=$(this).data("filter"),n=$(this).data("append-filter"),o=$(this).data("unique-filter"),a=$("#form-search");if(o){var i=o.substr(0,o.indexOf(":"));e=a.val().replace(new RegExp("("+i+":[#a-z0-9]+)","g"),""),e=e.replace(new RegExp("("+i+':"(.+)")',"g"),""),e=e.trim(),e+=" "+o}else n&&(e=a.val()+" "+n);a.val(e),$("form.search").submit()})},Kanboard.Subtask=function(t){this.app=t},Kanboard.Subtask.prototype.listen=function(){this.dragAndDrop(),$(document).on("click",".js-subtask-toggle-status",function(t){var e=$(this);t.preventDefault(),$.ajax({cache:!1,url:e.attr("href"),success:function(t){$(e).closest(".subtask-title").replaceWith(t)}})}),$(document).on("click",".js-subtask-toggle-timer",function(t){var e=$(this);t.preventDefault(),$.ajax({cache:!1,url:e.attr("href"),success:function(t){$(e).closest(".subtask-time-tracking").replaceWith(t)}})})},Kanboard.Subtask.prototype.dragAndDrop=function(){var t=this;$(".draggable-row-handle").mouseenter(function(){$(this).parent().parent().addClass("draggable-item-hover")}).mouseleave(function(){$(this).parent().parent().removeClass("draggable-item-hover")}),$(".subtasks-table tbody").sortable({forcePlaceholderSize:!0,handle:"td:first i",helper:function(t,e){return e.children().each(function(){$(this).width($(this).width())}),e},stop:function(e,n){var o=n.item;o.removeClass("draggable-item-selected"),t.savePosition(o.data("subtask-id"),o.index()+1)},start:function(t,e){e.item.addClass("draggable-item-selected")}}).disableSelection()},Kanboard.Subtask.prototype.savePosition=function(t,e){var n=$(".subtasks-table").data("save-position-url"),o=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:n,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({subtask_id:t,position:e}),complete:function(){o.app.hideLoadingIcon()}})},Kanboard.Swimlane=function(t){this.app=t},Kanboard.Swimlane.prototype.execute=function(){$(".swimlanes-table").length&&this.dragAndDrop()},Kanboard.Swimlane.prototype.listen=function(){var t=this;$(document).on("click",".board-swimlane-toggle",function(e){e.preventDefault();var n=$(this).data("swimlane-id");t.isCollapsed(n)?t.expand(n):t.collapse(n)})},Kanboard.Swimlane.prototype.onBoardRendered=function(){for(var t=this.getAllCollapsed(),e=0;e<t.length;e++)this.collapse(t[e])},Kanboard.Swimlane.prototype.getStorageKey=function(){return"hidden_swimlanes_"+$("#board").data("project-id")},Kanboard.Swimlane.prototype.expand=function(t){var e=this.getAllCollapsed(),n=e.indexOf(t);n>-1&&e.splice(n,1),localStorage.setItem(this.getStorageKey(),JSON.stringify(e)),$(".board-swimlane-columns-"+t).css("display","table-row"),$(".board-swimlane-tasks-"+t).css("display","table-row"),$(".hide-icon-swimlane-"+t).css("display","inline"),$(".show-icon-swimlane-"+t).css("display","none")},Kanboard.Swimlane.prototype.collapse=function(t){var e=this.getAllCollapsed();e.indexOf(t)<0&&(e.push(t),localStorage.setItem(this.getStorageKey(),JSON.stringify(e))),$(".board-swimlane-columns-"+t+":not(:first-child)").css("display","none"),$(".board-swimlane-tasks-"+t).css("display","none"),$(".hide-icon-swimlane-"+t).css("display","none"),$(".show-icon-swimlane-"+t).css("display","inline")},Kanboard.Swimlane.prototype.isCollapsed=function(t){return this.getAllCollapsed().indexOf(t)>-1},Kanboard.Swimlane.prototype.getAllCollapsed=function(){return JSON.parse(localStorage.getItem(this.getStorageKey()))||[]},Kanboard.Swimlane.prototype.dragAndDrop=function(){var t=this;$(".draggable-row-handle").mouseenter(function(){$(this).parent().parent().addClass("draggable-item-hover")}).mouseleave(function(){$(this).parent().parent().removeClass("draggable-item-hover")}),$(".swimlanes-table tbody").sortable({forcePlaceholderSize:!0,handle:"td:first i",helper:function(t,e){return e.children().each(function(){$(this).width($(this).width())}),e},stop:function(e,n){var o=n.item;o.removeClass("draggable-item-selected"),t.savePosition(o.data("swimlane-id"),o.index()+1)},start:function(t,e){e.item.addClass("draggable-item-selected")}}).disableSelection()},Kanboard.Swimlane.prototype.savePosition=function(t,e){var n=$(".swimlanes-table").data("save-position-url"),o=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:n,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({ -swimlane_id:t,position:e}),complete:function(){o.app.hideLoadingIcon()}})},Kanboard.Task=function(t){this.app=t},Kanboard.Task.prototype.onPopoverOpened=function(){var t=this;t.renderColorPicker(),$(document).on("click",".assign-me",function(t){var e=$(this).data("current-id"),n="#"+$(this).data("target-id");t.preventDefault(),$(n+" option[value="+e+"]").length&&$(n).val(e)})},Kanboard.Task.prototype.renderColorPicker=function(){function t(t){return $('<div class="color-picker-option"><div class="color-picker-square color-'+t.id+'"></div><div class="color-picker-label">'+t.text+"</div></div>")}$(".color-picker").select2({minimumResultsForSearch:1/0,templateResult:t,templateSelection:t})},Kanboard.Tooltip=function(t){this.app=t},Kanboard.Tooltip.prototype.onBoardRendered=function(){this.execute()},Kanboard.Tooltip.prototype.execute=function(){$(".tooltip").tooltip({track:!1,show:!1,hide:!1,position:{my:"left-20 top",at:"center bottom+9",using:function(t,e){$(this).css(t);var n=e.target.left+e.target.width/2-e.element.left-20;$("<div>").addClass("tooltip-arrow").addClass(e.vertical).addClass(n<1?"align-left":"align-right").appendTo(this)}},content:function(){var t=this,e=$(this).attr("data-href");return e?($.get(e,function(e){var n=$(".ui-tooltip:visible");$(".ui-tooltip-content:visible").html(e),n.css({top:"",left:""}),n.children(".tooltip-arrow").remove();var o=$(t).tooltip("option","position");o.of=$(t),n.position(o)}),'<i class="fa fa-spinner fa-spin"></i>'):'<div class="markdown">'+$(this).attr("title")+"</div>"}}).on("mouseenter",function(){var t=this;$(this).tooltip("open"),$(".ui-tooltip").on("mouseleave",function(){$(t).tooltip("close")})}).on("mouseleave focusout",function(t){t.stopImmediatePropagation();var e=this;setTimeout(function(){$(".ui-tooltip:hover").length||$(e).tooltip("close")},100)})},Kanboard.BoardDragAndDrop=function(t){this.app=t,this.savingInProgress=!1},Kanboard.BoardDragAndDrop.prototype.execute=function(){this.app.hasId("board")&&(this.executeListeners(),this.dragAndDrop())},Kanboard.BoardDragAndDrop.prototype.dragAndDrop=function(){var t=this,e=$(".board-task-list"),n={forcePlaceholderSize:!0,tolerance:"pointer",connectWith:".sortable-column",placeholder:"draggable-placeholder",items:".draggable-item",stop:function(e,n){var o=n.item,a=o.attr("data-task-id"),i=o.attr("data-position"),r=o.attr("data-column-id"),d=o.attr("data-swimlane-id"),s=o.parent().attr("data-column-id"),l=o.parent().attr("data-swimlane-id"),c=o.index()+1;o.removeClass("draggable-item-selected"),s==r&&l==d&&c==i||(t.changeTaskState(a),t.save(a,r,s,c,l))},start:function(t,e){e.item.addClass("draggable-item-selected"),e.placeholder.height(e.item.height())}};isMobile.any&&($(".task-board-sort-handle").css("display","inline"),n.handle=".task-board-sort-handle"),e.each(function(){$(this).css("min-height",$(this).parent().height())}),e.sortable(n)},Kanboard.BoardDragAndDrop.prototype.changeTaskState=function(t){var e=$("div[data-task-id="+t+"]");e.addClass("task-board-saving-state"),e.find(".task-board-saving-icon").show()},Kanboard.BoardDragAndDrop.prototype.save=function(t,e,n,o,a){var i=this;i.app.showLoadingIcon(),i.savingInProgress=!0,$.ajax({cache:!1,url:$("#board").data("save-url"),contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({task_id:t,src_column_id:e,dst_column_id:n,swimlane_id:a,position:o}),success:function(t){i.refresh(t),i.savingInProgress=!1},error:function(){i.app.hideLoadingIcon(),i.savingInProgress=!1},statusCode:{403:function(t){window.alert(t.responseJSON.message),document.location.reload(!0)}}})},Kanboard.BoardDragAndDrop.prototype.refresh=function(t){$("#board-container").replaceWith(t),this.app.hideLoadingIcon(),this.executeListeners(),this.dragAndDrop()},Kanboard.BoardDragAndDrop.prototype.executeListeners=function(){for(var t in this.app.controllers){var e=this.app.get(t);"function"==typeof e.onBoardRendered&&e.onBoardRendered()}};var _KB=null;jQuery(document).ready(function(){_KB=new Kanboard.App,_KB.execute()});
\ No newline at end of file +!function(){function e(e,a,i){if(!o)throw new Error("textarea-caret-position#getCaretCoordinates should only be called in a browser");var r=i&&i.debug||!1;if(r){var d=document.querySelector("#input-textarea-caret-position-mirror-div");d&&d.parentNode.removeChild(d)}var s=document.createElement("div");s.id="input-textarea-caret-position-mirror-div",document.body.appendChild(s);var l=s.style,c=window.getComputedStyle?getComputedStyle(e):e.currentStyle;l.whiteSpace="pre-wrap","INPUT"!==e.nodeName&&(l.wordWrap="break-word"),l.position="absolute",r||(l.visibility="hidden"),t.forEach(function(e){l[e]=c[e]}),n?e.scrollHeight>parseInt(c.height)&&(l.overflowY="scroll"):l.overflow="hidden",s.textContent=e.value.substring(0,a),"INPUT"===e.nodeName&&(s.textContent=s.textContent.replace(/\s/g,"Â "));var u=document.createElement("span");u.textContent=e.value.substring(a)||".",s.appendChild(u);var m={top:u.offsetTop+parseInt(c.borderTopWidth),left:u.offsetLeft+parseInt(c.borderLeftWidth)};return r?u.style.backgroundColor="#aaa":document.body.removeChild(s),m}var t=["direction","boxSizing","width","height","overflowX","overflowY","borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth","borderStyle","paddingTop","paddingRight","paddingBottom","paddingLeft","fontStyle","fontVariant","fontWeight","fontStretch","fontSize","fontSizeAdjust","lineHeight","fontFamily","textAlign","textTransform","textIndent","textDecoration","letterSpacing","wordSpacing","tabSize","MozTabSize"],o="undefined"!=typeof window,n=o&&null!=window.mozInnerScreenX;"undefined"!=typeof module&&void 0!==module.exports?module.exports=e:o&&(window.getCaretCoordinates=e)}(),function(){function e(){if(!("KeyboardEvent"in window)||"key"in KeyboardEvent.prototype)return!1;var e={get:function(e){var t=o.keys[this.which||this.keyCode];return Array.isArray(t)&&(t=t[+this.shiftKey]),t}};return Object.defineProperty(KeyboardEvent.prototype,"key",e),e}var t,o={polyfill:e,keys:{3:"Cancel",6:"Help",8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",28:"Convert",29:"NonConvert",30:"Accept",31:"ModeChange",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",41:"Select",42:"Print",43:"Execute",44:"PrintScreen",45:"Insert",46:"Delete",48:["0",")"],49:["1","!"],50:["2","@"],51:["3","#"],52:["4","$"],53:["5","%"],54:["6","^"],55:["7","&"],56:["8","*"],57:["9","("],91:"OS",93:"ContextMenu",144:"NumLock",145:"ScrollLock",181:"VolumeMute",182:"VolumeDown",183:"VolumeUp",186:[";",":"],187:["=","+"],188:[",","<"],189:["-","_"],190:[".",">"],191:["/","?"],192:["`","~"],219:["[","{"],220:["\\","|"],221:["]","}"],222:["'",'"'],224:"Meta",225:"AltGraph",246:"Attn",247:"CrSel",248:"ExSel",249:"EraseEof",250:"Play",251:"ZoomOut"}};for(t=1;t<25;t++)o.keys[111+t]="F"+t;var n="";for(t=65;t<91;t++)n=String.fromCharCode(t),o.keys[t]=[n.toLowerCase(),n.toUpperCase()];o.polyfill()}(),Element.prototype.matches||(Element.prototype.matches=Element.prototype.matchesSelector||Element.prototype.mozMatchesSelector||Element.prototype.msMatchesSelector||Element.prototype.oMatchesSelector||Element.prototype.webkitMatchesSelector||function(e){for(var t=(this.document||this.ownerDocument).querySelectorAll(e),o=t.length;--o>=0&&t.item(o)!==this;);return o>-1});var KB={components:{},utils:{},html:{},http:{},listeners:{clicks:{},changes:{},keys:[],internals:{}}};KB.on=function(e,t){this.listeners.internals.hasOwnProperty(e)||(this.listeners.internals[e]=[]),this.listeners.internals[e].push(t)},KB.trigger=function(e,t){if(this.listeners.internals.hasOwnProperty(e))for(var o=0;o<this.listeners.internals[e].length;o++)this.listeners.internals[e][o](t)},KB.removeListener=function(e,t){if(this.listeners.internals.hasOwnProperty(e))for(var o=0;o<this.listeners.internals[e].length;o++)this.listeners.internals[e][o]===t&&this.listeners.internals[e].splice(o,1)},KB.onClick=function(e,t,o){this.listeners.clicks[e]={callback:t,noPreventDefault:!0===o}},KB.onChange=function(e,t){this.listeners.changes[e]=t},KB.onKey=function(e,t,o,n,a){this.listeners.keys.push({combination:e,callback:t,ignoreInputField:o||!1,ctrlKey:n||!1,metaKey:a||!1})},KB.listen=function(){function e(e){for(var t in n.listeners.clicks)n.listeners.clicks.hasOwnProperty(t)&&e.target.matches(t)&&(n.listeners.clicks[t].noPreventDefault||e.preventDefault(),n.listeners.clicks[t].callback(e))}function t(e){for(var t in n.listeners.changes)n.listeners.changes.hasOwnProperty(t)&&e.target.matches(t)&&n.listeners.changes[t](e.target)}function o(e){var t=KB.utils.getKey(e),o=KB.utils.isInputField(e);if(o&&-1===["Escape","Enter"].indexOf(t)||a.push(t),a.length>0){for(var i=!0,r=0;r<n.listeners.keys.length;r++){var d=n.listeners.keys[r],s=d.combination,l=s.split("+");if(KB.utils.arraysIdentical(a,l)&&e.ctrlKey===d.ctrlKey&&e.metaKey===d.metaKey){if(o&&!d.ignoreInputField)return void(a=[]);e.preventDefault(),e.stopPropagation(),a=[],d.callback(e);break}KB.utils.arraysStartsWith(a,l)&&a.length<l.length&&(i=!1)}i&&(a=[])}}var n=this,a=[];document.addEventListener("click",e,!1),document.addEventListener("change",t,!1),window.addEventListener("keydown",o,!1)},KB.component=function(e,t){this.components[e]=t},KB.getComponent=function(e,t,o){return new(0,this.components[e])(t,o)},KB.render=function(){for(var e in this.components)for(var t=document.querySelectorAll(".js-"+e),o=0;o<t.length;o++)if(this.components.hasOwnProperty(e)){var n;t[o].dataset.params&&(n=JSON.parse(t[o].dataset.params));var a=KB.getComponent(e,t[o],n);a.render(),t[o].className=t[o].className+"-rendered"}},KB.interval=function(e,t){setInterval(t,1e3*e)},KB.dom=function(e){function t(e){var t="string"==typeof e?document.createElement(e):e;this.attr=function(e,o){return null!==o&&void 0!==o?(t.setAttribute(e,o),this):t.getAttribute(e)},this.data=function(e,o){return 1===arguments.length?t.dataset[e]:(t.dataset[e]=o,this)},this.hide=function(){return t.style.display="none",this},this.show=function(){return t.style.display="block",this},this.toggle=function(){return"none"===t.style.display?this.show():this.hide(),this},this.style=function(e,o){return t.style[e]=o,this},this.on=function(e,o,n){return t.addEventListener(e,function(e){n||e.preventDefault(),o(e.target)}),this},this.click=function(e){return this.on("click",e)},this.mouseover=function(e){return this.on("mouseover",e)},this.change=function(e){return this.on("change",e)},this.add=function(e){return t.appendChild(e),this},this.replace=function(e){return t.parentNode.replaceChild(e,t),this},this.html=function(e){return t.innerHTML=e,this},this.text=function(e){return t.appendChild(document.createTextNode(e)),this},this.replaceText=function(e){return t.textContent=e,this},this.addClass=function(e){return t.classList.add(e),this},this.removeClass=function(e){return t.classList.remove(e),this},this.toggleClass=function(e){return t.classList.toggle(e),this},this.hasClass=function(e){return t.classList.contains(e)},this.disable=function(){return t.disabled=!0,this},this.enable=function(){return t.disabled=!1,this},this.remove=function(){return t.parentNode.removeChild(t),this},this.empty=function(){for(;t.firstChild;)t.removeChild(t.firstChild);return this},this.parent=function(e){for(;t&&t!==document;t=t.parentNode)if(t.matches(e))return t;return null},this.find=function(e){return t.querySelector(e)},this.for=function(e,o){for(var n=0;n<o.length;n++){var a=o[n];if("object"!=typeof a)t.appendChild(KB.dom(e).text(a).build());else{var i=KB.dom(e);for(var r in a)a.hasOwnProperty(r)&&r in this&&"function"==typeof this[r]?i[r](a[r]):i.attr(r,a[r]);t.appendChild(i.build())}}return this},this.build=function(){return t}}return new t(e)},KB.find=function(e){var t=document.querySelector(e);return t?KB.dom(t):null},KB.exists=function(e){return!!document.querySelector(e)},KB.focus=function(e){var t=document.querySelector(e);if(t)return t.focus()},KB.html.label=function(e,t){return KB.dom("label").attr("for",t).text(e).build()},KB.html.radio=function(e,t,o){return KB.dom("label").add(KB.dom("input").attr("type","radio").attr("name",t).attr("value",o).build()).text(e).build()},KB.html.radios=function(e){var t=KB.dom("div");for(var o in e)e.hasOwnProperty(o)&&t.add(KB.html.radio(o.label,o.name,o.value))},KB.http.request=function(e,t,o,n){function a(e){var t=e.getResponseHeader("X-Ajax-Redirect"),o=e.getResponseHeader("Location");if("self"===t)window.location.reload();else if(t&&t.indexOf("#")>-1)window.location=t.split("#")[0];else if(t)window.location=t;else if(o)window.location=o;else if("application/json"===e.getResponseHeader("Content-Type"))try{return JSON.parse(e.responseText)}catch(e){}return e.responseText}var i=function(){},r=function(){};this.execute=function(){var d=new XMLHttpRequest;d.open(e,t,!0),d.setRequestHeader("X-Requested-With","XMLHttpRequest");for(var s in o)o.hasOwnProperty(s)&&d.setRequestHeader(s,o[s]);return d.onerror=function(){r()},d.onreadystatechange=function(){if(d.readyState===XMLHttpRequest.DONE){var e=a(d);200===d.status?i(e):r(e)}},d.send(n),this},this.success=function(e){return i=e,this},this.error=function(e){return r=e,this}},KB.http.get=function(e){return new KB.http.request("GET",e).execute()},KB.http.postJson=function(e,t){var o={"Content-Type":"application/json",Accept:"application/json"};return new KB.http.request("POST",e,o,JSON.stringify(t)).execute()},KB.http.postForm=function(e,t){var o=new FormData(t);return new KB.http.request("POST",e,{},o).execute()},KB.http.uploadFile=function(e,t,o,n,a,i){var r=new FormData;r.append("files[]",t);var d=new XMLHttpRequest;d.upload.addEventListener("progress",o),d.upload.addEventListener("error",a),d.open("POST",e,!0),d.setRequestHeader("X-Requested-With","XMLHttpRequest"),d.onreadystatechange=function(){d.readyState===XMLHttpRequest.DONE&&(200===d.status?n():void 0!==i&&i(JSON.parse(d.responseText)))},d.send(r)},function(){function e(e){e.target.matches("#modal-overlay")&&(e.stopPropagation(),e.preventDefault(),s())}function t(){KB.trigger("modal.close")}function o(){KB.trigger("modal.loading"),a()}function n(){return document.querySelector("#modal-content form:not(.js-modal-ignore-form)")}function a(){var e=n();if(e){var t=e.getAttribute("action");t&&KB.http.postForm(t,e).success(function(e){KB.trigger("modal.stop"),e?r(e):s()}).error(function(e){KB.trigger("modal.stop"),e.hasOwnProperty("message")&&window.alert(e.message)})}}function i(){var e=KB.find("#modal-content form");e&&e.on("submit",o,!1);var t=document.querySelector("#modal-content input[autofocus]");t&&t.focus(),KB.render(),_KB.datePicker(),_KB.autoComplete(),_KB.tagAutoComplete(),_KB.get("Task").onPopoverOpened(),KB.trigger("modal.afterRender")}function r(e){var t=KB.find("#modal-content");t&&(t.replace(KB.dom("div").attr("id","modal-content").html(e).build()),i())}function d(o,n,a){var r=KB.dom("a").attr("href","#").attr("id","modal-close-button").html('<i class="fa fa-times"></i>').click(t).build(),d=KB.dom("div").attr("id","modal-header").add(r).build(),s=KB.dom("div").attr("id","modal-content").html(o).build(),l=KB.dom("div").attr("id","modal-box").style("width",n).add(d).add(s).build(),c=KB.dom("div").attr("id","modal-overlay").add(l).build();a&&c.addEventListener("click",e,!1),document.body.appendChild(c),i()}function s(){c=!1;var e=KB.find("#modal-overlay");e&&(KB.trigger("modal.beforeDestroy"),e.remove())}function l(e){var t=KB.utils.getViewportSize();if(t.width<700)return"99%";switch(e){case"large":return t.width<1350?"98%":"1350px";case"medium":return t.width<1024?"70%":"1024px"}return t.width<800?"75%":"800px"}var c=!1;KB.on("modal.close",function(){s()}),KB.on("modal.submit",function(){a()}),KB.modal={open:function(e,t,o){KB.trigger("modal.open"),_KB.get("Dropdown").close(),s(),void 0===o&&(o=!0),KB.http.get(e).success(function(e){c=!0,d(e,l(t),o)})},close:function(){s()},isOpen:function(){return c},replace:function(e){KB.http.get(e).success(function(e){r(e)})},getForm:n,submitForm:a}}(),KB.utils.formatDuration=function(e){return e>=86400?Math.round(e/86400)+"d":e>=3600?Math.round(e/3600)+"h":e>=60?Math.round(e/60)+"m":e+"s"},KB.utils.getSelectionPosition=function(e){var t,o;return t=e.value.length<e.selectionStart?e.value.length:e.selectionStart,o=e.selectionStart===e.selectionEnd?t:e.selectionEnd,{selectionStart:t,selectionEnd:o}},KB.utils.arraysIdentical=function(e,t){var o=e.length;if(o!==t.length)return!1;for(;o--;)if(e[o]!==t[o])return!1;return!0},KB.utils.arraysStartsWith=function(e,t){for(var o=Math.min(e.length,t.length),n=0;n<o;n++)if(e[n]!==t[n])return!1;return!0},KB.utils.isInputField=function(e){var t=e.target;return!("INPUT"!==t.tagName&&"SELECT"!==t.tagName&&"TEXTAREA"!==t.tagName&&!t.isContentEditable)},KB.utils.getKey=function(e){var t={Esc:"Escape",Up:"ArrowUp",Down:"ArrowDown",Left:"ArrowLeft",Right:"ArrowRight"};for(var o in t)if(t.hasOwnProperty(o)&&o===e.key)return t[o];return e.key},KB.utils.getViewportSize=function(){return{width:Math.max(document.documentElement.clientWidth,window.innerWidth||0),height:Math.max(document.documentElement.clientHeight,window.innerHeight||0)}},KB.utils.isVisible=function(){var e="";return void 0!==document.hidden?e="visibilityState":void 0!==document.mozHidden?e="mozVisibilityState":void 0!==document.msHidden?e="msVisibilityState":void 0!==document.webkitHidden&&(e="webkitVisibilityState"),""===e||"visible"===document[e]},KB.onClick(".accordion-toggle",function(e){var t=KB.dom(e.target).parent(".accordion-section");t&&KB.dom(t).toggleClass("accordion-collapsed")}),KB.onClick(".js-autocomplete-email",function(e){var t=KB.dom(e.target).data("email"),o=KB.find('.js-mail-form input[type="email"]');t&&o&&(o.build().value=t),_KB.controllers.Dropdown.close()}),KB.onClick(".js-autocomplete-subject",function(e){var t=KB.dom(e.target).data("subject"),o=KB.find('.js-mail-form input[name="subject"]');t&&o&&(o.build().value=t),_KB.controllers.Dropdown.close()}),function(){function e(e){if(!KB.dom(e.target).parent("a, .task-board-change-assignee")){var t=KB.dom(e.target).parent(".task-board");if(t){var o=KB.dom(t).data("taskUrl");o&&(window.location=o)}}}function t(e){var t=KB.dom(e.target).parent(".task-board-change-assignee"),o=KB.dom(t).data("url");o&&KB.modal.open(o,"medium",!1)}KB.onClick(".task-board *",e,!0),KB.onClick(".task-board-change-assignee *",t,!0)}(),KB.component("chart-project-avg-time-column",function(e,t){this.render=function(){var o=t.metrics,n=[t.label],a=[];for(var i in o)n.push(o[i].average),a.push(o[i].title);KB.dom(e).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:[n],type:"bar"},bar:{width:{ratio:.5}},axis:{x:{type:"category",categories:a},y:{tick:{format:KB.utils.formatDuration}}},legend:{show:!1}})}}),KB.component("chart-project-burndown",function(e,t){this.render=function(){for(var o=t.metrics,n=[[t.labelTotal]],a=[],i=d3.time.format("%Y-%m-%d"),r=d3.time.format(t.dateFormat),d=0;d<o.length;d++)for(var s=0;s<o[d].length;s++){var l=o[d][s];0===d?s>0&&n.push([l]):s>0?(n[s].push(l),void 0===n[0][d]&&n[0].push(0),n[0][d]+=l):a.push(r(i.parse(l)))}KB.dom(e).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:n},axis:{x:{type:"category",categories:a}}})}}),KB.component("chart-project-cumulative-flow",function(e,t){this.render=function(){for(var o=t.metrics,n=[],a=[],i=[],r=d3.time.format("%Y-%m-%d"),d=d3.time.format(t.dateFormat),s=0;s<o.length;s++)for(var l=0;l<o[s].length;l++){var c=o[s][l];0===s?l>0&&(a.push(c),n.push([c])):l>0?n[l-1].push(c):i.push(d(r.parse(c)))}KB.dom(e).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:n.reverse(),type:"area-spline",groups:[a],order:null},axis:{x:{type:"category",categories:i}}})}}),KB.component("chart-project-lead-cycle-time",function(e,t){this.render=function(){var o=t.metrics,n=[t.labelCycle],a=[t.labelLead],i=[],r={};r[t.labelCycle]="area",r[t.labelLead]="area-spline";var d={};d[t.labelLead]="#afb42b",d[t.labelCycle]="#4e342e";for(var s=0;s<o.length;s++)n.push(parseInt(o[s].avg_cycle_time)),a.push(parseInt(o[s].avg_lead_time)),i.push(o[s].day);KB.dom(e).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:[a,n],types:r,colors:d},axis:{x:{type:"category",categories:i},y:{tick:{format:KB.utils.formatDuration}}}})}}),KB.component("chart-project-task-distribution",function(e,t){this.render=function(){for(var o=[],n=0;n<t.metrics.length;n++)o.push([t.metrics[n].column_title,t.metrics[n].nb_tasks]);KB.dom(e).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:o,type:"donut"}})}}),KB.component("chart-project-time-comparison",function(e,t){this.render=function(){var o=[t.labelSpent],n=[t.labelEstimated],a=[];for(var i in t.metrics)o.push(t.metrics[i].time_spent),n.push(t.metrics[i].time_estimated),a.push("open"===i?t.labelOpen:t.labelClosed);KB.dom(e).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:[o,n],type:"bar"},bar:{width:{ratio:.2}},axis:{x:{type:"category",categories:a}},legend:{show:!0}})}}),KB.component("chart-project-user-distribution",function(e,t){this.render=function(){for(var o=[],n=0;n<t.metrics.length;n++)o.push([t.metrics[n].user,t.metrics[n].nb_tasks]);KB.dom(e).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:o,type:"donut"}})}}),KB.component("chart-task-time-column",function(e,t){this.render=function(){for(var o=t.metrics,n=[t.label],a=[],i=0;i<o.length;i++)n.push(o[i].time_spent),a.push(o[i].title);KB.dom(e).add(KB.dom("div").attr("id","chart-task-time-column").build()),c3.generate({bindto:"#chart-task-time-column",data:{columns:[n],type:"bar"},bar:{width:{ratio:.5}},axis:{x:{type:"category",categories:a},y:{tick:{format:KB.utils.formatDuration}}},legend:{show:!1}})}}),KB.on("dom.ready",function(){function e(){if(0===window.location.hash.indexOf("#comment-")){var e=KB.find(window.location.hash);if(e){document.querySelectorAll(".comment").forEach(function(e){KB.dom(e).removeClass("comment-highlighted")}),e.addClass("comment-highlighted")}}}window.addEventListener("hashchange",e),e()}),KB.component("confirm-buttons",function(e,t){function o(){r=!0,KB.find("#modal-confirm-button").replace(i()),KB.http.get(t.url)}function n(){KB.trigger("modal.close")}function a(){r=!1,KB.find("#modal-confirm-button").replace(i())}function i(){var e=KB.dom("button").click(o).attr("id","modal-confirm-button").attr("type","button").attr("class","btn btn-red");return r&&e.disable().add(KB.dom("i").attr("class","fa fa-spinner fa-pulse").build()).text(" "),t.tabindex&&e.attr("tabindex",t.tabindex),e.text(t.submitLabel).build()}var r=!1;this.render=function(){KB.on("modal.stop",a),KB.on("modal.close",function(){KB.removeListener("modal.stop",a)});var o=KB.dom("div").attr("class","form-actions").add(i()).text(" "+t.orLabel+" ").add(KB.dom("a").attr("href","#").click(n).text(t.cancelLabel).build()).build();e.appendChild(o)}}),KB.component("external-task-view",function(e,t){this.render=function(){KB.dom(e).html('<div id="external-task-view"><i class="fa fa-spinner fa-2x fa-pulse"></div>'),$.ajax({cache:!1,url:t.url,success:function(t){KB.dom(e).html('<div id="external-task-view">'+t+"</div>")}})}}),KB.component("file-upload",function(e,t){function o(e){if(e.lengthComputable){var t=e.loaded/e.total,o=Math.floor(100*t);KB.find("#file-progress-"+y).attr("value",t),KB.find("#file-percentage-"+y).replaceText("("+o+"%)")}}function n(){var e=KB.dom("div").addClass("file-error").text(t.labelUploadError).build();KB.find("#file-item-"+y).add(e)}function a(e){var t=KB.dom("div").addClass("file-error").text(e.message).build();KB.find("#file-item-"+y).add(t),KB.trigger("modal.stop")}function i(){if(++y<w.length)KB.http.uploadFile(t.url,w[y],o,i,n,a);else{KB.trigger("modal.stop"),KB.trigger("modal.hide");var e=KB.dom("div").addClass("alert").addClass("alert-success").text(t.labelSuccess).build(),d=KB.dom("button").attr("type","button").addClass("btn").addClass("btn-blue").click(r).text(t.labelCloseSuccess).build();KB.dom(B).replace(KB.dom("div").add(e).add(d).build())}}function r(){window.location.reload()}function d(){y=0,m()}function s(){for(var e=0;e<K.files.length;e++)w.push(K.files[e]);p()}function l(){w=[],y=0,K.click()}function c(e){e.stopPropagation(),e.preventDefault()}function u(e){e.stopPropagation(),e.preventDefault();for(var t=0;t<e.dataTransfer.files.length;t++)w.push(e.dataTransfer.files[t]);p()}function m(){w.length>0&&KB.http.uploadFile(t.url,w[y],o,i,n,a)}function p(){w.length>0?(KB.trigger("modal.enable"),KB.dom(B).empty().add(b())):(KB.trigger("modal.disable"),KB.dom(B).empty().add(h()))}function f(){return KB.dom("input").attr("id","file-input-element").attr("type","file").attr("name","files[]").attr("multiple",!0).on("change",s).hide().build()}function h(){var e=KB.dom("a").attr("href","#").text(t.labelChooseFiles).click(l).build();return KB.dom("div").attr("id","file-dropzone-inner").text(t.labelDropzone+" "+t.labelOr+" ").add(e).build()}function g(){var e=KB.dom("div").attr("id","file-dropzone").add(h()).build();return e.ondragover=c,e.ondrop=u,e.ondragover=c,e}function v(e){var o=!1,n=KB.dom("progress").attr("id","file-progress-"+e).attr("value",0).build(),a=KB.dom("span").attr("id","file-percentage-"+e).text("(0%)").build(),i=KB.dom("span").attr("id","file-delete-"+e).html('<a href="#"><i class="fa fa-trash fa-fw"></i></a>').on("click",function(){w.splice(e,1),KB.find("#file-item-"+e).remove(),p()}).build(),r=KB.dom("li").attr("id","file-item-"+e).add(i).add(n).text(" "+w[e].name+" ").add(a);return w[e].size>t.maxSize&&(r.add(KB.dom("div").addClass("file-error").text(t.labelOversize).build()),o=!0),o&&KB.trigger("modal.disable"),r.build()}function b(){for(var e=KB.dom("ul").attr("id","file-list").build(),t=0;t<w.length;t++)e.appendChild(v(t));return e}var K=null,B=null,w=[],y=0;this.render=function(){KB.on("modal.submit",d),KB.on("modal.close",function(){KB.removeListener("modal.submit",d)}),K=f(),B=g(),e.appendChild(K),e.appendChild(B)}}),KB.onClick(".js-form-export",function(e){var t=document.querySelector("#modal-content form"),o=t.querySelector("#form-from"),n=t.querySelector("#form-to");""!==o.value&&""!==n.value&&t.submit()}),KB.component("image-slideshow",function(e,t){function o(e){switch(KB.utils.getKey(e)){case"Escape":s();break;case"ArrowRight":i();break;case"ArrowLeft":r()}}function n(e){e.matches(".slideshow-next-icon")?i():e.matches(".slideshow-previous-icon")?r():e.matches(".slideshow-download-icon")?window.location.href=e.href:s()}function a(e){var t=KB.dom(e).data("imageId");l(t);d()}function i(){s();for(var e=0;e<t.images.length;e++)if(t.images[e].id===m.id){var o=e+1;o>=t.images.length&&(o=0),m=t.images[o];break}d()}function r(){s();for(var e=0;e<t.images.length;e++)if(t.images[e].id===m.id){var o=e-1;o<0&&(o=t.images.length-1),m=t.images[o];break}d()}function d(){var e=KB.dom("div").attr("class","fa fa-window-close slideshow-icon slideshow-close-icon").build(),t=KB.dom("a").attr("class","fa fa-download slideshow-icon slideshow-download-icon").attr("href",c(m,"download")).build(),a=KB.dom("div").attr("class","fa fa-chevron-circle-left slideshow-icon slideshow-previous-icon").build(),i=KB.dom("div").attr("class","fa fa-chevron-circle-right slideshow-icon slideshow-next-icon").build(),r=KB.dom("img").attr("src",c(m,"image")).attr("alt",m.name).attr("title",m.name).style("maxHeight",window.innerHeight-50+"px").build(),d=KB.dom("figcaption").text(m.name).build(),s=KB.dom("figure").add(r).add(d).build(),l=KB.dom("div").addClass("image-slideshow-overlay").add(e).add(t).add(a).add(i).add(s).click(n).build();document.body.appendChild(l),document.addEventListener("keydown",o,!1)}function s(){var e=KB.find(".image-slideshow-overlay");null!==e&&(document.removeEventListener("keydown",o,!1),e.remove())}function l(e){for(var o=0;o<t.images.length;o++)if(t.images[o].id===e)return t.images[o];return null}function c(e,o){var n=new RegExp(t.regex,"g");return t.url[o].replace(n,e.id)}function u(e){return KB.dom("img").attr("src",c(e,"thumbnail")).attr("alt",e.name).attr("title",e.name).data("imageId",e.id).click(a).build()}var m;this.render=function(){m=t.image,e.appendChild(u(m))}}),KB.keyboardShortcuts=function(){function e(e){if(!KB.modal.isOpen()){var t=KB.find(e);null!==t&&(window.location=t.attr("href"))}}function t(){if(KB.modal.isOpen())KB.modal.submitForm();else{var e=$("form");1==e.length?e.submit():e.length>1&&("INPUT"!==document.activeElement.tagName&&"TEXTAREA"!==document.activeElement.tagName||$(document.activeElement).parents("form").submit())}}KB.onKey("?",function(){KB.modal.isOpen()||KB.modal.open(KB.find("body").data("keyboardShortcutUrl"))}),KB.onKey("Escape",function(){KB.exists("#suggest-menu")||(KB.trigger("modal.close"),_KB.get("Dropdown").close())}),KB.onKey("Enter",t,!0,!0),KB.onKey("Enter",t,!0,!1,!0),KB.onKey("b",function(){KB.modal.isOpen()||KB.trigger("board.selector.open")}),KB.exists("#board")&&(KB.onKey("c",function(){KB.modal.isOpen()||_KB.get("BoardHorizontalScrolling").toggle()}),KB.onKey("s",function(){KB.modal.isOpen()||_KB.get("BoardCollapsedMode").toggle()}),KB.onKey("n",function(){KB.modal.isOpen()||KB.modal.open(KB.find("#board").data("taskCreationUrl"),"large",!1)})),KB.exists("#task-view")&&(KB.onKey("e",function(){KB.modal.isOpen()||KB.modal.open(KB.find("#task-view").data("editUrl"),"large",!1)}),KB.onKey("c",function(){KB.modal.isOpen()||KB.modal.open(KB.find("#task-view").data("commentUrl"),"medium",!1)}),KB.onKey("s",function(){KB.modal.isOpen()||KB.modal.open(KB.find("#task-view").data("subtaskUrl"),"medium",!1)}),KB.onKey("l",function(){KB.modal.isOpen()||KB.modal.open(KB.find("#task-view").data("internalLinkUrl"),"medium",!1)})),KB.onKey("f",function(){KB.modal.isOpen()||KB.focus("#form-search")}),KB.onKey("r",function(){if(!KB.modal.isOpen()){var e=$(".filter-reset").data("filter");$("#form-search").val(e),$("form.search").submit()}}),KB.onKey("v+o",function(){e("a.view-overview")}),KB.onKey("v+b",function(){e("a.view-board")}),KB.onKey("v+l",function(){e("a.view-listing")})},function(){function e(e){return"I"===e.target.tagName?e.target.parentNode.getAttribute("href"):e.target.getAttribute("href")}KB.onClick(".js-modal-large",function(t){KB.modal.open(e(t),"large",!1)}),KB.onClick(".js-modal-medium",function(t){KB.modal.isOpen()?KB.modal.replace(e(t)):KB.modal.open(e(t),"medium",!1)}),KB.onClick(".js-modal-small",function(t){KB.modal.open(e(t),"small",!1)}),KB.onClick(".js-modal-confirm",function(t){KB.modal.open(e(t),"small")}),KB.onClick(".js-modal-close",function(){KB.modal.close()}),KB.onClick(".js-modal-replace",function(t){var o=e(t);KB.modal.isOpen()?KB.modal.replace(o):window.location.href=o})}(),KB.onChange(".js-project-creation-select-options",function(e){"0"===e.value?KB.find(".js-project-creation-options").hide():KB.find(".js-project-creation-options").show()}),KB.component("project-select-role",function(e,t){function o(e){d=!0,t.role=e.value,a(),n()}function n(){KB.http.postJson(t.url,{id:t.id,role:t.role}).success(function(){d=!1,s=!0,a()}).error(function(){d=!1,s=!1,l=!0,a()})}function a(){KB.dom(r).remove(),r=i(),e.appendChild(r)}function i(){var e=[],n=KB.dom("div");for(var a in t.roles)if(t.roles.hasOwnProperty(a)){var i={value:a,text:t.roles[a]};t.role===a&&(i.selected="selected"),e.push(i)}return n.add(KB.dom("select").change(o).for("option",e).build()),d?(n.text(" "),n.add(KB.dom("i").attr("class","fa fa-spinner fa-pulse fa-fw").build())):s?(n.text(" "),n.add(KB.dom("i").attr("class","fa fa-check fa-fw icon-fade-out icon-success").build())):l&&(n.text(" "),n.add(KB.dom("i").attr("class","fa fa-check fa-fw icon-fade-out icon-error").build())),n.build()}var r,d=!1,s=!1,l=!1;this.render=function(){r=i(),e.appendChild(r)}}),KB.component("screenshot",function(e){function t(e){d(e.target.result)}function o(e){if(e.clipboardData&&e.clipboardData.items){var o=e.clipboardData.items;if(o)for(var n=0;n<o.length;n++)if(-1!==o[n].type.indexOf("image")){var a=o[n].getAsFile(),i=new FileReader;i.onload=t,i.readAsDataURL(a)}}else setTimeout(r,100)}function n(){a(),window.Clipboard||(s=document.createElement("div"),s.id="screenshot-pastezone",s.contentEditable=!0,s.style.opacity=0,s.style.position="fixed",s.style.top=0,s.style.right=0,s.style.width=0,document.body.insertBefore(s,document.body.firstChild),s.focus(),document.addEventListener("click",i),document.getElementById("screenshot-zone").addEventListener("click",i)),window.addEventListener("paste",o,!1)}function a(){KB.exists("#screenshot-pastezone")&&KB.find("#screenshot-pastezone").remove(),document.removeEventListener("click",i),s=null}function i(){null!==s&&s.focus()}function r(){var e=s.childNodes[0];e&&"IMG"===e.tagName&&d(e.src),s.innerHTML=""}function d(e){var t=new Image;t.src=e,t.onload=function(){var t=e.split("base64,");l.value=t[1]};var o=document.getElementById("screenshot-zone");o.innerHTML="",o.className="screenshot-pasted",o.appendChild(t),a(),n()}var s=null,l=null;KB.on("modal.close",function(){a()}),this.render=function(){l=KB.dom("input").attr("type","hidden").attr("name","screenshot").build(),e.appendChild(l),n()}}),KB.component("select-dropdown-autocomplete",function(e,t){function o(){KB.dom(k).show(),KB.dom(x).hide()}function n(){KB.dom(k).hide(),KB.dom(x).show()}function a(){var e=KB.find("#select-dropdown-menu");if(e){var t=w.getBoundingClientRect();e.style("top",document.body.scrollTop+t.bottom+"px")}}function i(e){switch(KB.utils.getKey(e)){case"Escape":y.value="",b();break;case"ArrowUp":e.preventDefault(),e.stopImmediatePropagation(),p();break;case"ArrowDown":e.preventDefault(),e.stopImmediatePropagation(),f();break;case"Enter":e.preventDefault(),e.stopImmediatePropagation(),u()}}function r(){b(),K()}function d(e){KB.dom(e).hasClass("select-dropdown-menu-item")&&(KB.find(".select-dropdown-menu-item.active").removeClass("active"),KB.dom(e).addClass("active"))}function s(){u()}function l(t){e.contains(t.target)||(y.value="",b())}function c(){null===KB.find("#select-dropdown-menu")?K():b()}function u(){var e=KB.find(".select-dropdown-menu-item.active"),n=e.data("value");C.value=n,y.value=t.items[n],b(),t.redirect?window.location=t.redirect.url.replace(new RegExp(t.redirect.regex,"g"),n):t.replace&&(o(),KB.modal.replace(t.replace.url.replace(new RegExp(t.replace.regex,"g"),n)))}function m(){for(var e=document.querySelectorAll(".select-dropdown-menu-item"),t=0;t<e.length;t++)if(KB.dom(e[t]).hasClass("active")){KB.dom(e[t]).removeClass("active");break}return{items:e,index:t}}function p(){var e=m();e.index>0&&(e.index=e.index-1),KB.dom(e.items[e.index]).addClass("active")}function f(){var e=m();e.index<e.items.length-1&&e.index++,KB.dom(e.items[e.index]).addClass("active")}function h(e){var o=[];for(var n in e)e.hasOwnProperty(n)&&o.push({class:"select-dropdown-menu-item",text:e[n],"data-label":e[n],"data-value":n});return t.sortByKeys?o.sort(function(e,t){var o=e["data-value"].toLowerCase(),n=t["data-value"].toLowerCase();return o<n?-1:o>n?1:0}):o.sort(function(e,t){var o=e["data-label"].toLowerCase(),n=t["data-label"].toLowerCase();return o<n?-1:o>n?1:0}),o}function g(e,o){for(var n=[],a=!1,i=0;i<o.length;i++)if(0===e.length||o[i]["data-label"].toLowerCase().indexOf(e.toLowerCase())>-1){var r=o[i];void 0!==t.defaultValue&&String(t.defaultValue)===r["data-value"]&&(r.class+=" active",a=!0),n.push(r)}return!a&&n.length>0&&(n[0].class+=" active"),n}function v(){var e=g(y.value,h(t.items)),o=w.getBoundingClientRect(),n=document.body.scrollTop||document.documentElement.scrollTop;return 0===e.length?null:KB.dom("ul").attr("id","select-dropdown-menu").style("top",n+o.bottom+"px").style("left",o.left+"px").style("width",o.width+"px").style("maxHeight",window.innerHeight-o.bottom-20+"px").mouseover(d).click(s).for("li",e).build()}function b(){var e=KB.find("#select-dropdown-menu");null!==e&&e.remove(),document.removeEventListener("keydown",i,!1), +document.removeEventListener("click",l,!1)}function K(){var e=v();null!==e&&document.body.appendChild(e),document.addEventListener("keydown",i,!1),document.addEventListener("click",l,!1)}function B(){return t.defaultValue&&t.defaultValue in t.items?t.items[t.defaultValue]:t.placeholder?t.placeholder:""}var w,y,C,x,k;this.render=function(){KB.on("select.dropdown.loading.start",o),KB.on("select.dropdown.loading.stop",n),KB.on("modal.close",function(){KB.removeListener("select.dropdown.loading.start",o),KB.removeListener("select.dropdown.loading.stop",n)}),x=KB.dom("i").attr("class","fa fa-chevron-down select-dropdown-chevron").click(c).build(),k=KB.dom("span").hide().addClass("select-loading-icon").add(KB.dom("i").attr("class","fa fa-spinner fa-pulse").build()).build(),C=KB.dom("input").attr("type","hidden").attr("name",t.name).attr("value",t.defaultValue||"").build(),y=KB.dom("input").attr("type","text").attr("placeholder",B()).addClass("select-dropdown-input").style("width",e.offsetWidth-30+"px").on("focus",c).on("input",r,!0).build(),w=KB.dom("div").addClass("select-dropdown-input-container").add(C).add(y).add(x).add(k).build(),e.appendChild(w),t.onFocus&&t.onFocus.forEach(function(e){KB.on(e,function(){y.focus()})}),window.addEventListener("scroll",a,!1)}}),KB.interval(60,function(){var e=KB.find("body").data("statusUrl"),t=KB.find("body").data("loginUrl");null===KB.find(".form-login")&&KB.http.get(e).error(function(){window.location=t})}),KB.component("submit-buttons",function(e,t){function o(){u=!0,c(),KB.trigger("modal.submit")}function n(){KB.trigger("modal.close")}function a(){u=!1,c()}function i(){u=!1,m=!0,c()}function r(){u=!1,m=!1,c()}function d(){KB.dom(f).hide()}function s(e){p=e.submitLabel,c()}function l(){var e=KB.dom("button").attr("type","submit").attr("class","btn btn-"+(t.color||"blue"));return KB.modal.isOpen()&&e.click(o),t.tabindex&&e.attr("tabindex",t.tabindex),u&&e.disable().add(KB.dom("i").attr("class","fa fa-spinner fa-pulse").build()).text(" "),m&&e.disable(),e.text(p).build()}function c(){var e=l();KB.dom(h).replace(e),h=e}var u=!1,m=t.disabled||!1,p=t.submitLabel,f=null,h=null;this.render=function(){KB.on("modal.stop",a),KB.on("modal.disable",i),KB.on("modal.enable",r),KB.on("modal.hide",d),KB.on("modal.submit.label",s),KB.on("modal.close",function(){KB.removeListener("modal.stop",a),KB.removeListener("modal.disable",i),KB.removeListener("modal.enable",r),KB.removeListener("modal.hide",d),KB.removeListener("modal.submit.label",s)}),h=l();var o=KB.dom("div").attr("class","form-actions").add(h);KB.modal.isOpen()&&o.text(" "+t.orLabel+" ").add(KB.dom("a").attr("href","#").click(n).text(t.cancelLabel).build()),f=o.build(),e.appendChild(f)}}),KB.on("dom.ready",function(){function e(e,t){var o=$(".subtasks-table").data("save-position-url");$.ajax({cache:!1,url:o,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({subtask_id:e,position:t})})}$(".draggable-row-handle").mouseenter(function(){$(this).parent().parent().addClass("draggable-item-hover")}).mouseleave(function(){$(this).parent().parent().removeClass("draggable-item-hover")}),$(".subtasks-table tbody").sortable({forcePlaceholderSize:!0,handle:"td:first i",helper:function(e,t){return t.children().each(function(){$(this).width($(this).width())}),t},stop:function(t,o){var n=o.item;n.removeClass("draggable-item-selected"),e(n.data("subtask-id"),n.index()+1)},start:function(e,t){t.item.addClass("draggable-item-selected")}}).disableSelection()}),KB.on("dom.ready",function(){$(document).on("click",".js-subtask-toggle-status",function(e){var t=$(this),o=t.attr("href");e.preventDefault(),$.ajax({cache:!1,url:o,success:function(e){-1!=o.indexOf("fragment=table")?$(".subtasks-table").replaceWith(e):-1!=o.indexOf("fragment=rows")?$(t).closest(".task-list-subtasks").replaceWith(e):$(t).closest(".subtask-title").replaceWith(e)}})}),$(document).on("click",".js-subtask-toggle-timer",function(e){var t=$(this);e.preventDefault(),$.ajax({cache:!1,url:t.attr("href"),success:function(e){$(t).closest(".subtask-time-tracking").replaceWith(e)}})})}),KB.component("suggest-menu",function(e,t){function o(e){switch(KB.utils.getKey(e)){case"Escape":u();break;case"ArrowUp":e.preventDefault(),e.stopImmediatePropagation(),l();break;case"ArrowDown":e.preventDefault(),e.stopImmediatePropagation(),c();break;case"Enter":e.preventDefault(),e.stopImmediatePropagation(),i()}}function n(){i()}function a(e){KB.dom(e).hasClass("suggest-menu-item")&&(KB.find(".suggest-menu-item.active").removeClass("active"),KB.dom(e).addClass("active"))}function i(){e.focus();var t=KB.find(".suggest-menu-item.active"),o=t.data("value"),n=t.data("trigger"),a=e.value,i=r(e),d=n+o+" ",s=KB.utils.getSelectionPosition(e),l=a.substring(0,s.selectionStart-i.length),c=a.substring(s.selectionEnd),m=l.length+d.length;e.value=l+d+c,e.setSelectionRange(m,m),u()}function r(e){var t=e.value.substring(0,e.selectionEnd).split("\n"),o=t[t.length-1],n=o.split(" ");return n[n.length-1]}function d(){for(var e=["#modal-content form","#modal-content","body"],t=0;t<e.length;t++){var o=document.querySelector(e[t]);if(null!==o)return o}return null}function s(){for(var e=document.querySelectorAll(".suggest-menu-item"),t=0;t<e.length;t++)if(KB.dom(e[t]).hasClass("active")){KB.dom(e[t]).removeClass("active");break}return{items:e,index:t}}function l(){var e=s();e.index>0&&(e.index=e.index-1),KB.dom(e.items[e.index]).addClass("active")}function c(){var e=s();e.index<e.items.length-1&&e.index++,KB.dom(e.items[e.index]).addClass("active")}function u(){var e=KB.find("#suggest-menu");null!==e&&e.remove(),document.removeEventListener("keydown",o,!1)}function m(e){var o=r(e),n=p(o,t.triggers);u(),null!==n&&f(n,o.substring(n.length),t.triggers[n])}function p(e,t){for(var o in t)if(t.hasOwnProperty(o)&&0===e.indexOf(o))return o;return null}function f(e,t,o){if("string"==typeof o){var n=new RegExp("SEARCH_TERM","g"),a=o.replace(n,t);KB.http.get(a).success(function(o){h(e,t,o)})}else h(e,t,o)}function h(e,t,o){o=g(t,o),o.length>0&&b(v(e,o))}function g(e,t){var o=[];if(0===e.length)return t;for(var n=0;n<t.length;n++)0===t[n].value.toLowerCase().indexOf(e.toLowerCase())&&o.push(t[n]);return o}function v(e,t){for(var o=[],n=0;n<t.length;n++){var a="suggest-menu-item";0===n&&(a+=" active"),o.push({class:a,html:t[n].html,"data-value":t[n].value,"data-trigger":e})}return o}function b(t){var i=d(),r=getCaretCoordinates(e,e.selectionEnd),s=r.left+e.offsetLeft-e.scrollLeft,l=r.top+e.offsetTop-e.scrollTop+16;document.addEventListener("keydown",o,!1);var c=KB.dom("ul").attr("id","suggest-menu").click(n).mouseover(a).style("left",s+"px").style("top",l+"px").for("li",t).build();i.appendChild(c)}this.render=function(){e.addEventListener("input",function(){m(this)})}}),KB.component("task-move-position",function(e,t){function o(e){var t=KB.dom(document).find("#"+e);return t?parseInt(t.options[t.selectedIndex].value):null}function n(){var e=o("form-swimlanes");return null===e?t.board[0].id:e}function a(){var e=o("form-columns");return null===e?t.board[0].columns[0].id:e}function i(){var e=o("form-position");return null===e?1:e}function r(){var e=KB.find("input[name=positionChoice]:checked");return e?e.value:"before"}function d(){var e=KB.dom(document).find("#form-columns");KB.dom(e).replace(m());var t=KB.dom(document).find("#form-tasks");KB.dom(t).replace(p())}function s(){var e=KB.dom(document).find("#form-tasks");KB.dom(e).replace(p())}function l(e){KB.trigger("modal.stop"),KB.find("#message-container").replace(KB.dom("div").attr("id","message-container").attr("class","alert alert-error").text(e).build())}function c(){var e=i();"after"===r()&&e++,KB.find("#message-container").replace(KB.dom("div").attr("id","message-container").build()),KB.http.postJson(t.saveUrl,{column_id:a(),swimlane_id:n(),position:e}).error(function(e){e&&l(e.message)})}function u(){var e=[];return t.board.forEach(function(t){e.push({value:t.id,text:t.name})}),KB.dom("select").attr("id","form-swimlanes").change(d).for("option",e).build()}function m(){var e=[],o=n();return t.board.forEach(function(t){o===t.id&&t.columns.forEach(function(t){e.push({value:t.id,text:t.title})})}),KB.dom("select").attr("id","form-columns").change(s).for("option",e).build()}function p(){var e=[],o=n(),i=a(),r=KB.dom("div").attr("id","form-tasks");return t.board.forEach(function(t){o===t.id&&t.columns.forEach(function(t){i===t.id&&t.tasks.forEach(function(t){e.push({value:t.position,text:"#"+t.id+" - "+t.title})})})}),e.length>0&&r.add(KB.html.label(t.positionLabel,"form-position")).add(KB.dom("select").attr("id","form-position").for("option",e).build()).add(KB.html.radio(t.beforeLabel,"positionChoice","before")).add(KB.html.radio(t.afterLabel,"positionChoice","after")),r.build()}this.render=function(){KB.on("modal.submit",c),KB.on("modal.close",function(){KB.removeListener("modal.submit",c)});var o=KB.dom("div").add(KB.dom("div").attr("id","message-container").build()).add(KB.html.label(t.swimlaneLabel,"form-swimlanes")).add(u()).add(KB.html.label(t.columnLabel,"form-columns")).add(m()).add(p()).build();e.appendChild(o)}}),KB.onClick(".js-template",function(e){var t=KB.dom(e.target).data("template"),o=KB.dom(e.target).data("templateTarget"),n=KB.find(o);n&&(n.build().value=t),_KB.controllers.Dropdown.close()}),KB.component("text-editor",function(e,t){function o(){var e=KB.dom("div").attr("class","text-editor-toolbar").for("a",[{href:"#",html:'<i class="fa fa-pencil-square-o fa-fw"></i> '+t.labelWrite,click:function(){a()}}]).build();return h=KB.dom("div").attr("class","text-editor-preview-area markdown").build(),KB.dom("div").attr("class","text-editor-view-mode").add(e).add(h).hide().build()}function n(){var e=KB.dom("div").attr("class","text-editor-toolbar").for("a",[{href:"#",html:'<i class="fa fa-eye fa-fw"></i> '+t.labelPreview,click:function(){a()}},{href:"#",html:'<i class="fa fa-bold fa-fw"></i>',click:function(){d("**")}},{href:"#",html:'<i class="fa fa-italic fa-fw"></i>',click:function(){d("_")}},{href:"#",html:'<i class="fa fa-strikethrough fa-fw"></i>',click:function(){d("~~")}},{href:"#",html:'<i class="fa fa-quote-right fa-fw"></i>',click:function(){l("> ")}},{href:"#",html:'<i class="fa fa-list-ul fa-fw"></i>',click:function(){l("* ")}},{href:"#",html:'<i class="fa fa-code fa-fw"></i>',click:function(){s("```")}}]).build(),o=KB.dom("textarea");return o.attr("name",t.name),t.tabindex&&o.attr("tabindex",t.tabindex),t.required&&o.attr("required","required"),o.text(t.text),t.placeholder&&o.attr("placeholder",t.placeholder),m=o.build(),t.suggestOptions&&KB.getComponent("suggest-menu",m,t.suggestOptions).render(),KB.dom("div").attr("class","text-editor-write-mode").add(e).add(m).build()}function a(){KB.dom(h).html(marked(m.value,{sanitize:!0})),KB.dom(p).toggle(),KB.dom(f).toggle()}function i(){return m.value.substring(m.selectionStart,m.selectionEnd)}function r(e,t,o,n){return e.substring(0,t)+n+e.substring(o)}function d(e){c(e+i()+e),u(e)}function s(e){c("\n"+e+"\n"+i()+"\n"+e),u(e,2)}function l(e){var t=i();if(-1===t.indexOf("\n"))c("\n"+e+t);else{for(var o=t.split("\n"),n=0;n<o.length;n++)-1===o[n].indexOf(e)&&(o[n]=e+o[n]);c(o.join("\n"))}u(e,1)}function c(e){m.focus();var t=!1,o=KB.utils.getSelectionPosition(m);if(g=o.selectionStart,v=o.selectionEnd,document.queryCommandSupported("insertText")&&(t=document.execCommand("insertText",!1,e)),!t){try{document.execCommand("ms-beginUndoUnit")}catch(e){}m.value=r(m.value,g,v,e);try{document.execCommand("ms-endUndoUnit")}catch(e){}}}function u(e,t){t=t||0;var o=v+e.length+t;m.setSelectionRange(o,o)}var m,p,f,h,g,v;this.render=function(){f=n(),p=o(),e.appendChild(KB.dom("div").attr("class","text-editor").add(p).add(f).build()),t.autofocus&&m.focus()}}),document.addEventListener("DOMContentLoaded",function(){KB.render(),KB.listen(),KB.keyboardShortcuts(),KB.trigger("dom.ready")});var Kanboard={};Kanboard.App=function(){this.controllers={}},Kanboard.App.prototype.get=function(e){return this.controllers[e]},Kanboard.App.prototype.execute=function(){for(var e in Kanboard)if("App"!==e){var t=new Kanboard[e](this);this.controllers[e]=t,"function"==typeof t.execute&&t.execute(),"function"==typeof t.listen&&t.listen(),"function"==typeof t.focus&&t.focus()}this.focus(),this.datePicker(),this.autoComplete(),this.tagAutoComplete()},Kanboard.App.prototype.focus=function(){$(document).on("focus",".auto-select",function(){$(this).select()}),$(document).on("mouseup",".auto-select",function(e){e.preventDefault()})},Kanboard.App.prototype.datePicker=function(){var e=$("body"),t=e.data("js-date-format"),o=e.data("js-time-format"),n=e.data("js-lang");$.datepicker.setDefaults($.datepicker.regional[n]),$.timepicker.setDefaults($.timepicker.regional[n]),$(".form-date").datepicker({showOtherMonths:!0,selectOtherMonths:!0,dateFormat:t,constrainInput:!1}),$(".form-datetime").datetimepicker({dateFormat:t,timeFormat:o,constrainInput:!1})},Kanboard.App.prototype.tagAutoComplete=function(){$(".tag-autocomplete").select2({tags:!0})},Kanboard.App.prototype.autoComplete=function(){$(".autocomplete").each(function(){var e=$(this),t=e.data("dst-field"),o=e.data("dst-extra-fields");""===$("#form-"+t).val()&&e.parent().find("button[type=submit]").attr("disabled","disabled"),e.autocomplete({source:e.data("search-url"),minLength:1,select:function(n,a){if($("input[name="+t+"]").val(a.item.id),o)for(var i=o.split(","),r=0;r<i.length;r++){var d=i[r].trim();$("input[name="+d+"]").val(a.item[d])}e.parent().find("button[type=submit]").removeAttr("disabled")}})})},Kanboard.App.prototype.hasId=function(e){return!!document.getElementById(e)},Kanboard.App.prototype.showLoadingIcon=function(){$("body").append('<span id="app-loading-icon"> <i class="fa fa-spinner fa-spin"></i></span>')},Kanboard.App.prototype.hideLoadingIcon=function(){$("#app-loading-icon").remove()},Kanboard.BoardCollapsedMode=function(e){this.app=e},Kanboard.BoardCollapsedMode.prototype.toggle=function(){var e=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:$('.filter-display-mode:not([style="display: none;"]) a').attr("href"),success:function(t){$(".filter-display-mode").toggle(),e.app.get("BoardDragAndDrop").refresh(t)}})},Kanboard.BoardColumnView=function(e){this.app=e},Kanboard.BoardColumnView.prototype.execute=function(){this.app.hasId("board")&&this.render()},Kanboard.BoardColumnView.prototype.listen=function(){var e=this;$(document).on("click",".board-toggle-column-view",function(){e.toggle($(this).data("column-id"))})},Kanboard.BoardColumnView.prototype.onBoardRendered=function(){this.render()},Kanboard.BoardColumnView.prototype.render=function(){var e=this;$(".board-column-header").each(function(){var t=$(this).data("column-id");localStorage.getItem("hidden_column_"+t)&&e.hideColumn(t)})},Kanboard.BoardColumnView.prototype.toggle=function(e){localStorage.getItem("hidden_column_"+e)?this.showColumn(e):this.hideColumn(e),this.app.get("BoardDragAndDrop").dragAndDrop()},Kanboard.BoardColumnView.prototype.hideColumn=function(e){$(".board-column-"+e+" .board-column-expanded").hide(),$(".board-column-"+e+" .board-column-collapsed").show(),$(".board-column-header-"+e+" .board-column-expanded").hide(),$(".board-column-header-"+e+" .board-column-collapsed").show(),$(".board-column-header-"+e).each(function(){$(this).removeClass("board-column-compact"),$(this).addClass("board-column-header-collapsed")}),$(".board-column-"+e).each(function(){$(this).addClass("board-column-task-collapsed")}),$(".board-column-"+e+" .board-rotation").each(function(){$(this).css("width",$(".board-column-"+e).height())}),localStorage.setItem("hidden_column_"+e,1)},Kanboard.BoardColumnView.prototype.showColumn=function(e){$(".board-column-"+e+" .board-column-expanded").show(),$(".board-column-"+e+" .board-column-collapsed").hide(),$(".board-column-header-"+e+" .board-column-expanded").show(),$(".board-column-header-"+e+" .board-column-collapsed").hide(),$(".board-column-header-"+e).removeClass("board-column-header-collapsed"),$(".board-column-"+e).removeClass("board-column-task-collapsed"),0==localStorage.getItem("horizontal_scroll")&&$(".board-column-header-"+e).addClass("board-column-compact"),localStorage.removeItem("hidden_column_"+e)},Kanboard.BoardHorizontalScrolling=function(e){this.app=e},Kanboard.BoardHorizontalScrolling.prototype.execute=function(){this.app.hasId("board")&&this.render()},Kanboard.BoardHorizontalScrolling.prototype.listen=function(){var e=this;$(document).on("click",".filter-toggle-scrolling",function(t){t.preventDefault(),e.toggle()})},Kanboard.BoardHorizontalScrolling.prototype.onBoardRendered=function(){this.render()},Kanboard.BoardHorizontalScrolling.prototype.toggle=function(){var e=localStorage.getItem("horizontal_scroll")||1;localStorage.setItem("horizontal_scroll",0==e?1:0),this.render()},Kanboard.BoardHorizontalScrolling.prototype.render=function(){0==localStorage.getItem("horizontal_scroll")?($(".filter-wide").show(),$(".filter-compact").hide(),$("#board-container").addClass("board-container-compact"),$("#board th:not(.board-column-header-collapsed)").addClass("board-column-compact")):($(".filter-wide").hide(),$(".filter-compact").show(),$("#board-container").removeClass("board-container-compact"),$("#board th").removeClass("board-column-compact"))},Kanboard.BoardPolling=function(e){this.app=e},Kanboard.BoardPolling.prototype.execute=function(){if(this.app.hasId("board")){var e=parseInt($("#board").attr("data-check-interval"));e>0&&window.setInterval(this.check.bind(this),1e3*e)}},Kanboard.BoardPolling.prototype.check=function(){if(KB.utils.isVisible()&&!this.app.get("BoardDragAndDrop").savingInProgress){var e=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:$("#board").data("check-url"),statusCode:{200:function(t){e.app.get("BoardDragAndDrop").refresh(t)},304:function(){e.app.hideLoadingIcon()}}})}},Kanboard.Column=function(e){this.app=e},Kanboard.Column.prototype.listen=function(){this.dragAndDrop()},Kanboard.Column.prototype.dragAndDrop=function(){var e=this;$(".draggable-row-handle").mouseenter(function(){$(this).parent().parent().addClass("draggable-item-hover")}).mouseleave(function(){$(this).parent().parent().removeClass("draggable-item-hover")}),$(".columns-table tbody").sortable({forcePlaceholderSize:!0,handle:"td:first i",helper:function(e,t){return t.children().each(function(){$(this).width($(this).width())}),t},stop:function(t,o){var n=o.item;n.removeClass("draggable-item-selected"),e.savePosition(n.data("column-id"),n.index()+1)},start:function(e,t){t.item.addClass("draggable-item-selected")}}).disableSelection()},Kanboard.Column.prototype.savePosition=function(e,t){var o=$(".columns-table").data("save-position-url"),n=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:o,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({column_id:e,position:t}),complete:function(){n.app.hideLoadingIcon()}})},Kanboard.Dropdown=function(e){this.app=e},Kanboard.Dropdown.prototype.listen=function(){var e=this;$(document).on("click",function(){e.close()}),$(document).on("click",".dropdown-menu",function(t){t.preventDefault(),t.stopImmediatePropagation(),e.close();var o=$(this).next("ul"),n=$(this).offset();$("body").append(jQuery("<div>",{id:"dropdown"})),o.clone().appendTo("#dropdown");var a=$("#dropdown ul");a.addClass("dropdown-submenu-open");var i=a.outerHeight(),r=a.outerWidth();n.top+i-$(window).scrollTop()<$(window).height()||$(window).scrollTop()+n.top<i?a.css("top",n.top+$(this).height()):a.css("top",n.top-i-5),n.left+r>$(window).width()?a.css("left",n.left-r+$(this).outerWidth()):a.css("left",n.left),null!==document.getElementById("dropdown")&&KB.trigger("dropdown.afterRender")}),$(document).on("click",".dropdown-submenu-open li",function(e){if($(e.target).is("li")){KB.trigger("dropdown.clicked");var t=$(this).find("a:visible");t.length>0&&t[0].click()}})},Kanboard.Dropdown.prototype.close=function(){null!==document.getElementById("dropdown")&&KB.trigger("dropdown.beforeDestroy"),$("#dropdown").remove()},Kanboard.Search=function(e){this.app=e},Kanboard.Search.prototype.focus=function(){$(document).on("focus","#form-search",function(){var e=$("#form-search");if(e[0].setSelectionRange){var t=2*e.val().length;e[0].setSelectionRange(t,t)}})},Kanboard.Search.prototype.listen=function(){$(document).on("click",".filter-helper",function(e){e.preventDefault();var t=$(this).data("filter"),o=$(this).data("append-filter"),n=$(this).data("unique-filter"),a=$("#form-search");if(n){var i=n.substr(0,n.indexOf(":"));t=a.val().replace(new RegExp("("+i+":[#a-z0-9]+)","g"),""),t=t.replace(new RegExp("("+i+':"(.+)")',"g"),""),t=t.trim(),t+=" "+n}else o&&(t=a.val()+" "+o);a.val(t),$("form.search").submit()})},Kanboard.Swimlane=function(e){this.app=e},Kanboard.Swimlane.prototype.execute=function(){$(".swimlanes-table").length&&this.dragAndDrop()},Kanboard.Swimlane.prototype.listen=function(){var e=this;$(document).on("click",".board-swimlane-toggle",function(t){t.preventDefault();var o=$(this).data("swimlane-id");e.isCollapsed(o)?e.expand(o):e.collapse(o)})},Kanboard.Swimlane.prototype.onBoardRendered=function(){for(var e=this.getAllCollapsed(),t=0;t<e.length;t++)this.collapse(e[t])},Kanboard.Swimlane.prototype.getStorageKey=function(){return"hidden_swimlanes_"+$("#board").data("project-id")},Kanboard.Swimlane.prototype.expand=function(e){var t=this.getAllCollapsed(),o=t.indexOf(e);o>-1&&t.splice(o,1),localStorage.setItem(this.getStorageKey(),JSON.stringify(t)),$(".board-swimlane-columns-"+e).css("display","table-row"),$(".board-swimlane-tasks-"+e).css("display","table-row"),$(".hide-icon-swimlane-"+e).css("display","inline"),$(".show-icon-swimlane-"+e).css("display","none")},Kanboard.Swimlane.prototype.collapse=function(e){var t=this.getAllCollapsed();t.indexOf(e)<0&&(t.push(e),localStorage.setItem(this.getStorageKey(),JSON.stringify(t))),$(".board-swimlane-columns-"+e+":not(:first-child)").css("display","none"),$(".board-swimlane-tasks-"+e).css("display","none"),$(".hide-icon-swimlane-"+e).css("display","none"),$(".show-icon-swimlane-"+e).css("display","inline")},Kanboard.Swimlane.prototype.isCollapsed=function(e){return this.getAllCollapsed().indexOf(e)>-1},Kanboard.Swimlane.prototype.getAllCollapsed=function(){return JSON.parse(localStorage.getItem(this.getStorageKey()))||[]},Kanboard.Swimlane.prototype.dragAndDrop=function(){var e=this;$(".draggable-row-handle").mouseenter(function(){$(this).parent().parent().addClass("draggable-item-hover")}).mouseleave(function(){$(this).parent().parent().removeClass("draggable-item-hover")}),$(".swimlanes-table tbody").sortable({forcePlaceholderSize:!0,handle:"td:first i",helper:function(e,t){return t.children().each(function(){$(this).width($(this).width())}),t},stop:function(t,o){var n=o.item;n.removeClass("draggable-item-selected"),e.savePosition(n.data("swimlane-id"),n.index()+1)},start:function(e,t){t.item.addClass("draggable-item-selected")}}).disableSelection()},Kanboard.Swimlane.prototype.savePosition=function(e,t){var o=$(".swimlanes-table").data("save-position-url"),n=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:o,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({swimlane_id:e,position:t}),complete:function(){n.app.hideLoadingIcon()}})},Kanboard.Task=function(e){this.app=e},Kanboard.Task.prototype.onPopoverOpened=function(){this.renderColorPicker(),$(document).on("click",".assign-me",function(e){var t=$(this).data("current-id"),o="#"+$(this).data("target-id");e.preventDefault(),$(o+" option[value="+t+"]").length&&$(o).val(t)})},Kanboard.Task.prototype.renderColorPicker=function(){function e(e){return $('<div class="color-picker-option"><div class="color-picker-square color-'+e.id+'"></div><div class="color-picker-label">'+e.text+"</div></div>")}$(".color-picker").select2({minimumResultsForSearch:1/0,templateResult:e,templateSelection:e})},Kanboard.Tooltip=function(e){this.app=e},Kanboard.Tooltip.prototype.onBoardRendered=function(){this.execute()},Kanboard.Tooltip.prototype.execute=function(){$(".tooltip").tooltip({track:!1,show:!1,hide:!1,position:{my:"left-20 top",at:"center bottom+9",using:function(e,t){$(this).css(e);var o=t.target.left+t.target.width/2-t.element.left-20;$("<div>").addClass("tooltip-arrow").addClass(t.vertical).addClass(o<1?"align-left":"align-right").appendTo(this)}},content:function(){var e=this,t=$(this).attr("data-href");return t?($.get(t,function(t){var o=$(".ui-tooltip:visible");$(".ui-tooltip-content:visible").html(t),o.css({top:"",left:""}),o.children(".tooltip-arrow").remove();var n=$(e).tooltip("option","position");n.of=$(e),o.position(n)}),'<i class="fa fa-spinner fa-spin"></i>'):'<div class="markdown">'+$(this).attr("title")+"</div>"}}).on("mouseenter",function(){var e=this;$(this).tooltip("open"),$(".ui-tooltip").on("mouseleave",function(){$(e).tooltip("close")})}).on("mouseleave focusout",function(e){e.stopImmediatePropagation();var t=this;setTimeout(function(){$(".ui-tooltip:hover").length||$(t).tooltip("close")},100)})},Kanboard.BoardDragAndDrop=function(e){this.app=e,this.savingInProgress=!1},Kanboard.BoardDragAndDrop.prototype.execute=function(){this.app.hasId("board")&&(this.executeListeners(),this.dragAndDrop())},Kanboard.BoardDragAndDrop.prototype.dragAndDrop=function(){var e=this,t=$(".board-task-list"),o={forcePlaceholderSize:!0,tolerance:"pointer",connectWith:".sortable-column",placeholder:"draggable-placeholder",items:".draggable-item",stop:function(t,o){var n=o.item,a=n.attr("data-task-id"),i=n.attr("data-position"),r=n.attr("data-column-id"),d=n.attr("data-swimlane-id"),s=n.parent().attr("data-column-id"),l=n.parent().attr("data-swimlane-id"),c=n.index()+1;n.removeClass("draggable-item-selected"),s==r&&l==d&&c==i||(e.changeTaskState(a),e.save(a,r,s,c,l))},start:function(e,t){t.item.addClass("draggable-item-selected"),t.placeholder.height(t.item.height())}};isMobile.any&&($(".task-board-sort-handle").css("display","inline"),o.handle=".task-board-sort-handle"),t.each(function(){$(this).css("min-height",$(this).parent().height())}),t.sortable(o)},Kanboard.BoardDragAndDrop.prototype.changeTaskState=function(e){var t=$("div[data-task-id="+e+"]");t.addClass("task-board-saving-state"),t.find(".task-board-saving-icon").show()},Kanboard.BoardDragAndDrop.prototype.save=function(e,t,o,n,a){var i=this;i.app.showLoadingIcon(),i.savingInProgress=!0,$.ajax({cache:!1,url:$("#board").data("save-url"),contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({task_id:e,src_column_id:t,dst_column_id:o,swimlane_id:a,position:n}),success:function(e){i.refresh(e),i.savingInProgress=!1},error:function(){i.app.hideLoadingIcon(),i.savingInProgress=!1},statusCode:{403:function(e){window.alert(e.responseJSON.message),document.location.reload(!0)}}})},Kanboard.BoardDragAndDrop.prototype.refresh=function(e){$("#board-container").replaceWith(e),this.app.hideLoadingIcon(),this.executeListeners(),this.dragAndDrop()},Kanboard.BoardDragAndDrop.prototype.executeListeners=function(){for(var e in this.app.controllers){var t=this.app.get(e);"function"==typeof t.onBoardRendered&&t.onBoardRendered()}};var _KB=null;jQuery(document).ready(function(){_KB=new Kanboard.App,_KB.execute()});
\ No newline at end of file diff --git a/assets/js/components/autocomplete-email.js b/assets/js/components/autocomplete-email.js new file mode 100644 index 00000000..9d337e03 --- /dev/null +++ b/assets/js/components/autocomplete-email.js @@ -0,0 +1,21 @@ +KB.onClick('.js-autocomplete-email', function (e) { + var email = KB.dom(e.target).data('email'); + var emailField = KB.find('.js-mail-form input[type="email"]'); + + if (email && emailField) { + emailField.build().value = email; + } + + _KB.controllers.Dropdown.close(); +}); + +KB.onClick('.js-autocomplete-subject', function (e) { + var subject = KB.dom(e.target).data('subject'); + var subjectField = KB.find('.js-mail-form input[name="subject"]'); + + if (subject && subjectField) { + subjectField.build().value = subject; + } + + _KB.controllers.Dropdown.close(); +}); diff --git a/assets/js/components/board-task-click.js b/assets/js/components/board-task-click.js index fa530c38..a8c193ed 100644 --- a/assets/js/components/board-task-click.js +++ b/assets/js/components/board-task-click.js @@ -24,6 +24,6 @@ } } - KB.onClick('.task-board *', redirectToTaskView); - KB.onClick('.task-board-change-assignee *', openEditTask); + KB.onClick('.task-board *', redirectToTaskView, true); + KB.onClick('.task-board-change-assignee *', openEditTask, true); }()); diff --git a/assets/js/components/calendar.js b/assets/js/components/calendar.js deleted file mode 100644 index ed6916b2..00000000 --- a/assets/js/components/calendar.js +++ /dev/null @@ -1,65 +0,0 @@ -KB.component('calendar', function (containerElement, options) { - var modeMapping = { // Let's have bookable pretty mode names - month: 'month', - week: 'agendaWeek', - day: 'agendaDay' - }; - - this.render = function () { - var calendar = $(containerElement); - var mode = 'month'; - if (window.location.hash) { // Check if hash contains mode - var hashMode = window.location.hash.substr(1); - mode = modeMapping[hashMode] || mode; - } - - calendar.fullCalendar({ - locale: $("body").data("js-lang"), - editable: true, - eventLimit: true, - defaultView: mode, - header: { - left: 'prev,next today', - center: 'title', - right: 'month,agendaWeek,agendaDay' - }, - eventDrop: function(event) { - $.ajax({ - cache: false, - url: options.saveUrl, - contentType: "application/json", - type: "POST", - processData: false, - data: JSON.stringify({ - "task_id": event.id, - "date_due": event.start.format() - }) - }); - }, - viewRender: function(view) { - // Map view.name back and update location.hash - for (var id in modeMapping) { - if (modeMapping[id] === view.name) { // Found - window.location.hash = id; - break; - } - } - var url = options.checkUrl; - var params = { - "start": calendar.fullCalendar('getView').start.format(), - "end": calendar.fullCalendar('getView').end.format() - }; - - for (var key in params) { - url += "&" + key + "=" + params[key]; - } - - $.getJSON(url, function(events) { - calendar.fullCalendar('removeEvents'); - calendar.fullCalendar('addEventSource', events); - calendar.fullCalendar('rerenderEvents'); - }); - } - }); - }; -}); diff --git a/assets/js/components/external-task-view.js b/assets/js/components/external-task-view.js index d402640b..b5b8f77a 100644 --- a/assets/js/components/external-task-view.js +++ b/assets/js/components/external-task-view.js @@ -1,6 +1,8 @@ KB.component('external-task-view', function (containerElement, options) { this.render = function () { + KB.dom(containerElement).html('<div id="external-task-view"><i class="fa fa-spinner fa-2x fa-pulse"></div>'); + $.ajax({ cache: false, url: options.url, diff --git a/assets/js/components/file-upload.js b/assets/js/components/file-upload.js index ccc57ad7..3786077d 100644 --- a/assets/js/components/file-upload.js +++ b/assets/js/components/file-upload.js @@ -62,7 +62,9 @@ KB.component('file-upload', function (containerElement, options) { } function onFileChange() { - files = inputFileElement.files; + for (var i = 0; i < inputFileElement.files.length; i++) { + files.push(inputFileElement.files[i]); + } showFiles(); } @@ -81,7 +83,10 @@ KB.component('file-upload', function (containerElement, options) { e.stopPropagation(); e.preventDefault(); - files = e.dataTransfer.files; + for (var i = 0; i < e.dataTransfer.files.length; i++) { + files.push(e.dataTransfer.files[i]); + } + showFiles(); } @@ -157,8 +162,19 @@ KB.component('file-upload', function (containerElement, options) { .text('(0%)') .build(); + var deleteElement = KB.dom('span') + .attr('id', 'file-delete-' + index) + .html('<a href="#"><i class="fa fa-trash fa-fw"></i></a>') + .on('click', function () { + files.splice(index, 1); + KB.find('#file-item-' + index).remove(); + showFiles(); + }) + .build(); + var itemElement = KB.dom('li') .attr('id', 'file-item-' + index) + .add(deleteElement) .add(progressElement) .text(' ' + files[index].name + ' ') .add(percentageElement); diff --git a/assets/js/components/keyboard-shortcuts.js b/assets/js/components/keyboard-shortcuts.js index cffcd790..02d62119 100644 --- a/assets/js/components/keyboard-shortcuts.js +++ b/assets/js/components/keyboard-shortcuts.js @@ -117,15 +117,7 @@ KB.keyboardShortcuts = function () { goToLink('a.view-board'); }); - KB.onKey('v+c', function () { - goToLink('a.view-calendar'); - }); - KB.onKey('v+l', function () { goToLink('a.view-listing'); }); - - KB.onKey('v+g', function () { - goToLink('a.view-gantt'); - }); }; diff --git a/assets/js/components/select-dropdown-autocomplete.js b/assets/js/components/select-dropdown-autocomplete.js index 188dd5e9..8d1cd13d 100644 --- a/assets/js/components/select-dropdown-autocomplete.js +++ b/assets/js/components/select-dropdown-autocomplete.js @@ -183,6 +183,7 @@ KB.component('select-dropdown-autocomplete', function(containerElement, options) function buildDropdownMenu() { var itemElements = filterItems(inputElement.value, buildItems(options.items)); var componentPosition = componentElement.getBoundingClientRect(); + var windowPosition = document.body.scrollTop || document.documentElement.scrollTop; if (itemElements.length === 0) { return null; @@ -190,7 +191,7 @@ KB.component('select-dropdown-autocomplete', function(containerElement, options) return KB.dom('ul') .attr('id', 'select-dropdown-menu') - .style('top', (document.body.scrollTop + componentPosition.bottom) + 'px') + .style('top', (windowPosition + componentPosition.bottom) + 'px') .style('left', componentPosition.left + 'px') .style('width', componentPosition.width + 'px') .style('maxHeight', (window.innerHeight - componentPosition.bottom - 20) + 'px') diff --git a/assets/js/components/subtask-drag-and-drop.js b/assets/js/components/subtask-drag-and-drop.js new file mode 100644 index 00000000..f704376a --- /dev/null +++ b/assets/js/components/subtask-drag-and-drop.js @@ -0,0 +1,44 @@ +KB.on('dom.ready', function() { + + function savePosition(subtaskId, position) { + var url = $(".subtasks-table").data("save-position-url"); + + $.ajax({ + cache: false, + url: url, + contentType: "application/json", + type: "POST", + processData: false, + data: JSON.stringify({ + "subtask_id": subtaskId, + "position": position + }) + }); + } + + $(".draggable-row-handle").mouseenter(function() { + $(this).parent().parent().addClass("draggable-item-hover"); + }).mouseleave(function() { + $(this).parent().parent().removeClass("draggable-item-hover"); + }); + + $(".subtasks-table tbody").sortable({ + forcePlaceholderSize: true, + handle: "td:first i", + helper: function(e, ui) { + ui.children().each(function() { + $(this).width($(this).width()); + }); + + return ui; + }, + stop: function(event, ui) { + var subtask = ui.item; + subtask.removeClass("draggable-item-selected"); + savePosition(subtask.data("subtask-id"), subtask.index() + 1); + }, + start: function(event, ui) { + ui.item.addClass("draggable-item-selected"); + } + }).disableSelection(); +}); diff --git a/assets/js/components/subtask-toggle-status.js b/assets/js/components/subtask-toggle-status.js new file mode 100644 index 00000000..3014adfa --- /dev/null +++ b/assets/js/components/subtask-toggle-status.js @@ -0,0 +1,35 @@ +KB.on('dom.ready', function () { + $(document).on('click', '.js-subtask-toggle-status', function(e) { + var el = $(this); + var url = el.attr('href'); + + e.preventDefault(); + + $.ajax({ + cache: false, + url: url, + success: function(data) { + if (url.indexOf('fragment=table') != -1) { + $('.subtasks-table').replaceWith(data); + } else if (url.indexOf('fragment=rows') != -1) { + $(el).closest('.task-list-subtasks').replaceWith(data); + } else { + $(el).closest('.subtask-title').replaceWith(data); + } + } + }); + }); + + $(document).on('click', '.js-subtask-toggle-timer', function(e) { + var el = $(this); + e.preventDefault(); + + $.ajax({ + cache: false, + url: el.attr('href'), + success: function(data) { + $(el).closest('.subtask-time-tracking').replaceWith(data); + } + }); + }); +}); diff --git a/assets/js/components/template.js b/assets/js/components/template.js new file mode 100644 index 00000000..c9ca9ab6 --- /dev/null +++ b/assets/js/components/template.js @@ -0,0 +1,11 @@ +KB.onClick('.js-template', function (e) { + var template = KB.dom(e.target).data('template'); + var target = KB.dom(e.target).data('templateTarget'); + var targetField = KB.find(target); + + if (targetField) { + targetField.build().value = template; + } + + _KB.controllers.Dropdown.close(); +});
\ No newline at end of file diff --git a/assets/js/core/base.js b/assets/js/core/base.js index 6bfe22ba..165e74ac 100644 --- a/assets/js/core/base.js +++ b/assets/js/core/base.js @@ -37,8 +37,11 @@ KB.removeListener = function (eventType, callback) { } }; -KB.onClick = function (selector, callback) { - this.listeners.clicks[selector] = callback; +KB.onClick = function (selector, callback, noPreventDefault) { + this.listeners.clicks[selector] = { + callback: callback, + noPreventDefault: noPreventDefault === true + }; }; KB.onChange = function (selector, callback) { @@ -62,8 +65,11 @@ KB.listen = function () { function onClick(e) { for (var selector in self.listeners.clicks) { if (self.listeners.clicks.hasOwnProperty(selector) && e.target.matches(selector)) { - e.preventDefault(); - self.listeners.clicks[selector](e); + if (! self.listeners.clicks[selector].noPreventDefault) { + e.preventDefault(); + } + + self.listeners.clicks[selector].callback(e); } } } diff --git a/assets/js/core/modal.js b/assets/js/core/modal.js index d3b14192..e7f2c2ff 100644 --- a/assets/js/core/modal.js +++ b/assets/js/core/modal.js @@ -37,6 +37,12 @@ } else { destroy(); } + }).error(function (response) { + KB.trigger('modal.stop'); + + if (response.hasOwnProperty('message')) { + window.alert(response.message); + } }); } } @@ -58,6 +64,8 @@ _KB.autoComplete(); _KB.tagAutoComplete(); _KB.get('Task').onPopoverOpened(); + + KB.trigger('modal.afterRender'); } function replace(html) { @@ -117,6 +125,8 @@ var overlayElement = KB.find('#modal-overlay'); if (overlayElement) { + KB.trigger('modal.beforeDestroy'); + overlayElement.remove(); } } @@ -148,6 +158,8 @@ KB.modal = { open: function (url, size, overlayClickDestroy) { + KB.trigger('modal.open'); + _KB.get('Dropdown').close(); destroy(); diff --git a/assets/js/src/App.js b/assets/js/src/App.js index 12889c58..94488d79 100644 --- a/assets/js/src/App.js +++ b/assets/js/src/App.js @@ -79,7 +79,7 @@ Kanboard.App.prototype.autoComplete = function() { $(".autocomplete").each(function() { var input = $(this); var field = input.data("dst-field"); - var extraField = input.data("dst-extra-field"); + var extraFields = input.data("dst-extra-fields"); if ($('#form-' + field).val() === '') { input.parent().find("button[type=submit]").attr('disabled','disabled'); @@ -91,8 +91,13 @@ Kanboard.App.prototype.autoComplete = function() { select: function(event, ui) { $("input[name=" + field + "]").val(ui.item.id); - if (extraField) { - $("input[name=" + extraField + "]").val(ui.item[extraField]); + if (extraFields) { + var fields = extraFields.split(','); + + for (var i = 0; i < fields.length; i++) { + var fieldName = fields[i].trim(); + $("input[name=" + fieldName + "]").val(ui.item[fieldName]); + } } input.parent().find("button[type=submit]").removeAttr('disabled'); diff --git a/assets/js/src/Dropdown.js b/assets/js/src/Dropdown.js index ec033b3e..20f5b56d 100644 --- a/assets/js/src/Dropdown.js +++ b/assets/js/src/Dropdown.js @@ -41,15 +41,30 @@ Kanboard.Dropdown.prototype.listen = function() { else { clone.css('left', offset.left); } + + if (document.getElementById('dropdown') !== null) { + KB.trigger('dropdown.afterRender'); + } }); $(document).on('click', '.dropdown-submenu-open li', function(e) { + if ($(e.target).is('li')) { - $(this).find('a:visible')[0].click(); // Calling native click() not the jQuery one + KB.trigger('dropdown.clicked'); + + var element = $(this).find('a:visible'); + + if (element.length > 0) { + element[0].click(); // Calling native click() not the jQuery one + } } }); }; Kanboard.Dropdown.prototype.close = function() { + if (document.getElementById('dropdown') !== null) { + KB.trigger('dropdown.beforeDestroy'); + } + $("#dropdown").remove(); }; diff --git a/assets/js/src/Gantt.js b/assets/js/src/Gantt.js deleted file mode 100644 index cd6cac00..00000000 --- a/assets/js/src/Gantt.js +++ /dev/null @@ -1,489 +0,0 @@ -// Based on jQuery.ganttView v.0.8.8 Copyright (c) 2010 JC Grubbs - jc.grubbs@devmynd.com - MIT License -Kanboard.Gantt = function(app) { - this.app = app; - this.data = []; - - this.options = { - container: "#gantt-chart", - showWeekends: true, - allowMoves: true, - allowResizes: true, - cellWidth: 21, - cellHeight: 31, - slideWidth: 1000, - vHeaderWidth: 200 - }; -}; - -Kanboard.Gantt.prototype.execute = function() { - if (this.app.hasId("gantt-chart")) { - this.show(); - } -}; - -// Save record after a resize or move -Kanboard.Gantt.prototype.saveRecord = function(record) { - this.app.showLoadingIcon(); - - $.ajax({ - cache: false, - url: $(this.options.container).data("save-url"), - contentType: "application/json", - type: "POST", - processData: false, - data: JSON.stringify(record), - complete: this.app.hideLoadingIcon.bind(this) - }); -}; - -// Build the Gantt chart -Kanboard.Gantt.prototype.show = function() { - this.data = this.prepareData($(this.options.container).data('records')); - - var minDays = Math.floor((this.options.slideWidth / this.options.cellWidth) + 5); - var range = this.getDateRange(minDays); - var startDate = range[0]; - var endDate = range[1]; - var container = $(this.options.container); - var chart = jQuery("<div>", { "class": "ganttview" }); - - chart.append(this.renderVerticalHeader()); - chart.append(this.renderSlider(startDate, endDate)); - container.append(chart); - - jQuery("div.ganttview-grid-row div.ganttview-grid-row-cell:last-child", container).addClass("last"); - jQuery("div.ganttview-hzheader-days div.ganttview-hzheader-day:last-child", container).addClass("last"); - jQuery("div.ganttview-hzheader-months div.ganttview-hzheader-month:last-child", container).addClass("last"); - - if (! $(this.options.container).data('readonly')) { - this.listenForBlockResize(startDate); - this.listenForBlockMove(startDate); - } - else { - this.options.allowResizes = false; - this.options.allowMoves = false; - } -}; - -// Render record list on the left -Kanboard.Gantt.prototype.renderVerticalHeader = function() { - var headerDiv = jQuery("<div>", { "class": "ganttview-vtheader" }); - var itemDiv = jQuery("<div>", { "class": "ganttview-vtheader-item" }); - var seriesDiv = jQuery("<div>", { "class": "ganttview-vtheader-series" }); - - for (var i = 0; i < this.data.length; i++) { - var content = jQuery("<span>") - .append(jQuery("<i>", {"class": "fa fa-info-circle tooltip", "title": this.getVerticalHeaderTooltip(this.data[i])})) - .append(" "); - - if (this.data[i].type == "task") { - content.append(jQuery("<a>", {"href": this.data[i].link, "title": this.data[i].title}).text(this.data[i].title)); - } - else { - content - .append(jQuery("<a>", {"href": this.data[i].board_link, "title": $(this.options.container).data("label-board-link")}).append('<i class="fa fa-th"></i>')) - .append(" ") - .append(jQuery("<a>", {"href": this.data[i].gantt_link, "title": $(this.options.container).data("label-gantt-link")}).append('<i class="fa fa-sliders"></i>')) - .append(" ") - .append(jQuery("<a>", {"href": this.data[i].link}).text(this.data[i].title)); - } - - seriesDiv.append(jQuery("<div>", {"class": "ganttview-vtheader-series-name"}).append(content)); - } - - itemDiv.append(seriesDiv); - headerDiv.append(itemDiv); - - return headerDiv; -}; - -// Render right part of the chart (top header + grid + bars) -Kanboard.Gantt.prototype.renderSlider = function(startDate, endDate) { - var slideDiv = jQuery("<div>", {"class": "ganttview-slide-container"}); - var dates = this.getDates(startDate, endDate); - - slideDiv.append(this.renderHorizontalHeader(dates)); - slideDiv.append(this.renderGrid(dates)); - slideDiv.append(this.addBlockContainers()); - this.addBlocks(slideDiv, startDate); - - return slideDiv; -}; - -// Render top header (days) -Kanboard.Gantt.prototype.renderHorizontalHeader = function(dates) { - var headerDiv = jQuery("<div>", { "class": "ganttview-hzheader" }); - var monthsDiv = jQuery("<div>", { "class": "ganttview-hzheader-months" }); - var daysDiv = jQuery("<div>", { "class": "ganttview-hzheader-days" }); - var totalW = 0; - - for (var y in dates) { - for (var m in dates[y]) { - var w = dates[y][m].length * this.options.cellWidth; - totalW = totalW + w; - - monthsDiv.append(jQuery("<div>", { - "class": "ganttview-hzheader-month", - "css": { "width": (w - 1) + "px" } - }).append($.datepicker.regional[$("body").data('js-lang')].monthNames[m] + " " + y)); - - for (var d in dates[y][m]) { - daysDiv.append(jQuery("<div>", { "class": "ganttview-hzheader-day" }).append(dates[y][m][d].getDate())); - } - } - } - - monthsDiv.css("width", totalW + "px"); - daysDiv.css("width", totalW + "px"); - headerDiv.append(monthsDiv).append(daysDiv); - - return headerDiv; -}; - -// Render grid -Kanboard.Gantt.prototype.renderGrid = function(dates) { - var gridDiv = jQuery("<div>", { "class": "ganttview-grid" }); - var rowDiv = jQuery("<div>", { "class": "ganttview-grid-row" }); - - for (var y in dates) { - for (var m in dates[y]) { - for (var d in dates[y][m]) { - var cellDiv = jQuery("<div>", { "class": "ganttview-grid-row-cell" }); - if (this.options.showWeekends && this.isWeekend(dates[y][m][d])) { - cellDiv.addClass("ganttview-weekend"); - } - rowDiv.append(cellDiv); - } - } - } - var w = jQuery("div.ganttview-grid-row-cell", rowDiv).length * this.options.cellWidth; - rowDiv.css("width", w + "px"); - gridDiv.css("width", w + "px"); - - for (var i = 0; i < this.data.length; i++) { - gridDiv.append(rowDiv.clone()); - } - - return gridDiv; -}; - -// Render bar containers -Kanboard.Gantt.prototype.addBlockContainers = function() { - var blocksDiv = jQuery("<div>", { "class": "ganttview-blocks" }); - - for (var i = 0; i < this.data.length; i++) { - blocksDiv.append(jQuery("<div>", { "class": "ganttview-block-container" })); - } - - return blocksDiv; -}; - -// Render bars -Kanboard.Gantt.prototype.addBlocks = function(slider, start) { - var rows = jQuery("div.ganttview-blocks div.ganttview-block-container", slider); - var rowIdx = 0; - - for (var i = 0; i < this.data.length; i++) { - var series = this.data[i]; - var size = this.daysBetween(series.start, series.end) + 1; - var offset = this.daysBetween(start, series.start); - var text = jQuery("<div>", {"class": "ganttview-block-text"}); - - var block = jQuery("<div>", { - "class": "ganttview-block tooltip" + (this.options.allowMoves ? " ganttview-block-movable" : ""), - "title": this.getBarTooltip(series), - "css": { - "width": ((size * this.options.cellWidth) - 9) + "px", - "margin-left": (offset * this.options.cellWidth) + "px" - } - }).append(text); - - if (size >= 2) { - text.append(series.progress); - } - - block.data("record", series); - this.setBarColor(block, series); - - jQuery(rows[rowIdx]).append(block); - rowIdx = rowIdx + 1; - } -}; - -// Get tooltip for vertical header -Kanboard.Gantt.prototype.getVerticalHeaderTooltip = function(record) { - var tooltip = ""; - - if (record.type == "task") { - tooltip = jQuery("<span>") - .append(jQuery("<strong>").text(record.column_title)) - .append(document.createTextNode(' (' + record.progress + ')')) - .append(jQuery("<br>")) - .append(document.createTextNode(record.title)).prop('outerHTML'); - } - else { - var types = ["project-manager", "project-member"]; - - for (var index in types) { - var type = types[index]; - if (! jQuery.isEmptyObject(record.users[type])) { - var list = jQuery("<ul>"); - - for (var user_id in record.users[type]) { - if (user_id) { - list.append(jQuery("<li>").text(record.users[type][user_id])); - } - } - - tooltip += "<p><strong>" + $(this.options.container).data("label-" + type) + "</strong></p>" + list.prop('outerHTML'); - } - } - } - - return tooltip; -}; - -// Get tooltip for bars -Kanboard.Gantt.prototype.getBarTooltip = function(record) { - var tooltip = ""; - - if (record.not_defined) { - tooltip = $(this.options.container).data("label-not-defined"); - } - else { - if (record.type == "task") { - var assigneeLabel = $(this.options.container).data("label-assignee"); - tooltip += jQuery("<strong>").text(record.progress).prop('outerHTML'); - tooltip += "<br>"; - tooltip += jQuery('<span>').append(document.createTextNode(assigneeLabel + " " + (record.assignee ? record.assignee : ''))).prop('outerHTML'); - tooltip += "<br>"; - } - - tooltip += $(this.options.container).data("label-start-date") + " " + $.datepicker.formatDate('yy-mm-dd', record.start) + "<br/>"; - tooltip += $(this.options.container).data("label-end-date") + " " + $.datepicker.formatDate('yy-mm-dd', record.end); - } - - return tooltip; -}; - -// Set bar color -Kanboard.Gantt.prototype.setBarColor = function(block, record) { - if (record.not_defined) { - block.addClass("ganttview-block-not-defined"); - } - else { - block.css("background-color", record.color.background); - block.css("border-color", record.color.border); - - if (record.progress != "0%") { - block.append(jQuery("<div>", { - "css": { - "z-index": 0, - "position": "absolute", - "top": 0, - "bottom": 0, - "background-color": record.color.border, - "width": record.progress, - "opacity": 0.4 - } - })); - } - } -}; - -// Setup jquery-ui resizable -Kanboard.Gantt.prototype.listenForBlockResize = function(startDate) { - var self = this; - - jQuery("div.ganttview-block", this.options.container).resizable({ - grid: this.options.cellWidth, - handles: "e,w", - delay: 300, - stop: function() { - var block = jQuery(this); - self.updateDataAndPosition(block, startDate); - self.saveRecord(block.data("record")); - } - }); -}; - -// Setup jquery-ui drag and drop -Kanboard.Gantt.prototype.listenForBlockMove = function(startDate) { - var self = this; - - jQuery("div.ganttview-block", this.options.container).draggable({ - axis: "x", - delay: 300, - grid: [this.options.cellWidth, this.options.cellWidth], - stop: function() { - var block = jQuery(this); - self.updateDataAndPosition(block, startDate); - self.saveRecord(block.data("record")); - } - }); -}; - -// Update the record data and the position on the chart -Kanboard.Gantt.prototype.updateDataAndPosition = function(block, startDate) { - var container = jQuery("div.ganttview-slide-container", this.options.container); - var scroll = container.scrollLeft(); - var offset = block.offset().left - container.offset().left - 1 + scroll; - var record = block.data("record"); - - // Restore color for defined block - record.not_defined = false; - this.setBarColor(block, record); - - // Set new start date - var daysFromStart = Math.round(offset / this.options.cellWidth); - var newStart = this.addDays(this.cloneDate(startDate), daysFromStart); - record.start = newStart; - - // Set new end date - var width = block.outerWidth(); - var numberOfDays = Math.round(width / this.options.cellWidth) - 1; - record.end = this.addDays(this.cloneDate(newStart), numberOfDays); - - if (record.type === "task" && numberOfDays > 0) { - jQuery("div.ganttview-block-text", block).text(record.progress); - } - - // Update tooltip - block.attr("title", this.getBarTooltip(record)); - - block.data("record", record); - - // Remove top and left properties to avoid incorrect block positioning, - // set position to relative to keep blocks relative to scrollbar when scrolling - block - .css("top", "") - .css("left", "") - .css("position", "relative") - .css("margin-left", offset + "px"); -}; - -// Creates a 3 dimensional array [year][month][day] of every day -// between the given start and end dates -Kanboard.Gantt.prototype.getDates = function(start, end) { - var dates = []; - dates[start.getFullYear()] = []; - dates[start.getFullYear()][start.getMonth()] = [start]; - var last = start; - - while (this.compareDate(last, end) == -1) { - var next = this.addDays(this.cloneDate(last), 1); - - if (! dates[next.getFullYear()]) { - dates[next.getFullYear()] = []; - } - - if (! dates[next.getFullYear()][next.getMonth()]) { - dates[next.getFullYear()][next.getMonth()] = []; - } - - dates[next.getFullYear()][next.getMonth()].push(next); - last = next; - } - - return dates; -}; - -// Convert data to Date object -Kanboard.Gantt.prototype.prepareData = function(data) { - for (var i = 0; i < data.length; i++) { - var start = new Date(data[i].start[0], data[i].start[1] - 1, data[i].start[2], 0, 0, 0, 0); - data[i].start = start; - - var end = new Date(data[i].end[0], data[i].end[1] - 1, data[i].end[2], 0, 0, 0, 0); - data[i].end = end; - } - - return data; -}; - -// Get the start and end date from the data provided -Kanboard.Gantt.prototype.getDateRange = function(minDays) { - var minStart = new Date(); - var maxEnd = new Date(); - - for (var i = 0; i < this.data.length; i++) { - var start = new Date(); - start.setTime(Date.parse(this.data[i].start)); - - var end = new Date(); - end.setTime(Date.parse(this.data[i].end)); - - if (i == 0) { - minStart = start; - maxEnd = end; - } - - if (this.compareDate(minStart, start) == 1) { - minStart = start; - } - - if (this.compareDate(maxEnd, end) == -1) { - maxEnd = end; - } - } - - // Insure that the width of the chart is at least the slide width to avoid empty - // whitespace to the right of the grid - if (this.daysBetween(minStart, maxEnd) < minDays) { - maxEnd = this.addDays(this.cloneDate(minStart), minDays); - } - - // Always start one day before the minStart - minStart.setDate(minStart.getDate() - 1); - - return [minStart, maxEnd]; -}; - -// Returns the number of day between 2 dates -Kanboard.Gantt.prototype.daysBetween = function(start, end) { - if (! start || ! end) { - return 0; - } - - var count = 0, date = this.cloneDate(start); - - while (this.compareDate(date, end) == -1) { - count = count + 1; - this.addDays(date, 1); - } - - return count; -}; - -// Return true if it's the weekend -Kanboard.Gantt.prototype.isWeekend = function(date) { - return date.getDay() % 6 == 0; -}; - -// Clone Date object -Kanboard.Gantt.prototype.cloneDate = function(date) { - return new Date(date.getTime()); -}; - -// Add days to a Date object -Kanboard.Gantt.prototype.addDays = function(date, value) { - date.setDate(date.getDate() + value * 1); - return date; -}; - -/** - * Compares the first date to the second date and returns an number indication of their relative values. - * - * -1 = date1 is lessthan date2 - * 0 = values are equal - * 1 = date1 is greaterthan date2. - */ -Kanboard.Gantt.prototype.compareDate = function(date1, date2) { - if (isNaN(date1) || isNaN(date2)) { - throw new Error(date1 + " - " + date2); - } else if (date1 instanceof Date && date2 instanceof Date) { - return (date1 < date2) ? -1 : (date1 > date2) ? 1 : 0; - } else { - throw new TypeError(date1 + " - " + date2); - } -}; diff --git a/assets/js/src/Subtask.js b/assets/js/src/Subtask.js deleted file mode 100644 index eef160d7..00000000 --- a/assets/js/src/Subtask.js +++ /dev/null @@ -1,86 +0,0 @@ -Kanboard.Subtask = function(app) { - this.app = app; -}; - -Kanboard.Subtask.prototype.listen = function() { - var self = this; - this.dragAndDrop(); - - $(document).on("click", ".js-subtask-toggle-status", function(e) { - var el = $(this); - e.preventDefault(); - - $.ajax({ - cache: false, - url: el.attr("href"), - success: function(data) { - $(el).closest('.subtask-title').replaceWith(data); - } - }); - }); - - $(document).on("click", ".js-subtask-toggle-timer", function(e) { - var el = $(this); - e.preventDefault(); - - $.ajax({ - cache: false, - url: el.attr("href"), - success: function(data) { - $(el).closest('.subtask-time-tracking').replaceWith(data); - } - }); - }); -}; - -Kanboard.Subtask.prototype.dragAndDrop = function() { - var self = this; - - $(".draggable-row-handle").mouseenter(function() { - $(this).parent().parent().addClass("draggable-item-hover"); - }).mouseleave(function() { - $(this).parent().parent().removeClass("draggable-item-hover"); - }); - - $(".subtasks-table tbody").sortable({ - forcePlaceholderSize: true, - handle: "td:first i", - helper: function(e, ui) { - ui.children().each(function() { - $(this).width($(this).width()); - }); - - return ui; - }, - stop: function(event, ui) { - var subtask = ui.item; - subtask.removeClass("draggable-item-selected"); - self.savePosition(subtask.data("subtask-id"), subtask.index() + 1); - }, - start: function(event, ui) { - ui.item.addClass("draggable-item-selected"); - } - }).disableSelection(); -}; - -Kanboard.Subtask.prototype.savePosition = function(subtaskId, position) { - var url = $(".subtasks-table").data("save-position-url"); - var self = this; - - this.app.showLoadingIcon(); - - $.ajax({ - cache: false, - url: url, - contentType: "application/json", - type: "POST", - processData: false, - data: JSON.stringify({ - "subtask_id": subtaskId, - "position": position - }), - complete: function() { - self.app.hideLoadingIcon(); - } - }); -}; diff --git a/assets/js/vendor.min.js b/assets/js/vendor.min.js index 77d5776e..ac8ee966 100644 --- a/assets/js/vendor.min.js +++ b/assets/js/vendor.min.js @@ -16,6 +16,302 @@ this.isMultiLine=o||!a&&this._isContentEditable(this.element),this.valueMethod=t },disable:t.noop,enable:t.noop,close:function(e){var i=this;this._isOpen&&this._trigger("beforeClose",e)!==!1&&(this._isOpen=!1,this._focusedElement=null,this._destroyOverlay(),this._untrackInstance(),this.opener.filter(":focusable").trigger("focus").length||t.ui.safeBlur(t.ui.safeActiveElement(this.document[0])),this._hide(this.uiDialog,this.options.hide,function(){i._trigger("close",e)}))},isOpen:function(){return this._isOpen},moveToTop:function(){this._moveToTop()},_moveToTop:function(e,i){var s=!1,n=this.uiDialog.siblings(".ui-front:visible").map(function(){return+t(this).css("z-index")}).get(),o=Math.max.apply(null,n);return o>=+this.uiDialog.css("z-index")&&(this.uiDialog.css("z-index",o+1),s=!0),s&&!i&&this._trigger("focus",e),s},open:function(){var e=this;return this._isOpen?(this._moveToTop()&&this._focusTabbable(),void 0):(this._isOpen=!0,this.opener=t(t.ui.safeActiveElement(this.document[0])),this._size(),this._position(),this._createOverlay(),this._moveToTop(null,!0),this.overlay&&this.overlay.css("z-index",this.uiDialog.css("z-index")-1),this._show(this.uiDialog,this.options.show,function(){e._focusTabbable(),e._trigger("focus")}),this._makeFocusTarget(),this._trigger("open"),void 0)},_focusTabbable:function(){var t=this._focusedElement;t||(t=this.element.find("[autofocus]")),t.length||(t=this.element.find(":tabbable")),t.length||(t=this.uiDialogButtonPane.find(":tabbable")),t.length||(t=this.uiDialogTitlebarClose.filter(":tabbable")),t.length||(t=this.uiDialog),t.eq(0).trigger("focus")},_keepFocus:function(e){function i(){var e=t.ui.safeActiveElement(this.document[0]),i=this.uiDialog[0]===e||t.contains(this.uiDialog[0],e);i||this._focusTabbable()}e.preventDefault(),i.call(this),this._delay(i)},_createWrapper:function(){this.uiDialog=t("<div>").hide().attr({tabIndex:-1,role:"dialog"}).appendTo(this._appendTo()),this._addClass(this.uiDialog,"ui-dialog","ui-widget ui-widget-content ui-front"),this._on(this.uiDialog,{keydown:function(e){if(this.options.closeOnEscape&&!e.isDefaultPrevented()&&e.keyCode&&e.keyCode===t.ui.keyCode.ESCAPE)return e.preventDefault(),this.close(e),void 0;if(e.keyCode===t.ui.keyCode.TAB&&!e.isDefaultPrevented()){var i=this.uiDialog.find(":tabbable"),s=i.filter(":first"),n=i.filter(":last");e.target!==n[0]&&e.target!==this.uiDialog[0]||e.shiftKey?e.target!==s[0]&&e.target!==this.uiDialog[0]||!e.shiftKey||(this._delay(function(){n.trigger("focus")}),e.preventDefault()):(this._delay(function(){s.trigger("focus")}),e.preventDefault())}},mousedown:function(t){this._moveToTop(t)&&this._focusTabbable()}}),this.element.find("[aria-describedby]").length||this.uiDialog.attr({"aria-describedby":this.element.uniqueId().attr("id")})},_createTitlebar:function(){var e;this.uiDialogTitlebar=t("<div>"),this._addClass(this.uiDialogTitlebar,"ui-dialog-titlebar","ui-widget-header ui-helper-clearfix"),this._on(this.uiDialogTitlebar,{mousedown:function(e){t(e.target).closest(".ui-dialog-titlebar-close")||this.uiDialog.trigger("focus")}}),this.uiDialogTitlebarClose=t("<button type='button'></button>").button({label:t("<a>").text(this.options.closeText).html(),icon:"ui-icon-closethick",showLabel:!1}).appendTo(this.uiDialogTitlebar),this._addClass(this.uiDialogTitlebarClose,"ui-dialog-titlebar-close"),this._on(this.uiDialogTitlebarClose,{click:function(t){t.preventDefault(),this.close(t)}}),e=t("<span>").uniqueId().prependTo(this.uiDialogTitlebar),this._addClass(e,"ui-dialog-title"),this._title(e),this.uiDialogTitlebar.prependTo(this.uiDialog),this.uiDialog.attr({"aria-labelledby":e.attr("id")})},_title:function(t){this.options.title?t.text(this.options.title):t.html(" ")},_createButtonPane:function(){this.uiDialogButtonPane=t("<div>"),this._addClass(this.uiDialogButtonPane,"ui-dialog-buttonpane","ui-widget-content ui-helper-clearfix"),this.uiButtonSet=t("<div>").appendTo(this.uiDialogButtonPane),this._addClass(this.uiButtonSet,"ui-dialog-buttonset"),this._createButtons()},_createButtons:function(){var e=this,i=this.options.buttons;return this.uiDialogButtonPane.remove(),this.uiButtonSet.empty(),t.isEmptyObject(i)||t.isArray(i)&&!i.length?(this._removeClass(this.uiDialog,"ui-dialog-buttons"),void 0):(t.each(i,function(i,s){var n,o;s=t.isFunction(s)?{click:s,text:i}:s,s=t.extend({type:"button"},s),n=s.click,o={icon:s.icon,iconPosition:s.iconPosition,showLabel:s.showLabel,icons:s.icons,text:s.text},delete s.click,delete s.icon,delete s.iconPosition,delete s.showLabel,delete s.icons,"boolean"==typeof s.text&&delete s.text,t("<button></button>",s).button(o).appendTo(e.uiButtonSet).on("click",function(){n.apply(e.element[0],arguments)})}),this._addClass(this.uiDialog,"ui-dialog-buttons"),this.uiDialogButtonPane.appendTo(this.uiDialog),void 0)},_makeDraggable:function(){function e(t){return{position:t.position,offset:t.offset}}var i=this,s=this.options;this.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(s,n){i._addClass(t(this),"ui-dialog-dragging"),i._blockFrames(),i._trigger("dragStart",s,e(n))},drag:function(t,s){i._trigger("drag",t,e(s))},stop:function(n,o){var a=o.offset.left-i.document.scrollLeft(),r=o.offset.top-i.document.scrollTop();s.position={my:"left top",at:"left"+(a>=0?"+":"")+a+" "+"top"+(r>=0?"+":"")+r,of:i.window},i._removeClass(t(this),"ui-dialog-dragging"),i._unblockFrames(),i._trigger("dragStop",n,e(o))}})},_makeResizable:function(){function e(t){return{originalPosition:t.originalPosition,originalSize:t.originalSize,position:t.position,size:t.size}}var i=this,s=this.options,n=s.resizable,o=this.uiDialog.css("position"),a="string"==typeof n?n:"n,e,s,w,se,sw,ne,nw";this.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:this.element,maxWidth:s.maxWidth,maxHeight:s.maxHeight,minWidth:s.minWidth,minHeight:this._minHeight(),handles:a,start:function(s,n){i._addClass(t(this),"ui-dialog-resizing"),i._blockFrames(),i._trigger("resizeStart",s,e(n))},resize:function(t,s){i._trigger("resize",t,e(s))},stop:function(n,o){var a=i.uiDialog.offset(),r=a.left-i.document.scrollLeft(),h=a.top-i.document.scrollTop();s.height=i.uiDialog.height(),s.width=i.uiDialog.width(),s.position={my:"left top",at:"left"+(r>=0?"+":"")+r+" "+"top"+(h>=0?"+":"")+h,of:i.window},i._removeClass(t(this),"ui-dialog-resizing"),i._unblockFrames(),i._trigger("resizeStop",n,e(o))}}).css("position",o)},_trackFocus:function(){this._on(this.widget(),{focusin:function(e){this._makeFocusTarget(),this._focusedElement=t(e.target)}})},_makeFocusTarget:function(){this._untrackInstance(),this._trackingInstances().unshift(this)},_untrackInstance:function(){var e=this._trackingInstances(),i=t.inArray(this,e);-1!==i&&e.splice(i,1)},_trackingInstances:function(){var t=this.document.data("ui-dialog-instances");return t||(t=[],this.document.data("ui-dialog-instances",t)),t},_minHeight:function(){var t=this.options;return"auto"===t.height?t.minHeight:Math.min(t.minHeight,t.height)},_position:function(){var t=this.uiDialog.is(":visible");t||this.uiDialog.show(),this.uiDialog.position(this.options.position),t||this.uiDialog.hide()},_setOptions:function(e){var i=this,s=!1,n={};t.each(e,function(t,e){i._setOption(t,e),t in i.sizeRelatedOptions&&(s=!0),t in i.resizableRelatedOptions&&(n[t]=e)}),s&&(this._size(),this._position()),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option",n)},_setOption:function(e,i){var s,n,o=this.uiDialog;"disabled"!==e&&(this._super(e,i),"appendTo"===e&&this.uiDialog.appendTo(this._appendTo()),"buttons"===e&&this._createButtons(),"closeText"===e&&this.uiDialogTitlebarClose.button({label:t("<a>").text(""+this.options.closeText).html()}),"draggable"===e&&(s=o.is(":data(ui-draggable)"),s&&!i&&o.draggable("destroy"),!s&&i&&this._makeDraggable()),"position"===e&&this._position(),"resizable"===e&&(n=o.is(":data(ui-resizable)"),n&&!i&&o.resizable("destroy"),n&&"string"==typeof i&&o.resizable("option","handles",i),n||i===!1||this._makeResizable()),"title"===e&&this._title(this.uiDialogTitlebar.find(".ui-dialog-title")))},_size:function(){var t,e,i,s=this.options;this.element.show().css({width:"auto",minHeight:0,maxHeight:"none",height:0}),s.minWidth>s.width&&(s.width=s.minWidth),t=this.uiDialog.css({height:"auto",width:s.width}).outerHeight(),e=Math.max(0,s.minHeight-t),i="number"==typeof s.maxHeight?Math.max(0,s.maxHeight-t):"none","auto"===s.height?this.element.css({minHeight:e,maxHeight:i,height:"auto"}):this.element.height(Math.max(0,s.height-t)),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())},_blockFrames:function(){this.iframeBlocks=this.document.find("iframe").map(function(){var e=t(this);return t("<div>").css({position:"absolute",width:e.outerWidth(),height:e.outerHeight()}).appendTo(e.parent()).offset(e.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_allowInteraction:function(e){return t(e.target).closest(".ui-dialog").length?!0:!!t(e.target).closest(".ui-datepicker").length},_createOverlay:function(){if(this.options.modal){var e=!0;this._delay(function(){e=!1}),this.document.data("ui-dialog-overlays")||this._on(this.document,{focusin:function(t){e||this._allowInteraction(t)||(t.preventDefault(),this._trackingInstances()[0]._focusTabbable())}}),this.overlay=t("<div>").appendTo(this._appendTo()),this._addClass(this.overlay,null,"ui-widget-overlay ui-front"),this._on(this.overlay,{mousedown:"_keepFocus"}),this.document.data("ui-dialog-overlays",(this.document.data("ui-dialog-overlays")||0)+1)}},_destroyOverlay:function(){if(this.options.modal&&this.overlay){var t=this.document.data("ui-dialog-overlays")-1;t?this.document.data("ui-dialog-overlays",t):(this._off(this.document,"focusin"),this.document.removeData("ui-dialog-overlays")),this.overlay.remove(),this.overlay=null}}}),t.uiBackCompat!==!1&&t.widget("ui.dialog",t.ui.dialog,{options:{dialogClass:""},_createWrapper:function(){this._super(),this.uiDialog.addClass(this.options.dialogClass)},_setOption:function(t,e){"dialogClass"===t&&this.uiDialog.removeClass(this.options.dialogClass).addClass(e),this._superApply(arguments)}}),t.ui.dialog,t.widget("ui.droppable",{version:"1.12.1",widgetEventPrefix:"drop",options:{accept:"*",addClasses:!0,greedy:!1,scope:"default",tolerance:"intersect",activate:null,deactivate:null,drop:null,out:null,over:null},_create:function(){var e,i=this.options,s=i.accept;this.isover=!1,this.isout=!0,this.accept=t.isFunction(s)?s:function(t){return t.is(s)},this.proportions=function(){return arguments.length?(e=arguments[0],void 0):e?e:e={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight}},this._addToManager(i.scope),i.addClasses&&this._addClass("ui-droppable")},_addToManager:function(e){t.ui.ddmanager.droppables[e]=t.ui.ddmanager.droppables[e]||[],t.ui.ddmanager.droppables[e].push(this)},_splice:function(t){for(var e=0;t.length>e;e++)t[e]===this&&t.splice(e,1)},_destroy:function(){var e=t.ui.ddmanager.droppables[this.options.scope];this._splice(e)},_setOption:function(e,i){if("accept"===e)this.accept=t.isFunction(i)?i:function(t){return t.is(i)};else if("scope"===e){var s=t.ui.ddmanager.droppables[this.options.scope];this._splice(s),this._addToManager(i)}this._super(e,i)},_activate:function(e){var i=t.ui.ddmanager.current;this._addActiveClass(),i&&this._trigger("activate",e,this.ui(i))},_deactivate:function(e){var i=t.ui.ddmanager.current;this._removeActiveClass(),i&&this._trigger("deactivate",e,this.ui(i))},_over:function(e){var i=t.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this._addHoverClass(),this._trigger("over",e,this.ui(i)))},_out:function(e){var i=t.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this._removeHoverClass(),this._trigger("out",e,this.ui(i)))},_drop:function(e,i){var s=i||t.ui.ddmanager.current,n=!1;return s&&(s.currentItem||s.element)[0]!==this.element[0]?(this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function(){var i=t(this).droppable("instance");return i.options.greedy&&!i.options.disabled&&i.options.scope===s.options.scope&&i.accept.call(i.element[0],s.currentItem||s.element)&&v(s,t.extend(i,{offset:i.element.offset()}),i.options.tolerance,e)?(n=!0,!1):void 0}),n?!1:this.accept.call(this.element[0],s.currentItem||s.element)?(this._removeActiveClass(),this._removeHoverClass(),this._trigger("drop",e,this.ui(s)),this.element):!1):!1},ui:function(t){return{draggable:t.currentItem||t.element,helper:t.helper,position:t.position,offset:t.positionAbs}},_addHoverClass:function(){this._addClass("ui-droppable-hover")},_removeHoverClass:function(){this._removeClass("ui-droppable-hover")},_addActiveClass:function(){this._addClass("ui-droppable-active")},_removeActiveClass:function(){this._removeClass("ui-droppable-active")}});var v=t.ui.intersect=function(){function t(t,e,i){return t>=e&&e+i>t}return function(e,i,s,n){if(!i.offset)return!1;var o=(e.positionAbs||e.position.absolute).left+e.margins.left,a=(e.positionAbs||e.position.absolute).top+e.margins.top,r=o+e.helperProportions.width,h=a+e.helperProportions.height,l=i.offset.left,c=i.offset.top,u=l+i.proportions().width,d=c+i.proportions().height;switch(s){case"fit":return o>=l&&u>=r&&a>=c&&d>=h;case"intersect":return o+e.helperProportions.width/2>l&&u>r-e.helperProportions.width/2&&a+e.helperProportions.height/2>c&&d>h-e.helperProportions.height/2;case"pointer":return t(n.pageY,c,i.proportions().height)&&t(n.pageX,l,i.proportions().width);case"touch":return(a>=c&&d>=a||h>=c&&d>=h||c>a&&h>d)&&(o>=l&&u>=o||r>=l&&u>=r||l>o&&r>u);default:return!1}}}();t.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(e,i){var s,n,o=t.ui.ddmanager.droppables[e.options.scope]||[],a=i?i.type:null,r=(e.currentItem||e.element).find(":data(ui-droppable)").addBack();t:for(s=0;o.length>s;s++)if(!(o[s].options.disabled||e&&!o[s].accept.call(o[s].element[0],e.currentItem||e.element))){for(n=0;r.length>n;n++)if(r[n]===o[s].element[0]){o[s].proportions().height=0;continue t}o[s].visible="none"!==o[s].element.css("display"),o[s].visible&&("mousedown"===a&&o[s]._activate.call(o[s],i),o[s].offset=o[s].element.offset(),o[s].proportions({width:o[s].element[0].offsetWidth,height:o[s].element[0].offsetHeight}))}},drop:function(e,i){var s=!1;return t.each((t.ui.ddmanager.droppables[e.options.scope]||[]).slice(),function(){this.options&&(!this.options.disabled&&this.visible&&v(e,this,this.options.tolerance,i)&&(s=this._drop.call(this,i)||s),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],e.currentItem||e.element)&&(this.isout=!0,this.isover=!1,this._deactivate.call(this,i)))}),s},dragStart:function(e,i){e.element.parentsUntil("body").on("scroll.droppable",function(){e.options.refreshPositions||t.ui.ddmanager.prepareOffsets(e,i)})},drag:function(e,i){e.options.refreshPositions&&t.ui.ddmanager.prepareOffsets(e,i),t.each(t.ui.ddmanager.droppables[e.options.scope]||[],function(){if(!this.options.disabled&&!this.greedyChild&&this.visible){var s,n,o,a=v(e,this,this.options.tolerance,i),r=!a&&this.isover?"isout":a&&!this.isover?"isover":null;r&&(this.options.greedy&&(n=this.options.scope,o=this.element.parents(":data(ui-droppable)").filter(function(){return t(this).droppable("instance").options.scope===n}),o.length&&(s=t(o[0]).droppable("instance"),s.greedyChild="isover"===r)),s&&"isover"===r&&(s.isover=!1,s.isout=!0,s._out.call(s,i)),this[r]=!0,this["isout"===r?"isover":"isout"]=!1,this["isover"===r?"_over":"_out"].call(this,i),s&&"isout"===r&&(s.isout=!1,s.isover=!0,s._over.call(s,i)))}})},dragStop:function(e,i){e.element.parentsUntil("body").off("scroll.droppable"),e.options.refreshPositions||t.ui.ddmanager.prepareOffsets(e,i)}},t.uiBackCompat!==!1&&t.widget("ui.droppable",t.ui.droppable,{options:{hoverClass:!1,activeClass:!1},_addActiveClass:function(){this._super(),this.options.activeClass&&this.element.addClass(this.options.activeClass)},_removeActiveClass:function(){this._super(),this.options.activeClass&&this.element.removeClass(this.options.activeClass)},_addHoverClass:function(){this._super(),this.options.hoverClass&&this.element.addClass(this.options.hoverClass)},_removeHoverClass:function(){this._super(),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass)}}),t.ui.droppable,t.widget("ui.progressbar",{version:"1.12.1",options:{classes:{"ui-progressbar":"ui-corner-all","ui-progressbar-value":"ui-corner-left","ui-progressbar-complete":"ui-corner-right"},max:100,value:0,change:null,complete:null},min:0,_create:function(){this.oldValue=this.options.value=this._constrainedValue(),this.element.attr({role:"progressbar","aria-valuemin":this.min}),this._addClass("ui-progressbar","ui-widget ui-widget-content"),this.valueDiv=t("<div>").appendTo(this.element),this._addClass(this.valueDiv,"ui-progressbar-value","ui-widget-header"),this._refreshValue()},_destroy:function(){this.element.removeAttr("role aria-valuemin aria-valuemax aria-valuenow"),this.valueDiv.remove()},value:function(t){return void 0===t?this.options.value:(this.options.value=this._constrainedValue(t),this._refreshValue(),void 0)},_constrainedValue:function(t){return void 0===t&&(t=this.options.value),this.indeterminate=t===!1,"number"!=typeof t&&(t=0),this.indeterminate?!1:Math.min(this.options.max,Math.max(this.min,t))},_setOptions:function(t){var e=t.value;delete t.value,this._super(t),this.options.value=this._constrainedValue(e),this._refreshValue()},_setOption:function(t,e){"max"===t&&(e=Math.max(this.min,e)),this._super(t,e)},_setOptionDisabled:function(t){this._super(t),this.element.attr("aria-disabled",t),this._toggleClass(null,"ui-state-disabled",!!t)},_percentage:function(){return this.indeterminate?100:100*(this.options.value-this.min)/(this.options.max-this.min)},_refreshValue:function(){var e=this.options.value,i=this._percentage();this.valueDiv.toggle(this.indeterminate||e>this.min).width(i.toFixed(0)+"%"),this._toggleClass(this.valueDiv,"ui-progressbar-complete",null,e===this.options.max)._toggleClass("ui-progressbar-indeterminate",null,this.indeterminate),this.indeterminate?(this.element.removeAttr("aria-valuenow"),this.overlayDiv||(this.overlayDiv=t("<div>").appendTo(this.valueDiv),this._addClass(this.overlayDiv,"ui-progressbar-overlay"))):(this.element.attr({"aria-valuemax":this.options.max,"aria-valuenow":e}),this.overlayDiv&&(this.overlayDiv.remove(),this.overlayDiv=null)),this.oldValue!==e&&(this.oldValue=e,this._trigger("change")),e===this.options.max&&this._trigger("complete")}}),t.widget("ui.selectable",t.ui.mouse,{version:"1.12.1",options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch",selected:null,selecting:null,start:null,stop:null,unselected:null,unselecting:null},_create:function(){var e=this;this._addClass("ui-selectable"),this.dragged=!1,this.refresh=function(){e.elementPos=t(e.element[0]).offset(),e.selectees=t(e.options.filter,e.element[0]),e._addClass(e.selectees,"ui-selectee"),e.selectees.each(function(){var i=t(this),s=i.offset(),n={left:s.left-e.elementPos.left,top:s.top-e.elementPos.top};t.data(this,"selectable-item",{element:this,$element:i,left:n.left,top:n.top,right:n.left+i.outerWidth(),bottom:n.top+i.outerHeight(),startselected:!1,selected:i.hasClass("ui-selected"),selecting:i.hasClass("ui-selecting"),unselecting:i.hasClass("ui-unselecting")})})},this.refresh(),this._mouseInit(),this.helper=t("<div>"),this._addClass(this.helper,"ui-selectable-helper")},_destroy:function(){this.selectees.removeData("selectable-item"),this._mouseDestroy()},_mouseStart:function(e){var i=this,s=this.options;this.opos=[e.pageX,e.pageY],this.elementPos=t(this.element[0]).offset(),this.options.disabled||(this.selectees=t(s.filter,this.element[0]),this._trigger("start",e),t(s.appendTo).append(this.helper),this.helper.css({left:e.pageX,top:e.pageY,width:0,height:0}),s.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var s=t.data(this,"selectable-item");s.startselected=!0,e.metaKey||e.ctrlKey||(i._removeClass(s.$element,"ui-selected"),s.selected=!1,i._addClass(s.$element,"ui-unselecting"),s.unselecting=!0,i._trigger("unselecting",e,{unselecting:s.element}))}),t(e.target).parents().addBack().each(function(){var s,n=t.data(this,"selectable-item");return n?(s=!e.metaKey&&!e.ctrlKey||!n.$element.hasClass("ui-selected"),i._removeClass(n.$element,s?"ui-unselecting":"ui-selected")._addClass(n.$element,s?"ui-selecting":"ui-unselecting"),n.unselecting=!s,n.selecting=s,n.selected=s,s?i._trigger("selecting",e,{selecting:n.element}):i._trigger("unselecting",e,{unselecting:n.element}),!1):void 0}))},_mouseDrag:function(e){if(this.dragged=!0,!this.options.disabled){var i,s=this,n=this.options,o=this.opos[0],a=this.opos[1],r=e.pageX,h=e.pageY;return o>r&&(i=r,r=o,o=i),a>h&&(i=h,h=a,a=i),this.helper.css({left:o,top:a,width:r-o,height:h-a}),this.selectees.each(function(){var i=t.data(this,"selectable-item"),l=!1,c={};i&&i.element!==s.element[0]&&(c.left=i.left+s.elementPos.left,c.right=i.right+s.elementPos.left,c.top=i.top+s.elementPos.top,c.bottom=i.bottom+s.elementPos.top,"touch"===n.tolerance?l=!(c.left>r||o>c.right||c.top>h||a>c.bottom):"fit"===n.tolerance&&(l=c.left>o&&r>c.right&&c.top>a&&h>c.bottom),l?(i.selected&&(s._removeClass(i.$element,"ui-selected"),i.selected=!1),i.unselecting&&(s._removeClass(i.$element,"ui-unselecting"),i.unselecting=!1),i.selecting||(s._addClass(i.$element,"ui-selecting"),i.selecting=!0,s._trigger("selecting",e,{selecting:i.element}))):(i.selecting&&((e.metaKey||e.ctrlKey)&&i.startselected?(s._removeClass(i.$element,"ui-selecting"),i.selecting=!1,s._addClass(i.$element,"ui-selected"),i.selected=!0):(s._removeClass(i.$element,"ui-selecting"),i.selecting=!1,i.startselected&&(s._addClass(i.$element,"ui-unselecting"),i.unselecting=!0),s._trigger("unselecting",e,{unselecting:i.element}))),i.selected&&(e.metaKey||e.ctrlKey||i.startselected||(s._removeClass(i.$element,"ui-selected"),i.selected=!1,s._addClass(i.$element,"ui-unselecting"),i.unselecting=!0,s._trigger("unselecting",e,{unselecting:i.element})))))}),!1}},_mouseStop:function(e){var i=this;return this.dragged=!1,t(".ui-unselecting",this.element[0]).each(function(){var s=t.data(this,"selectable-item");i._removeClass(s.$element,"ui-unselecting"),s.unselecting=!1,s.startselected=!1,i._trigger("unselected",e,{unselected:s.element})}),t(".ui-selecting",this.element[0]).each(function(){var s=t.data(this,"selectable-item");i._removeClass(s.$element,"ui-selecting")._addClass(s.$element,"ui-selected"),s.selecting=!1,s.selected=!0,s.startselected=!0,i._trigger("selected",e,{selected:s.element})}),this._trigger("stop",e),this.helper.remove(),!1}}),t.widget("ui.selectmenu",[t.ui.formResetMixin,{version:"1.12.1",defaultElement:"<select>",options:{appendTo:null,classes:{"ui-selectmenu-button-open":"ui-corner-top","ui-selectmenu-button-closed":"ui-corner-all"},disabled:null,icons:{button:"ui-icon-triangle-1-s"},position:{my:"left top",at:"left bottom",collision:"none"},width:!1,change:null,close:null,focus:null,open:null,select:null},_create:function(){var e=this.element.uniqueId().attr("id");this.ids={element:e,button:e+"-button",menu:e+"-menu"},this._drawButton(),this._drawMenu(),this._bindFormResetHandler(),this._rendered=!1,this.menuItems=t()},_drawButton:function(){var e,i=this,s=this._parseOption(this.element.find("option:selected"),this.element[0].selectedIndex);this.labels=this.element.labels().attr("for",this.ids.button),this._on(this.labels,{click:function(t){this.button.focus(),t.preventDefault()}}),this.element.hide(),this.button=t("<span>",{tabindex:this.options.disabled?-1:0,id:this.ids.button,role:"combobox","aria-expanded":"false","aria-autocomplete":"list","aria-owns":this.ids.menu,"aria-haspopup":"true",title:this.element.attr("title")}).insertAfter(this.element),this._addClass(this.button,"ui-selectmenu-button ui-selectmenu-button-closed","ui-button ui-widget"),e=t("<span>").appendTo(this.button),this._addClass(e,"ui-selectmenu-icon","ui-icon "+this.options.icons.button),this.buttonItem=this._renderButtonItem(s).appendTo(this.button),this.options.width!==!1&&this._resizeButton(),this._on(this.button,this._buttonEvents),this.button.one("focusin",function(){i._rendered||i._refreshMenu()})},_drawMenu:function(){var e=this;this.menu=t("<ul>",{"aria-hidden":"true","aria-labelledby":this.ids.button,id:this.ids.menu}),this.menuWrap=t("<div>").append(this.menu),this._addClass(this.menuWrap,"ui-selectmenu-menu","ui-front"),this.menuWrap.appendTo(this._appendTo()),this.menuInstance=this.menu.menu({classes:{"ui-menu":"ui-corner-bottom"},role:"listbox",select:function(t,i){t.preventDefault(),e._setSelection(),e._select(i.item.data("ui-selectmenu-item"),t)},focus:function(t,i){var s=i.item.data("ui-selectmenu-item");null!=e.focusIndex&&s.index!==e.focusIndex&&(e._trigger("focus",t,{item:s}),e.isOpen||e._select(s,t)),e.focusIndex=s.index,e.button.attr("aria-activedescendant",e.menuItems.eq(s.index).attr("id"))}}).menu("instance"),this.menuInstance._off(this.menu,"mouseleave"),this.menuInstance._closeOnDocumentClick=function(){return!1},this.menuInstance._isDivider=function(){return!1}},refresh:function(){this._refreshMenu(),this.buttonItem.replaceWith(this.buttonItem=this._renderButtonItem(this._getSelectedItem().data("ui-selectmenu-item")||{})),null===this.options.width&&this._resizeButton()},_refreshMenu:function(){var t,e=this.element.find("option");this.menu.empty(),this._parseOptions(e),this._renderMenu(this.menu,this.items),this.menuInstance.refresh(),this.menuItems=this.menu.find("li").not(".ui-selectmenu-optgroup").find(".ui-menu-item-wrapper"),this._rendered=!0,e.length&&(t=this._getSelectedItem(),this.menuInstance.focus(null,t),this._setAria(t.data("ui-selectmenu-item")),this._setOption("disabled",this.element.prop("disabled")))},open:function(t){this.options.disabled||(this._rendered?(this._removeClass(this.menu.find(".ui-state-active"),null,"ui-state-active"),this.menuInstance.focus(null,this._getSelectedItem())):this._refreshMenu(),this.menuItems.length&&(this.isOpen=!0,this._toggleAttr(),this._resizeMenu(),this._position(),this._on(this.document,this._documentClick),this._trigger("open",t)))},_position:function(){this.menuWrap.position(t.extend({of:this.button},this.options.position))},close:function(t){this.isOpen&&(this.isOpen=!1,this._toggleAttr(),this.range=null,this._off(this.document),this._trigger("close",t))},widget:function(){return this.button},menuWidget:function(){return this.menu},_renderButtonItem:function(e){var i=t("<span>");return this._setText(i,e.label),this._addClass(i,"ui-selectmenu-text"),i},_renderMenu:function(e,i){var s=this,n="";t.each(i,function(i,o){var a;o.optgroup!==n&&(a=t("<li>",{text:o.optgroup}),s._addClass(a,"ui-selectmenu-optgroup","ui-menu-divider"+(o.element.parent("optgroup").prop("disabled")?" ui-state-disabled":"")),a.appendTo(e),n=o.optgroup),s._renderItemData(e,o)})},_renderItemData:function(t,e){return this._renderItem(t,e).data("ui-selectmenu-item",e)},_renderItem:function(e,i){var s=t("<li>"),n=t("<div>",{title:i.element.attr("title")});return i.disabled&&this._addClass(s,null,"ui-state-disabled"),this._setText(n,i.label),s.append(n).appendTo(e)},_setText:function(t,e){e?t.text(e):t.html(" ")},_move:function(t,e){var i,s,n=".ui-menu-item";this.isOpen?i=this.menuItems.eq(this.focusIndex).parent("li"):(i=this.menuItems.eq(this.element[0].selectedIndex).parent("li"),n+=":not(.ui-state-disabled)"),s="first"===t||"last"===t?i["first"===t?"prevAll":"nextAll"](n).eq(-1):i[t+"All"](n).eq(0),s.length&&this.menuInstance.focus(e,s)},_getSelectedItem:function(){return this.menuItems.eq(this.element[0].selectedIndex).parent("li")},_toggle:function(t){this[this.isOpen?"close":"open"](t)},_setSelection:function(){var t;this.range&&(window.getSelection?(t=window.getSelection(),t.removeAllRanges(),t.addRange(this.range)):this.range.select(),this.button.focus())},_documentClick:{mousedown:function(e){this.isOpen&&(t(e.target).closest(".ui-selectmenu-menu, #"+t.ui.escapeSelector(this.ids.button)).length||this.close(e))}},_buttonEvents:{mousedown:function(){var t;window.getSelection?(t=window.getSelection(),t.rangeCount&&(this.range=t.getRangeAt(0))):this.range=document.selection.createRange()},click:function(t){this._setSelection(),this._toggle(t)},keydown:function(e){var i=!0;switch(e.keyCode){case t.ui.keyCode.TAB:case t.ui.keyCode.ESCAPE:this.close(e),i=!1;break;case t.ui.keyCode.ENTER:this.isOpen&&this._selectFocusedItem(e);break;case t.ui.keyCode.UP:e.altKey?this._toggle(e):this._move("prev",e);break;case t.ui.keyCode.DOWN:e.altKey?this._toggle(e):this._move("next",e);break;case t.ui.keyCode.SPACE:this.isOpen?this._selectFocusedItem(e):this._toggle(e);break;case t.ui.keyCode.LEFT:this._move("prev",e);break;case t.ui.keyCode.RIGHT:this._move("next",e);break;case t.ui.keyCode.HOME:case t.ui.keyCode.PAGE_UP:this._move("first",e);break;case t.ui.keyCode.END:case t.ui.keyCode.PAGE_DOWN:this._move("last",e);break;default:this.menu.trigger(e),i=!1}i&&e.preventDefault()}},_selectFocusedItem:function(t){var e=this.menuItems.eq(this.focusIndex).parent("li");e.hasClass("ui-state-disabled")||this._select(e.data("ui-selectmenu-item"),t)},_select:function(t,e){var i=this.element[0].selectedIndex;this.element[0].selectedIndex=t.index,this.buttonItem.replaceWith(this.buttonItem=this._renderButtonItem(t)),this._setAria(t),this._trigger("select",e,{item:t}),t.index!==i&&this._trigger("change",e,{item:t}),this.close(e)},_setAria:function(t){var e=this.menuItems.eq(t.index).attr("id");this.button.attr({"aria-labelledby":e,"aria-activedescendant":e}),this.menu.attr("aria-activedescendant",e)},_setOption:function(t,e){if("icons"===t){var i=this.button.find("span.ui-icon");this._removeClass(i,null,this.options.icons.button)._addClass(i,null,e.button)}this._super(t,e),"appendTo"===t&&this.menuWrap.appendTo(this._appendTo()),"width"===t&&this._resizeButton()},_setOptionDisabled:function(t){this._super(t),this.menuInstance.option("disabled",t),this.button.attr("aria-disabled",t),this._toggleClass(this.button,null,"ui-state-disabled",t),this.element.prop("disabled",t),t?(this.button.attr("tabindex",-1),this.close()):this.button.attr("tabindex",0)},_appendTo:function(){var e=this.options.appendTo;return e&&(e=e.jquery||e.nodeType?t(e):this.document.find(e).eq(0)),e&&e[0]||(e=this.element.closest(".ui-front, dialog")),e.length||(e=this.document[0].body),e},_toggleAttr:function(){this.button.attr("aria-expanded",this.isOpen),this._removeClass(this.button,"ui-selectmenu-button-"+(this.isOpen?"closed":"open"))._addClass(this.button,"ui-selectmenu-button-"+(this.isOpen?"open":"closed"))._toggleClass(this.menuWrap,"ui-selectmenu-open",null,this.isOpen),this.menu.attr("aria-hidden",!this.isOpen)},_resizeButton:function(){var t=this.options.width;return t===!1?(this.button.css("width",""),void 0):(null===t&&(t=this.element.show().outerWidth(),this.element.hide()),this.button.outerWidth(t),void 0)},_resizeMenu:function(){this.menu.outerWidth(Math.max(this.button.outerWidth(),this.menu.width("").outerWidth()+1))},_getCreateOptions:function(){var t=this._super();return t.disabled=this.element.prop("disabled"),t},_parseOptions:function(e){var i=this,s=[];e.each(function(e,n){s.push(i._parseOption(t(n),e))}),this.items=s},_parseOption:function(t,e){var i=t.parent("optgroup");return{element:t,index:e,value:t.val(),label:t.text(),optgroup:i.attr("label")||"",disabled:i.prop("disabled")||t.prop("disabled")}},_destroy:function(){this._unbindFormResetHandler(),this.menuWrap.remove(),this.button.remove(),this.element.show(),this.element.removeUniqueId(),this.labels.attr("for",this.ids.element)}}]),t.widget("ui.slider",t.ui.mouse,{version:"1.12.1",widgetEventPrefix:"slide",options:{animate:!1,classes:{"ui-slider":"ui-corner-all","ui-slider-handle":"ui-corner-all","ui-slider-range":"ui-corner-all ui-widget-header"},distance:0,max:100,min:0,orientation:"horizontal",range:!1,step:1,value:0,values:null,change:null,slide:null,start:null,stop:null},numPages:5,_create:function(){this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this._calculateNewMax(),this._addClass("ui-slider ui-slider-"+this.orientation,"ui-widget ui-widget-content"),this._refresh(),this._animateOff=!1 },_refresh:function(){this._createRange(),this._createHandles(),this._setupEvents(),this._refreshValue()},_createHandles:function(){var e,i,s=this.options,n=this.element.find(".ui-slider-handle"),o="<span tabindex='0'></span>",a=[];for(i=s.values&&s.values.length||1,n.length>i&&(n.slice(i).remove(),n=n.slice(0,i)),e=n.length;i>e;e++)a.push(o);this.handles=n.add(t(a.join("")).appendTo(this.element)),this._addClass(this.handles,"ui-slider-handle","ui-state-default"),this.handle=this.handles.eq(0),this.handles.each(function(e){t(this).data("ui-slider-handle-index",e).attr("tabIndex",0)})},_createRange:function(){var e=this.options;e.range?(e.range===!0&&(e.values?e.values.length&&2!==e.values.length?e.values=[e.values[0],e.values[0]]:t.isArray(e.values)&&(e.values=e.values.slice(0)):e.values=[this._valueMin(),this._valueMin()]),this.range&&this.range.length?(this._removeClass(this.range,"ui-slider-range-min ui-slider-range-max"),this.range.css({left:"",bottom:""})):(this.range=t("<div>").appendTo(this.element),this._addClass(this.range,"ui-slider-range")),("min"===e.range||"max"===e.range)&&this._addClass(this.range,"ui-slider-range-"+e.range)):(this.range&&this.range.remove(),this.range=null)},_setupEvents:function(){this._off(this.handles),this._on(this.handles,this._handleEvents),this._hoverable(this.handles),this._focusable(this.handles)},_destroy:function(){this.handles.remove(),this.range&&this.range.remove(),this._mouseDestroy()},_mouseCapture:function(e){var i,s,n,o,a,r,h,l,c=this,u=this.options;return u.disabled?!1:(this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()},this.elementOffset=this.element.offset(),i={x:e.pageX,y:e.pageY},s=this._normValueFromMouse(i),n=this._valueMax()-this._valueMin()+1,this.handles.each(function(e){var i=Math.abs(s-c.values(e));(n>i||n===i&&(e===c._lastChangedValue||c.values(e)===u.min))&&(n=i,o=t(this),a=e)}),r=this._start(e,a),r===!1?!1:(this._mouseSliding=!0,this._handleIndex=a,this._addClass(o,null,"ui-state-active"),o.trigger("focus"),h=o.offset(),l=!t(e.target).parents().addBack().is(".ui-slider-handle"),this._clickOffset=l?{left:0,top:0}:{left:e.pageX-h.left-o.width()/2,top:e.pageY-h.top-o.height()/2-(parseInt(o.css("borderTopWidth"),10)||0)-(parseInt(o.css("borderBottomWidth"),10)||0)+(parseInt(o.css("marginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(e,a,s),this._animateOff=!0,!0))},_mouseStart:function(){return!0},_mouseDrag:function(t){var e={x:t.pageX,y:t.pageY},i=this._normValueFromMouse(e);return this._slide(t,this._handleIndex,i),!1},_mouseStop:function(t){return this._removeClass(this.handles,null,"ui-state-active"),this._mouseSliding=!1,this._stop(t,this._handleIndex),this._change(t,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1,!1},_detectOrientation:function(){this.orientation="vertical"===this.options.orientation?"vertical":"horizontal"},_normValueFromMouse:function(t){var e,i,s,n,o;return"horizontal"===this.orientation?(e=this.elementSize.width,i=t.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(e=this.elementSize.height,i=t.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),s=i/e,s>1&&(s=1),0>s&&(s=0),"vertical"===this.orientation&&(s=1-s),n=this._valueMax()-this._valueMin(),o=this._valueMin()+s*n,this._trimAlignValue(o)},_uiHash:function(t,e,i){var s={handle:this.handles[t],handleIndex:t,value:void 0!==e?e:this.value()};return this._hasMultipleValues()&&(s.value=void 0!==e?e:this.values(t),s.values=i||this.values()),s},_hasMultipleValues:function(){return this.options.values&&this.options.values.length},_start:function(t,e){return this._trigger("start",t,this._uiHash(e))},_slide:function(t,e,i){var s,n,o=this.value(),a=this.values();this._hasMultipleValues()&&(n=this.values(e?0:1),o=this.values(e),2===this.options.values.length&&this.options.range===!0&&(i=0===e?Math.min(n,i):Math.max(n,i)),a[e]=i),i!==o&&(s=this._trigger("slide",t,this._uiHash(e,i,a)),s!==!1&&(this._hasMultipleValues()?this.values(e,i):this.value(i)))},_stop:function(t,e){this._trigger("stop",t,this._uiHash(e))},_change:function(t,e){this._keySliding||this._mouseSliding||(this._lastChangedValue=e,this._trigger("change",t,this._uiHash(e)))},value:function(t){return arguments.length?(this.options.value=this._trimAlignValue(t),this._refreshValue(),this._change(null,0),void 0):this._value()},values:function(e,i){var s,n,o;if(arguments.length>1)return this.options.values[e]=this._trimAlignValue(i),this._refreshValue(),this._change(null,e),void 0;if(!arguments.length)return this._values();if(!t.isArray(arguments[0]))return this._hasMultipleValues()?this._values(e):this.value();for(s=this.options.values,n=arguments[0],o=0;s.length>o;o+=1)s[o]=this._trimAlignValue(n[o]),this._change(null,o);this._refreshValue()},_setOption:function(e,i){var s,n=0;switch("range"===e&&this.options.range===!0&&("min"===i?(this.options.value=this._values(0),this.options.values=null):"max"===i&&(this.options.value=this._values(this.options.values.length-1),this.options.values=null)),t.isArray(this.options.values)&&(n=this.options.values.length),this._super(e,i),e){case"orientation":this._detectOrientation(),this._removeClass("ui-slider-horizontal ui-slider-vertical")._addClass("ui-slider-"+this.orientation),this._refreshValue(),this.options.range&&this._refreshRange(i),this.handles.css("horizontal"===i?"bottom":"left","");break;case"value":this._animateOff=!0,this._refreshValue(),this._change(null,0),this._animateOff=!1;break;case"values":for(this._animateOff=!0,this._refreshValue(),s=n-1;s>=0;s--)this._change(null,s);this._animateOff=!1;break;case"step":case"min":case"max":this._animateOff=!0,this._calculateNewMax(),this._refreshValue(),this._animateOff=!1;break;case"range":this._animateOff=!0,this._refresh(),this._animateOff=!1}},_setOptionDisabled:function(t){this._super(t),this._toggleClass(null,"ui-state-disabled",!!t)},_value:function(){var t=this.options.value;return t=this._trimAlignValue(t)},_values:function(t){var e,i,s;if(arguments.length)return e=this.options.values[t],e=this._trimAlignValue(e);if(this._hasMultipleValues()){for(i=this.options.values.slice(),s=0;i.length>s;s+=1)i[s]=this._trimAlignValue(i[s]);return i}return[]},_trimAlignValue:function(t){if(this._valueMin()>=t)return this._valueMin();if(t>=this._valueMax())return this._valueMax();var e=this.options.step>0?this.options.step:1,i=(t-this._valueMin())%e,s=t-i;return 2*Math.abs(i)>=e&&(s+=i>0?e:-e),parseFloat(s.toFixed(5))},_calculateNewMax:function(){var t=this.options.max,e=this._valueMin(),i=this.options.step,s=Math.round((t-e)/i)*i;t=s+e,t>this.options.max&&(t-=i),this.max=parseFloat(t.toFixed(this._precision()))},_precision:function(){var t=this._precisionOf(this.options.step);return null!==this.options.min&&(t=Math.max(t,this._precisionOf(this.options.min))),t},_precisionOf:function(t){var e=""+t,i=e.indexOf(".");return-1===i?0:e.length-i-1},_valueMin:function(){return this.options.min},_valueMax:function(){return this.max},_refreshRange:function(t){"vertical"===t&&this.range.css({width:"",left:""}),"horizontal"===t&&this.range.css({height:"",bottom:""})},_refreshValue:function(){var e,i,s,n,o,a=this.options.range,r=this.options,h=this,l=this._animateOff?!1:r.animate,c={};this._hasMultipleValues()?this.handles.each(function(s){i=100*((h.values(s)-h._valueMin())/(h._valueMax()-h._valueMin())),c["horizontal"===h.orientation?"left":"bottom"]=i+"%",t(this).stop(1,1)[l?"animate":"css"](c,r.animate),h.options.range===!0&&("horizontal"===h.orientation?(0===s&&h.range.stop(1,1)[l?"animate":"css"]({left:i+"%"},r.animate),1===s&&h.range[l?"animate":"css"]({width:i-e+"%"},{queue:!1,duration:r.animate})):(0===s&&h.range.stop(1,1)[l?"animate":"css"]({bottom:i+"%"},r.animate),1===s&&h.range[l?"animate":"css"]({height:i-e+"%"},{queue:!1,duration:r.animate}))),e=i}):(s=this.value(),n=this._valueMin(),o=this._valueMax(),i=o!==n?100*((s-n)/(o-n)):0,c["horizontal"===this.orientation?"left":"bottom"]=i+"%",this.handle.stop(1,1)[l?"animate":"css"](c,r.animate),"min"===a&&"horizontal"===this.orientation&&this.range.stop(1,1)[l?"animate":"css"]({width:i+"%"},r.animate),"max"===a&&"horizontal"===this.orientation&&this.range.stop(1,1)[l?"animate":"css"]({width:100-i+"%"},r.animate),"min"===a&&"vertical"===this.orientation&&this.range.stop(1,1)[l?"animate":"css"]({height:i+"%"},r.animate),"max"===a&&"vertical"===this.orientation&&this.range.stop(1,1)[l?"animate":"css"]({height:100-i+"%"},r.animate))},_handleEvents:{keydown:function(e){var i,s,n,o,a=t(e.target).data("ui-slider-handle-index");switch(e.keyCode){case t.ui.keyCode.HOME:case t.ui.keyCode.END:case t.ui.keyCode.PAGE_UP:case t.ui.keyCode.PAGE_DOWN:case t.ui.keyCode.UP:case t.ui.keyCode.RIGHT:case t.ui.keyCode.DOWN:case t.ui.keyCode.LEFT:if(e.preventDefault(),!this._keySliding&&(this._keySliding=!0,this._addClass(t(e.target),null,"ui-state-active"),i=this._start(e,a),i===!1))return}switch(o=this.options.step,s=n=this._hasMultipleValues()?this.values(a):this.value(),e.keyCode){case t.ui.keyCode.HOME:n=this._valueMin();break;case t.ui.keyCode.END:n=this._valueMax();break;case t.ui.keyCode.PAGE_UP:n=this._trimAlignValue(s+(this._valueMax()-this._valueMin())/this.numPages);break;case t.ui.keyCode.PAGE_DOWN:n=this._trimAlignValue(s-(this._valueMax()-this._valueMin())/this.numPages);break;case t.ui.keyCode.UP:case t.ui.keyCode.RIGHT:if(s===this._valueMax())return;n=this._trimAlignValue(s+o);break;case t.ui.keyCode.DOWN:case t.ui.keyCode.LEFT:if(s===this._valueMin())return;n=this._trimAlignValue(s-o)}this._slide(e,a,n)},keyup:function(e){var i=t(e.target).data("ui-slider-handle-index");this._keySliding&&(this._keySliding=!1,this._stop(e,i),this._change(e,i),this._removeClass(t(e.target),null,"ui-state-active"))}}}),t.widget("ui.sortable",t.ui.mouse,{version:"1.12.1",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_isOverAxis:function(t,e,i){return t>=e&&e+i>t},_isFloating:function(t){return/left|right/.test(t.css("float"))||/inline|table-cell/.test(t.css("display"))},_create:function(){this.containerCache={},this._addClass("ui-sortable"),this.refresh(),this.offset=this.element.offset(),this._mouseInit(),this._setHandleClassName(),this.ready=!0},_setOption:function(t,e){this._super(t,e),"handle"===t&&this._setHandleClassName()},_setHandleClassName:function(){var e=this;this._removeClass(this.element.find(".ui-sortable-handle"),"ui-sortable-handle"),t.each(this.items,function(){e._addClass(this.instance.options.handle?this.item.find(this.instance.options.handle):this.item,"ui-sortable-handle")})},_destroy:function(){this._mouseDestroy();for(var t=this.items.length-1;t>=0;t--)this.items[t].item.removeData(this.widgetName+"-item");return this},_mouseCapture:function(e,i){var s=null,n=!1,o=this;return this.reverting?!1:this.options.disabled||"static"===this.options.type?!1:(this._refreshItems(e),t(e.target).parents().each(function(){return t.data(this,o.widgetName+"-item")===o?(s=t(this),!1):void 0}),t.data(e.target,o.widgetName+"-item")===o&&(s=t(e.target)),s?!this.options.handle||i||(t(this.options.handle,s).find("*").addBack().each(function(){this===e.target&&(n=!0)}),n)?(this.currentItem=s,this._removeCurrentsFromItems(),!0):!1:!1)},_mouseStart:function(e,i,s){var n,o,a=this.options;if(this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(e),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},t.extend(this.offset,{click:{left:e.pageX-this.offset.left,top:e.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(e),this.originalPageX=e.pageX,this.originalPageY=e.pageY,a.cursorAt&&this._adjustOffsetFromHelper(a.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!==this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),a.containment&&this._setContainment(),a.cursor&&"auto"!==a.cursor&&(o=this.document.find("body"),this.storedCursor=o.css("cursor"),o.css("cursor",a.cursor),this.storedStylesheet=t("<style>*{ cursor: "+a.cursor+" !important; }</style>").appendTo(o)),a.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",a.opacity)),a.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",a.zIndex)),this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",e,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!s)for(n=this.containers.length-1;n>=0;n--)this.containers[n]._trigger("activate",e,this._uiHash(this));return t.ui.ddmanager&&(t.ui.ddmanager.current=this),t.ui.ddmanager&&!a.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this.dragging=!0,this._addClass(this.helper,"ui-sortable-helper"),this._mouseDrag(e),!0},_mouseDrag:function(e){var i,s,n,o,a=this.options,r=!1;for(this.position=this._generatePosition(e),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs),this.options.scroll&&(this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-e.pageY<a.scrollSensitivity?this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop+a.scrollSpeed:e.pageY-this.overflowOffset.top<a.scrollSensitivity&&(this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop-a.scrollSpeed),this.overflowOffset.left+this.scrollParent[0].offsetWidth-e.pageX<a.scrollSensitivity?this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft+a.scrollSpeed:e.pageX-this.overflowOffset.left<a.scrollSensitivity&&(this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft-a.scrollSpeed)):(e.pageY-this.document.scrollTop()<a.scrollSensitivity?r=this.document.scrollTop(this.document.scrollTop()-a.scrollSpeed):this.window.height()-(e.pageY-this.document.scrollTop())<a.scrollSensitivity&&(r=this.document.scrollTop(this.document.scrollTop()+a.scrollSpeed)),e.pageX-this.document.scrollLeft()<a.scrollSensitivity?r=this.document.scrollLeft(this.document.scrollLeft()-a.scrollSpeed):this.window.width()-(e.pageX-this.document.scrollLeft())<a.scrollSensitivity&&(r=this.document.scrollLeft(this.document.scrollLeft()+a.scrollSpeed))),r!==!1&&t.ui.ddmanager&&!a.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e)),this.positionAbs=this._convertPositionTo("absolute"),this.options.axis&&"y"===this.options.axis||(this.helper[0].style.left=this.position.left+"px"),this.options.axis&&"x"===this.options.axis||(this.helper[0].style.top=this.position.top+"px"),i=this.items.length-1;i>=0;i--)if(s=this.items[i],n=s.item[0],o=this._intersectsWithPointer(s),o&&s.instance===this.currentContainer&&n!==this.currentItem[0]&&this.placeholder[1===o?"next":"prev"]()[0]!==n&&!t.contains(this.placeholder[0],n)&&("semi-dynamic"===this.options.type?!t.contains(this.element[0],n):!0)){if(this.direction=1===o?"down":"up","pointer"!==this.options.tolerance&&!this._intersectsWithSides(s))break;this._rearrange(e,s),this._trigger("change",e,this._uiHash());break}return this._contactContainers(e),t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),this._trigger("sort",e,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(e,i){if(e){if(t.ui.ddmanager&&!this.options.dropBehaviour&&t.ui.ddmanager.drop(this,e),this.options.revert){var s=this,n=this.placeholder.offset(),o=this.options.axis,a={};o&&"x"!==o||(a.left=n.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===this.document[0].body?0:this.offsetParent[0].scrollLeft)),o&&"y"!==o||(a.top=n.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===this.document[0].body?0:this.offsetParent[0].scrollTop)),this.reverting=!0,t(this.helper).animate(a,parseInt(this.options.revert,10)||500,function(){s._clear(e)})}else this._clear(e,i);return!1}},cancel:function(){if(this.dragging){this._mouseUp(new t.Event("mouseup",{target:null})),"original"===this.options.helper?(this.currentItem.css(this._storedCSS),this._removeClass(this.currentItem,"ui-sortable-helper")):this.currentItem.show();for(var e=this.containers.length-1;e>=0;e--)this.containers[e]._trigger("deactivate",null,this._uiHash(this)),this.containers[e].containerCache.over&&(this.containers[e]._trigger("out",null,this._uiHash(this)),this.containers[e].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),"original"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),t.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?t(this.domPosition.prev).after(this.currentItem):t(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},t(i).each(function(){var i=(t(e.item||this).attr(e.attribute||"id")||"").match(e.expression||/(.+)[\-=_](.+)/);i&&s.push((e.key||i[1]+"[]")+"="+(e.key&&e.expression?i[1]:i[2]))}),!s.length&&e.key&&s.push(e.key+"="),s.join("&")},toArray:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},i.each(function(){s.push(t(e.item||this).attr(e.attribute||"id")||"")}),s},_intersectsWith:function(t){var e=this.positionAbs.left,i=e+this.helperProportions.width,s=this.positionAbs.top,n=s+this.helperProportions.height,o=t.left,a=o+t.width,r=t.top,h=r+t.height,l=this.offset.click.top,c=this.offset.click.left,u="x"===this.options.axis||s+l>r&&h>s+l,d="y"===this.options.axis||e+c>o&&a>e+c,p=u&&d;return"pointer"===this.options.tolerance||this.options.forcePointerForContainers||"pointer"!==this.options.tolerance&&this.helperProportions[this.floating?"width":"height"]>t[this.floating?"width":"height"]?p:e+this.helperProportions.width/2>o&&a>i-this.helperProportions.width/2&&s+this.helperProportions.height/2>r&&h>n-this.helperProportions.height/2},_intersectsWithPointer:function(t){var e,i,s="x"===this.options.axis||this._isOverAxis(this.positionAbs.top+this.offset.click.top,t.top,t.height),n="y"===this.options.axis||this._isOverAxis(this.positionAbs.left+this.offset.click.left,t.left,t.width),o=s&&n;return o?(e=this._getDragVerticalDirection(),i=this._getDragHorizontalDirection(),this.floating?"right"===i||"down"===e?2:1:e&&("down"===e?2:1)):!1},_intersectsWithSides:function(t){var e=this._isOverAxis(this.positionAbs.top+this.offset.click.top,t.top+t.height/2,t.height),i=this._isOverAxis(this.positionAbs.left+this.offset.click.left,t.left+t.width/2,t.width),s=this._getDragVerticalDirection(),n=this._getDragHorizontalDirection();return this.floating&&n?"right"===n&&i||"left"===n&&!i:s&&("down"===s&&e||"up"===s&&!e)},_getDragVerticalDirection:function(){var t=this.positionAbs.top-this.lastPositionAbs.top;return 0!==t&&(t>0?"down":"up")},_getDragHorizontalDirection:function(){var t=this.positionAbs.left-this.lastPositionAbs.left;return 0!==t&&(t>0?"right":"left")},refresh:function(t){return this._refreshItems(t),this._setHandleClassName(),this.refreshPositions(),this},_connectWith:function(){var t=this.options;return t.connectWith.constructor===String?[t.connectWith]:t.connectWith},_getItemsAsjQuery:function(e){function i(){r.push(this)}var s,n,o,a,r=[],h=[],l=this._connectWith();if(l&&e)for(s=l.length-1;s>=0;s--)for(o=t(l[s],this.document[0]),n=o.length-1;n>=0;n--)a=t.data(o[n],this.widgetFullName),a&&a!==this&&!a.options.disabled&&h.push([t.isFunction(a.options.items)?a.options.items.call(a.element):t(a.options.items,a.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),a]);for(h.push([t.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):t(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]),s=h.length-1;s>=0;s--)h[s][0].each(i);return t(r)},_removeCurrentsFromItems:function(){var e=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=t.grep(this.items,function(t){for(var i=0;e.length>i;i++)if(e[i]===t.item[0])return!1;return!0})},_refreshItems:function(e){this.items=[],this.containers=[this];var i,s,n,o,a,r,h,l,c=this.items,u=[[t.isFunction(this.options.items)?this.options.items.call(this.element[0],e,{item:this.currentItem}):t(this.options.items,this.element),this]],d=this._connectWith();if(d&&this.ready)for(i=d.length-1;i>=0;i--)for(n=t(d[i],this.document[0]),s=n.length-1;s>=0;s--)o=t.data(n[s],this.widgetFullName),o&&o!==this&&!o.options.disabled&&(u.push([t.isFunction(o.options.items)?o.options.items.call(o.element[0],e,{item:this.currentItem}):t(o.options.items,o.element),o]),this.containers.push(o));for(i=u.length-1;i>=0;i--)for(a=u[i][1],r=u[i][0],s=0,l=r.length;l>s;s++)h=t(r[s]),h.data(this.widgetName+"-item",a),c.push({item:h,instance:a,width:0,height:0,left:0,top:0})},refreshPositions:function(e){this.floating=this.items.length?"x"===this.options.axis||this._isFloating(this.items[0].item):!1,this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());var i,s,n,o;for(i=this.items.length-1;i>=0;i--)s=this.items[i],s.instance!==this.currentContainer&&this.currentContainer&&s.item[0]!==this.currentItem[0]||(n=this.options.toleranceElement?t(this.options.toleranceElement,s.item):s.item,e||(s.width=n.outerWidth(),s.height=n.outerHeight()),o=n.offset(),s.left=o.left,s.top=o.top);if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(i=this.containers.length-1;i>=0;i--)o=this.containers[i].element.offset(),this.containers[i].containerCache.left=o.left,this.containers[i].containerCache.top=o.top,this.containers[i].containerCache.width=this.containers[i].element.outerWidth(),this.containers[i].containerCache.height=this.containers[i].element.outerHeight();return this},_createPlaceholder:function(e){e=e||this;var i,s=e.options;s.placeholder&&s.placeholder.constructor!==String||(i=s.placeholder,s.placeholder={element:function(){var s=e.currentItem[0].nodeName.toLowerCase(),n=t("<"+s+">",e.document[0]);return e._addClass(n,"ui-sortable-placeholder",i||e.currentItem[0].className)._removeClass(n,"ui-sortable-helper"),"tbody"===s?e._createTrPlaceholder(e.currentItem.find("tr").eq(0),t("<tr>",e.document[0]).appendTo(n)):"tr"===s?e._createTrPlaceholder(e.currentItem,n):"img"===s&&n.attr("src",e.currentItem.attr("src")),i||n.css("visibility","hidden"),n},update:function(t,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(e.currentItem.innerHeight()-parseInt(e.currentItem.css("paddingTop")||0,10)-parseInt(e.currentItem.css("paddingBottom")||0,10)),n.width()||n.width(e.currentItem.innerWidth()-parseInt(e.currentItem.css("paddingLeft")||0,10)-parseInt(e.currentItem.css("paddingRight")||0,10)))}}),e.placeholder=t(s.placeholder.element.call(e.element,e.currentItem)),e.currentItem.after(e.placeholder),s.placeholder.update(e,e.placeholder)},_createTrPlaceholder:function(e,i){var s=this;e.children().each(function(){t("<td> </td>",s.document[0]).attr("colspan",t(this).attr("colspan")||1).appendTo(i)})},_contactContainers:function(e){var i,s,n,o,a,r,h,l,c,u,d=null,p=null;for(i=this.containers.length-1;i>=0;i--)if(!t.contains(this.currentItem[0],this.containers[i].element[0]))if(this._intersectsWith(this.containers[i].containerCache)){if(d&&t.contains(this.containers[i].element[0],d.element[0]))continue;d=this.containers[i],p=i}else this.containers[i].containerCache.over&&(this.containers[i]._trigger("out",e,this._uiHash(this)),this.containers[i].containerCache.over=0);if(d)if(1===this.containers.length)this.containers[p].containerCache.over||(this.containers[p]._trigger("over",e,this._uiHash(this)),this.containers[p].containerCache.over=1);else{for(n=1e4,o=null,c=d.floating||this._isFloating(this.currentItem),a=c?"left":"top",r=c?"width":"height",u=c?"pageX":"pageY",s=this.items.length-1;s>=0;s--)t.contains(this.containers[p].element[0],this.items[s].item[0])&&this.items[s].item[0]!==this.currentItem[0]&&(h=this.items[s].item.offset()[a],l=!1,e[u]-h>this.items[s][r]/2&&(l=!0),n>Math.abs(e[u]-h)&&(n=Math.abs(e[u]-h),o=this.items[s],this.direction=l?"up":"down"));if(!o&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[p])return this.currentContainer.containerCache.over||(this.containers[p]._trigger("over",e,this._uiHash()),this.currentContainer.containerCache.over=1),void 0;o?this._rearrange(e,o,null,!0):this._rearrange(e,null,this.containers[p].element,!0),this._trigger("change",e,this._uiHash()),this.containers[p]._trigger("change",e,this._uiHash(this)),this.currentContainer=this.containers[p],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[p]._trigger("over",e,this._uiHash(this)),this.containers[p].containerCache.over=1}},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper)?t(i.helper.apply(this.element[0],[e,this.currentItem])):"clone"===i.helper?this.currentItem.clone():this.currentItem;return s.parents("body").length||t("parent"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var e=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===this.document[0].body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&t.ui.ie)&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var t=this.currentItem.position();return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options;"parent"===n.containment&&(n.containment=this.helper[0].parentNode),("document"===n.containment||"window"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,"document"===n.containment?this.document.width():this.window.width()-this.helperProportions.width-this.margins.left,("document"===n.containment?this.document.height()||document.body.parentNode.scrollHeight:this.window.height()||this.document[0].body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(e=t(n.containment)[0],i=t(n.containment).offset(),s="hidden"!==t(e).css("overflow"),this.containment=[i.left+(parseInt(t(e).css("borderLeftWidth"),10)||0)+(parseInt(t(e).css("paddingLeft"),10)||0)-this.margins.left,i.top+(parseInt(t(e).css("borderTopWidth"),10)||0)+(parseInt(t(e).css("paddingTop"),10)||0)-this.margins.top,i.left+(s?Math.max(e.scrollWidth,e.offsetWidth):e.offsetWidth)-(parseInt(t(e).css("borderLeftWidth"),10)||0)-(parseInt(t(e).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(e.scrollHeight,e.offsetHeight):e.offsetHeight)-(parseInt(t(e).css("borderTopWidth"),10)||0)-(parseInt(t(e).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(e,i){i||(i=this.position);var s="absolute"===e?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,o=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():o?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():o?0:n.scrollLeft())*s}},_generatePosition:function(e){var i,s,n=this.options,o=e.pageX,a=e.pageY,r="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=/(html|body)/i.test(r[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(e.pageX-this.offset.click.left<this.containment[0]&&(o=this.containment[0]+this.offset.click.left),e.pageY-this.offset.click.top<this.containment[1]&&(a=this.containment[1]+this.offset.click.top),e.pageX-this.offset.click.left>this.containment[2]&&(o=this.containment[2]+this.offset.click.left),e.pageY-this.offset.click.top>this.containment[3]&&(a=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((a-this.originalPageY)/n.grid[1])*n.grid[1],a=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((o-this.originalPageX)/n.grid[0])*n.grid[0],o=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:a-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():h?0:r.scrollTop()),left:o-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():h?0:r.scrollLeft())}},_rearrange:function(t,e,i,s){i?i[0].appendChild(this.placeholder[0]):e.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?e.item[0]:e.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter; this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(t,e){function i(t,e,i){return function(s){i._trigger(t,s,e._uiHash(e))}}this.reverting=!1;var s,n=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(s in this._storedCSS)("auto"===this._storedCSS[s]||"static"===this._storedCSS[s])&&(this._storedCSS[s]="");this.currentItem.css(this._storedCSS),this._removeClass(this.currentItem,"ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!e&&n.push(function(t){this._trigger("receive",t,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||e||n.push(function(t){this._trigger("update",t,this._uiHash())}),this!==this.currentContainer&&(e||(n.push(function(t){this._trigger("remove",t,this._uiHash())}),n.push(function(t){return function(e){t._trigger("receive",e,this._uiHash(this))}}.call(this,this.currentContainer)),n.push(function(t){return function(e){t._trigger("update",e,this._uiHash(this))}}.call(this,this.currentContainer)))),s=this.containers.length-1;s>=0;s--)e||n.push(i("deactivate",this,this.containers[s])),this.containers[s].containerCache.over&&(n.push(i("out",this,this.containers[s])),this.containers[s].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,e||this._trigger("beforeStop",t,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.cancelHelperRemoval||(this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null),!e){for(s=0;n.length>s;s++)n[s].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!this.cancelHelperRemoval},_trigger:function(){t.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(e){var i=e||this;return{helper:i.helper,placeholder:i.placeholder||t([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:e?e.element:null}}}),t.widget("ui.spinner",{version:"1.12.1",defaultElement:"<input>",widgetEventPrefix:"spin",options:{classes:{"ui-spinner":"ui-corner-all","ui-spinner-down":"ui-corner-br","ui-spinner-up":"ui-corner-tr"},culture:null,icons:{down:"ui-icon-triangle-1-s",up:"ui-icon-triangle-1-n"},incremental:!0,max:null,min:null,numberFormat:null,page:10,step:1,change:null,spin:null,start:null,stop:null},_create:function(){this._setOption("max",this.options.max),this._setOption("min",this.options.min),this._setOption("step",this.options.step),""!==this.value()&&this._value(this.element.val(),!0),this._draw(),this._on(this._events),this._refresh(),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_getCreateOptions:function(){var e=this._super(),i=this.element;return t.each(["min","max","step"],function(t,s){var n=i.attr(s);null!=n&&n.length&&(e[s]=n)}),e},_events:{keydown:function(t){this._start(t)&&this._keydown(t)&&t.preventDefault()},keyup:"_stop",focus:function(){this.previous=this.element.val()},blur:function(t){return this.cancelBlur?(delete this.cancelBlur,void 0):(this._stop(),this._refresh(),this.previous!==this.element.val()&&this._trigger("change",t),void 0)},mousewheel:function(t,e){if(e){if(!this.spinning&&!this._start(t))return!1;this._spin((e>0?1:-1)*this.options.step,t),clearTimeout(this.mousewheelTimer),this.mousewheelTimer=this._delay(function(){this.spinning&&this._stop(t)},100),t.preventDefault()}},"mousedown .ui-spinner-button":function(e){function i(){var e=this.element[0]===t.ui.safeActiveElement(this.document[0]);e||(this.element.trigger("focus"),this.previous=s,this._delay(function(){this.previous=s}))}var s;s=this.element[0]===t.ui.safeActiveElement(this.document[0])?this.previous:this.element.val(),e.preventDefault(),i.call(this),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur,i.call(this)}),this._start(e)!==!1&&this._repeat(null,t(e.currentTarget).hasClass("ui-spinner-up")?1:-1,e)},"mouseup .ui-spinner-button":"_stop","mouseenter .ui-spinner-button":function(e){return t(e.currentTarget).hasClass("ui-state-active")?this._start(e)===!1?!1:(this._repeat(null,t(e.currentTarget).hasClass("ui-spinner-up")?1:-1,e),void 0):void 0},"mouseleave .ui-spinner-button":"_stop"},_enhance:function(){this.uiSpinner=this.element.attr("autocomplete","off").wrap("<span>").parent().append("<a></a><a></a>")},_draw:function(){this._enhance(),this._addClass(this.uiSpinner,"ui-spinner","ui-widget ui-widget-content"),this._addClass("ui-spinner-input"),this.element.attr("role","spinbutton"),this.buttons=this.uiSpinner.children("a").attr("tabIndex",-1).attr("aria-hidden",!0).button({classes:{"ui-button":""}}),this._removeClass(this.buttons,"ui-corner-all"),this._addClass(this.buttons.first(),"ui-spinner-button ui-spinner-up"),this._addClass(this.buttons.last(),"ui-spinner-button ui-spinner-down"),this.buttons.first().button({icon:this.options.icons.up,showLabel:!1}),this.buttons.last().button({icon:this.options.icons.down,showLabel:!1}),this.buttons.height()>Math.ceil(.5*this.uiSpinner.height())&&this.uiSpinner.height()>0&&this.uiSpinner.height(this.uiSpinner.height())},_keydown:function(e){var i=this.options,s=t.ui.keyCode;switch(e.keyCode){case s.UP:return this._repeat(null,1,e),!0;case s.DOWN:return this._repeat(null,-1,e),!0;case s.PAGE_UP:return this._repeat(null,i.page,e),!0;case s.PAGE_DOWN:return this._repeat(null,-i.page,e),!0}return!1},_start:function(t){return this.spinning||this._trigger("start",t)!==!1?(this.counter||(this.counter=1),this.spinning=!0,!0):!1},_repeat:function(t,e,i){t=t||500,clearTimeout(this.timer),this.timer=this._delay(function(){this._repeat(40,e,i)},t),this._spin(e*this.options.step,i)},_spin:function(t,e){var i=this.value()||0;this.counter||(this.counter=1),i=this._adjustValue(i+t*this._increment(this.counter)),this.spinning&&this._trigger("spin",e,{value:i})===!1||(this._value(i),this.counter++)},_increment:function(e){var i=this.options.incremental;return i?t.isFunction(i)?i(e):Math.floor(e*e*e/5e4-e*e/500+17*e/200+1):1},_precision:function(){var t=this._precisionOf(this.options.step);return null!==this.options.min&&(t=Math.max(t,this._precisionOf(this.options.min))),t},_precisionOf:function(t){var e=""+t,i=e.indexOf(".");return-1===i?0:e.length-i-1},_adjustValue:function(t){var e,i,s=this.options;return e=null!==s.min?s.min:0,i=t-e,i=Math.round(i/s.step)*s.step,t=e+i,t=parseFloat(t.toFixed(this._precision())),null!==s.max&&t>s.max?s.max:null!==s.min&&s.min>t?s.min:t},_stop:function(t){this.spinning&&(clearTimeout(this.timer),clearTimeout(this.mousewheelTimer),this.counter=0,this.spinning=!1,this._trigger("stop",t))},_setOption:function(t,e){var i,s,n;return"culture"===t||"numberFormat"===t?(i=this._parse(this.element.val()),this.options[t]=e,this.element.val(this._format(i)),void 0):(("max"===t||"min"===t||"step"===t)&&"string"==typeof e&&(e=this._parse(e)),"icons"===t&&(s=this.buttons.first().find(".ui-icon"),this._removeClass(s,null,this.options.icons.up),this._addClass(s,null,e.up),n=this.buttons.last().find(".ui-icon"),this._removeClass(n,null,this.options.icons.down),this._addClass(n,null,e.down)),this._super(t,e),void 0)},_setOptionDisabled:function(t){this._super(t),this._toggleClass(this.uiSpinner,null,"ui-state-disabled",!!t),this.element.prop("disabled",!!t),this.buttons.button(t?"disable":"enable")},_setOptions:r(function(t){this._super(t)}),_parse:function(t){return"string"==typeof t&&""!==t&&(t=window.Globalize&&this.options.numberFormat?Globalize.parseFloat(t,10,this.options.culture):+t),""===t||isNaN(t)?null:t},_format:function(t){return""===t?"":window.Globalize&&this.options.numberFormat?Globalize.format(t,this.options.numberFormat,this.options.culture):t},_refresh:function(){this.element.attr({"aria-valuemin":this.options.min,"aria-valuemax":this.options.max,"aria-valuenow":this._parse(this.element.val())})},isValid:function(){var t=this.value();return null===t?!1:t===this._adjustValue(t)},_value:function(t,e){var i;""!==t&&(i=this._parse(t),null!==i&&(e||(i=this._adjustValue(i)),t=this._format(i))),this.element.val(t),this._refresh()},_destroy:function(){this.element.prop("disabled",!1).removeAttr("autocomplete role aria-valuemin aria-valuemax aria-valuenow"),this.uiSpinner.replaceWith(this.element)},stepUp:r(function(t){this._stepUp(t)}),_stepUp:function(t){this._start()&&(this._spin((t||1)*this.options.step),this._stop())},stepDown:r(function(t){this._stepDown(t)}),_stepDown:function(t){this._start()&&(this._spin((t||1)*-this.options.step),this._stop())},pageUp:r(function(t){this._stepUp((t||1)*this.options.page)}),pageDown:r(function(t){this._stepDown((t||1)*this.options.page)}),value:function(t){return arguments.length?(r(this._value).call(this,t),void 0):this._parse(this.element.val())},widget:function(){return this.uiSpinner}}),t.uiBackCompat!==!1&&t.widget("ui.spinner",t.ui.spinner,{_enhance:function(){this.uiSpinner=this.element.attr("autocomplete","off").wrap(this._uiSpinnerHtml()).parent().append(this._buttonHtml())},_uiSpinnerHtml:function(){return"<span>"},_buttonHtml:function(){return"<a></a><a></a>"}}),t.ui.spinner,t.widget("ui.tabs",{version:"1.12.1",delay:300,options:{active:null,classes:{"ui-tabs":"ui-corner-all","ui-tabs-nav":"ui-corner-all","ui-tabs-panel":"ui-corner-bottom","ui-tabs-tab":"ui-corner-top"},collapsible:!1,event:"click",heightStyle:"content",hide:null,show:null,activate:null,beforeActivate:null,beforeLoad:null,load:null},_isLocal:function(){var t=/#.*$/;return function(e){var i,s;i=e.href.replace(t,""),s=location.href.replace(t,"");try{i=decodeURIComponent(i)}catch(n){}try{s=decodeURIComponent(s)}catch(n){}return e.hash.length>1&&i===s}}(),_create:function(){var e=this,i=this.options;this.running=!1,this._addClass("ui-tabs","ui-widget ui-widget-content"),this._toggleClass("ui-tabs-collapsible",null,i.collapsible),this._processTabs(),i.active=this._initialActive(),t.isArray(i.disabled)&&(i.disabled=t.unique(i.disabled.concat(t.map(this.tabs.filter(".ui-state-disabled"),function(t){return e.tabs.index(t)}))).sort()),this.active=this.options.active!==!1&&this.anchors.length?this._findActive(i.active):t(),this._refresh(),this.active.length&&this.load(i.active)},_initialActive:function(){var e=this.options.active,i=this.options.collapsible,s=location.hash.substring(1);return null===e&&(s&&this.tabs.each(function(i,n){return t(n).attr("aria-controls")===s?(e=i,!1):void 0}),null===e&&(e=this.tabs.index(this.tabs.filter(".ui-tabs-active"))),(null===e||-1===e)&&(e=this.tabs.length?0:!1)),e!==!1&&(e=this.tabs.index(this.tabs.eq(e)),-1===e&&(e=i?!1:0)),!i&&e===!1&&this.anchors.length&&(e=0),e},_getCreateEventData:function(){return{tab:this.active,panel:this.active.length?this._getPanelForTab(this.active):t()}},_tabKeydown:function(e){var i=t(t.ui.safeActiveElement(this.document[0])).closest("li"),s=this.tabs.index(i),n=!0;if(!this._handlePageNav(e)){switch(e.keyCode){case t.ui.keyCode.RIGHT:case t.ui.keyCode.DOWN:s++;break;case t.ui.keyCode.UP:case t.ui.keyCode.LEFT:n=!1,s--;break;case t.ui.keyCode.END:s=this.anchors.length-1;break;case t.ui.keyCode.HOME:s=0;break;case t.ui.keyCode.SPACE:return e.preventDefault(),clearTimeout(this.activating),this._activate(s),void 0;case t.ui.keyCode.ENTER:return e.preventDefault(),clearTimeout(this.activating),this._activate(s===this.options.active?!1:s),void 0;default:return}e.preventDefault(),clearTimeout(this.activating),s=this._focusNextTab(s,n),e.ctrlKey||e.metaKey||(i.attr("aria-selected","false"),this.tabs.eq(s).attr("aria-selected","true"),this.activating=this._delay(function(){this.option("active",s)},this.delay))}},_panelKeydown:function(e){this._handlePageNav(e)||e.ctrlKey&&e.keyCode===t.ui.keyCode.UP&&(e.preventDefault(),this.active.trigger("focus"))},_handlePageNav:function(e){return e.altKey&&e.keyCode===t.ui.keyCode.PAGE_UP?(this._activate(this._focusNextTab(this.options.active-1,!1)),!0):e.altKey&&e.keyCode===t.ui.keyCode.PAGE_DOWN?(this._activate(this._focusNextTab(this.options.active+1,!0)),!0):void 0},_findNextTab:function(e,i){function s(){return e>n&&(e=0),0>e&&(e=n),e}for(var n=this.tabs.length-1;-1!==t.inArray(s(),this.options.disabled);)e=i?e+1:e-1;return e},_focusNextTab:function(t,e){return t=this._findNextTab(t,e),this.tabs.eq(t).trigger("focus"),t},_setOption:function(t,e){return"active"===t?(this._activate(e),void 0):(this._super(t,e),"collapsible"===t&&(this._toggleClass("ui-tabs-collapsible",null,e),e||this.options.active!==!1||this._activate(0)),"event"===t&&this._setupEvents(e),"heightStyle"===t&&this._setupHeightStyle(e),void 0)},_sanitizeSelector:function(t){return t?t.replace(/[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g,"\\$&"):""},refresh:function(){var e=this.options,i=this.tablist.children(":has(a[href])");e.disabled=t.map(i.filter(".ui-state-disabled"),function(t){return i.index(t)}),this._processTabs(),e.active!==!1&&this.anchors.length?this.active.length&&!t.contains(this.tablist[0],this.active[0])?this.tabs.length===e.disabled.length?(e.active=!1,this.active=t()):this._activate(this._findNextTab(Math.max(0,e.active-1),!1)):e.active=this.tabs.index(this.active):(e.active=!1,this.active=t()),this._refresh()},_refresh:function(){this._setOptionDisabled(this.options.disabled),this._setupEvents(this.options.event),this._setupHeightStyle(this.options.heightStyle),this.tabs.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}),this.panels.not(this._getPanelForTab(this.active)).hide().attr({"aria-hidden":"true"}),this.active.length?(this.active.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}),this._addClass(this.active,"ui-tabs-active","ui-state-active"),this._getPanelForTab(this.active).show().attr({"aria-hidden":"false"})):this.tabs.eq(0).attr("tabIndex",0)},_processTabs:function(){var e=this,i=this.tabs,s=this.anchors,n=this.panels;this.tablist=this._getList().attr("role","tablist"),this._addClass(this.tablist,"ui-tabs-nav","ui-helper-reset ui-helper-clearfix ui-widget-header"),this.tablist.on("mousedown"+this.eventNamespace,"> li",function(e){t(this).is(".ui-state-disabled")&&e.preventDefault()}).on("focus"+this.eventNamespace,".ui-tabs-anchor",function(){t(this).closest("li").is(".ui-state-disabled")&&this.blur()}),this.tabs=this.tablist.find("> li:has(a[href])").attr({role:"tab",tabIndex:-1}),this._addClass(this.tabs,"ui-tabs-tab","ui-state-default"),this.anchors=this.tabs.map(function(){return t("a",this)[0]}).attr({role:"presentation",tabIndex:-1}),this._addClass(this.anchors,"ui-tabs-anchor"),this.panels=t(),this.anchors.each(function(i,s){var n,o,a,r=t(s).uniqueId().attr("id"),h=t(s).closest("li"),l=h.attr("aria-controls");e._isLocal(s)?(n=s.hash,a=n.substring(1),o=e.element.find(e._sanitizeSelector(n))):(a=h.attr("aria-controls")||t({}).uniqueId()[0].id,n="#"+a,o=e.element.find(n),o.length||(o=e._createPanel(a),o.insertAfter(e.panels[i-1]||e.tablist)),o.attr("aria-live","polite")),o.length&&(e.panels=e.panels.add(o)),l&&h.data("ui-tabs-aria-controls",l),h.attr({"aria-controls":a,"aria-labelledby":r}),o.attr("aria-labelledby",r)}),this.panels.attr("role","tabpanel"),this._addClass(this.panels,"ui-tabs-panel","ui-widget-content"),i&&(this._off(i.not(this.tabs)),this._off(s.not(this.anchors)),this._off(n.not(this.panels)))},_getList:function(){return this.tablist||this.element.find("ol, ul").eq(0)},_createPanel:function(e){return t("<div>").attr("id",e).data("ui-tabs-destroy",!0)},_setOptionDisabled:function(e){var i,s,n;for(t.isArray(e)&&(e.length?e.length===this.anchors.length&&(e=!0):e=!1),n=0;s=this.tabs[n];n++)i=t(s),e===!0||-1!==t.inArray(n,e)?(i.attr("aria-disabled","true"),this._addClass(i,null,"ui-state-disabled")):(i.removeAttr("aria-disabled"),this._removeClass(i,null,"ui-state-disabled"));this.options.disabled=e,this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,e===!0)},_setupEvents:function(e){var i={};e&&t.each(e.split(" "),function(t,e){i[e]="_eventHandler"}),this._off(this.anchors.add(this.tabs).add(this.panels)),this._on(!0,this.anchors,{click:function(t){t.preventDefault()}}),this._on(this.anchors,i),this._on(this.tabs,{keydown:"_tabKeydown"}),this._on(this.panels,{keydown:"_panelKeydown"}),this._focusable(this.tabs),this._hoverable(this.tabs)},_setupHeightStyle:function(e){var i,s=this.element.parent();"fill"===e?(i=s.height(),i-=this.element.outerHeight()-this.element.height(),this.element.siblings(":visible").each(function(){var e=t(this),s=e.css("position");"absolute"!==s&&"fixed"!==s&&(i-=e.outerHeight(!0))}),this.element.children().not(this.panels).each(function(){i-=t(this).outerHeight(!0)}),this.panels.each(function(){t(this).height(Math.max(0,i-t(this).innerHeight()+t(this).height()))}).css("overflow","auto")):"auto"===e&&(i=0,this.panels.each(function(){i=Math.max(i,t(this).height("").height())}).height(i))},_eventHandler:function(e){var i=this.options,s=this.active,n=t(e.currentTarget),o=n.closest("li"),a=o[0]===s[0],r=a&&i.collapsible,h=r?t():this._getPanelForTab(o),l=s.length?this._getPanelForTab(s):t(),c={oldTab:s,oldPanel:l,newTab:r?t():o,newPanel:h};e.preventDefault(),o.hasClass("ui-state-disabled")||o.hasClass("ui-tabs-loading")||this.running||a&&!i.collapsible||this._trigger("beforeActivate",e,c)===!1||(i.active=r?!1:this.tabs.index(o),this.active=a?t():o,this.xhr&&this.xhr.abort(),l.length||h.length||t.error("jQuery UI Tabs: Mismatching fragment identifier."),h.length&&this.load(this.tabs.index(o),e),this._toggle(e,c))},_toggle:function(e,i){function s(){o.running=!1,o._trigger("activate",e,i)}function n(){o._addClass(i.newTab.closest("li"),"ui-tabs-active","ui-state-active"),a.length&&o.options.show?o._show(a,o.options.show,s):(a.show(),s())}var o=this,a=i.newPanel,r=i.oldPanel;this.running=!0,r.length&&this.options.hide?this._hide(r,this.options.hide,function(){o._removeClass(i.oldTab.closest("li"),"ui-tabs-active","ui-state-active"),n()}):(this._removeClass(i.oldTab.closest("li"),"ui-tabs-active","ui-state-active"),r.hide(),n()),r.attr("aria-hidden","true"),i.oldTab.attr({"aria-selected":"false","aria-expanded":"false"}),a.length&&r.length?i.oldTab.attr("tabIndex",-1):a.length&&this.tabs.filter(function(){return 0===t(this).attr("tabIndex")}).attr("tabIndex",-1),a.attr("aria-hidden","false"),i.newTab.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0})},_activate:function(e){var i,s=this._findActive(e);s[0]!==this.active[0]&&(s.length||(s=this.active),i=s.find(".ui-tabs-anchor")[0],this._eventHandler({target:i,currentTarget:i,preventDefault:t.noop}))},_findActive:function(e){return e===!1?t():this.tabs.eq(e)},_getIndex:function(e){return"string"==typeof e&&(e=this.anchors.index(this.anchors.filter("[href$='"+t.ui.escapeSelector(e)+"']"))),e},_destroy:function(){this.xhr&&this.xhr.abort(),this.tablist.removeAttr("role").off(this.eventNamespace),this.anchors.removeAttr("role tabIndex").removeUniqueId(),this.tabs.add(this.panels).each(function(){t.data(this,"ui-tabs-destroy")?t(this).remove():t(this).removeAttr("role tabIndex aria-live aria-busy aria-selected aria-labelledby aria-hidden aria-expanded")}),this.tabs.each(function(){var e=t(this),i=e.data("ui-tabs-aria-controls");i?e.attr("aria-controls",i).removeData("ui-tabs-aria-controls"):e.removeAttr("aria-controls")}),this.panels.show(),"content"!==this.options.heightStyle&&this.panels.css("height","")},enable:function(e){var i=this.options.disabled;i!==!1&&(void 0===e?i=!1:(e=this._getIndex(e),i=t.isArray(i)?t.map(i,function(t){return t!==e?t:null}):t.map(this.tabs,function(t,i){return i!==e?i:null})),this._setOptionDisabled(i))},disable:function(e){var i=this.options.disabled;if(i!==!0){if(void 0===e)i=!0;else{if(e=this._getIndex(e),-1!==t.inArray(e,i))return;i=t.isArray(i)?t.merge([e],i).sort():[e]}this._setOptionDisabled(i)}},load:function(e,i){e=this._getIndex(e);var s=this,n=this.tabs.eq(e),o=n.find(".ui-tabs-anchor"),a=this._getPanelForTab(n),r={tab:n,panel:a},h=function(t,e){"abort"===e&&s.panels.stop(!1,!0),s._removeClass(n,"ui-tabs-loading"),a.removeAttr("aria-busy"),t===s.xhr&&delete s.xhr};this._isLocal(o[0])||(this.xhr=t.ajax(this._ajaxSettings(o,i,r)),this.xhr&&"canceled"!==this.xhr.statusText&&(this._addClass(n,"ui-tabs-loading"),a.attr("aria-busy","true"),this.xhr.done(function(t,e,n){setTimeout(function(){a.html(t),s._trigger("load",i,r),h(n,e)},1)}).fail(function(t,e){setTimeout(function(){h(t,e)},1)})))},_ajaxSettings:function(e,i,s){var n=this;return{url:e.attr("href").replace(/#.*$/,""),beforeSend:function(e,o){return n._trigger("beforeLoad",i,t.extend({jqXHR:e,ajaxSettings:o},s))}}},_getPanelForTab:function(e){var i=t(e).attr("aria-controls");return this.element.find(this._sanitizeSelector("#"+i))}}),t.uiBackCompat!==!1&&t.widget("ui.tabs",t.ui.tabs,{_processTabs:function(){this._superApply(arguments),this._addClass(this.tabs,"ui-tab")}}),t.ui.tabs,t.widget("ui.tooltip",{version:"1.12.1",options:{classes:{"ui-tooltip":"ui-corner-all ui-widget-shadow"},content:function(){var e=t(this).attr("title")||"";return t("<a>").text(e).html()},hide:!0,items:"[title]:not([disabled])",position:{my:"left top+15",at:"left bottom",collision:"flipfit flip"},show:!0,track:!1,close:null,open:null},_addDescribedBy:function(e,i){var s=(e.attr("aria-describedby")||"").split(/\s+/);s.push(i),e.data("ui-tooltip-id",i).attr("aria-describedby",t.trim(s.join(" ")))},_removeDescribedBy:function(e){var i=e.data("ui-tooltip-id"),s=(e.attr("aria-describedby")||"").split(/\s+/),n=t.inArray(i,s);-1!==n&&s.splice(n,1),e.removeData("ui-tooltip-id"),s=t.trim(s.join(" ")),s?e.attr("aria-describedby",s):e.removeAttr("aria-describedby")},_create:function(){this._on({mouseover:"open",focusin:"open"}),this.tooltips={},this.parents={},this.liveRegion=t("<div>").attr({role:"log","aria-live":"assertive","aria-relevant":"additions"}).appendTo(this.document[0].body),this._addClass(this.liveRegion,null,"ui-helper-hidden-accessible"),this.disabledTitles=t([])},_setOption:function(e,i){var s=this;this._super(e,i),"content"===e&&t.each(this.tooltips,function(t,e){s._updateContent(e.element)})},_setOptionDisabled:function(t){this[t?"_disable":"_enable"]()},_disable:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event("blur");n.target=n.currentTarget=s.element[0],e.close(n,!0)}),this.disabledTitles=this.disabledTitles.add(this.element.find(this.options.items).addBack().filter(function(){var e=t(this);return e.is("[title]")?e.data("ui-tooltip-title",e.attr("title")).removeAttr("title"):void 0}))},_enable:function(){this.disabledTitles.each(function(){var e=t(this);e.data("ui-tooltip-title")&&e.attr("title",e.data("ui-tooltip-title"))}),this.disabledTitles=t([])},open:function(e){var i=this,s=t(e?e.target:this.element).closest(this.options.items);s.length&&!s.data("ui-tooltip-id")&&(s.attr("title")&&s.data("ui-tooltip-title",s.attr("title")),s.data("ui-tooltip-open",!0),e&&"mouseover"===e.type&&s.parents().each(function(){var e,s=t(this);s.data("ui-tooltip-open")&&(e=t.Event("blur"),e.target=e.currentTarget=this,i.close(e,!0)),s.attr("title")&&(s.uniqueId(),i.parents[this.id]={element:this,title:s.attr("title")},s.attr("title",""))}),this._registerCloseHandlers(e,s),this._updateContent(s,e))},_updateContent:function(t,e){var i,s=this.options.content,n=this,o=e?e.type:null;return"string"==typeof s||s.nodeType||s.jquery?this._open(e,t,s):(i=s.call(t[0],function(i){n._delay(function(){t.data("ui-tooltip-open")&&(e&&(e.type=o),this._open(e,t,i))})}),i&&this._open(e,t,i),void 0)},_open:function(e,i,s){function n(t){l.of=t,a.is(":hidden")||a.position(l)}var o,a,r,h,l=t.extend({},this.options.position);if(s){if(o=this._find(i))return o.tooltip.find(".ui-tooltip-content").html(s),void 0;i.is("[title]")&&(e&&"mouseover"===e.type?i.attr("title",""):i.removeAttr("title")),o=this._tooltip(i),a=o.tooltip,this._addDescribedBy(i,a.attr("id")),a.find(".ui-tooltip-content").html(s),this.liveRegion.children().hide(),h=t("<div>").html(a.find(".ui-tooltip-content").html()),h.removeAttr("name").find("[name]").removeAttr("name"),h.removeAttr("id").find("[id]").removeAttr("id"),h.appendTo(this.liveRegion),this.options.track&&e&&/^mouse/.test(e.type)?(this._on(this.document,{mousemove:n}),n(e)):a.position(t.extend({of:i},this.options.position)),a.hide(),this._show(a,this.options.show),this.options.track&&this.options.show&&this.options.show.delay&&(r=this.delayedShow=setInterval(function(){a.is(":visible")&&(n(l.of),clearInterval(r))},t.fx.interval)),this._trigger("open",e,{tooltip:a})}},_registerCloseHandlers:function(e,i){var s={keyup:function(e){if(e.keyCode===t.ui.keyCode.ESCAPE){var s=t.Event(e);s.currentTarget=i[0],this.close(s,!0)}}};i[0]!==this.element[0]&&(s.remove=function(){this._removeTooltip(this._find(i).tooltip)}),e&&"mouseover"!==e.type||(s.mouseleave="close"),e&&"focusin"!==e.type||(s.focusout="close"),this._on(!0,i,s)},close:function(e){var i,s=this,n=t(e?e.currentTarget:this.element),o=this._find(n);return o?(i=o.tooltip,o.closing||(clearInterval(this.delayedShow),n.data("ui-tooltip-title")&&!n.attr("title")&&n.attr("title",n.data("ui-tooltip-title")),this._removeDescribedBy(n),o.hiding=!0,i.stop(!0),this._hide(i,this.options.hide,function(){s._removeTooltip(t(this))}),n.removeData("ui-tooltip-open"),this._off(n,"mouseleave focusout keyup"),n[0]!==this.element[0]&&this._off(n,"remove"),this._off(this.document,"mousemove"),e&&"mouseleave"===e.type&&t.each(this.parents,function(e,i){t(i.element).attr("title",i.title),delete s.parents[e]}),o.closing=!0,this._trigger("close",e,{tooltip:i}),o.hiding||(o.closing=!1)),void 0):(n.removeData("ui-tooltip-open"),void 0)},_tooltip:function(e){var i=t("<div>").attr("role","tooltip"),s=t("<div>").appendTo(i),n=i.uniqueId().attr("id");return this._addClass(s,"ui-tooltip-content"),this._addClass(i,"ui-tooltip","ui-widget ui-widget-content"),i.appendTo(this._appendTo(e)),this.tooltips[n]={element:e,tooltip:i}},_find:function(t){var e=t.data("ui-tooltip-id");return e?this.tooltips[e]:null},_removeTooltip:function(t){t.remove(),delete this.tooltips[t.attr("id")]},_appendTo:function(t){var e=t.closest(".ui-front, dialog");return e.length||(e=this.document[0].body),e},_destroy:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event("blur"),o=s.element;n.target=n.currentTarget=o[0],e.close(n,!0),t("#"+i).remove(),o.data("ui-tooltip-title")&&(o.attr("title")||o.attr("title",o.data("ui-tooltip-title")),o.removeData("ui-tooltip-title"))}),this.liveRegion.remove()}}),t.uiBackCompat!==!1&&t.widget("ui.tooltip",t.ui.tooltip,{options:{tooltipClass:null},_tooltip:function(){var t=this._superApply(arguments);return this.options.tooltipClass&&t.tooltip.addClass(this.options.tooltipClass),t}}),t.ui.tooltip}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.af={closeText:"Selekteer",prevText:"Vorige",nextText:"Volgende",currentText:"Vandag",monthNames:["Januarie","Februarie","Maart","April","Mei","Junie","Julie","Augustus","September","Oktober","November","Desember"],monthNamesShort:["Jan","Feb","Mrt","Apr","Mei","Jun","Jul","Aug","Sep","Okt","Nov","Des"],dayNames:["Sondag","Maandag","Dinsdag","Woensdag","Donderdag","Vrydag","Saterdag"],dayNamesShort:["Son","Maa","Din","Woe","Don","Vry","Sat"],dayNamesMin:["So","Ma","Di","Wo","Do","Vr","Sa"],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.af),a.regional.af}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional["ar-DZ"]={closeText:"إغلاق",prevText:"<السابق",nextText:"التالي>",currentText:"اليوم",monthNames:["جانÙÙŠ","ÙÙŠÙØ±ÙŠ","مارس","Ø£ÙØ±ÙŠÙ„","ماي","جوان","جويلية","أوت","سبتمبر","أكتوبر","نوÙمبر","ديسمبر"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["Ø§Ù„Ø£ØØ¯","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesShort:["Ø§Ù„Ø£ØØ¯","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesMin:["Ø","Ù†","Ø«","ر","Ø®","ج","س"],weekHeader:"أسبوع",dateFormat:"dd/mm/yy",firstDay:6,isRTL:!0,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional["ar-DZ"]),a.regional["ar-DZ"]}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.ar={closeText:"إغلاق",prevText:"<السابق",nextText:"التالي>",currentText:"اليوم",monthNames:["يناير","ÙØ¨Ø±Ø§ÙŠØ±","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوÙمبر","ديسمبر"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["Ø§Ù„Ø£ØØ¯","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesShort:["Ø£ØØ¯","اثنين","ثلاثاء","أربعاء","خميس","جمعة","سبت"],dayNamesMin:["Ø","Ù†","Ø«","ر","Ø®","ج","س"],weekHeader:"أسبوع",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!0,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.ar),a.regional.ar}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.az={closeText:"BaÄŸla",prevText:"<Geri",nextText:"İrÉ™li>",currentText:"Bugün",monthNames:["Yanvar","Fevral","Mart","Aprel","May","İyun","İyul","Avqust","Sentyabr","Oktyabr","Noyabr","Dekabr"],monthNamesShort:["Yan","Fev","Mar","Apr","May","İyun","İyul","Avq","Sen","Okt","Noy","Dek"],dayNames:["Bazar","Bazar ertÉ™si","ÇərÅŸÉ™nbÉ™ axÅŸamı","ÇərÅŸÉ™nbÉ™","CümÉ™ axÅŸamı","CümÉ™","ŞənbÉ™"],dayNamesShort:["B","Be","Ça","Ç","Ca","C","Åž"],dayNamesMin:["B","B","Ç","С","Ç","C","Åž"],weekHeader:"Hf",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.az),a.regional.az}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.be={closeText:"Зачыніць",prevText:"←ПапÑÑ€.",nextText:"ÐаÑÑ‚.→",currentText:"СёньнÑ",monthNames:["Студзень","Люты","Сакавік","КраÑавік","Травень","ЧÑрвень","Ліпень","Жнівень","ВераÑень","КаÑтрычнік","ЛіÑтапад","Сьнежань"],monthNamesShort:["Сту","Лют","Сак","Кра","Тра","ЧÑÑ€","Ліп","Жні","Вер","КаÑ","ЛіÑ","Сьн"],dayNames:["нÑдзелÑ","панÑдзелак","аўторак","Ñерада","чацьвер","пÑтніца","Ñубота"],dayNamesShort:["ндз","пнд","аўт","Ñрд","чцв","птн","Ñбт"],dayNamesMin:["Ðд","Пн","ÐÑž","Ср","Чц","Пт","Сб"],weekHeader:"Тд",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.be),a.regional.be}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.bg={closeText:"затвори",prevText:"<назад",nextText:"напред>",nextBigText:">>",currentText:"днеÑ",monthNames:["Януари","Февруари","Март","Ðприл","Май","Юни","Юли","ÐвгуÑÑ‚","Септември","Октомври","Ðоември","Декември"],monthNamesShort:["Яну","Фев","Мар","Ðпр","Май","Юни","Юли","Ðвг","Сеп","Окт","Ðов","Дек"],dayNames:["ÐеделÑ","Понеделник","Вторник","СрÑда","Четвъртък","Петък","Събота"],dayNamesShort:["Ðед","Пон","Вто","СрÑ","Чет","Пет","Съб"],dayNamesMin:["Ðе","По","Ð’Ñ‚","Ср","Че","Пе","Съ"],weekHeader:"Wk",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.bg),a.regional.bg}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.bs={closeText:"Zatvori",prevText:"<",nextText:">",currentText:"Danas",monthNames:["Januar","Februar","Mart","April","Maj","Juni","Juli","August","Septembar","Oktobar","Novembar","Decembar"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],dayNames:["Nedelja","Ponedeljak","Utorak","Srijeda","ÄŒetvrtak","Petak","Subota"],dayNamesShort:["Ned","Pon","Uto","Sri","ÄŒet","Pet","Sub"],dayNamesMin:["Ne","Po","Ut","Sr","ÄŒe","Pe","Su"],weekHeader:"Wk",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.bs),a.regional.bs}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.ca={closeText:"Tanca",prevText:"Anterior",nextText:"Següent",currentText:"Avui",monthNames:["gener","febrer","març","abril","maig","juny","juliol","agost","setembre","octubre","novembre","desembre"],monthNamesShort:["gen","feb","març","abr","maig","juny","jul","ag","set","oct","nov","des"],dayNames:["diumenge","dilluns","dimarts","dimecres","dijous","divendres","dissabte"],dayNamesShort:["dg","dl","dt","dc","dj","dv","ds"],dayNamesMin:["dg","dl","dt","dc","dj","dv","ds"],weekHeader:"Set",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.ca),a.regional.ca}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.cs={closeText:"ZavÅ™Ãt",prevText:"<DÅ™Ãve",nextText:"PozdÄ›ji>",currentText:"NynÃ",monthNames:["leden","únor","bÅ™ezen","duben","kvÄ›ten","Äerven","Äervenec","srpen","zářÃ","Å™Ãjen","listopad","prosinec"],monthNamesShort:["led","úno","bÅ™e","dub","kvÄ›","Äer","Ävc","srp","zář","Å™Ãj","lis","pro"],dayNames:["nedÄ›le","pondÄ›lÃ","úterý","stÅ™eda","Ätvrtek","pátek","sobota"],dayNamesShort:["ne","po","út","st","Ät","pá","so"],dayNamesMin:["ne","po","út","st","Ät","pá","so"],weekHeader:"Týd",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.cs),a.regional.cs}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional["cy-GB"]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["Ionawr","Chwefror","Mawrth","Ebrill","Mai","Mehefin","Gorffennaf","Awst","Medi","Hydref","Tachwedd","Rhagfyr"],monthNamesShort:["Ion","Chw","Maw","Ebr","Mai","Meh","Gor","Aws","Med","Hyd","Tac","Rha"],dayNames:["Dydd Sul","Dydd Llun","Dydd Mawrth","Dydd Mercher","Dydd Iau","Dydd Gwener","Dydd Sadwrn"],dayNamesShort:["Sul","Llu","Maw","Mer","Iau","Gwe","Sad"],dayNamesMin:["Su","Ll","Ma","Me","Ia","Gw","Sa"],weekHeader:"Wy",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional["cy-GB"]),a.regional["cy-GB"]}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.da={closeText:"Luk",prevText:"<Forrige",nextText:"Næste>",currentText:"Idag",monthNames:["Januar","Februar","Marts","April","Maj","Juni","Juli","August","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],dayNames:["Søndag","Mandag","Tirsdag","Onsdag","Torsdag","Fredag","Lørdag"],dayNamesShort:["Søn","Man","Tir","Ons","Tor","Fre","Lør"],dayNamesMin:["Sø","Ma","Ti","On","To","Fr","Lø"],weekHeader:"Uge",dateFormat:"dd-mm-yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.da),a.regional.da}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.de={closeText:"Schließen",prevText:"<Zurück",nextText:"Vor>",currentText:"Heute",monthNames:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthNamesShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],dayNames:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],dayNamesShort:["So","Mo","Di","Mi","Do","Fr","Sa"],dayNamesMin:["So","Mo","Di","Mi","Do","Fr","Sa"],weekHeader:"KW",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.de),a.regional.de}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.el={closeText:"Κλείσιμο",prevText:"Î ÏοηγοÏμενος",nextText:"Επόμενος",currentText:"ΣήμεÏα",monthNames:["ΙανουάÏιος","ΦεβÏουάÏιος","ΜάÏτιος","ΑπÏίλιος","Μάιος","ΙοÏνιος","ΙοÏλιος","ΑÏγουστος","ΣεπτÎμβÏιος","ΟκτώβÏιος","ÎοÎμβÏιος","ΔεκÎμβÏιος"],monthNamesShort:["Ιαν","Φεβ","ΜαÏ","ΑπÏ","Μαι","Ιουν","Ιουλ","Αυγ","Σεπ","Οκτ","Îοε","Δεκ"],dayNames:["ΚυÏιακή","ΔευτÎÏα","ΤÏίτη","ΤετάÏτη","Î Îμπτη","ΠαÏασκευή","Σάββατο"],dayNamesShort:["ΚυÏ","Δευ","ΤÏι","Τετ","Πεμ","ΠαÏ","Σαβ"],dayNamesMin:["Κυ","Δε","ΤÏ","Τε","Πε","Πα","Σα"],weekHeader:"Εβδ",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.el),a.regional.el}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional["en-AU"]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional["en-AU"]),a.regional["en-AU"]}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional["en-GB"]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional["en-GB"]),a.regional["en-GB"]}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional["en-NZ"]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional["en-NZ"]),a.regional["en-NZ"]}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.eo={closeText:"Fermi",prevText:"<Anta",nextText:"Sekv>",currentText:"Nuna",monthNames:["Januaro","Februaro","Marto","Aprilo","Majo","Junio","Julio","AÅgusto","Septembro","Oktobro","Novembro","Decembro"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","AÅg","Sep","Okt","Nov","Dec"],dayNames:["Dimanĉo","Lundo","Mardo","Merkredo","Ä´aÅdo","Vendredo","Sabato"],dayNamesShort:["Dim","Lun","Mar","Mer","Ä´aÅ","Ven","Sab"],dayNamesMin:["Di","Lu","Ma","Me","Ä´a","Ve","Sa"],weekHeader:"Sb",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.eo),a.regional.eo}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.es={closeText:"Cerrar",prevText:"<Ant",nextText:"Sig>",currentText:"Hoy",monthNames:["enero","febrero","marzo","abril","mayo","junio","julio","agosto","septiembre","octubre","noviembre","diciembre"],monthNamesShort:["ene","feb","mar","abr","may","jun","jul","ago","sep","oct","nov","dic"],dayNames:["domingo","lunes","martes","miércoles","jueves","viernes","sábado"],dayNamesShort:["dom","lun","mar","mié","jue","vie","sáb"],dayNamesMin:["D","L","M","X","J","V","S"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.es),a.regional.es}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.et={closeText:"Sulge",prevText:"Eelnev",nextText:"Järgnev",currentText:"Täna",monthNames:["Jaanuar","Veebruar","Märts","Aprill","Mai","Juuni","Juuli","August","September","Oktoober","November","Detsember"],monthNamesShort:["Jaan","Veebr","Märts","Apr","Mai","Juuni","Juuli","Aug","Sept","Okt","Nov","Dets"],dayNames:["Pühapäev","Esmaspäev","Teisipäev","Kolmapäev","Neljapäev","Reede","Laupäev"],dayNamesShort:["Pühap","Esmasp","Teisip","Kolmap","Neljap","Reede","Laup"],dayNamesMin:["P","E","T","K","N","R","L"],weekHeader:"näd",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.et),a.regional.et}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.eu={closeText:"Egina",prevText:"<Aur",nextText:"Hur>",currentText:"Gaur",monthNames:["urtarrila","otsaila","martxoa","apirila","maiatza","ekaina","uztaila","abuztua","iraila","urria","azaroa","abendua"],monthNamesShort:["urt.","ots.","mar.","api.","mai.","eka.","uzt.","abu.","ira.","urr.","aza.","abe."],dayNames:["igandea","astelehena","asteartea","asteazkena","osteguna","ostirala","larunbata"],dayNamesShort:["ig.","al.","ar.","az.","og.","ol.","lr."],dayNamesMin:["ig","al","ar","az","og","ol","lr"],weekHeader:"As",dateFormat:"yy-mm-dd",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.eu),a.regional.eu}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.fa={closeText:"بستن",prevText:"<قبلی",nextText:"بعدی>",currentText:"امروز",monthNames:["ژانویه","Ùوریه","مارس","آوریل","مه","ژوئن","ژوئیه","اوت","سپتامبر","اکتبر","نوامبر","دسامبر"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["يکشنبه","دوشنبه","سه‌شنبه","چهارشنبه","پنجشنبه","جمعه","شنبه"],dayNamesShort:["ÛŒ","د","س","Ú†","Ù¾","ج","Ø´"],dayNamesMin:["ÛŒ","د","س","Ú†","Ù¾","ج","Ø´"],weekHeader:"Ù‡Ù",dateFormat:"yy/mm/dd",firstDay:6,isRTL:!0,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.fa),a.regional.fa}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.fi={closeText:"Sulje",prevText:"«Edellinen",nextText:"Seuraava»",currentText:"Tänään",monthNames:["Tammikuu","Helmikuu","Maaliskuu","Huhtikuu","Toukokuu","Kesäkuu","Heinäkuu","Elokuu","Syyskuu","Lokakuu","Marraskuu","Joulukuu"],monthNamesShort:["Tammi","Helmi","Maalis","Huhti","Touko","Kesä","Heinä","Elo","Syys","Loka","Marras","Joulu"],dayNamesShort:["Su","Ma","Ti","Ke","To","Pe","La"],dayNames:["Sunnuntai","Maanantai","Tiistai","Keskiviikko","Torstai","Perjantai","Lauantai"],dayNamesMin:["Su","Ma","Ti","Ke","To","Pe","La"],weekHeader:"Vk",dateFormat:"d.m.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.fi),a.regional.fi}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.fo={closeText:"Lat aftur",prevText:"<Fyrra",nextText:"Næsta>",currentText:"à dag",monthNames:["Januar","Februar","Mars","AprÃl","Mei","Juni","Juli","August","September","Oktober","November","Desember"],monthNamesShort:["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Aug","Sep","Okt","Nov","Des"],dayNames:["Sunnudagur","Mánadagur","Týsdagur","Mikudagur","Hósdagur","FrÃggjadagur","Leyardagur"],dayNamesShort:["Sun","Mán","Týs","Mik","Hós","FrÃ","Ley"],dayNamesMin:["Su","Má","Tý","Mi","Hó","Fr","Le"],weekHeader:"Vk",dateFormat:"dd-mm-yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.fo),a.regional.fo}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional["fr-CA"]={closeText:"Fermer",prevText:"Précédent",nextText:"Suivant",currentText:"Aujourd'hui",monthNames:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],monthNamesShort:["janv.","févr.","mars","avril","mai","juin","juil.","août","sept.","oct.","nov.","déc."],dayNames:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],dayNamesShort:["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],dayNamesMin:["D","L","M","M","J","V","S"],weekHeader:"Sem.",dateFormat:"yy-mm-dd",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional["fr-CA"]),a.regional["fr-CA"]}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional["fr-CH"]={closeText:"Fermer",prevText:"<Préc",nextText:"Suiv>",currentText:"Courant",monthNames:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],monthNamesShort:["janv.","févr.","mars","avril","mai","juin","juil.","août","sept.","oct.","nov.","déc."],dayNames:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],dayNamesShort:["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],dayNamesMin:["D","L","M","M","J","V","S"],weekHeader:"Sm",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional["fr-CH"]),a.regional["fr-CH"]}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.fr={closeText:"Fermer",prevText:"Précédent",nextText:"Suivant",currentText:"Aujourd'hui",monthNames:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],monthNamesShort:["janv.","févr.","mars","avr.","mai","juin","juil.","août","sept.","oct.","nov.","déc."],dayNames:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],dayNamesShort:["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],dayNamesMin:["D","L","M","M","J","V","S"],weekHeader:"Sem.",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.fr),a.regional.fr}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.gl={closeText:"Pechar",prevText:"<Ant",nextText:"Seg>",currentText:"Hoxe",monthNames:["Xaneiro","Febreiro","Marzo","Abril","Maio","Xuño","Xullo","Agosto","Setembro","Outubro","Novembro","Decembro"],monthNamesShort:["Xan","Feb","Mar","Abr","Mai","Xuñ","Xul","Ago","Set","Out","Nov","Dec"],dayNames:["Domingo","Luns","Martes","Mércores","Xoves","Venres","Sábado"],dayNamesShort:["Dom","Lun","Mar","Mér","Xov","Ven","Sáb"],dayNamesMin:["Do","Lu","Ma","Mé","Xo","Ve","Sá"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.gl),a.regional.gl}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.he={closeText:"סגור",prevText:"<הקוד×",nextText:"הב×>",currentText:"היו×",monthNames:["×™× ×•×ר","פברו×ר","מרץ","×פריל","מ××™","×™×•× ×™","יולי","×וגוסט","ספטמבר","×וקטובר","× ×•×‘×ž×‘×¨","דצמבר"],monthNamesShort:["×™× ×•","פבר","מרץ","×פר","מ××™","×™×•× ×™","יולי","×וג","ספט","×וק","× ×•×‘","דצמ"],dayNames:["ר×שון","×©× ×™","שלישי","רביעי","חמישי","שישי","שבת"],dayNamesShort:["×'","ב'","×’'","ד'","×”'","ו'","שבת"],dayNamesMin:["×'","ב'","×’'","ד'","×”'","ו'","שבת"],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!0,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.he),a.regional.he}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.hi={closeText:"बंद",prevText:"पिछला",nextText:"अगला",currentText:"आज",monthNames:["जनवरी ","फरवरी","मारà¥à¤š","अपà¥à¤°à¥‡à¤²","मई","जून","जूलाई","अगसà¥à¤¤ ","सितमà¥à¤¬à¤°","अकà¥à¤Ÿà¥‚बर","नवमà¥à¤¬à¤°","दिसमà¥à¤¬à¤°"],monthNamesShort:["जन","फर","मारà¥à¤š","अपà¥à¤°à¥‡à¤²","मई","जून","जूलाई","अग","सित","अकà¥à¤Ÿ","नव","दि"],dayNames:["रविवार","सोमवार","मंगलवार","बà¥à¤§à¤µà¤¾à¤°","गà¥à¤°à¥à¤µà¤¾à¤°","शà¥à¤•à¥à¤°à¤µà¤¾à¤°","शनिवार"],dayNamesShort:["रवि","सोम","मंगल","बà¥à¤§","गà¥à¤°à¥","शà¥à¤•à¥à¤°","शनि"],dayNamesMin:["रवि","सोम","मंगल","बà¥à¤§","गà¥à¤°à¥","शà¥à¤•à¥à¤°","शनि"],weekHeader:"हफà¥à¤¤à¤¾",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.hi),a.regional.hi}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.hr={closeText:"Zatvori",prevText:"<",nextText:">",currentText:"Danas",monthNames:["SijeÄanj","VeljaÄa","Ožujak","Travanj","Svibanj","Lipanj","Srpanj","Kolovoz","Rujan","Listopad","Studeni","Prosinac"],monthNamesShort:["Sij","Velj","Ožu","Tra","Svi","Lip","Srp","Kol","Ruj","Lis","Stu","Pro"],dayNames:["Nedjelja","Ponedjeljak","Utorak","Srijeda","ÄŒetvrtak","Petak","Subota"],dayNamesShort:["Ned","Pon","Uto","Sri","ÄŒet","Pet","Sub"],dayNamesMin:["Ne","Po","Ut","Sr","ÄŒe","Pe","Su"],weekHeader:"Tje",dateFormat:"dd.mm.yy.",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.hr),a.regional.hr}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.hu={closeText:"bezár",prevText:"vissza",nextText:"elÅ‘re",currentText:"ma",monthNames:["Január","Február","Március","Ãprilis","Május","Június","Július","Augusztus","Szeptember","Október","November","December"],monthNamesShort:["Jan","Feb","Már","Ãpr","Máj","Jún","Júl","Aug","Szep","Okt","Nov","Dec"],dayNames:["Vasárnap","HétfÅ‘","Kedd","Szerda","Csütörtök","Péntek","Szombat"],dayNamesShort:["Vas","Hét","Ked","Sze","Csü","Pén","Szo"],dayNamesMin:["V","H","K","Sze","Cs","P","Szo"],weekHeader:"Hét",dateFormat:"yy.mm.dd.",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:""},a.setDefaults(a.regional.hu),a.regional.hu}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.hy={closeText:"Õ“Õ¡Õ¯Õ¥Õ¬",prevText:"<Õ†Õ¡Õ.",nextText:"Õ€Õ¡Õ».>",currentText:"Ô±ÕµÕ½Ö…Ö€",monthNames:["Õ€Õ¸Ö‚Õ¶Õ¾Õ¡Ö€","Õ“Õ¥Õ¿Ö€Õ¾Õ¡Ö€","Õ„Õ¡Ö€Õ¿","Ô±ÕºÖ€Õ«Õ¬","Õ„Õ¡ÕµÕ«Õ½","Õ€Õ¸Ö‚Õ¶Õ«Õ½","Õ€Õ¸Ö‚Õ¬Õ«Õ½","Õ•Õ£Õ¸Õ½Õ¿Õ¸Õ½","ÕÕ¥ÕºÕ¿Õ¥Õ´Õ¢Õ¥Ö€","Õ€Õ¸Õ¯Õ¿Õ¥Õ´Õ¢Õ¥Ö€","Õ†Õ¸ÕµÕ¥Õ´Õ¢Õ¥Ö€","Ô´Õ¥Õ¯Õ¿Õ¥Õ´Õ¢Õ¥Ö€"],monthNamesShort:["Õ€Õ¸Ö‚Õ¶Õ¾","Õ“Õ¥Õ¿Ö€","Õ„Õ¡Ö€Õ¿","Ô±ÕºÖ€","Õ„Õ¡ÕµÕ«Õ½","Õ€Õ¸Ö‚Õ¶Õ«Õ½","Õ€Õ¸Ö‚Õ¬","Õ•Õ£Õ½","ÕÕ¥Õº","Õ€Õ¸Õ¯","Õ†Õ¸Õµ","Ô´Õ¥Õ¯"],dayNames:["Õ¯Õ«Ö€Õ¡Õ¯Õ«","Õ¥Õ¯Õ¸Ö‚Õ·Õ¡Õ¢Õ©Õ«","Õ¥Ö€Õ¥Ö„Õ·Õ¡Õ¢Õ©Õ«","Õ¹Õ¸Ö€Õ¥Ö„Õ·Õ¡Õ¢Õ©Õ«","Õ°Õ«Õ¶Õ£Õ·Õ¡Õ¢Õ©Õ«","Õ¸Ö‚Ö€Õ¢Õ¡Õ©","Õ·Õ¡Õ¢Õ¡Õ©"],dayNamesShort:["Õ¯Õ«Ö€","Õ¥Ö€Õ¯","Õ¥Ö€Ö„","Õ¹Ö€Ö„","Õ°Õ¶Õ£","Õ¸Ö‚Ö€Õ¢","Õ·Õ¢Õ©"],dayNamesMin:["Õ¯Õ«Ö€","Õ¥Ö€Õ¯","Õ¥Ö€Ö„","Õ¹Ö€Ö„","Õ°Õ¶Õ£","Õ¸Ö‚Ö€Õ¢","Õ·Õ¢Õ©"],weekHeader:"Õ‡Ô²Õ",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.hy),a.regional.hy}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.id={closeText:"Tutup",prevText:"<mundur",nextText:"maju>",currentText:"hari ini",monthNames:["Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","Nopember","Desember"],monthNamesShort:["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Agus","Sep","Okt","Nop","Des"],dayNames:["Minggu","Senin","Selasa","Rabu","Kamis","Jumat","Sabtu"],dayNamesShort:["Min","Sen","Sel","Rab","kam","Jum","Sab"],dayNamesMin:["Mg","Sn","Sl","Rb","Km","jm","Sb"],weekHeader:"Mg",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.id),a.regional.id}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.is={closeText:"Loka",prevText:"< Fyrri",nextText:"Næsti >",currentText:"à dag",monthNames:["Janúar","Febrúar","Mars","AprÃl","MaÃ","JúnÃ","JúlÃ","Ãgúst","September","Október","Nóvember","Desember"],monthNamesShort:["Jan","Feb","Mar","Apr","MaÃ","Jún","Júl","Ãgú","Sep","Okt","Nóv","Des"],dayNames:["Sunnudagur","Mánudagur","Þriðjudagur","Miðvikudagur","Fimmtudagur","Föstudagur","Laugardagur"],dayNamesShort:["Sun","Mán","Þri","Mið","Fim","Fös","Lau"],dayNamesMin:["Su","Má","Þr","Mi","Fi","Fö","La"],weekHeader:"Vika",dateFormat:"dd.mm.yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.is),a.regional.is}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional["it-CH"]={closeText:"Chiudi",prevText:"<Prec",nextText:"Succ>",currentText:"Oggi",monthNames:["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"],monthNamesShort:["Gen","Feb","Mar","Apr","Mag","Giu","Lug","Ago","Set","Ott","Nov","Dic"],dayNames:["Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato"],dayNamesShort:["Dom","Lun","Mar","Mer","Gio","Ven","Sab"],dayNamesMin:["Do","Lu","Ma","Me","Gi","Ve","Sa"],weekHeader:"Sm",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional["it-CH"]),a.regional["it-CH"]}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.it={closeText:"Chiudi",prevText:"<Prec",nextText:"Succ>",currentText:"Oggi",monthNames:["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"],monthNamesShort:["Gen","Feb","Mar","Apr","Mag","Giu","Lug","Ago","Set","Ott","Nov","Dic"],dayNames:["Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato"],dayNamesShort:["Dom","Lun","Mar","Mer","Gio","Ven","Sab"],dayNamesMin:["Do","Lu","Ma","Me","Gi","Ve","Sa"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.it),a.regional.it}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.ja={closeText:"é–‰ã˜ã‚‹",prevText:"<å‰",nextText:"次>",currentText:"今日",monthNames:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],monthNamesShort:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],dayNames:["日曜日","月曜日","ç«æ›œæ—¥","水曜日","木曜日","金曜日","土曜日"],dayNamesShort:["æ—¥","月","ç«","æ°´","木","金","土"],dayNamesMin:["æ—¥","月","ç«","æ°´","木","金","土"],weekHeader:"週",dateFormat:"yy/mm/dd",firstDay:0,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"å¹´"},a.setDefaults(a.regional.ja),a.regional.ja}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.ka={closeText:"დáƒáƒ®áƒ£áƒ ვáƒ",prevText:"< წინáƒ",nextText:"შემდეგი >",currentText:"დღეს",monthNames:["იáƒáƒœáƒ•áƒáƒ ი","თებერვáƒáƒšáƒ˜","მáƒáƒ ტი","áƒáƒžáƒ ილი","მáƒáƒ˜áƒ¡áƒ˜","ივნისი","ივლისი","áƒáƒ’ვისტáƒ","სექტემბერი","áƒáƒ¥áƒ¢áƒáƒ›áƒ‘ერი","ნáƒáƒ”მბერი","დეკემბერი"],monthNamesShort:["იáƒáƒœ","თებ","მáƒáƒ ","áƒáƒžáƒ ","მáƒáƒ˜","ივნ","ივლ","áƒáƒ’ვ","სექ","áƒáƒ¥áƒ¢","ნáƒáƒ”","დეკ"],dayNames:["კვირáƒ","áƒáƒ შáƒáƒ‘áƒáƒ—ი","სáƒáƒ›áƒ¨áƒáƒ‘áƒáƒ—ი","áƒáƒ—ხშáƒáƒ‘áƒáƒ—ი","ხუთშáƒáƒ‘áƒáƒ—ი","პáƒáƒ áƒáƒ¡áƒ™áƒ”ვი","შáƒáƒ‘áƒáƒ—ი"],dayNamesShort:["კვ","áƒáƒ შ","სáƒáƒ›","áƒáƒ—ხ","ხუთ","პáƒáƒ ","შáƒáƒ‘"],dayNamesMin:["კვ","áƒáƒ შ","სáƒáƒ›","áƒáƒ—ხ","ხუთ","პáƒáƒ ","შáƒáƒ‘"],weekHeader:"კვირáƒ",dateFormat:"dd-mm-yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.ka),a.regional.ka}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.kk={closeText:"Жабу",prevText:"<Ðлдыңғы",nextText:"КелеÑÑ–>",currentText:"Бүгін",monthNames:["Қаңтар","Ðқпан","Ðаурыз","Сәуір","Мамыр","МауÑым","Шілде","Тамыз","Қыркүйек","Қазан","Қараша","ЖелтоқÑан"],monthNamesShort:["Қаң","Ðқп","Ðау","Сәу","Мам","Мау","Шіл","Там","Қыр","Қаз","Қар","Жел"],dayNames:["ЖекÑенбі","ДүйÑенбі","СейÑенбі","СәрÑенбі","БейÑенбі","Жұма","Сенбі"],dayNamesShort:["жкÑ","дÑн","ÑÑн","ÑÑ€Ñ","бÑн","жма","Ñнб"],dayNamesMin:["Жк","ДÑ","СÑ","Ср","БÑ","Жм","Сн"],weekHeader:"Ðе",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.kk),a.regional.kk}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.km={closeText:"ធ្វើ​រួច",prevText:"មុន",nextText:"បន្ទាប់",currentText:"ážáŸ’ងៃ​នáŸáŸ‡",monthNames:["មករា","កុម្ភៈ","មីនា","មáŸážŸáž¶","ឧសភា","មិážáž»áž“áž¶","កក្កដា","សីហា","កញ្ញា","ážáž»áž›áž¶","វិច្ឆិកា","ធ្នូ"],monthNamesShort:["មករា","កុម្ភៈ","មីនា","មáŸážŸáž¶","ឧសភា","មិážáž»áž“áž¶","កក្កដា","សីហា","កញ្ញា","ážáž»áž›áž¶","វិច្ឆិកា","ធ្នូ"],dayNames:["អាទិážáŸ’áž™","ចន្ទ","អង្គារ","ពុធ","ព្រហស្បážáž·áŸ","សុក្រ","សៅរáŸ"],dayNamesShort:["អា","áž…","អ","áž–áž»","ព្រហ","សុ","សៅ"],dayNamesMin:["អា","áž…","អ","áž–áž»","ព្រហ","សុ","សៅ"],weekHeader:"សប្ដាហáŸ",dateFormat:"dd-mm-yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.km),a.regional.km}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.ko={closeText:"닫기",prevText:"ì´ì „달",nextText:"다ìŒë‹¬",currentText:"오늘",monthNames:["1ì›”","2ì›”","3ì›”","4ì›”","5ì›”","6ì›”","7ì›”","8ì›”","9ì›”","10ì›”","11ì›”","12ì›”"],monthNamesShort:["1ì›”","2ì›”","3ì›”","4ì›”","5ì›”","6ì›”","7ì›”","8ì›”","9ì›”","10ì›”","11ì›”","12ì›”"],dayNames:["ì¼ìš”ì¼","월요ì¼","화요ì¼","수요ì¼","목요ì¼","금요ì¼","í† ìš”ì¼"],dayNamesShort:["ì¼","ì›”","í™”","수","목","금","í† "],dayNamesMin:["ì¼","ì›”","í™”","수","목","금","í† "],weekHeader:"주",dateFormat:"yy. m. d.",firstDay:0,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"ë…„"},a.setDefaults(a.regional.ko),a.regional.ko}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.ky={closeText:"Жабуу",prevText:"<Мур",nextText:"Кий>",currentText:"Бүгүн",monthNames:["Январь","Февраль","Март","Ðпрель","Май","Июнь","Июль","ÐвгуÑÑ‚","СентÑбрь","ОктÑбрь","ÐоÑбрь","Декабрь"],monthNamesShort:["Янв","Фев","Мар","Ðпр","Май","Июн","Июл","Ðвг","Сен","Окт","ÐоÑ","Дек"],dayNames:["жекшемби","дүйшөмбү","шейшемби","шаршемби","бейшемби","жума","ишемби"],dayNamesShort:["жек","дүй","шей","шар","бей","жум","ише"],dayNamesMin:["Жк","Дш","Шш","Шр","Бш","Жм","Иш"],weekHeader:"Жум",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.ky),a.regional.ky}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.lb={closeText:"Fäerdeg",prevText:"Zréck",nextText:"Weider",currentText:"Haut",monthNames:["Januar","Februar","Mäerz","Abrëll","Mee","Juni","Juli","August","September","Oktober","November","Dezember"],monthNamesShort:["Jan","Feb","Mäe","Abr","Mee","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],dayNames:["Sonndeg","Méindeg","Dënschdeg","Mëttwoch","Donneschdeg","Freideg","Samschdeg"],dayNamesShort:["Son","Méi","Dën","Mët","Don","Fre","Sam"],dayNamesMin:["So","Mé","Dë","Më","Do","Fr","Sa"],weekHeader:"W",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.lb),a.regional.lb}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.lt={closeText:"Uždaryti",prevText:"<Atgal",nextText:"Pirmyn>",currentText:"Å iandien",monthNames:["Sausis","Vasaris","Kovas","Balandis","Gegužė","Birželis","Liepa","RugpjÅ«tis","RugsÄ—jis","Spalis","Lapkritis","Gruodis"],monthNamesShort:["Sau","Vas","Kov","Bal","Geg","Bir","Lie","Rugp","Rugs","Spa","Lap","Gru"],dayNames:["sekmadienis","pirmadienis","antradienis","treÄiadienis","ketvirtadienis","penktadienis","Å¡eÅ¡tadienis"],dayNamesShort:["sek","pir","ant","tre","ket","pen","Å¡eÅ¡"],dayNamesMin:["Se","Pr","An","Tr","Ke","Pe","Å e"],weekHeader:"SAV",dateFormat:"yy-mm-dd",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:""},a.setDefaults(a.regional.lt),a.regional.lt}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.lv={closeText:"AizvÄ“rt",prevText:"Iepr.",nextText:"NÄk.",currentText:"Å odien",monthNames:["JanvÄris","FebruÄris","Marts","AprÄ«lis","Maijs","JÅ«nijs","JÅ«lijs","Augusts","Septembris","Oktobris","Novembris","Decembris"],monthNamesShort:["Jan","Feb","Mar","Apr","Mai","JÅ«n","JÅ«l","Aug","Sep","Okt","Nov","Dec"],dayNames:["svÄ“tdiena","pirmdiena","otrdiena","treÅ¡diena","ceturtdiena","piektdiena","sestdiena"],dayNamesShort:["svt","prm","otr","tre","ctr","pkt","sst"],dayNamesMin:["Sv","Pr","Ot","Tr","Ct","Pk","Ss"],weekHeader:"Ned.",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.lv),a.regional.lv}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.mk={closeText:"Затвори",prevText:"<",nextText:">",currentText:"ДенеÑ",monthNames:["Јануари","Февруари","Март","Ðприл","Мај","Јуни","Јули","ÐвгуÑÑ‚","Септември","Октомври","Ðоември","Декември"],monthNamesShort:["Јан","Фев","Мар","Ðпр","Мај","Јун","Јул","Ðвг","Сеп","Окт","Ðое","Дек"],dayNames:["Ðедела","Понеделник","Вторник","Среда","Четврток","Петок","Сабота"],dayNamesShort:["Ðед","Пон","Вто","Сре","Чет","Пет","Саб"],dayNamesMin:["Ðе","По","Ð’Ñ‚","Ср","Че","Пе","Са"],weekHeader:"Сед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.mk),a.regional.mk}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.ml={closeText:"à´¶à´°à´¿",prevText:"à´®àµà´¨àµà´¨à´¤àµà´¤àµ†",nextText:"à´…à´Ÿàµà´¤àµà´¤à´¤àµ ",currentText:"ഇനàµà´¨àµ",monthNames:["ജനàµà´µà´°à´¿","ഫെബàµà´°àµà´µà´°à´¿","മാരàµâ€à´šàµà´šàµ","à´à´ªàµà´°à´¿à´²àµâ€","മേയàµ","ജൂണàµâ€","ജൂലൈ","ആഗസàµà´±àµà´±àµ","സെപàµà´±àµà´±à´‚ബരàµâ€","à´’à´•àµà´Ÿàµ‹à´¬à´°àµâ€","നവംബരàµâ€","ഡിസംബരàµâ€"],monthNamesShort:["ജനàµ","ഫെബàµ","മാരàµâ€","à´à´ªàµà´°à´¿","മേയàµ","ജൂണàµâ€","ജൂലാ","ആഗ","സെപàµ","à´’à´•àµà´Ÿàµ‹","നവം","à´¡à´¿à´¸"],dayNames:["ഞായരàµâ€","തിങàµà´•à´³àµâ€","ചൊവàµà´µ","à´¬àµà´§à´¨àµâ€","à´µàµà´¯à´¾à´´à´‚","വെളàµà´³à´¿","ശനി"],dayNamesShort:["ഞായ","തിങàµà´•","ചൊവàµà´µ","à´¬àµà´§","à´µàµà´¯à´¾à´´à´‚","വെളàµà´³à´¿","ശനി"],dayNamesMin:["à´žà´¾","തി","ചൊ","à´¬àµ","à´µàµà´¯à´¾","വെ","à´¶"],weekHeader:"à´†",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.ml),a.regional.ml}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.ms={closeText:"Tutup",prevText:"<Sebelum",nextText:"Selepas>",currentText:"hari ini",monthNames:["Januari","Februari","Mac","April","Mei","Jun","Julai","Ogos","September","Oktober","November","Disember"],monthNamesShort:["Jan","Feb","Mac","Apr","Mei","Jun","Jul","Ogo","Sep","Okt","Nov","Dis"],dayNames:["Ahad","Isnin","Selasa","Rabu","Khamis","Jumaat","Sabtu"],dayNamesShort:["Aha","Isn","Sel","Rab","kha","Jum","Sab"],dayNamesMin:["Ah","Is","Se","Ra","Kh","Ju","Sa"],weekHeader:"Mg",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.ms),a.regional.ms}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.nb={closeText:"Lukk",prevText:"«Forrige",nextText:"Neste»",currentText:"I dag",monthNames:["januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember"],monthNamesShort:["jan","feb","mar","apr","mai","jun","jul","aug","sep","okt","nov","des"],dayNamesShort:["søn","man","tir","ons","tor","fre","lør"],dayNames:["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"],dayNamesMin:["sø","ma","ti","on","to","fr","lø"],weekHeader:"Uke",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.nb),a.regional.nb}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional["nl-BE"]={closeText:"Sluiten",prevText:"â†",nextText:"→",currentText:"Vandaag",monthNames:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],monthNamesShort:["jan","feb","mrt","apr","mei","jun","jul","aug","sep","okt","nov","dec"],dayNames:["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],dayNamesShort:["zon","maa","din","woe","don","vri","zat"],dayNamesMin:["zo","ma","di","wo","do","vr","za"],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional["nl-BE"]),a.regional["nl-BE"]}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.nl={closeText:"Sluiten",prevText:"â†",nextText:"→",currentText:"Vandaag",monthNames:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],monthNamesShort:["jan","feb","mrt","apr","mei","jun","jul","aug","sep","okt","nov","dec"],dayNames:["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],dayNamesShort:["zon","maa","din","woe","don","vri","zat"],dayNamesMin:["zo","ma","di","wo","do","vr","za"],weekHeader:"Wk",dateFormat:"dd-mm-yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.nl),a.regional.nl}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.nn={closeText:"Lukk",prevText:"«Førre",nextText:"Neste»",currentText:"I dag",monthNames:["januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember"],monthNamesShort:["jan","feb","mar","apr","mai","jun","jul","aug","sep","okt","nov","des"],dayNamesShort:["sun","mÃ¥n","tys","ons","tor","fre","lau"],dayNames:["sundag","mÃ¥ndag","tysdag","onsdag","torsdag","fredag","laurdag"],dayNamesMin:["su","mÃ¥","ty","on","to","fr","la"],weekHeader:"Veke",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.nn),a.regional.nn}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.no={closeText:"Lukk",prevText:"«Forrige",nextText:"Neste»",currentText:"I dag",monthNames:["januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember"],monthNamesShort:["jan","feb","mar","apr","mai","jun","jul","aug","sep","okt","nov","des"],dayNamesShort:["søn","man","tir","ons","tor","fre","lør"],dayNames:["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"],dayNamesMin:["sø","ma","ti","on","to","fr","lø"],weekHeader:"Uke",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.no),a.regional.no}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.pl={closeText:"Zamknij",prevText:"<Poprzedni",nextText:"NastÄ™pny>",currentText:"DziÅ›",monthNames:["StyczeÅ„","Luty","Marzec","KwiecieÅ„","Maj","Czerwiec","Lipiec","SierpieÅ„","WrzesieÅ„","Październik","Listopad","GrudzieÅ„"],monthNamesShort:["Sty","Lu","Mar","Kw","Maj","Cze","Lip","Sie","Wrz","Pa","Lis","Gru"],dayNames:["Niedziela","PoniedziaÅ‚ek","Wtorek","Åšroda","Czwartek","PiÄ…tek","Sobota"],dayNamesShort:["Nie","Pn","Wt","Åšr","Czw","Pt","So"],dayNamesMin:["N","Pn","Wt","Åšr","Cz","Pt","So"],weekHeader:"Tydz",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.pl),a.regional.pl}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional["pt-BR"]={closeText:"Fechar",prevText:"<Anterior",nextText:"Próximo>",currentText:"Hoje",monthNames:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],monthNamesShort:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],dayNames:["Domingo","Segunda-feira","Terça-feira","Quarta-feira","Quinta-feira","Sexta-feira","Sábado"],dayNamesShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],dayNamesMin:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional["pt-BR"]),a.regional["pt-BR"]}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.pt={closeText:"Fechar",prevText:"Anterior",nextText:"Seguinte",currentText:"Hoje",monthNames:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],monthNamesShort:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],dayNames:["Domingo","Segunda-feira","Terça-feira","Quarta-feira","Quinta-feira","Sexta-feira","Sábado"],dayNamesShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],dayNamesMin:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],weekHeader:"Sem",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.pt),a.regional.pt}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.rm={closeText:"Serrar",prevText:"<Suandant",nextText:"Precedent>",currentText:"Actual",monthNames:["Schaner","Favrer","Mars","Avrigl","Matg","Zercladur","Fanadur","Avust","Settember","October","November","December"],monthNamesShort:["Scha","Fev","Mar","Avr","Matg","Zer","Fan","Avu","Sett","Oct","Nov","Dec"],dayNames:["Dumengia","Glindesdi","Mardi","Mesemna","Gievgia","Venderdi","Sonda"],dayNamesShort:["Dum","Gli","Mar","Mes","Gie","Ven","Som"],dayNamesMin:["Du","Gl","Ma","Me","Gi","Ve","So"],weekHeader:"emna",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.rm),a.regional.rm}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.ro={closeText:"ÃŽnchide",prevText:"« Luna precedentă",nextText:"Luna următoare »",currentText:"Azi",monthNames:["Ianuarie","Februarie","Martie","Aprilie","Mai","Iunie","Iulie","August","Septembrie","Octombrie","Noiembrie","Decembrie"],monthNamesShort:["Ian","Feb","Mar","Apr","Mai","Iun","Iul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Duminică","Luni","MarÅ£i","Miercuri","Joi","Vineri","Sâmbătă"],dayNamesShort:["Dum","Lun","Mar","Mie","Joi","Vin","Sâm"],dayNamesMin:["Du","Lu","Ma","Mi","Jo","Vi","Sâ"],weekHeader:"Săpt",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.ro),a.regional.ro}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.ru={closeText:"Закрыть",prevText:"<Пред",nextText:"След>",currentText:"СегоднÑ",monthNames:["Январь","Февраль","Март","Ðпрель","Май","Июнь","Июль","ÐвгуÑÑ‚","СентÑбрь","ОктÑбрь","ÐоÑбрь","Декабрь"],monthNamesShort:["Янв","Фев","Мар","Ðпр","Май","Июн","Июл","Ðвг","Сен","Окт","ÐоÑ","Дек"],dayNames:["воÑкреÑенье","понедельник","вторник","Ñреда","четверг","пÑтница","Ñуббота"],dayNamesShort:["вÑк","пнд","втр","Ñрд","чтв","птн","Ñбт"],dayNamesMin:["Ð’Ñ","Пн","Ð’Ñ‚","Ср","Чт","Пт","Сб"],weekHeader:"Ðед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.ru),a.regional.ru}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.sk={closeText:"ZavrieÅ¥",prevText:"<Predchádzajúci",nextText:"Nasledujúci>",currentText:"Dnes",monthNames:["január","február","marec","aprÃl","máj","jún","júl","august","september","október","november","december"],monthNamesShort:["Jan","Feb","Mar","Apr","Máj","Jún","Júl","Aug","Sep","Okt","Nov","Dec"],dayNames:["nedeľa","pondelok","utorok","streda","Å¡tvrtok","piatok","sobota"],dayNamesShort:["Ned","Pon","Uto","Str","Å tv","Pia","Sob"],dayNamesMin:["Ne","Po","Ut","St","Å t","Pia","So"],weekHeader:"Ty",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.sk),a.regional.sk}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.sl={closeText:"Zapri",prevText:"<PrejÅ¡nji",nextText:"Naslednji>",currentText:"Trenutni",monthNames:["Januar","Februar","Marec","April","Maj","Junij","Julij","Avgust","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Avg","Sep","Okt","Nov","Dec"],dayNames:["Nedelja","Ponedeljek","Torek","Sreda","ÄŒetrtek","Petek","Sobota"],dayNamesShort:["Ned","Pon","Tor","Sre","ÄŒet","Pet","Sob"],dayNamesMin:["Ne","Po","To","Sr","ÄŒe","Pe","So"],weekHeader:"Teden",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.sl),a.regional.sl}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.sq={closeText:"mbylle",prevText:"<mbrapa",nextText:"Përpara>",currentText:"sot",monthNames:["Janar","Shkurt","Mars","Prill","Maj","Qershor","Korrik","Gusht","Shtator","Tetor","Nëntor","Dhjetor"],monthNamesShort:["Jan","Shk","Mar","Pri","Maj","Qer","Kor","Gus","Sht","Tet","Nën","Dhj"],dayNames:["E Diel","E Hënë","E Martë","E Mërkurë","E Enjte","E Premte","E Shtune"],dayNamesShort:["Di","Hë","Ma","Më","En","Pr","Sh"],dayNamesMin:["Di","Hë","Ma","Më","En","Pr","Sh"],weekHeader:"Ja",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.sq),a.regional.sq}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional["sr-SR"]={closeText:"Zatvori",prevText:"<",nextText:">",currentText:"Danas",monthNames:["Januar","Februar","Mart","April","Maj","Jun","Jul","Avgust","Septembar","Oktobar","Novembar","Decembar"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Avg","Sep","Okt","Nov","Dec"],dayNames:["Nedelja","Ponedeljak","Utorak","Sreda","ÄŒetvrtak","Petak","Subota"],dayNamesShort:["Ned","Pon","Uto","Sre","ÄŒet","Pet","Sub"],dayNamesMin:["Ne","Po","Ut","Sr","ÄŒe","Pe","Su"],weekHeader:"Sed",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional["sr-SR"]),a.regional["sr-SR"]}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.sr={closeText:"Затвори",prevText:"<",nextText:">",currentText:"ДанаÑ",monthNames:["Јануар","Фебруар","Март","Ðприл","Мај","Јун","Јул","ÐвгуÑÑ‚","Септембар","Октобар","Ðовембар","Децембар"],monthNamesShort:["Јан","Феб","Мар","Ðпр","Мај","Јун","Јул","Ðвг","Сеп","Окт","Ðов","Дец"],dayNames:["Ðедеља","Понедељак","Уторак","Среда","Четвртак","Петак","Субота"],dayNamesShort:["Ðед","Пон","Уто","Сре","Чет","Пет","Суб"],dayNamesMin:["Ðе","По","Ут","Ср","Че","Пе","Су"],weekHeader:"Сед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.sr),a.regional.sr}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.sv={closeText:"Stäng",prevText:"«Förra",nextText:"Nästa»",currentText:"Idag",monthNames:["Januari","Februari","Mars","April","Maj","Juni","Juli","Augusti","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],dayNamesShort:["Sön","MÃ¥n","Tis","Ons","Tor","Fre","Lör"],dayNames:["Söndag","MÃ¥ndag","Tisdag","Onsdag","Torsdag","Fredag","Lördag"],dayNamesMin:["Sö","MÃ¥","Ti","On","To","Fr","Lö"],weekHeader:"Ve",dateFormat:"yy-mm-dd",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.sv),a.regional.sv}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.ta={closeText:"மூடà¯",prevText:"à®®à¯à®©à¯à®©à¯ˆà®¯à®¤à¯",nextText:"அடà¯à®¤à¯à®¤à®¤à¯",currentText:"இனà¯à®±à¯",monthNames:["தை","மாசி","பஙà¯à®•à¯à®©à®¿","சிதà¯à®¤à®¿à®°à¯ˆ","வைகாசி","ஆனி","ஆடி","ஆவணி","பà¯à®°à®Ÿà¯à®Ÿà®¾à®šà®¿","à®à®ªà¯à®ªà®šà®¿","காரà¯à®¤à¯à®¤à®¿à®•ை","மாரà¯à®•ழி"],monthNamesShort:["தை","மாசி","பஙà¯","சிதà¯","வைகா","ஆனி","ஆடி","ஆவ","பà¯à®°","à®à®ªà¯","காரà¯","மாரà¯"],dayNames:["ஞாயிறà¯à®±à¯à®•à¯à®•ிழமை","திஙà¯à®•டà¯à®•ிழமை","செவà¯à®µà®¾à®¯à¯à®•à¯à®•ிழமை","பà¯à®¤à®©à¯à®•ிழமை","வியாழகà¯à®•ிழமை","வெளà¯à®³à®¿à®•à¯à®•ிழமை","சனிகà¯à®•ிழமை"],dayNamesShort:["ஞாயிறà¯","திஙà¯à®•ளà¯","செவà¯à®µà®¾à®¯à¯","பà¯à®¤à®©à¯","வியாழனà¯","வெளà¯à®³à®¿","சனி"],dayNamesMin:["ஞா","தி","செ","பà¯","வி","வெ","ச"],weekHeader:"Ðе",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.ta),a.regional.ta}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.th={closeText:"ปิด",prevText:"« à¸¢à¹‰à¸à¸™",nextText:"ถัดไป »",currentText:"วันนี้",monthNames:["มà¸à¸£à¸²à¸„ม","à¸à¸¸à¸¡à¸ าพันธ์","มีนาคม","เมษายน","พฤษภาคม","มิถุนายน","à¸à¸£à¸à¸Žà¸²à¸„ม","สิงหาคม","à¸à¸±à¸™à¸¢à¸²à¸¢à¸™","ตุลาคม","พฤศจิà¸à¸²à¸¢à¸™","ธันวาคม"],monthNamesShort:["ม.ค.","à¸.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.","à¸.ค.","ส.ค.","à¸.ย.","ต.ค.","พ.ย.","ธ.ค."],dayNames:["à¸à¸²à¸—ิตย์","จันทร์","à¸à¸±à¸‡à¸„าร","พุธ","พฤหัสบดี","ศุà¸à¸£à¹Œ","เสาร์"],dayNamesShort:["à¸à¸².","จ.","à¸.","พ.","พฤ.","ศ.","ส."],dayNamesMin:["à¸à¸².","จ.","à¸.","พ.","พฤ.","ศ.","ส."],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.th),a.regional.th}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.tj={closeText:"Идома",prevText:"<Қафо",nextText:"Пеш>",currentText:"Имрӯз",monthNames:["Январ","Феврал","Март","Ðпрел","Май","Июн","Июл","ÐвгуÑÑ‚","СентÑбр","ОктÑбр","ÐоÑбр","Декабр"],monthNamesShort:["Янв","Фев","Мар","Ðпр","Май","Июн","Июл","Ðвг","Сен","Окт","ÐоÑ","Дек"],dayNames:["Ñкшанбе","душанбе","Ñешанбе","чоршанбе","панҷшанбе","ҷумъа","шанбе"],dayNamesShort:["Ñкш","душ","Ñеш","чор","пан","ҷум","шан"],dayNamesMin:["Як","Дш","Сш","Чш","Пш","Ҷм","Шн"],weekHeader:"Хф",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.tj),a.regional.tj}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.tr={closeText:"kapat",prevText:"<geri",nextText:"ileri>",currentText:"bugün",monthNames:["Ocak","Åžubat","Mart","Nisan","Mayıs","Haziran","Temmuz","AÄŸustos","Eylül","Ekim","Kasım","Aralık"],monthNamesShort:["Oca","Åžub","Mar","Nis","May","Haz","Tem","AÄŸu","Eyl","Eki","Kas","Ara"],dayNames:["Pazar","Pazartesi","Salı","ÇarÅŸamba","PerÅŸembe","Cuma","Cumartesi"],dayNamesShort:["Pz","Pt","Sa","Ça","Pe","Cu","Ct"],dayNamesMin:["Pz","Pt","Sa","Ça","Pe","Cu","Ct"],weekHeader:"Hf",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.tr),a.regional.tr}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.uk={closeText:"Закрити",prevText:"<",nextText:">",currentText:"Сьогодні",monthNames:["Січень","Лютий","Березень","Квітень","Травень","Червень","Липень","Серпень","ВереÑень","Жовтень","ЛиÑтопад","Грудень"],monthNamesShort:["Січ","Лют","Бер","Кві","Тра","Чер","Лип","Сер","Вер","Жов","ЛиÑ","Гру"],dayNames:["неділÑ","понеділок","вівторок","Ñереда","четвер","п’ÑтницÑ","Ñубота"],dayNamesShort:["нед","пнд","вів","Ñрд","чтв","птн","Ñбт"],dayNamesMin:["Ðд","Пн","Ð’Ñ‚","Ср","Чт","Пт","Сб"],weekHeader:"Тиж",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.uk),a.regional.uk}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional.vi={closeText:"Äóng",prevText:"<Trước",nextText:"Tiếp>",currentText:"Hôm nay",monthNames:["Tháng Má»™t","Tháng Hai","Tháng Ba","Tháng Tư","Tháng Năm","Tháng Sáu","Tháng Bảy","Tháng Tám","Tháng ChÃn","Tháng Mưá»i","Tháng Mưá»i Má»™t","Tháng Mưá»i Hai"],monthNamesShort:["Tháng 1","Tháng 2","Tháng 3","Tháng 4","Tháng 5","Tháng 6","Tháng 7","Tháng 8","Tháng 9","Tháng 10","Tháng 11","Tháng 12"],dayNames:["Chá»§ Nháºt","Thứ Hai","Thứ Ba","Thứ Tư","Thứ Năm","Thứ Sáu","Thứ Bảy"],dayNamesShort:["CN","T2","T3","T4","T5","T6","T7"],dayNamesMin:["CN","T2","T3","T4","T5","T6","T7"],weekHeader:"Tu",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},a.setDefaults(a.regional.vi),a.regional.vi}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional["zh-CN"]={closeText:"å…³é—",prevText:"<上月",nextText:"下月>",currentText:"今天",monthNames:["一月","二月","三月","四月","五月","å…æœˆ","七月","八月","乿œˆ","åæœˆ","å一月","å二月"],monthNamesShort:["一月","二月","三月","四月","五月","å…æœˆ","七月","八月","乿œˆ","åæœˆ","å一月","å二月"],dayNames:["星期日","星期一","星期二","星期三","星期四","星期五","星期å…"],dayNamesShort:["周日","周一","周二","周三","周四","周五","周å…"],dayNamesMin:["æ—¥","一","二","三","å››","五","å…"],weekHeader:"周",dateFormat:"yy-mm-dd",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"å¹´"},a.setDefaults(a.regional["zh-CN"]),a.regional["zh-CN"]}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional["zh-HK"]={closeText:"關閉",prevText:"<上月",nextText:"下月>",currentText:"今天",monthNames:["一月","二月","三月","四月","五月","å…æœˆ","七月","八月","乿œˆ","åæœˆ","å一月","å二月"],monthNamesShort:["一月","二月","三月","四月","五月","å…æœˆ","七月","八月","乿œˆ","åæœˆ","å一月","å二月"],dayNames:["星期日","星期一","星期二","星期三","星期四","星期五","星期å…"],dayNamesShort:["周日","周一","周二","周三","周四","周五","周å…"],dayNamesMin:["æ—¥","一","二","三","å››","五","å…"],weekHeader:"周",dateFormat:"dd-mm-yy",firstDay:0,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"å¹´"},a.setDefaults(a.regional["zh-HK"]),a.regional["zh-HK"]}); +/*! jQuery UI - v1.12.1 - 2016-09-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["../widgets/datepicker"],a):a(jQuery.datepicker)}(function(a){return a.regional["zh-TW"]={closeText:"關閉",prevText:"<上月",nextText:"下月>",currentText:"今天",monthNames:["一月","二月","三月","四月","五月","å…æœˆ","七月","八月","乿œˆ","åæœˆ","å一月","å二月"],monthNamesShort:["一月","二月","三月","四月","五月","å…æœˆ","七月","八月","乿œˆ","åæœˆ","å一月","å二月"],dayNames:["星期日","星期一","星期二","星期三","星期四","星期五","星期å…"],dayNamesShort:["周日","周一","周二","周三","周四","周五","周å…"],dayNamesMin:["æ—¥","一","二","三","å››","五","å…"],weekHeader:"周",dateFormat:"yy/mm/dd",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"å¹´"},a.setDefaults(a.regional["zh-TW"]),a.regional["zh-TW"]}); /*! jQuery Timepicker Addon - v1.6.3 - 2016-04-20 * http://trentrichardson.com/examples/timepicker * Copyright (c) 2016 Trent Richardson; Licensed MIT */ @@ -38,583 +334,12 @@ if(c&&c._defaults.timeOnly&&b.input.val()!==b.lastVal)try{$.datepicker._updateDa !function(a){function f(a,b){if(!(a.originalEvent.touches.length>1)){a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)}}if(a.support.touch="ontouchend"in document,a.support.touch){var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a){var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown"))},b._touchMove=function(a){e&&(this._touchMoved=!0,f(a,"mousemove"))},b._touchEnd=function(a){e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1)},b._mouseInit=function(){var b=this;b.element.bind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),c.call(b)},b._mouseDestroy=function(){var b=this;b.element.unbind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),d.call(b)}}}(jQuery); /*! Select2 4.0.2 | https://github.com/select2/select2/blob/master/LICENSE.md */!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){var b=function(){if(a&&a.fn&&a.fn.select2&&a.fn.select2.amd)var b=a.fn.select2.amd;var b;return function(){if(!b||!b.requirejs){b?c=b:b={};var a,c,d;!function(b){function e(a,b){return u.call(a,b)}function f(a,b){var c,d,e,f,g,h,i,j,k,l,m,n=b&&b.split("/"),o=s.map,p=o&&o["*"]||{};if(a&&"."===a.charAt(0))if(b){for(a=a.split("/"),g=a.length-1,s.nodeIdCompat&&w.test(a[g])&&(a[g]=a[g].replace(w,"")),a=n.slice(0,n.length-1).concat(a),k=0;k<a.length;k+=1)if(m=a[k],"."===m)a.splice(k,1),k-=1;else if(".."===m){if(1===k&&(".."===a[2]||".."===a[0]))break;k>0&&(a.splice(k-1,2),k-=2)}a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if((n||p)&&o){for(c=a.split("/"),k=c.length;k>0;k-=1){if(d=c.slice(0,k).join("/"),n)for(l=n.length;l>0;l-=1)if(e=o[n.slice(0,l).join("/")],e&&(e=e[d])){f=e,h=k;break}if(f)break;!i&&p&&p[d]&&(i=p[d],j=k)}!f&&i&&(f=i,h=j),f&&(c.splice(0,h,f),a=c.join("/"))}return a}function g(a,c){return function(){var d=v.call(arguments,0);return"string"!=typeof d[0]&&1===d.length&&d.push(null),n.apply(b,d.concat([a,c]))}}function h(a){return function(b){return f(b,a)}}function i(a){return function(b){q[a]=b}}function j(a){if(e(r,a)){var c=r[a];delete r[a],t[a]=!0,m.apply(b,c)}if(!e(q,a)&&!e(t,a))throw new Error("No "+a);return q[a]}function k(a){var b,c=a?a.indexOf("!"):-1;return c>-1&&(b=a.substring(0,c),a=a.substring(c+1,a.length)),[b,a]}function l(a){return function(){return s&&s.config&&s.config[a]||{}}}var m,n,o,p,q={},r={},s={},t={},u=Object.prototype.hasOwnProperty,v=[].slice,w=/\.js$/;o=function(a,b){var c,d=k(a),e=d[0];return a=d[1],e&&(e=f(e,b),c=j(e)),e?a=c&&c.normalize?c.normalize(a,h(b)):f(a,b):(a=f(a,b),d=k(a),e=d[0],a=d[1],e&&(c=j(e))),{f:e?e+"!"+a:a,n:a,pr:e,p:c}},p={require:function(a){return g(a)},exports:function(a){var b=q[a];return"undefined"!=typeof b?b:q[a]={}},module:function(a){return{id:a,uri:"",exports:q[a],config:l(a)}}},m=function(a,c,d,f){var h,k,l,m,n,s,u=[],v=typeof d;if(f=f||a,"undefined"===v||"function"===v){for(c=!c.length&&d.length?["require","exports","module"]:c,n=0;n<c.length;n+=1)if(m=o(c[n],f),k=m.f,"require"===k)u[n]=p.require(a);else if("exports"===k)u[n]=p.exports(a),s=!0;else if("module"===k)h=u[n]=p.module(a);else if(e(q,k)||e(r,k)||e(t,k))u[n]=j(k);else{if(!m.p)throw new Error(a+" missing "+k);m.p.load(m.n,g(f,!0),i(k),{}),u[n]=q[k]}l=d?d.apply(q[a],u):void 0,a&&(h&&h.exports!==b&&h.exports!==q[a]?q[a]=h.exports:l===b&&s||(q[a]=l))}else a&&(q[a]=d)},a=c=n=function(a,c,d,e,f){if("string"==typeof a)return p[a]?p[a](c):j(o(a,c).f);if(!a.splice){if(s=a,s.deps&&n(s.deps,s.callback),!c)return;c.splice?(a=c,c=d,d=null):a=b}return c=c||function(){},"function"==typeof d&&(d=e,e=f),e?m(b,a,c,d):setTimeout(function(){m(b,a,c,d)},4),n},n.config=function(a){return n(a)},a._defined=q,d=function(a,b,c){if("string"!=typeof a)throw new Error("See almond README: incorrect module build, no module name");b.splice||(c=b,b=[]),e(q,a)||e(r,a)||(r[a]=[a,b,c])},d.amd={jQuery:!0}}(),b.requirejs=a,b.require=c,b.define=d}}(),b.define("almond",function(){}),b.define("jquery",[],function(){var b=a||$;return null==b&&console&&console.error&&console.error("Select2: An instance of jQuery or a jQuery-compatible library was not found. Make sure that you are including jQuery before Select2 on your web page."),b}),b.define("select2/utils",["jquery"],function(a){function b(a){var b=a.prototype,c=[];for(var d in b){var e=b[d];"function"==typeof e&&"constructor"!==d&&c.push(d)}return c}var c={};c.Extend=function(a,b){function c(){this.constructor=a}var d={}.hasOwnProperty;for(var e in b)d.call(b,e)&&(a[e]=b[e]);return c.prototype=b.prototype,a.prototype=new c,a.__super__=b.prototype,a},c.Decorate=function(a,c){function d(){var b=Array.prototype.unshift,d=c.prototype.constructor.length,e=a.prototype.constructor;d>0&&(b.call(arguments,a.prototype.constructor),e=c.prototype.constructor),e.apply(this,arguments)}function e(){this.constructor=d}var f=b(c),g=b(a);c.displayName=a.displayName,d.prototype=new e;for(var h=0;h<g.length;h++){var i=g[h];d.prototype[i]=a.prototype[i]}for(var j=(function(a){var b=function(){};a in d.prototype&&(b=d.prototype[a]);var e=c.prototype[a];return function(){var a=Array.prototype.unshift;return a.call(arguments,b),e.apply(this,arguments)}}),k=0;k<f.length;k++){var l=f[k];d.prototype[l]=j(l)}return d};var d=function(){this.listeners={}};return d.prototype.on=function(a,b){this.listeners=this.listeners||{},a in this.listeners?this.listeners[a].push(b):this.listeners[a]=[b]},d.prototype.trigger=function(a){var b=Array.prototype.slice;this.listeners=this.listeners||{},a in this.listeners&&this.invoke(this.listeners[a],b.call(arguments,1)),"*"in this.listeners&&this.invoke(this.listeners["*"],arguments)},d.prototype.invoke=function(a,b){for(var c=0,d=a.length;d>c;c++)a[c].apply(this,b)},c.Observable=d,c.generateChars=function(a){for(var b="",c=0;a>c;c++){var d=Math.floor(36*Math.random());b+=d.toString(36)}return b},c.bind=function(a,b){return function(){a.apply(b,arguments)}},c._convertData=function(a){for(var b in a){var c=b.split("-"),d=a;if(1!==c.length){for(var e=0;e<c.length;e++){var f=c[e];f=f.substring(0,1).toLowerCase()+f.substring(1),f in d||(d[f]={}),e==c.length-1&&(d[f]=a[b]),d=d[f]}delete a[b]}}return a},c.hasScroll=function(b,c){var d=a(c),e=c.style.overflowX,f=c.style.overflowY;return e!==f||"hidden"!==f&&"visible"!==f?"scroll"===e||"scroll"===f?!0:d.innerHeight()<c.scrollHeight||d.innerWidth()<c.scrollWidth:!1},c.escapeMarkup=function(a){var b={"\\":"\","&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};return"string"!=typeof a?a:String(a).replace(/[&<>"'\/\\]/g,function(a){return b[a]})},c.appendMany=function(b,c){if("1.7"===a.fn.jquery.substr(0,3)){var d=a();a.map(c,function(a){d=d.add(a)}),c=d}b.append(c)},c}),b.define("select2/results",["jquery","./utils"],function(a,b){function c(a,b,d){this.$element=a,this.data=d,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('<ul class="select2-results__options" role="tree"></ul>');return this.options.get("multiple")&&b.attr("aria-multiselectable","true"),this.$results=b,b},c.prototype.clear=function(){this.$results.empty()},c.prototype.displayMessage=function(b){var c=this.options.get("escapeMarkup");this.clear(),this.hideLoading();var d=a('<li role="treeitem" aria-live="assertive" class="select2-results__option"></li>'),e=this.options.get("translations").get(b.message);d.append(c(e(b.args))),d[0].className+=" select2-results__message",this.$results.append(d)},c.prototype.hideMessages=function(){this.$results.find(".select2-results__message").remove()},c.prototype.append=function(a){this.hideLoading();var b=[];if(null==a.results||0===a.results.length)return void(0===this.$results.children().length&&this.trigger("results:message",{message:"noResults"}));a.results=this.sort(a.results);for(var c=0;c<a.results.length;c++){var d=a.results[c],e=this.option(d);b.push(e)}this.$results.append(b)},c.prototype.position=function(a,b){var c=b.find(".select2-results");c.append(a)},c.prototype.sort=function(a){var b=this.options.get("sorter");return b(a)},c.prototype.setClasses=function(){var b=this;this.data.current(function(c){var d=a.map(c,function(a){return a.id.toString()}),e=b.$results.find(".select2-results__option[aria-selected]");e.each(function(){var b=a(this),c=a.data(this,"data"),e=""+c.id;null!=c.element&&c.element.selected||null==c.element&&a.inArray(e,d)>-1?b.attr("aria-selected","true"):b.attr("aria-selected","false")});var f=e.filter("[aria-selected=true]");f.length>0?f.first().trigger("mouseenter"):e.first().trigger("mouseenter")})},c.prototype.showLoading=function(a){this.hideLoading();var b=this.options.get("translations").get("searching"),c={disabled:!0,loading:!0,text:b(a)},d=this.option(c);d.className+=" loading-results",this.$results.prepend(d)},c.prototype.hideLoading=function(){this.$results.find(".loading-results").remove()},c.prototype.option=function(b){var c=document.createElement("li");c.className="select2-results__option";var d={role:"treeitem","aria-selected":"false"};b.disabled&&(delete d["aria-selected"],d["aria-disabled"]="true"),null==b.id&&delete d["aria-selected"],null!=b._resultId&&(c.id=b._resultId),b.title&&(c.title=b.title),b.children&&(d.role="group",d["aria-label"]=b.text,delete d["aria-selected"]);for(var e in d){var f=d[e];c.setAttribute(e,f)}if(b.children){var g=a(c),h=document.createElement("strong");h.className="select2-results__group";a(h);this.template(b,h);for(var i=[],j=0;j<b.children.length;j++){var k=b.children[j],l=this.option(k);i.push(l)}var m=a("<ul></ul>",{"class":"select2-results__options select2-results__options--nested"});m.append(i),g.append(h),g.append(m)}else this.template(b,c);return a.data(c,"data",b),c},c.prototype.bind=function(b,c){var d=this,e=b.id+"-results";this.$results.attr("id",e),b.on("results:all",function(a){d.clear(),d.append(a.data),b.isOpen()&&d.setClasses()}),b.on("results:append",function(a){d.append(a.data),b.isOpen()&&d.setClasses()}),b.on("query",function(a){d.hideMessages(),d.showLoading(a)}),b.on("select",function(){b.isOpen()&&d.setClasses()}),b.on("unselect",function(){b.isOpen()&&d.setClasses()}),b.on("open",function(){d.$results.attr("aria-expanded","true"),d.$results.attr("aria-hidden","false"),d.setClasses(),d.ensureHighlightVisible()}),b.on("close",function(){d.$results.attr("aria-expanded","false"),d.$results.attr("aria-hidden","true"),d.$results.removeAttr("aria-activedescendant")}),b.on("results:toggle",function(){var a=d.getHighlightedResults();0!==a.length&&a.trigger("mouseup")}),b.on("results:select",function(){var a=d.getHighlightedResults();if(0!==a.length){var b=a.data("data");"true"==a.attr("aria-selected")?d.trigger("close",{}):d.trigger("select",{data:b})}}),b.on("results:previous",function(){var a=d.getHighlightedResults(),b=d.$results.find("[aria-selected]"),c=b.index(a);if(0!==c){var e=c-1;0===a.length&&(e=0);var f=b.eq(e);f.trigger("mouseenter");var g=d.$results.offset().top,h=f.offset().top,i=d.$results.scrollTop()+(h-g);0===e?d.$results.scrollTop(0):0>h-g&&d.$results.scrollTop(i)}}),b.on("results:next",function(){var a=d.getHighlightedResults(),b=d.$results.find("[aria-selected]"),c=b.index(a),e=c+1;if(!(e>=b.length)){var f=b.eq(e);f.trigger("mouseenter");var g=d.$results.offset().top+d.$results.outerHeight(!1),h=f.offset().top+f.outerHeight(!1),i=d.$results.scrollTop()+h-g;0===e?d.$results.scrollTop(0):h>g&&d.$results.scrollTop(i)}}),b.on("results:focus",function(a){a.element.addClass("select2-results__option--highlighted")}),b.on("results:message",function(a){d.displayMessage(a)}),a.fn.mousewheel&&this.$results.on("mousewheel",function(a){var b=d.$results.scrollTop(),c=d.$results.get(0).scrollHeight-b+a.deltaY,e=a.deltaY>0&&b-a.deltaY<=0,f=a.deltaY<0&&c<=d.$results.height();e?(d.$results.scrollTop(0),a.preventDefault(),a.stopPropagation()):f&&(d.$results.scrollTop(d.$results.get(0).scrollHeight-d.$results.height()),a.preventDefault(),a.stopPropagation())}),this.$results.on("mouseup",".select2-results__option[aria-selected]",function(b){var c=a(this),e=c.data("data");return"true"===c.attr("aria-selected")?void(d.options.get("multiple")?d.trigger("unselect",{originalEvent:b,data:e}):d.trigger("close",{})):void d.trigger("select",{originalEvent:b,data:e})}),this.$results.on("mouseenter",".select2-results__option[aria-selected]",function(b){var c=a(this).data("data");d.getHighlightedResults().removeClass("select2-results__option--highlighted"),d.trigger("results:focus",{data:c,element:a(this)})})},c.prototype.getHighlightedResults=function(){var a=this.$results.find(".select2-results__option--highlighted");return a},c.prototype.destroy=function(){this.$results.remove()},c.prototype.ensureHighlightVisible=function(){var a=this.getHighlightedResults();if(0!==a.length){var b=this.$results.find("[aria-selected]"),c=b.index(a),d=this.$results.offset().top,e=a.offset().top,f=this.$results.scrollTop()+(e-d),g=e-d;f-=2*a.outerHeight(!1),2>=c?this.$results.scrollTop(0):(g>this.$results.outerHeight()||0>g)&&this.$results.scrollTop(f)}},c.prototype.template=function(b,c){var d=this.options.get("templateResult"),e=this.options.get("escapeMarkup"),f=d(b,c);null==f?c.style.display="none":"string"==typeof f?c.innerHTML=e(f):a(c).append(f)},c}),b.define("select2/keys",[],function(){var a={BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46};return a}),b.define("select2/selection/base",["jquery","../utils","../keys"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,b.Observable),d.prototype.render=function(){var b=a('<span class="select2-selection" role="combobox" aria-haspopup="true" aria-expanded="false"></span>');return this._tabindex=0,null!=this.$element.data("old-tabindex")?this._tabindex=this.$element.data("old-tabindex"):null!=this.$element.attr("tabindex")&&(this._tabindex=this.$element.attr("tabindex")),b.attr("title",this.$element.attr("title")),b.attr("tabindex",this._tabindex),this.$selection=b,b},d.prototype.bind=function(a,b){var d=this,e=(a.id+"-container",a.id+"-results");this.container=a,this.$selection.on("focus",function(a){d.trigger("focus",a)}),this.$selection.on("blur",function(a){d._handleBlur(a)}),this.$selection.on("keydown",function(a){d.trigger("keypress",a),a.which===c.SPACE&&a.preventDefault()}),a.on("results:focus",function(a){d.$selection.attr("aria-activedescendant",a.data._resultId)}),a.on("selection:update",function(a){d.update(a.data)}),a.on("open",function(){d.$selection.attr("aria-expanded","true"),d.$selection.attr("aria-owns",e),d._attachCloseHandler(a)}),a.on("close",function(){d.$selection.attr("aria-expanded","false"),d.$selection.removeAttr("aria-activedescendant"),d.$selection.removeAttr("aria-owns"),d.$selection.focus(),d._detachCloseHandler(a)}),a.on("enable",function(){d.$selection.attr("tabindex",d._tabindex)}),a.on("disable",function(){d.$selection.attr("tabindex","-1")})},d.prototype._handleBlur=function(b){var c=this;window.setTimeout(function(){document.activeElement==c.$selection[0]||a.contains(c.$selection[0],document.activeElement)||c.trigger("blur",b)},1)},d.prototype._attachCloseHandler=function(b){a(document.body).on("mousedown.select2."+b.id,function(b){var c=a(b.target),d=c.closest(".select2"),e=a(".select2.select2-container--open");e.each(function(){var b=a(this);if(this!=d[0]){var c=b.data("element");c.select2("close")}})})},d.prototype._detachCloseHandler=function(b){a(document.body).off("mousedown.select2."+b.id)},d.prototype.position=function(a,b){var c=b.find(".selection");c.append(a)},d.prototype.destroy=function(){this._detachCloseHandler(this.container)},d.prototype.update=function(a){throw new Error("The `update` method must be defined in child classes.")},d}),b.define("select2/selection/single",["jquery","./base","../utils","../keys"],function(a,b,c,d){function e(){e.__super__.constructor.apply(this,arguments)}return c.Extend(e,b),e.prototype.render=function(){var a=e.__super__.render.call(this);return a.addClass("select2-selection--single"),a.html('<span class="select2-selection__rendered"></span><span class="select2-selection__arrow" role="presentation"><b role="presentation"></b></span>'),a},e.prototype.bind=function(a,b){var c=this;e.__super__.bind.apply(this,arguments);var d=a.id+"-container";this.$selection.find(".select2-selection__rendered").attr("id",d),this.$selection.attr("aria-labelledby",d),this.$selection.on("mousedown",function(a){1===a.which&&c.trigger("toggle",{originalEvent:a})}),this.$selection.on("focus",function(a){}),this.$selection.on("blur",function(a){}),a.on("selection:update",function(a){c.update(a.data)})},e.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},e.prototype.display=function(a,b){var c=this.options.get("templateSelection"),d=this.options.get("escapeMarkup");return d(c(a,b))},e.prototype.selectionContainer=function(){return a("<span></span>")},e.prototype.update=function(a){if(0===a.length)return void this.clear();var b=a[0],c=this.$selection.find(".select2-selection__rendered"),d=this.display(b,c);c.empty().append(d),c.prop("title",b.title||b.text)},e}),b.define("select2/selection/multiple",["jquery","./base","../utils"],function(a,b,c){function d(a,b){d.__super__.constructor.apply(this,arguments)}return c.Extend(d,b),d.prototype.render=function(){var a=d.__super__.render.call(this);return a.addClass("select2-selection--multiple"),a.html('<ul class="select2-selection__rendered"></ul>'),a},d.prototype.bind=function(b,c){var e=this;d.__super__.bind.apply(this,arguments),this.$selection.on("click",function(a){e.trigger("toggle",{originalEvent:a})}),this.$selection.on("click",".select2-selection__choice__remove",function(b){if(!e.options.get("disabled")){var c=a(this),d=c.parent(),f=d.data("data");e.trigger("unselect",{originalEvent:b,data:f})}})},d.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},d.prototype.display=function(a,b){var c=this.options.get("templateSelection"),d=this.options.get("escapeMarkup");return d(c(a,b))},d.prototype.selectionContainer=function(){var b=a('<li class="select2-selection__choice"><span class="select2-selection__choice__remove" role="presentation">×</span></li>');return b},d.prototype.update=function(a){if(this.clear(),0!==a.length){for(var b=[],d=0;d<a.length;d++){var e=a[d],f=this.selectionContainer(),g=this.display(e,f);f.append(g),f.prop("title",e.title||e.text),f.data("data",e),b.push(f)}var h=this.$selection.find(".select2-selection__rendered");c.appendMany(h,b)}},d}),b.define("select2/selection/placeholder",["../utils"],function(a){function b(a,b,c){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c)}return b.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},b.prototype.createPlaceholder=function(a,b){var c=this.selectionContainer();return c.html(this.display(b)),c.addClass("select2-selection__placeholder").removeClass("select2-selection__choice"),c},b.prototype.update=function(a,b){var c=1==b.length&&b[0].id!=this.placeholder.id,d=b.length>1;if(d||c)return a.call(this,b);this.clear();var e=this.createPlaceholder(this.placeholder);this.$selection.find(".select2-selection__rendered").append(e)},b}),b.define("select2/selection/allowClear",["jquery","../keys"],function(a,b){function c(){}return c.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),null==this.placeholder&&this.options.get("debug")&&window.console&&console.error&&console.error("Select2: The `allowClear` option should be used in combination with the `placeholder` option."),this.$selection.on("mousedown",".select2-selection__clear",function(a){d._handleClear(a)}),b.on("keypress",function(a){d._handleKeyboardClear(a,b)})},c.prototype._handleClear=function(a,b){if(!this.options.get("disabled")){var c=this.$selection.find(".select2-selection__clear");if(0!==c.length){b.stopPropagation();for(var d=c.data("data"),e=0;e<d.length;e++){var f={data:d[e]};if(this.trigger("unselect",f),f.prevented)return}this.$element.val(this.placeholder.id).trigger("change"),this.trigger("toggle",{})}}},c.prototype._handleKeyboardClear=function(a,c,d){d.isOpen()||(c.which==b.DELETE||c.which==b.BACKSPACE)&&this._handleClear(c)},c.prototype.update=function(b,c){if(b.call(this,c),!(this.$selection.find(".select2-selection__placeholder").length>0||0===c.length)){var d=a('<span class="select2-selection__clear">×</span>');d.data("data",c),this.$selection.find(".select2-selection__rendered").prepend(d)}},c}),b.define("select2/selection/search",["jquery","../utils","../keys"],function(a,b,c){function d(a,b,c){a.call(this,b,c)}return d.prototype.render=function(b){var c=a('<li class="select2-search select2-search--inline"><input class="select2-search__field" type="search" tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" role="textbox" aria-autocomplete="list" /></li>');this.$searchContainer=c,this.$search=c.find("input");var d=b.call(this);return this._transferTabIndex(),d},d.prototype.bind=function(a,b,d){var e=this;a.call(this,b,d),b.on("open",function(){e.$search.trigger("focus")}),b.on("close",function(){e.$search.val(""),e.$search.removeAttr("aria-activedescendant"),e.$search.trigger("focus")}),b.on("enable",function(){e.$search.prop("disabled",!1),e._transferTabIndex()}),b.on("disable",function(){e.$search.prop("disabled",!0)}),b.on("focus",function(a){e.$search.trigger("focus")}),b.on("results:focus",function(a){e.$search.attr("aria-activedescendant",a.id)}),this.$selection.on("focusin",".select2-search--inline",function(a){e.trigger("focus",a)}),this.$selection.on("focusout",".select2-search--inline",function(a){e._handleBlur(a)}),this.$selection.on("keydown",".select2-search--inline",function(a){a.stopPropagation(),e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented();var b=a.which;if(b===c.BACKSPACE&&""===e.$search.val()){var d=e.$searchContainer.prev(".select2-selection__choice");if(d.length>0){var f=d.data("data");e.searchRemoveChoice(f),a.preventDefault()}}});var f=document.documentMode,g=f&&11>=f;this.$selection.on("input.searchcheck",".select2-search--inline",function(a){return g?void e.$selection.off("input.search input.searchcheck"):void e.$selection.off("keyup.search")}),this.$selection.on("keyup.search input.search",".select2-search--inline",function(a){if(g&&"input"===a.type)return void e.$selection.off("input.search input.searchcheck");var b=a.which;b!=c.SHIFT&&b!=c.CTRL&&b!=c.ALT&&b!=c.TAB&&e.handleSearch(a)})},d.prototype._transferTabIndex=function(a){this.$search.attr("tabindex",this.$selection.attr("tabindex")),this.$selection.attr("tabindex","-1")},d.prototype.createPlaceholder=function(a,b){this.$search.attr("placeholder",b.text)},d.prototype.update=function(a,b){var c=this.$search[0]==document.activeElement;this.$search.attr("placeholder",""),a.call(this,b),this.$selection.find(".select2-selection__rendered").append(this.$searchContainer),this.resizeSearch(),c&&this.$search.focus()},d.prototype.handleSearch=function(){if(this.resizeSearch(),!this._keyUpPrevented){var a=this.$search.val();this.trigger("query",{term:a})}this._keyUpPrevented=!1},d.prototype.searchRemoveChoice=function(a,b){this.trigger("unselect",{data:b}),this.$search.val(b.text),this.handleSearch()},d.prototype.resizeSearch=function(){this.$search.css("width","25px");var a="";if(""!==this.$search.attr("placeholder"))a=this.$selection.find(".select2-selection__rendered").innerWidth();else{var b=this.$search.val().length+1;a=.75*b+"em"}this.$search.css("width",a)},d}),b.define("select2/selection/eventRelay",["jquery"],function(a){function b(){}return b.prototype.bind=function(b,c,d){var e=this,f=["open","opening","close","closing","select","selecting","unselect","unselecting"],g=["opening","closing","selecting","unselecting"];b.call(this,c,d),c.on("*",function(b,c){if(-1!==a.inArray(b,f)){c=c||{};var d=a.Event("select2:"+b,{params:c});e.$element.trigger(d),-1!==a.inArray(b,g)&&(c.prevented=d.isDefaultPrevented())}})},b}),b.define("select2/translation",["jquery","require"],function(a,b){function c(a){this.dict=a||{}}return c.prototype.all=function(){return this.dict},c.prototype.get=function(a){return this.dict[a]},c.prototype.extend=function(b){this.dict=a.extend({},b.all(),this.dict)},c._cache={},c.loadPath=function(a){if(!(a in c._cache)){var d=b(a);c._cache[a]=d}return new c(c._cache[a])},c}),b.define("select2/diacritics",[],function(){var a={"â’¶":"A","A":"A","À":"A","Ã":"A","Â":"A","Ầ":"A","Ấ":"A","Ẫ":"A","Ẩ":"A","Ã":"A","Ä€":"A","Ä‚":"A","Ằ":"A","Ắ":"A","Ẵ":"A","Ẳ":"A","Ȧ":"A","Ç ":"A","Ä":"A","Çž":"A","Ả":"A","Ã…":"A","Ǻ":"A","Ç":"A","È€":"A","È‚":"A","Ạ":"A","Ậ":"A","Ặ":"A","Ḁ":"A","Ä„":"A","Ⱥ":"A","Ɐ":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ç¢":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","â’·":"B","ï¼¢":"B","Ḃ":"B","Ḅ":"B","Ḇ":"B","Ƀ":"B","Æ‚":"B","Æ":"B","â’¸":"C","ï¼£":"C","Ć":"C","Ĉ":"C","ÄŠ":"C","ÄŒ":"C","Ç":"C","Ḉ":"C","Ƈ":"C","È»":"C","Ꜿ":"C","â’¹":"D","D":"D","Ḋ":"D","ÄŽ":"D","Ḍ":"D","á¸":"D","Ḓ":"D","Ḏ":"D","Ä":"D","Æ‹":"D","ÆŠ":"D","Ɖ":"D","ê¹":"D","DZ":"DZ","Ç„":"DZ","Dz":"Dz","Ç…":"Dz","â’º":"E","ï¼¥":"E","È":"E","É":"E","Ê":"E","Ề":"E","Ế":"E","Ễ":"E","Ể":"E","Ẽ":"E","Ä’":"E","Ḕ":"E","Ḗ":"E","Ä”":"E","Ä–":"E","Ë":"E","Ẻ":"E","Äš":"E","È„":"E","Ȇ":"E","Ẹ":"E","Ệ":"E","Ȩ":"E","Ḝ":"E","Ę":"E","Ḙ":"E","Ḛ":"E","Æ":"E","ÆŽ":"E","â’»":"F","F":"F","Ḟ":"F","Æ‘":"F","ê»":"F","â’¼":"G","ï¼§":"G","Ç´":"G","Äœ":"G","Ḡ":"G","Äž":"G","Ä ":"G","Ǧ":"G","Ä¢":"G","Ǥ":"G","Æ“":"G","êž ":"G","ê½":"G","ê¾":"G","â’½":"H","H":"H","Ĥ":"H","Ḣ":"H","Ḧ":"H","Èž":"H","Ḥ":"H","Ḩ":"H","Ḫ":"H","Ħ":"H","â±§":"H","â±µ":"H","êž":"H","â’¾":"I","I":"I","ÃŒ":"I","Ã":"I","ÃŽ":"I","Ĩ":"I","Ī":"I","Ĭ":"I","İ":"I","Ã":"I","Ḯ":"I","Ỉ":"I","Ç":"I","Ȉ":"I","ÈŠ":"I","Ị":"I","Ä®":"I","Ḭ":"I","Æ—":"I","â’¿":"J","J":"J","Ä´":"J","Ɉ":"J","â“€":"K","K":"K","Ḱ":"K","Ǩ":"K","Ḳ":"K","Ķ":"K","Ḵ":"K","Ƙ":"K","Ⱪ":"K","ê€":"K","ê‚":"K","ê„":"K","Ꞣ":"K","â“":"L","L":"L","Ä¿":"L","Ĺ":"L","Ľ":"L","Ḷ":"L","Ḹ":"L","Ä»":"L","Ḽ":"L","Ḻ":"L","Å":"L","Ƚ":"L","â±¢":"L","â± ":"L","êˆ":"L","ê†":"L","Ꞁ":"L","LJ":"LJ","Lj":"Lj","â“‚":"M","ï¼":"M","Ḿ":"M","á¹€":"M","Ṃ":"M","â±®":"M","Æœ":"M","Ⓝ":"N","ï¼®":"N","Ǹ":"N","Ń":"N","Ñ":"N","Ṅ":"N","Ň":"N","Ṇ":"N","Å…":"N","Ṋ":"N","Ṉ":"N","È ":"N","Æ":"N","êž":"N","Ꞥ":"N","ÇŠ":"NJ","Ç‹":"Nj","â“„":"O","O":"O","Ã’":"O","Ó":"O","Ô":"O","á»’":"O","á»":"O","á»–":"O","á»”":"O","Õ":"O","Ṍ":"O","Ȭ":"O","Ṏ":"O","ÅŒ":"O","á¹":"O","á¹’":"O","ÅŽ":"O","È®":"O","Ȱ":"O","Ö":"O","Ȫ":"O","Ỏ":"O","Å":"O","Ç‘":"O","ÈŒ":"O","ÈŽ":"O","Æ ":"O","Ờ":"O","Ớ":"O","á» ":"O","Ở":"O","Ợ":"O","Ọ":"O","Ộ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Ɔ":"O","ÆŸ":"O","êŠ":"O","êŒ":"O","Æ¢":"OI","êŽ":"OO","È¢":"OU","â“…":"P","ï¼°":"P","á¹”":"P","á¹–":"P","Ƥ":"P","â±£":"P","ê":"P","ê’":"P","ê”":"P","Ⓠ":"Q","ï¼±":"Q","ê–":"Q","ê˜":"Q","ÉŠ":"Q","Ⓡ":"R","ï¼²":"R","Å”":"R","Ṙ":"R","Ř":"R","È":"R","È’":"R","Ṛ":"R","Ṝ":"R","Å–":"R","Ṟ":"R","ÉŒ":"R","Ɽ":"R","êš":"R","Ꞧ":"R","êž‚":"R","Ⓢ":"S","ï¼³":"S","ẞ":"S","Åš":"S","Ṥ":"S","Åœ":"S","á¹ ":"S","Å ":"S","Ṧ":"S","á¹¢":"S","Ṩ":"S","Ș":"S","Åž":"S","â±¾":"S","Ꞩ":"S","êž„":"S","Ⓣ":"T","ï¼´":"T","Ṫ":"T","Ť":"T","Ṭ":"T","Èš":"T","Å¢":"T","á¹°":"T","á¹®":"T","Ŧ":"T","Ƭ":"T","Æ®":"T","Ⱦ":"T","Ꞇ":"T","Ꜩ":"TZ","Ⓤ":"U","ï¼µ":"U","Ù":"U","Ú":"U","Û":"U","Ũ":"U","Ṹ":"U","Ū":"U","Ṻ":"U","Ŭ":"U","Ü":"U","Ç›":"U","Ç—":"U","Ç•":"U","Ç™":"U","Ủ":"U","Å®":"U","Ű":"U","Ç“":"U","È”":"U","È–":"U","Ư":"U","Ừ":"U","Ứ":"U","á»®":"U","Ử":"U","á»°":"U","Ụ":"U","á¹²":"U","Ų":"U","á¹¶":"U","á¹´":"U","É„":"U","â“‹":"V","ï¼¶":"V","á¹¼":"V","á¹¾":"V","Ʋ":"V","êž":"V","É…":"V","ê ":"VY","Ⓦ":"W","ï¼·":"W","Ẁ":"W","Ẃ":"W","Å´":"W","Ẇ":"W","Ẅ":"W","Ẉ":"W","â±²":"W","â“":"X","X":"X","Ẋ":"X","Ẍ":"X","Ⓨ":"Y","ï¼¹":"Y","Ỳ":"Y","Ã":"Y","Ŷ":"Y","Ỹ":"Y","Ȳ":"Y","Ẏ":"Y","Ÿ":"Y","á»¶":"Y","á»´":"Y","Ƴ":"Y","ÉŽ":"Y","Ỿ":"Y","â“":"Z","Z":"Z","Ź":"Z","áº":"Z","Å»":"Z","Ž":"Z","Ẓ":"Z","Ẕ":"Z","Ƶ":"Z","Ȥ":"Z","Ɀ":"Z","Ⱬ":"Z","ê¢":"Z","â“":"a","ï½":"a","ẚ":"a","à ":"a","á":"a","â":"a","ầ":"a","ấ":"a","ẫ":"a","ẩ":"a","ã":"a","Ä":"a","ă":"a","ằ":"a","ắ":"a","ẵ":"a","ẳ":"a","ȧ":"a","Ç¡":"a","ä":"a","ÇŸ":"a","ả":"a","Ã¥":"a","Ç»":"a","ÇŽ":"a","È":"a","ȃ":"a","ạ":"a","áº":"a","ặ":"a","á¸":"a","Ä…":"a","â±¥":"a","É":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","Ç£":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","â“‘":"b","b":"b","ḃ":"b","ḅ":"b","ḇ":"b","Æ€":"b","ƃ":"b","É“":"b","â“’":"c","c":"c","ć":"c","ĉ":"c","Ä‹":"c","Ä":"c","ç":"c","ḉ":"c","ƈ":"c","ȼ":"c","ꜿ":"c","ↄ":"c","â““":"d","d":"d","ḋ":"d","Ä":"d","á¸":"d","ḑ":"d","ḓ":"d","á¸":"d","Ä‘":"d","ÆŒ":"d","É–":"d","É—":"d","êº":"d","dz":"dz","dž":"dz","â“”":"e","ï½…":"e","è":"e","é":"e","ê":"e","á»":"e","ế":"e","á»…":"e","ể":"e","ẽ":"e","Ä“":"e","ḕ":"e","ḗ":"e","Ä•":"e","Ä—":"e","ë":"e","ẻ":"e","Ä›":"e","È…":"e","ȇ":"e","ẹ":"e","ệ":"e","È©":"e","á¸":"e","Ä™":"e","ḙ":"e","ḛ":"e","ɇ":"e","É›":"e","Ç":"e","â“•":"f","f":"f","ḟ":"f","Æ’":"f","ê¼":"f","â“–":"g","g":"g","ǵ":"g","Ä":"g","ḡ":"g","ÄŸ":"g","Ä¡":"g","ǧ":"g","Ä£":"g","Ç¥":"g","É ":"g","êž¡":"g","áµ¹":"g","ê¿":"g","â“—":"h","h":"h","Ä¥":"h","ḣ":"h","ḧ":"h","ÈŸ":"h","ḥ":"h","ḩ":"h","ḫ":"h","ẖ":"h","ħ":"h","ⱨ":"h","â±¶":"h","É¥":"h","Æ•":"hv","ⓘ":"i","i":"i","ì":"i","Ã":"i","î":"i","Ä©":"i","Ä«":"i","Ä":"i","ï":"i","ḯ":"i","ỉ":"i","Ç":"i","ȉ":"i","È‹":"i","ị":"i","į":"i","á¸":"i","ɨ":"i","ı":"i","â“™":"j","j":"j","ĵ":"j","ǰ":"j","ɉ":"j","ⓚ":"k","k":"k","ḱ":"k","Ç©":"k","ḳ":"k","Ä·":"k","ḵ":"k","Æ™":"k","ⱪ":"k","ê":"k","êƒ":"k","ê…":"k","ꞣ":"k","â“›":"l","l":"l","Å€":"l","ĺ":"l","ľ":"l","ḷ":"l","ḹ":"l","ļ":"l","ḽ":"l","ḻ":"l","Å¿":"l","Å‚":"l","Æš":"l","É«":"l","ⱡ":"l","ê‰":"l","êž":"l","ê‡":"l","lj":"lj","ⓜ":"m","ï½":"m","ḿ":"m","á¹":"m","ṃ":"m","ɱ":"m","ɯ":"m","â“":"n","n":"n","ǹ":"n","Å„":"n","ñ":"n","á¹…":"n","ň":"n","ṇ":"n","ņ":"n","ṋ":"n","ṉ":"n","Æž":"n","ɲ":"n","ʼn":"n","êž‘":"n","ꞥ":"n","ÇŒ":"nj","ⓞ":"o","ï½":"o","ò":"o","ó":"o","ô":"o","ồ":"o","ố":"o","á»—":"o","ổ":"o","õ":"o","á¹":"o","È":"o","á¹":"o","Å":"o","ṑ":"o","ṓ":"o","Å":"o","ȯ":"o","ȱ":"o","ö":"o","È«":"o","á»":"o","Å‘":"o","Ç’":"o","È":"o","È":"o","Æ¡":"o","á»":"o","á»›":"o","ỡ":"o","ở":"o","ợ":"o","á»":"o","á»™":"o","Ç«":"o","Ç":"o","ø":"o","Ç¿":"o","É”":"o","ê‹":"o","ê":"o","ɵ":"o","Æ£":"oi","È£":"ou","ê":"oo","ⓟ":"p","ï½":"p","ṕ":"p","á¹—":"p","Æ¥":"p","áµ½":"p","ê‘":"p","ê“":"p","ê•":"p","â“ ":"q","q":"q","É‹":"q","ê—":"q","ê™":"q","â“¡":"r","ï½’":"r","Å•":"r","á¹™":"r","Å™":"r","È‘":"r","È“":"r","á¹›":"r","á¹":"r","Å—":"r","ṟ":"r","É":"r","ɽ":"r","ê›":"r","êž§":"r","ꞃ":"r","â“¢":"s","s":"s","ß":"s","Å›":"s","á¹¥":"s","Å":"s","ṡ":"s","Å¡":"s","á¹§":"s","á¹£":"s","ṩ":"s","È™":"s","ÅŸ":"s","È¿":"s","êž©":"s","êž…":"s","ẛ":"s","â“£":"t","ï½”":"t","ṫ":"t","ẗ":"t","Å¥":"t","á¹":"t","È›":"t","Å£":"t","á¹±":"t","ṯ":"t","ŧ":"t","Æ":"t","ʈ":"t","ⱦ":"t","ꞇ":"t","ꜩ":"tz","ⓤ":"u","u":"u","ù":"u","ú":"u","û":"u","Å©":"u","á¹¹":"u","Å«":"u","á¹»":"u","Å":"u","ü":"u","Çœ":"u","ǘ":"u","Ç–":"u","Çš":"u","á»§":"u","ů":"u","ű":"u","Ç”":"u","È•":"u","È—":"u","ư":"u","ừ":"u","ứ":"u","ữ":"u","á»":"u","á»±":"u","ụ":"u","á¹³":"u","ų":"u","á¹·":"u","á¹µ":"u","ʉ":"u","â“¥":"v","ï½–":"v","á¹½":"v","ṿ":"v","Ê‹":"v","êŸ":"v","ÊŒ":"v","ê¡":"vy","ⓦ":"w","ï½—":"w","áº":"w","ẃ":"w","ŵ":"w","ẇ":"w","ẅ":"w","ẘ":"w","ẉ":"w","â±³":"w","â“§":"x","x":"x","ẋ":"x","áº":"x","ⓨ":"y","ï½™":"y","ỳ":"y","ý":"y","Å·":"y","ỹ":"y","ȳ":"y","áº":"y","ÿ":"y","á»·":"y","ẙ":"y","ỵ":"y","Æ´":"y","É":"y","ỿ":"y","â“©":"z","z":"z","ź":"z","ẑ":"z","ż":"z","ž":"z","ẓ":"z","ẕ":"z","ƶ":"z","È¥":"z","É€":"z","ⱬ":"z","ê£":"z","Ά":"Α","Έ":"Ε","Ή":"Η","Ί":"Ι","Ϊ":"Ι","ÎŒ":"Ο","ÎŽ":"Î¥","Ϋ":"Î¥","Î":"Ω","ά":"α","Î":"ε","ή":"η","ί":"ι","ÏŠ":"ι","Î":"ι","ÏŒ":"ο","Ï":"Ï…","Ï‹":"Ï…","ΰ":"Ï…","ω":"ω","Ï‚":"σ"};return a}),b.define("select2/data/base",["../utils"],function(a){function b(a,c){b.__super__.constructor.call(this)}return a.Extend(b,a.Observable),b.prototype.current=function(a){throw new Error("The `current` method must be defined in child classes.")},b.prototype.query=function(a,b){throw new Error("The `query` method must be defined in child classes.")},b.prototype.bind=function(a,b){},b.prototype.destroy=function(){},b.prototype.generateResultId=function(b,c){var d=b.id+"-result-";return d+=a.generateChars(4),d+=null!=c.id?"-"+c.id.toString():"-"+a.generateChars(4)},b}),b.define("select2/data/select",["./base","../utils","jquery"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,a),d.prototype.current=function(a){var b=[],d=this;this.$element.find(":selected").each(function(){var a=c(this),e=d.item(a);b.push(e)}),a(b)},d.prototype.select=function(a){var b=this;if(a.selected=!0,c(a.element).is("option"))return a.element.selected=!0,void this.$element.trigger("change");if(this.$element.prop("multiple"))this.current(function(d){var e=[];a=[a],a.push.apply(a,d);for(var f=0;f<a.length;f++){var g=a[f].id;-1===c.inArray(g,e)&&e.push(g)}b.$element.val(e),b.$element.trigger("change")});else{var d=a.id;this.$element.val(d),this.$element.trigger("change")}},d.prototype.unselect=function(a){var b=this;if(this.$element.prop("multiple"))return a.selected=!1, c(a.element).is("option")?(a.element.selected=!1,void this.$element.trigger("change")):void this.current(function(d){for(var e=[],f=0;f<d.length;f++){var g=d[f].id;g!==a.id&&-1===c.inArray(g,e)&&e.push(g)}b.$element.val(e),b.$element.trigger("change")})},d.prototype.bind=function(a,b){var c=this;this.container=a,a.on("select",function(a){c.select(a.data)}),a.on("unselect",function(a){c.unselect(a.data)})},d.prototype.destroy=function(){this.$element.find("*").each(function(){c.removeData(this,"data")})},d.prototype.query=function(a,b){var d=[],e=this,f=this.$element.children();f.each(function(){var b=c(this);if(b.is("option")||b.is("optgroup")){var f=e.item(b),g=e.matches(a,f);null!==g&&d.push(g)}}),b({results:d})},d.prototype.addOptions=function(a){b.appendMany(this.$element,a)},d.prototype.option=function(a){var b;a.children?(b=document.createElement("optgroup"),b.label=a.text):(b=document.createElement("option"),void 0!==b.textContent?b.textContent=a.text:b.innerText=a.text),a.id&&(b.value=a.id),a.disabled&&(b.disabled=!0),a.selected&&(b.selected=!0),a.title&&(b.title=a.title);var d=c(b),e=this._normalizeItem(a);return e.element=b,c.data(b,"data",e),d},d.prototype.item=function(a){var b={};if(b=c.data(a[0],"data"),null!=b)return b;if(a.is("option"))b={id:a.val(),text:a.text(),disabled:a.prop("disabled"),selected:a.prop("selected"),title:a.prop("title")};else if(a.is("optgroup")){b={text:a.prop("label"),children:[],title:a.prop("title")};for(var d=a.children("option"),e=[],f=0;f<d.length;f++){var g=c(d[f]),h=this.item(g);e.push(h)}b.children=e}return b=this._normalizeItem(b),b.element=a[0],c.data(a[0],"data",b),b},d.prototype._normalizeItem=function(a){c.isPlainObject(a)||(a={id:a,text:a}),a=c.extend({},{text:""},a);var b={selected:!1,disabled:!1};return null!=a.id&&(a.id=a.id.toString()),null!=a.text&&(a.text=a.text.toString()),null==a._resultId&&a.id&&null!=this.container&&(a._resultId=this.generateResultId(this.container,a)),c.extend({},b,a)},d.prototype.matches=function(a,b){var c=this.options.get("matcher");return c(a,b)},d}),b.define("select2/data/array",["./select","../utils","jquery"],function(a,b,c){function d(a,b){var c=b.get("data")||[];d.__super__.constructor.call(this,a,b),this.addOptions(this.convertToOptions(c))}return b.Extend(d,a),d.prototype.select=function(a){var b=this.$element.find("option").filter(function(b,c){return c.value==a.id.toString()});0===b.length&&(b=this.option(a),this.addOptions(b)),d.__super__.select.call(this,a)},d.prototype.convertToOptions=function(a){function d(a){return function(){return c(this).val()==a.id}}for(var e=this,f=this.$element.find("option"),g=f.map(function(){return e.item(c(this)).id}).get(),h=[],i=0;i<a.length;i++){var j=this._normalizeItem(a[i]);if(c.inArray(j.id,g)>=0){var k=f.filter(d(j)),l=this.item(k),m=c.extend(!0,{},j,l),n=this.option(m);k.replaceWith(n)}else{var o=this.option(j);if(j.children){var p=this.convertToOptions(j.children);b.appendMany(o,p)}h.push(o)}}return h},d}),b.define("select2/data/ajax",["./array","../utils","jquery"],function(a,b,c){function d(a,b){this.ajaxOptions=this._applyDefaults(b.get("ajax")),null!=this.ajaxOptions.processResults&&(this.processResults=this.ajaxOptions.processResults),d.__super__.constructor.call(this,a,b)}return b.Extend(d,a),d.prototype._applyDefaults=function(a){var b={data:function(a){return c.extend({},a,{q:a.term})},transport:function(a,b,d){var e=c.ajax(a);return e.then(b),e.fail(d),e}};return c.extend({},b,a,!0)},d.prototype.processResults=function(a){return a},d.prototype.query=function(a,b){function d(){var d=f.transport(f,function(d){var f=e.processResults(d,a);e.options.get("debug")&&window.console&&console.error&&(f&&f.results&&c.isArray(f.results)||console.error("Select2: The AJAX results did not return an array in the `results` key of the response.")),b(f)},function(){e.trigger("results:message",{message:"errorLoading"})});e._request=d}var e=this;null!=this._request&&(c.isFunction(this._request.abort)&&this._request.abort(),this._request=null);var f=c.extend({type:"GET"},this.ajaxOptions);"function"==typeof f.url&&(f.url=f.url.call(this.$element,a)),"function"==typeof f.data&&(f.data=f.data.call(this.$element,a)),this.ajaxOptions.delay&&""!==a.term?(this._queryTimeout&&window.clearTimeout(this._queryTimeout),this._queryTimeout=window.setTimeout(d,this.ajaxOptions.delay)):d()},d}),b.define("select2/data/tags",["jquery"],function(a){function b(b,c,d){var e=d.get("tags"),f=d.get("createTag");void 0!==f&&(this.createTag=f);var g=d.get("insertTag");if(void 0!==g&&(this.insertTag=g),b.call(this,c,d),a.isArray(e))for(var h=0;h<e.length;h++){var i=e[h],j=this._normalizeItem(i),k=this.option(j);this.$element.append(k)}}return b.prototype.query=function(a,b,c){function d(a,f){for(var g=a.results,h=0;h<g.length;h++){var i=g[h],j=null!=i.children&&!d({results:i.children},!0),k=i.text===b.term;if(k||j)return f?!1:(a.data=g,void c(a))}if(f)return!0;var l=e.createTag(b);if(null!=l){var m=e.option(l);m.attr("data-select2-tag",!0),e.addOptions([m]),e.insertTag(g,l)}a.results=g,c(a)}var e=this;return this._removeOldTags(),null==b.term||null!=b.page?void a.call(this,b,c):void a.call(this,b,d)},b.prototype.createTag=function(b,c){var d=a.trim(c.term);return""===d?null:{id:d,text:d}},b.prototype.insertTag=function(a,b,c){b.unshift(c)},b.prototype._removeOldTags=function(b){var c=(this._lastTag,this.$element.find("option[data-select2-tag]"));c.each(function(){this.selected||a(this).remove()})},b}),b.define("select2/data/tokenizer",["jquery"],function(a){function b(a,b,c){var d=c.get("tokenizer");void 0!==d&&(this.tokenizer=d),a.call(this,b,c)}return b.prototype.bind=function(a,b,c){a.call(this,b,c),this.$search=b.dropdown.$search||b.selection.$search||c.find(".select2-search__field")},b.prototype.query=function(a,b,c){function d(a){e.trigger("select",{data:a})}var e=this;b.term=b.term||"";var f=this.tokenizer(b,this.options,d);f.term!==b.term&&(this.$search.length&&(this.$search.val(f.term),this.$search.focus()),b.term=f.term),a.call(this,b,c)},b.prototype.tokenizer=function(b,c,d,e){for(var f=d.get("tokenSeparators")||[],g=c.term,h=0,i=this.createTag||function(a){return{id:a.term,text:a.term}};h<g.length;){var j=g[h];if(-1!==a.inArray(j,f)){var k=g.substr(0,h),l=a.extend({},c,{term:k}),m=i(l);null!=m?(e(m),g=g.substr(h+1)||"",h=0):h++}else h++}return{term:g}},b}),b.define("select2/data/minimumInputLength",[],function(){function a(a,b,c){this.minimumInputLength=c.get("minimumInputLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){return b.term=b.term||"",b.term.length<this.minimumInputLength?void this.trigger("results:message",{message:"inputTooShort",args:{minimum:this.minimumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumInputLength",[],function(){function a(a,b,c){this.maximumInputLength=c.get("maximumInputLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){return b.term=b.term||"",this.maximumInputLength>0&&b.term.length>this.maximumInputLength?void this.trigger("results:message",{message:"inputTooLong",args:{maximum:this.maximumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumSelectionLength",[],function(){function a(a,b,c){this.maximumSelectionLength=c.get("maximumSelectionLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){var d=this;this.current(function(e){var f=null!=e?e.length:0;return d.maximumSelectionLength>0&&f>=d.maximumSelectionLength?void d.trigger("results:message",{message:"maximumSelected",args:{maximum:d.maximumSelectionLength}}):void a.call(d,b,c)})},a}),b.define("select2/dropdown",["jquery","./utils"],function(a,b){function c(a,b){this.$element=a,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('<span class="select2-dropdown"><span class="select2-results"></span></span>');return b.attr("dir",this.options.get("dir")),this.$dropdown=b,b},c.prototype.bind=function(){},c.prototype.position=function(a,b){},c.prototype.destroy=function(){this.$dropdown.remove()},c}),b.define("select2/dropdown/search",["jquery","../utils"],function(a,b){function c(){}return c.prototype.render=function(b){var c=b.call(this),d=a('<span class="select2-search select2-search--dropdown"><input class="select2-search__field" type="search" tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" role="textbox" /></span>');return this.$searchContainer=d,this.$search=d.find("input"),c.prepend(d),c},c.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),this.$search.on("keydown",function(a){e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented()}),this.$search.on("input",function(b){a(this).off("keyup")}),this.$search.on("keyup input",function(a){e.handleSearch(a)}),c.on("open",function(){e.$search.attr("tabindex",0),e.$search.focus(),window.setTimeout(function(){e.$search.focus()},0)}),c.on("close",function(){e.$search.attr("tabindex",-1),e.$search.val("")}),c.on("results:all",function(a){if(null==a.query.term||""===a.query.term){var b=e.showSearch(a);b?e.$searchContainer.removeClass("select2-search--hide"):e.$searchContainer.addClass("select2-search--hide")}})},c.prototype.handleSearch=function(a){if(!this._keyUpPrevented){var b=this.$search.val();this.trigger("query",{term:b})}this._keyUpPrevented=!1},c.prototype.showSearch=function(a,b){return!0},c}),b.define("select2/dropdown/hidePlaceholder",[],function(){function a(a,b,c,d){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c,d)}return a.prototype.append=function(a,b){b.results=this.removePlaceholder(b.results),a.call(this,b)},a.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},a.prototype.removePlaceholder=function(a,b){for(var c=b.slice(0),d=b.length-1;d>=0;d--){var e=b[d];this.placeholder.id===e.id&&c.splice(d,1)}return c},a}),b.define("select2/dropdown/infiniteScroll",["jquery"],function(a){function b(a,b,c,d){this.lastParams={},a.call(this,b,c,d),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return b.prototype.append=function(a,b){this.$loadingMore.remove(),this.loading=!1,a.call(this,b),this.showLoadingMore(b)&&this.$results.append(this.$loadingMore)},b.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),c.on("query",function(a){e.lastParams=a,e.loading=!0}),c.on("query:append",function(a){e.lastParams=a,e.loading=!0}),this.$results.on("scroll",function(){var b=a.contains(document.documentElement,e.$loadingMore[0]);if(!e.loading&&b){var c=e.$results.offset().top+e.$results.outerHeight(!1),d=e.$loadingMore.offset().top+e.$loadingMore.outerHeight(!1);c+50>=d&&e.loadMore()}})},b.prototype.loadMore=function(){this.loading=!0;var b=a.extend({},{page:1},this.lastParams);b.page++,this.trigger("query:append",b)},b.prototype.showLoadingMore=function(a,b){return b.pagination&&b.pagination.more},b.prototype.createLoadingMore=function(){var b=a('<li class="select2-results__option select2-results__option--load-more"role="treeitem" aria-disabled="true"></li>'),c=this.options.get("translations").get("loadingMore");return b.html(c(this.lastParams)),b},b}),b.define("select2/dropdown/attachBody",["jquery","../utils"],function(a,b){function c(b,c,d){this.$dropdownParent=d.get("dropdownParent")||a(document.body),b.call(this,c,d)}return c.prototype.bind=function(a,b,c){var d=this,e=!1;a.call(this,b,c),b.on("open",function(){d._showDropdown(),d._attachPositioningHandler(b),e||(e=!0,b.on("results:all",function(){d._positionDropdown(),d._resizeDropdown()}),b.on("results:append",function(){d._positionDropdown(),d._resizeDropdown()}))}),b.on("close",function(){d._hideDropdown(),d._detachPositioningHandler(b)}),this.$dropdownContainer.on("mousedown",function(a){a.stopPropagation()})},c.prototype.destroy=function(a){a.call(this),this.$dropdownContainer.remove()},c.prototype.position=function(a,b,c){b.attr("class",c.attr("class")),b.removeClass("select2"),b.addClass("select2-container--open"),b.css({position:"absolute",top:-999999}),this.$container=c},c.prototype.render=function(b){var c=a("<span></span>"),d=b.call(this);return c.append(d),this.$dropdownContainer=c,c},c.prototype._hideDropdown=function(a){this.$dropdownContainer.detach()},c.prototype._attachPositioningHandler=function(c,d){var e=this,f="scroll.select2."+d.id,g="resize.select2."+d.id,h="orientationchange.select2."+d.id,i=this.$container.parents().filter(b.hasScroll);i.each(function(){a(this).data("select2-scroll-position",{x:a(this).scrollLeft(),y:a(this).scrollTop()})}),i.on(f,function(b){var c=a(this).data("select2-scroll-position");a(this).scrollTop(c.y)}),a(window).on(f+" "+g+" "+h,function(a){e._positionDropdown(),e._resizeDropdown()})},c.prototype._detachPositioningHandler=function(c,d){var e="scroll.select2."+d.id,f="resize.select2."+d.id,g="orientationchange.select2."+d.id,h=this.$container.parents().filter(b.hasScroll);h.off(e),a(window).off(e+" "+f+" "+g)},c.prototype._positionDropdown=function(){var b=a(window),c=this.$dropdown.hasClass("select2-dropdown--above"),d=this.$dropdown.hasClass("select2-dropdown--below"),e=null,f=this.$container.offset();f.bottom=f.top+this.$container.outerHeight(!1);var g={height:this.$container.outerHeight(!1)};g.top=f.top,g.bottom=f.top+g.height;var h={height:this.$dropdown.outerHeight(!1)},i={top:b.scrollTop(),bottom:b.scrollTop()+b.height()},j=i.top<f.top-h.height,k=i.bottom>f.bottom+h.height,l={left:f.left,top:g.bottom},m=this.$dropdownParent;"static"===m.css("position")&&(m=m.offsetParent());var n=m.offset();l.top-=n.top,l.left-=n.left,c||d||(e="below"),k||!j||c?!j&&k&&c&&(e="below"):e="above",("above"==e||c&&"below"!==e)&&(l.top=g.top-h.height),null!=e&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+e),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+e)),this.$dropdownContainer.css(l)},c.prototype._resizeDropdown=function(){var a={width:this.$container.outerWidth(!1)+"px"};this.options.get("dropdownAutoWidth")&&(a.minWidth=a.width,a.width="auto"),this.$dropdown.css(a)},c.prototype._showDropdown=function(a){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},c}),b.define("select2/dropdown/minimumResultsForSearch",[],function(){function a(b){for(var c=0,d=0;d<b.length;d++){var e=b[d];e.children?c+=a(e.children):c++}return c}function b(a,b,c,d){this.minimumResultsForSearch=c.get("minimumResultsForSearch"),this.minimumResultsForSearch<0&&(this.minimumResultsForSearch=1/0),a.call(this,b,c,d)}return b.prototype.showSearch=function(b,c){return a(c.data.results)<this.minimumResultsForSearch?!1:b.call(this,c)},b}),b.define("select2/dropdown/selectOnClose",[],function(){function a(){}return a.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),b.on("close",function(){d._handleSelectOnClose()})},a.prototype._handleSelectOnClose=function(){var a=this.getHighlightedResults();if(!(a.length<1)){var b=a.data("data");null!=b.element&&b.element.selected||null==b.element&&b.selected||this.trigger("select",{data:b})}},a}),b.define("select2/dropdown/closeOnSelect",[],function(){function a(){}return a.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),b.on("select",function(a){d._selectTriggered(a)}),b.on("unselect",function(a){d._selectTriggered(a)})},a.prototype._selectTriggered=function(a,b){var c=b.originalEvent;c&&c.ctrlKey||this.trigger("close",{})},a}),b.define("select2/i18n/en",[],function(){return{errorLoading:function(){return"The results could not be loaded."},inputTooLong:function(a){var b=a.input.length-a.maximum,c="Please delete "+b+" character";return 1!=b&&(c+="s"),c},inputTooShort:function(a){var b=a.minimum-a.input.length,c="Please enter "+b+" or more characters";return c},loadingMore:function(){return"Loading more results…"},maximumSelected:function(a){var b="You can only select "+a.maximum+" item";return 1!=a.maximum&&(b+="s"),b},noResults:function(){return"No results found"},searching:function(){return"Searching…"}}}),b.define("select2/defaults",["jquery","require","./results","./selection/single","./selection/multiple","./selection/placeholder","./selection/allowClear","./selection/search","./selection/eventRelay","./utils","./translation","./diacritics","./data/select","./data/array","./data/ajax","./data/tags","./data/tokenizer","./data/minimumInputLength","./data/maximumInputLength","./data/maximumSelectionLength","./dropdown","./dropdown/search","./dropdown/hidePlaceholder","./dropdown/infiniteScroll","./dropdown/attachBody","./dropdown/minimumResultsForSearch","./dropdown/selectOnClose","./dropdown/closeOnSelect","./i18n/en"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C){function D(){this.reset()}D.prototype.apply=function(l){if(l=a.extend(!0,{},this.defaults,l),null==l.dataAdapter){if(null!=l.ajax?l.dataAdapter=o:null!=l.data?l.dataAdapter=n:l.dataAdapter=m,l.minimumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,r)),l.maximumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,s)),l.maximumSelectionLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,t)),l.tags&&(l.dataAdapter=j.Decorate(l.dataAdapter,p)),(null!=l.tokenSeparators||null!=l.tokenizer)&&(l.dataAdapter=j.Decorate(l.dataAdapter,q)),null!=l.query){var C=b(l.amdBase+"compat/query");l.dataAdapter=j.Decorate(l.dataAdapter,C)}if(null!=l.initSelection){var D=b(l.amdBase+"compat/initSelection");l.dataAdapter=j.Decorate(l.dataAdapter,D)}}if(null==l.resultsAdapter&&(l.resultsAdapter=c,null!=l.ajax&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,x)),null!=l.placeholder&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,w)),l.selectOnClose&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,A))),null==l.dropdownAdapter){if(l.multiple)l.dropdownAdapter=u;else{var E=j.Decorate(u,v);l.dropdownAdapter=E}if(0!==l.minimumResultsForSearch&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,z)),l.closeOnSelect&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,B)),null!=l.dropdownCssClass||null!=l.dropdownCss||null!=l.adaptDropdownCssClass){var F=b(l.amdBase+"compat/dropdownCss");l.dropdownAdapter=j.Decorate(l.dropdownAdapter,F)}l.dropdownAdapter=j.Decorate(l.dropdownAdapter,y)}if(null==l.selectionAdapter){if(l.multiple?l.selectionAdapter=e:l.selectionAdapter=d,null!=l.placeholder&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,f)),l.allowClear&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,g)),l.multiple&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,h)),null!=l.containerCssClass||null!=l.containerCss||null!=l.adaptContainerCssClass){var G=b(l.amdBase+"compat/containerCss");l.selectionAdapter=j.Decorate(l.selectionAdapter,G)}l.selectionAdapter=j.Decorate(l.selectionAdapter,i)}if("string"==typeof l.language)if(l.language.indexOf("-")>0){var H=l.language.split("-"),I=H[0];l.language=[l.language,I]}else l.language=[l.language];if(a.isArray(l.language)){var J=new k;l.language.push("en");for(var K=l.language,L=0;L<K.length;L++){var M=K[L],N={};try{N=k.loadPath(M)}catch(O){try{M=this.defaults.amdLanguageBase+M,N=k.loadPath(M)}catch(P){l.debug&&window.console&&console.warn&&console.warn('Select2: The language file for "'+M+'" could not be automatically loaded. A fallback will be used instead.');continue}}J.extend(N)}l.translations=J}else{var Q=k.loadPath(this.defaults.amdLanguageBase+"en"),R=new k(l.language);R.extend(Q),l.translations=R}return l},D.prototype.reset=function(){function b(a){function b(a){return l[a]||a}return a.replace(/[^\u0000-\u007E]/g,b)}function c(d,e){if(""===a.trim(d.term))return e;if(e.children&&e.children.length>0){for(var f=a.extend(!0,{},e),g=e.children.length-1;g>=0;g--){var h=e.children[g],i=c(d,h);null==i&&f.children.splice(g,1)}return f.children.length>0?f:c(d,f)}var j=b(e.text).toUpperCase(),k=b(d.term).toUpperCase();return j.indexOf(k)>-1?e:null}this.defaults={amdBase:"./",amdLanguageBase:"./i18n/",closeOnSelect:!0,debug:!1,dropdownAutoWidth:!1,escapeMarkup:j.escapeMarkup,language:C,matcher:c,minimumInputLength:0,maximumInputLength:0,maximumSelectionLength:0,minimumResultsForSearch:0,selectOnClose:!1,sorter:function(a){return a},templateResult:function(a){return a.text},templateSelection:function(a){return a.text},theme:"default",width:"resolve"}},D.prototype.set=function(b,c){var d=a.camelCase(b),e={};e[d]=c;var f=j._convertData(e);a.extend(this.defaults,f)};var E=new D;return E}),b.define("select2/options",["require","jquery","./defaults","./utils"],function(a,b,c,d){function e(b,e){if(this.options=b,null!=e&&this.fromElement(e),this.options=c.apply(this.options),e&&e.is("input")){var f=a(this.get("amdBase")+"compat/inputData");this.options.dataAdapter=d.Decorate(this.options.dataAdapter,f)}}return e.prototype.fromElement=function(a){var c=["select2"];null==this.options.multiple&&(this.options.multiple=a.prop("multiple")),null==this.options.disabled&&(this.options.disabled=a.prop("disabled")),null==this.options.language&&(a.prop("lang")?this.options.language=a.prop("lang").toLowerCase():a.closest("[lang]").prop("lang")&&(this.options.language=a.closest("[lang]").prop("lang"))),null==this.options.dir&&(a.prop("dir")?this.options.dir=a.prop("dir"):a.closest("[dir]").prop("dir")?this.options.dir=a.closest("[dir]").prop("dir"):this.options.dir="ltr"),a.prop("disabled",this.options.disabled),a.prop("multiple",this.options.multiple),a.data("select2Tags")&&(this.options.debug&&window.console&&console.warn&&console.warn('Select2: The `data-select2-tags` attribute has been changed to use the `data-data` and `data-tags="true"` attributes and will be removed in future versions of Select2.'),a.data("data",a.data("select2Tags")),a.data("tags",!0)),a.data("ajaxUrl")&&(this.options.debug&&window.console&&console.warn&&console.warn("Select2: The `data-ajax-url` attribute has been changed to `data-ajax--url` and support for the old attribute will be removed in future versions of Select2."),a.attr("ajax--url",a.data("ajaxUrl")),a.data("ajax--url",a.data("ajaxUrl")));var e={};e=b.fn.jquery&&"1."==b.fn.jquery.substr(0,2)&&a[0].dataset?b.extend(!0,{},a[0].dataset,a.data()):a.data();var f=b.extend(!0,{},e);f=d._convertData(f);for(var g in f)b.inArray(g,c)>-1||(b.isPlainObject(this.options[g])?b.extend(this.options[g],f[g]):this.options[g]=f[g]);return this},e.prototype.get=function(a){return this.options[a]},e.prototype.set=function(a,b){this.options[a]=b},e}),b.define("select2/core",["jquery","./options","./utils","./keys"],function(a,b,c,d){var e=function(a,c){null!=a.data("select2")&&a.data("select2").destroy(),this.$element=a,this.id=this._generateId(a),c=c||{},this.options=new b(c,a),e.__super__.constructor.call(this);var d=a.attr("tabindex")||0;a.data("old-tabindex",d),a.attr("tabindex","-1");var f=this.options.get("dataAdapter");this.dataAdapter=new f(a,this.options);var g=this.render();this._placeContainer(g);var h=this.options.get("selectionAdapter");this.selection=new h(a,this.options),this.$selection=this.selection.render(),this.selection.position(this.$selection,g);var i=this.options.get("dropdownAdapter");this.dropdown=new i(a,this.options),this.$dropdown=this.dropdown.render(),this.dropdown.position(this.$dropdown,g);var j=this.options.get("resultsAdapter");this.results=new j(a,this.options,this.dataAdapter),this.$results=this.results.render(),this.results.position(this.$results,this.$dropdown);var k=this;this._bindAdapters(),this._registerDomEvents(),this._registerDataEvents(),this._registerSelectionEvents(),this._registerDropdownEvents(),this._registerResultsEvents(),this._registerEvents(),this.dataAdapter.current(function(a){k.trigger("selection:update",{data:a})}),a.addClass("select2-hidden-accessible"),a.attr("aria-hidden","true"),this._syncAttributes(),a.data("select2",this)};return c.Extend(e,c.Observable),e.prototype._generateId=function(a){var b="";return b=null!=a.attr("id")?a.attr("id"):null!=a.attr("name")?a.attr("name")+"-"+c.generateChars(2):c.generateChars(4),b=b.replace(/(:|\.|\[|\]|,)/g,""),b="select2-"+b},e.prototype._placeContainer=function(a){a.insertAfter(this.$element);var b=this._resolveWidth(this.$element,this.options.get("width"));null!=b&&a.css("width",b)},e.prototype._resolveWidth=function(a,b){var c=/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;if("resolve"==b){var d=this._resolveWidth(a,"style");return null!=d?d:this._resolveWidth(a,"element")}if("element"==b){var e=a.outerWidth(!1);return 0>=e?"auto":e+"px"}if("style"==b){var f=a.attr("style");if("string"!=typeof f)return null;for(var g=f.split(";"),h=0,i=g.length;i>h;h+=1){var j=g[h].replace(/\s/g,""),k=j.match(c);if(null!==k&&k.length>=1)return k[1]}return null}return b},e.prototype._bindAdapters=function(){this.dataAdapter.bind(this,this.$container),this.selection.bind(this,this.$container),this.dropdown.bind(this,this.$container),this.results.bind(this,this.$container)},e.prototype._registerDomEvents=function(){var b=this;this.$element.on("change.select2",function(){b.dataAdapter.current(function(a){b.trigger("selection:update",{data:a})})}),this._sync=c.bind(this._syncAttributes,this),this.$element[0].attachEvent&&this.$element[0].attachEvent("onpropertychange",this._sync);var d=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;null!=d?(this._observer=new d(function(c){a.each(c,b._sync)}),this._observer.observe(this.$element[0],{attributes:!0,subtree:!1})):this.$element[0].addEventListener&&this.$element[0].addEventListener("DOMAttrModified",b._sync,!1)},e.prototype._registerDataEvents=function(){var a=this;this.dataAdapter.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerSelectionEvents=function(){var b=this,c=["toggle","focus"];this.selection.on("toggle",function(){b.toggleDropdown()}),this.selection.on("focus",function(a){b.focus(a)}),this.selection.on("*",function(d,e){-1===a.inArray(d,c)&&b.trigger(d,e)})},e.prototype._registerDropdownEvents=function(){var a=this;this.dropdown.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerResultsEvents=function(){var a=this;this.results.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerEvents=function(){var a=this;this.on("open",function(){a.$container.addClass("select2-container--open")}),this.on("close",function(){a.$container.removeClass("select2-container--open")}),this.on("enable",function(){a.$container.removeClass("select2-container--disabled")}),this.on("disable",function(){a.$container.addClass("select2-container--disabled")}),this.on("blur",function(){a.$container.removeClass("select2-container--focus")}),this.on("query",function(b){a.isOpen()||a.trigger("open",{}),this.dataAdapter.query(b,function(c){a.trigger("results:all",{data:c,query:b})})}),this.on("query:append",function(b){this.dataAdapter.query(b,function(c){a.trigger("results:append",{data:c,query:b})})}),this.on("keypress",function(b){var c=b.which;a.isOpen()?c===d.ESC||c===d.TAB||c===d.UP&&b.altKey?(a.close(),b.preventDefault()):c===d.ENTER?(a.trigger("results:select",{}),b.preventDefault()):c===d.SPACE&&b.ctrlKey?(a.trigger("results:toggle",{}),b.preventDefault()):c===d.UP?(a.trigger("results:previous",{}),b.preventDefault()):c===d.DOWN&&(a.trigger("results:next",{}),b.preventDefault()):(c===d.ENTER||c===d.SPACE||c===d.DOWN&&b.altKey)&&(a.open(),b.preventDefault())})},e.prototype._syncAttributes=function(){this.options.set("disabled",this.$element.prop("disabled")),this.options.get("disabled")?(this.isOpen()&&this.close(),this.trigger("disable",{})):this.trigger("enable",{})},e.prototype.trigger=function(a,b){var c=e.__super__.trigger,d={open:"opening",close:"closing",select:"selecting",unselect:"unselecting"};if(void 0===b&&(b={}),a in d){var f=d[a],g={prevented:!1,name:a,args:b};if(c.call(this,f,g),g.prevented)return void(b.prevented=!0)}c.call(this,a,b)},e.prototype.toggleDropdown=function(){this.options.get("disabled")||(this.isOpen()?this.close():this.open())},e.prototype.open=function(){this.isOpen()||this.trigger("query",{})},e.prototype.close=function(){this.isOpen()&&this.trigger("close",{})},e.prototype.isOpen=function(){return this.$container.hasClass("select2-container--open")},e.prototype.hasFocus=function(){return this.$container.hasClass("select2-container--focus")},e.prototype.focus=function(a){this.hasFocus()||(this.$container.addClass("select2-container--focus"),this.trigger("focus",{}))},e.prototype.enable=function(a){this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("enable")` method has been deprecated and will be removed in later Select2 versions. Use $element.prop("disabled") instead.'),(null==a||0===a.length)&&(a=[!0]);var b=!a[0];this.$element.prop("disabled",b)},e.prototype.data=function(){this.options.get("debug")&&arguments.length>0&&window.console&&console.warn&&console.warn('Select2: Data can no longer be set using `select2("data")`. You should consider setting the value instead using `$element.val()`.');var a=[];return this.dataAdapter.current(function(b){a=b}),a},e.prototype.val=function(b){if(this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("val")` method has been deprecated and will be removed in later Select2 versions. Use $element.val() instead.'),null==b||0===b.length)return this.$element.val();var c=b[0];a.isArray(c)&&(c=a.map(c,function(a){return a.toString()})),this.$element.val(c).trigger("change")},e.prototype.destroy=function(){this.$container.remove(),this.$element[0].detachEvent&&this.$element[0].detachEvent("onpropertychange",this._sync),null!=this._observer?(this._observer.disconnect(),this._observer=null):this.$element[0].removeEventListener&&this.$element[0].removeEventListener("DOMAttrModified",this._sync,!1),this._sync=null,this.$element.off(".select2"),this.$element.attr("tabindex",this.$element.data("old-tabindex")),this.$element.removeClass("select2-hidden-accessible"),this.$element.attr("aria-hidden","false"),this.$element.removeData("select2"),this.dataAdapter.destroy(),this.selection.destroy(),this.dropdown.destroy(),this.results.destroy(),this.dataAdapter=null,this.selection=null,this.dropdown=null,this.results=null},e.prototype.render=function(){var b=a('<span class="select2 select2-container"><span class="selection"></span><span class="dropdown-wrapper" aria-hidden="true"></span></span>');return b.attr("dir",this.options.get("dir")),this.$container=b,this.$container.addClass("select2-container--"+this.options.get("theme")),b.data("element",this.$element),b},e}),b.define("jquery-mousewheel",["jquery"],function(a){return a}),b.define("jquery.select2",["jquery","jquery-mousewheel","./select2/core","./select2/defaults"],function(a,b,c,d){if(null==a.fn.select2){var e=["open","close","destroy"];a.fn.select2=function(b){if(b=b||{},"object"==typeof b)return this.each(function(){var d=a.extend(!0,{},b);new c(a(this),d)}),this;if("string"==typeof b){var d;return this.each(function(){var c=a(this).data("select2");null==c&&window.console&&console.error&&console.error("The select2('"+b+"') method was called on an element that is not using Select2.");var e=Array.prototype.slice.call(arguments,1);d=c[b].apply(c,e)}),a.inArray(b,e)>-1?this:d}throw new Error("Invalid arguments for Select2: "+b)}}return null==a.fn.select2.defaults&&(a.fn.select2.defaults=d),c}),{define:b.define,require:b.require}}(),c=b.require("jquery.select2");return a.fn.select2.amd=b,c}); -//! moment.js -//! version : 2.17.1 -//! authors : Tim Wood, Iskren Chernev, Moment.js contributors -//! license : MIT -//! momentjs.com -!function(a,b){"object"==typeof exports&&"undefined"!=typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):a.moment=b()}(this,function(){"use strict";function a(){return od.apply(null,arguments)} -// This is done to register the method called with moment() -// without creating circular dependencies. -function b(a){od=a}function c(a){return a instanceof Array||"[object Array]"===Object.prototype.toString.call(a)}function d(a){ -// IE8 will treat undefined and null as object if it wasn't for -// input != null -return null!=a&&"[object Object]"===Object.prototype.toString.call(a)}function e(a){var b;for(b in a) -// even if its not own property I'd still call it non-empty -return!1;return!0}function f(a){return"number"==typeof a||"[object Number]"===Object.prototype.toString.call(a)}function g(a){return a instanceof Date||"[object Date]"===Object.prototype.toString.call(a)}function h(a,b){var c,d=[];for(c=0;c<a.length;++c)d.push(b(a[c],c));return d}function i(a,b){return Object.prototype.hasOwnProperty.call(a,b)}function j(a,b){for(var c in b)i(b,c)&&(a[c]=b[c]);return i(b,"toString")&&(a.toString=b.toString),i(b,"valueOf")&&(a.valueOf=b.valueOf),a}function k(a,b,c,d){return rb(a,b,c,d,!0).utc()}function l(){ -// We need to deep clone this object. -return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1,parsedDateParts:[],meridiem:null}}function m(a){return null==a._pf&&(a._pf=l()),a._pf}function n(a){if(null==a._isValid){var b=m(a),c=qd.call(b.parsedDateParts,function(a){return null!=a}),d=!isNaN(a._d.getTime())&&b.overflow<0&&!b.empty&&!b.invalidMonth&&!b.invalidWeekday&&!b.nullInput&&!b.invalidFormat&&!b.userInvalidated&&(!b.meridiem||b.meridiem&&c);if(a._strict&&(d=d&&0===b.charsLeftOver&&0===b.unusedTokens.length&&void 0===b.bigHour),null!=Object.isFrozen&&Object.isFrozen(a))return d;a._isValid=d}return a._isValid}function o(a){var b=k(NaN);return null!=a?j(m(b),a):m(b).userInvalidated=!0,b}function p(a){return void 0===a}function q(a,b){var c,d,e;if(p(b._isAMomentObject)||(a._isAMomentObject=b._isAMomentObject),p(b._i)||(a._i=b._i),p(b._f)||(a._f=b._f),p(b._l)||(a._l=b._l),p(b._strict)||(a._strict=b._strict),p(b._tzm)||(a._tzm=b._tzm),p(b._isUTC)||(a._isUTC=b._isUTC),p(b._offset)||(a._offset=b._offset),p(b._pf)||(a._pf=m(b)),p(b._locale)||(a._locale=b._locale),rd.length>0)for(c in rd)d=rd[c],e=b[d],p(e)||(a[d]=e);return a} -// Moment prototype object -function r(b){q(this,b),this._d=new Date(null!=b._d?b._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)), -// Prevent infinite loop in case updateOffset creates new moment -// objects. -sd===!1&&(sd=!0,a.updateOffset(this),sd=!1)}function s(a){return a instanceof r||null!=a&&null!=a._isAMomentObject}function t(a){return a<0?Math.ceil(a)||0:Math.floor(a)}function u(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=t(b)),c} -// compare two arrays, return the number of differences -function v(a,b,c){var d,e=Math.min(a.length,b.length),f=Math.abs(a.length-b.length),g=0;for(d=0;d<e;d++)(c&&a[d]!==b[d]||!c&&u(a[d])!==u(b[d]))&&g++;return g+f}function w(b){a.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+b)}function x(b,c){var d=!0;return j(function(){if(null!=a.deprecationHandler&&a.deprecationHandler(null,b),d){for(var e,f=[],g=0;g<arguments.length;g++){if(e="","object"==typeof arguments[g]){e+="\n["+g+"] ";for(var h in arguments[0])e+=h+": "+arguments[0][h]+", ";e=e.slice(0,-2)}else e=arguments[g];f.push(e)}w(b+"\nArguments: "+Array.prototype.slice.call(f).join("")+"\n"+(new Error).stack),d=!1}return c.apply(this,arguments)},c)}function y(b,c){null!=a.deprecationHandler&&a.deprecationHandler(b,c),td[b]||(w(c),td[b]=!0)}function z(a){return a instanceof Function||"[object Function]"===Object.prototype.toString.call(a)}function A(a){var b,c;for(c in a)b=a[c],z(b)?this[c]=b:this["_"+c]=b;this._config=a, -// Lenient ordinal parsing accepts just a number in addition to -// number + (possibly) stuff coming from _ordinalParseLenient. -this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)}function B(a,b){var c,e=j({},a);for(c in b)i(b,c)&&(d(a[c])&&d(b[c])?(e[c]={},j(e[c],a[c]),j(e[c],b[c])):null!=b[c]?e[c]=b[c]:delete e[c]);for(c in a)i(a,c)&&!i(b,c)&&d(a[c])&&( -// make sure changes to properties don't modify parent config -e[c]=j({},e[c]));return e}function C(a){null!=a&&this.set(a)}function D(a,b,c){var d=this._calendar[a]||this._calendar.sameElse;return z(d)?d.call(b,c):d}function E(a){var b=this._longDateFormat[a],c=this._longDateFormat[a.toUpperCase()];return b||!c?b:(this._longDateFormat[a]=c.replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a])}function F(){return this._invalidDate}function G(a){return this._ordinal.replace("%d",a)}function H(a,b,c,d){var e=this._relativeTime[c];return z(e)?e(a,b,c,d):e.replace(/%d/i,a)}function I(a,b){var c=this._relativeTime[a>0?"future":"past"];return z(c)?c(b):c.replace(/%s/i,b)}function J(a,b){var c=a.toLowerCase();Dd[c]=Dd[c+"s"]=Dd[b]=a}function K(a){return"string"==typeof a?Dd[a]||Dd[a.toLowerCase()]:void 0}function L(a){var b,c,d={};for(c in a)i(a,c)&&(b=K(c),b&&(d[b]=a[c]));return d}function M(a,b){Ed[a]=b}function N(a){var b=[];for(var c in a)b.push({unit:c,priority:Ed[c]});return b.sort(function(a,b){return a.priority-b.priority}),b}function O(b,c){return function(d){return null!=d?(Q(this,b,d),a.updateOffset(this,c),this):P(this,b)}}function P(a,b){return a.isValid()?a._d["get"+(a._isUTC?"UTC":"")+b]():NaN}function Q(a,b,c){a.isValid()&&a._d["set"+(a._isUTC?"UTC":"")+b](c)} -// MOMENTS -function R(a){return a=K(a),z(this[a])?this[a]():this}function S(a,b){if("object"==typeof a){a=L(a);for(var c=N(a),d=0;d<c.length;d++)this[c[d].unit](a[c[d].unit])}else if(a=K(a),z(this[a]))return this[a](b);return this}function T(a,b,c){var d=""+Math.abs(a),e=b-d.length,f=a>=0;return(f?c?"+":"":"-")+Math.pow(10,Math.max(0,e)).toString().substr(1)+d} -// token: 'M' -// padded: ['MM', 2] -// ordinal: 'Mo' -// callback: function () { this.month() + 1 } -function U(a,b,c,d){var e=d;"string"==typeof d&&(e=function(){return this[d]()}),a&&(Id[a]=e),b&&(Id[b[0]]=function(){return T(e.apply(this,arguments),b[1],b[2])}),c&&(Id[c]=function(){return this.localeData().ordinal(e.apply(this,arguments),a)})}function V(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function W(a){var b,c,d=a.match(Fd);for(b=0,c=d.length;b<c;b++)Id[d[b]]?d[b]=Id[d[b]]:d[b]=V(d[b]);return function(b){var e,f="";for(e=0;e<c;e++)f+=d[e]instanceof Function?d[e].call(b,a):d[e];return f}} -// format date using native date object -function X(a,b){return a.isValid()?(b=Y(b,a.localeData()),Hd[b]=Hd[b]||W(b),Hd[b](a)):a.localeData().invalidDate()}function Y(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(Gd.lastIndex=0;d>=0&&Gd.test(a);)a=a.replace(Gd,c),Gd.lastIndex=0,d-=1;return a}function Z(a,b,c){$d[a]=z(b)?b:function(a,d){return a&&c?c:b}}function $(a,b){return i($d,a)?$d[a](b._strict,b._locale):new RegExp(_(a))} -// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript -function _(a){return aa(a.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e}))}function aa(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function ba(a,b){var c,d=b;for("string"==typeof a&&(a=[a]),f(b)&&(d=function(a,c){c[b]=u(a)}),c=0;c<a.length;c++)_d[a[c]]=d}function ca(a,b){ba(a,function(a,c,d,e){d._w=d._w||{},b(a,d._w,d,e)})}function da(a,b,c){null!=b&&i(_d,a)&&_d[a](b,c._a,c,a)}function ea(a,b){return new Date(Date.UTC(a,b+1,0)).getUTCDate()}function fa(a,b){return a?c(this._months)?this._months[a.month()]:this._months[(this._months.isFormat||ke).test(b)?"format":"standalone"][a.month()]:this._months}function ga(a,b){return a?c(this._monthsShort)?this._monthsShort[a.month()]:this._monthsShort[ke.test(b)?"format":"standalone"][a.month()]:this._monthsShort}function ha(a,b,c){var d,e,f,g=a.toLocaleLowerCase();if(!this._monthsParse)for( -// this is not used -this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[],d=0;d<12;++d)f=k([2e3,d]),this._shortMonthsParse[d]=this.monthsShort(f,"").toLocaleLowerCase(),this._longMonthsParse[d]=this.months(f,"").toLocaleLowerCase();return c?"MMM"===b?(e=je.call(this._shortMonthsParse,g),e!==-1?e:null):(e=je.call(this._longMonthsParse,g),e!==-1?e:null):"MMM"===b?(e=je.call(this._shortMonthsParse,g),e!==-1?e:(e=je.call(this._longMonthsParse,g),e!==-1?e:null)):(e=je.call(this._longMonthsParse,g),e!==-1?e:(e=je.call(this._shortMonthsParse,g),e!==-1?e:null))}function ia(a,b,c){var d,e,f;if(this._monthsParseExact)return ha.call(this,a,b,c); -// TODO: add sorting -// Sorting makes sure if one month (or abbr) is a prefix of another -// see sorting in computeMonthsParse -for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),d=0;d<12;d++){ -// test the regex -if( -// make the regex if we don't have it already -e=k([2e3,d]),c&&!this._longMonthsParse[d]&&(this._longMonthsParse[d]=new RegExp("^"+this.months(e,"").replace(".","")+"$","i"),this._shortMonthsParse[d]=new RegExp("^"+this.monthsShort(e,"").replace(".","")+"$","i")),c||this._monthsParse[d]||(f="^"+this.months(e,"")+"|^"+this.monthsShort(e,""),this._monthsParse[d]=new RegExp(f.replace(".",""),"i")),c&&"MMMM"===b&&this._longMonthsParse[d].test(a))return d;if(c&&"MMM"===b&&this._shortMonthsParse[d].test(a))return d;if(!c&&this._monthsParse[d].test(a))return d}} -// MOMENTS -function ja(a,b){var c;if(!a.isValid()) -// No op -return a;if("string"==typeof b)if(/^\d+$/.test(b))b=u(b);else -// TODO: Another silent failure? -if(b=a.localeData().monthsParse(b),!f(b))return a;return c=Math.min(a.date(),ea(a.year(),b)),a._d["set"+(a._isUTC?"UTC":"")+"Month"](b,c),a}function ka(b){return null!=b?(ja(this,b),a.updateOffset(this,!0),this):P(this,"Month")}function la(){return ea(this.year(),this.month())}function ma(a){return this._monthsParseExact?(i(this,"_monthsRegex")||oa.call(this),a?this._monthsShortStrictRegex:this._monthsShortRegex):(i(this,"_monthsShortRegex")||(this._monthsShortRegex=ne),this._monthsShortStrictRegex&&a?this._monthsShortStrictRegex:this._monthsShortRegex)}function na(a){return this._monthsParseExact?(i(this,"_monthsRegex")||oa.call(this),a?this._monthsStrictRegex:this._monthsRegex):(i(this,"_monthsRegex")||(this._monthsRegex=oe),this._monthsStrictRegex&&a?this._monthsStrictRegex:this._monthsRegex)}function oa(){function a(a,b){return b.length-a.length}var b,c,d=[],e=[],f=[];for(b=0;b<12;b++) -// make the regex if we don't have it already -c=k([2e3,b]),d.push(this.monthsShort(c,"")),e.push(this.months(c,"")),f.push(this.months(c,"")),f.push(this.monthsShort(c,""));for( -// Sorting makes sure if one month (or abbr) is a prefix of another it -// will match the longer piece. -d.sort(a),e.sort(a),f.sort(a),b=0;b<12;b++)d[b]=aa(d[b]),e[b]=aa(e[b]);for(b=0;b<24;b++)f[b]=aa(f[b]);this._monthsRegex=new RegExp("^("+f.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+e.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+d.join("|")+")","i")} -// HELPERS -function pa(a){return qa(a)?366:365}function qa(a){return a%4===0&&a%100!==0||a%400===0}function ra(){return qa(this.year())}function sa(a,b,c,d,e,f,g){ -//can't just apply() to create a date: -//http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply -var h=new Date(a,b,c,d,e,f,g); -//the date constructor remaps years 0-99 to 1900-1999 -return a<100&&a>=0&&isFinite(h.getFullYear())&&h.setFullYear(a),h}function ta(a){var b=new Date(Date.UTC.apply(null,arguments)); -//the Date.UTC function remaps years 0-99 to 1900-1999 -return a<100&&a>=0&&isFinite(b.getUTCFullYear())&&b.setUTCFullYear(a),b} -// start-of-first-week - start-of-year -function ua(a,b,c){var// first-week day -- which january is always in the first week (4 for iso, 1 for other) -d=7+b-c, -// first-week day local weekday -- which local weekday is fwd -e=(7+ta(a,0,d).getUTCDay()-b)%7;return-e+d-1} -//http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday -function va(a,b,c,d,e){var f,g,h=(7+c-d)%7,i=ua(a,d,e),j=1+7*(b-1)+h+i;return j<=0?(f=a-1,g=pa(f)+j):j>pa(a)?(f=a+1,g=j-pa(a)):(f=a,g=j),{year:f,dayOfYear:g}}function wa(a,b,c){var d,e,f=ua(a.year(),b,c),g=Math.floor((a.dayOfYear()-f-1)/7)+1;return g<1?(e=a.year()-1,d=g+xa(e,b,c)):g>xa(a.year(),b,c)?(d=g-xa(a.year(),b,c),e=a.year()+1):(e=a.year(),d=g),{week:d,year:e}}function xa(a,b,c){var d=ua(a,b,c),e=ua(a+1,b,c);return(pa(a)-d+e)/7} -// HELPERS -// LOCALES -function ya(a){return wa(a,this._week.dow,this._week.doy).week}function za(){return this._week.dow}function Aa(){return this._week.doy} -// MOMENTS -function Ba(a){var b=this.localeData().week(this);return null==a?b:this.add(7*(a-b),"d")}function Ca(a){var b=wa(this,1,4).week;return null==a?b:this.add(7*(a-b),"d")} -// HELPERS -function Da(a,b){return"string"!=typeof a?a:isNaN(a)?(a=b.weekdaysParse(a),"number"==typeof a?a:null):parseInt(a,10)}function Ea(a,b){return"string"==typeof a?b.weekdaysParse(a)%7||7:isNaN(a)?null:a}function Fa(a,b){return a?c(this._weekdays)?this._weekdays[a.day()]:this._weekdays[this._weekdays.isFormat.test(b)?"format":"standalone"][a.day()]:this._weekdays}function Ga(a){return a?this._weekdaysShort[a.day()]:this._weekdaysShort}function Ha(a){return a?this._weekdaysMin[a.day()]:this._weekdaysMin}function Ia(a,b,c){var d,e,f,g=a.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],d=0;d<7;++d)f=k([2e3,1]).day(d),this._minWeekdaysParse[d]=this.weekdaysMin(f,"").toLocaleLowerCase(),this._shortWeekdaysParse[d]=this.weekdaysShort(f,"").toLocaleLowerCase(),this._weekdaysParse[d]=this.weekdays(f,"").toLocaleLowerCase();return c?"dddd"===b?(e=je.call(this._weekdaysParse,g),e!==-1?e:null):"ddd"===b?(e=je.call(this._shortWeekdaysParse,g),e!==-1?e:null):(e=je.call(this._minWeekdaysParse,g),e!==-1?e:null):"dddd"===b?(e=je.call(this._weekdaysParse,g),e!==-1?e:(e=je.call(this._shortWeekdaysParse,g),e!==-1?e:(e=je.call(this._minWeekdaysParse,g),e!==-1?e:null))):"ddd"===b?(e=je.call(this._shortWeekdaysParse,g),e!==-1?e:(e=je.call(this._weekdaysParse,g),e!==-1?e:(e=je.call(this._minWeekdaysParse,g),e!==-1?e:null))):(e=je.call(this._minWeekdaysParse,g),e!==-1?e:(e=je.call(this._weekdaysParse,g),e!==-1?e:(e=je.call(this._shortWeekdaysParse,g),e!==-1?e:null)))}function Ja(a,b,c){var d,e,f;if(this._weekdaysParseExact)return Ia.call(this,a,b,c);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),d=0;d<7;d++){ -// test the regex -if( -// make the regex if we don't have it already -e=k([2e3,1]).day(d),c&&!this._fullWeekdaysParse[d]&&(this._fullWeekdaysParse[d]=new RegExp("^"+this.weekdays(e,"").replace(".",".?")+"$","i"),this._shortWeekdaysParse[d]=new RegExp("^"+this.weekdaysShort(e,"").replace(".",".?")+"$","i"),this._minWeekdaysParse[d]=new RegExp("^"+this.weekdaysMin(e,"").replace(".",".?")+"$","i")),this._weekdaysParse[d]||(f="^"+this.weekdays(e,"")+"|^"+this.weekdaysShort(e,"")+"|^"+this.weekdaysMin(e,""),this._weekdaysParse[d]=new RegExp(f.replace(".",""),"i")),c&&"dddd"===b&&this._fullWeekdaysParse[d].test(a))return d;if(c&&"ddd"===b&&this._shortWeekdaysParse[d].test(a))return d;if(c&&"dd"===b&&this._minWeekdaysParse[d].test(a))return d;if(!c&&this._weekdaysParse[d].test(a))return d}} -// MOMENTS -function Ka(a){if(!this.isValid())return null!=a?this:NaN;var b=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=a?(a=Da(a,this.localeData()),this.add(a-b,"d")):b}function La(a){if(!this.isValid())return null!=a?this:NaN;var b=(this.day()+7-this.localeData()._week.dow)%7;return null==a?b:this.add(a-b,"d")}function Ma(a){if(!this.isValid())return null!=a?this:NaN; -// behaves the same as moment#day except -// as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) -// as a setter, sunday should belong to the previous week. -if(null!=a){var b=Ea(a,this.localeData());return this.day(this.day()%7?b:b-7)}return this.day()||7}function Na(a){return this._weekdaysParseExact?(i(this,"_weekdaysRegex")||Qa.call(this),a?this._weekdaysStrictRegex:this._weekdaysRegex):(i(this,"_weekdaysRegex")||(this._weekdaysRegex=ue),this._weekdaysStrictRegex&&a?this._weekdaysStrictRegex:this._weekdaysRegex)}function Oa(a){return this._weekdaysParseExact?(i(this,"_weekdaysRegex")||Qa.call(this),a?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(i(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=ve),this._weekdaysShortStrictRegex&&a?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)}function Pa(a){return this._weekdaysParseExact?(i(this,"_weekdaysRegex")||Qa.call(this),a?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(i(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=we),this._weekdaysMinStrictRegex&&a?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)}function Qa(){function a(a,b){return b.length-a.length}var b,c,d,e,f,g=[],h=[],i=[],j=[];for(b=0;b<7;b++) -// make the regex if we don't have it already -c=k([2e3,1]).day(b),d=this.weekdaysMin(c,""),e=this.weekdaysShort(c,""),f=this.weekdays(c,""),g.push(d),h.push(e),i.push(f),j.push(d),j.push(e),j.push(f);for( -// Sorting makes sure if one weekday (or abbr) is a prefix of another it -// will match the longer piece. -g.sort(a),h.sort(a),i.sort(a),j.sort(a),b=0;b<7;b++)h[b]=aa(h[b]),i[b]=aa(i[b]),j[b]=aa(j[b]);this._weekdaysRegex=new RegExp("^("+j.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+h.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+g.join("|")+")","i")} -// FORMATTING -function Ra(){return this.hours()%12||12}function Sa(){return this.hours()||24}function Ta(a,b){U(a,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),b)})} -// PARSING -function Ua(a,b){return b._meridiemParse} -// LOCALES -function Va(a){ -// IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays -// Using charAt should be more compatible. -return"p"===(a+"").toLowerCase().charAt(0)}function Wa(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"}function Xa(a){return a?a.toLowerCase().replace("_","-"):a} -// pick the locale from the array -// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each -// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root -function Ya(a){for(var b,c,d,e,f=0;f<a.length;){for(e=Xa(a[f]).split("-"),b=e.length,c=Xa(a[f+1]),c=c?c.split("-"):null;b>0;){if(d=Za(e.slice(0,b).join("-")))return d;if(c&&c.length>=b&&v(e,c,!0)>=b-1) -//the next array item is better than a shallower substring of this one -break;b--}f++}return null}function Za(a){var b=null; -// TODO: Find a better way to register and load all the locales in Node -if(!Be[a]&&"undefined"!=typeof module&&module&&module.exports)try{b=xe._abbr,require("./locale/"+a), -// because defineLocale currently also sets the global locale, we -// want to undo that for lazy loaded locales -$a(b)}catch(a){}return Be[a]} -// This function will load locale and then set the global locale. If -// no arguments are passed in, it will simply return the current global -// locale key. -function $a(a,b){var c; -// moment.duration._locale = moment._locale = data; -return a&&(c=p(b)?bb(a):_a(a,b),c&&(xe=c)),xe._abbr}function _a(a,b){if(null!==b){var c=Ae;if(b.abbr=a,null!=Be[a])y("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),c=Be[a]._config;else if(null!=b.parentLocale){if(null==Be[b.parentLocale])return Ce[b.parentLocale]||(Ce[b.parentLocale]=[]),Ce[b.parentLocale].push({name:a,config:b}),null;c=Be[b.parentLocale]._config} -// backwards compat for now: also set the locale -// make sure we set the locale AFTER all child locales have been -// created, so we won't end up with the child locale set. -return Be[a]=new C(B(c,b)),Ce[a]&&Ce[a].forEach(function(a){_a(a.name,a.config)}),$a(a),Be[a]} -// useful for testing -return delete Be[a],null}function ab(a,b){if(null!=b){var c,d=Ae; -// MERGE -null!=Be[a]&&(d=Be[a]._config),b=B(d,b),c=new C(b),c.parentLocale=Be[a],Be[a]=c, -// backwards compat for now: also set the locale -$a(a)}else -// pass null for config to unupdate, useful for tests -null!=Be[a]&&(null!=Be[a].parentLocale?Be[a]=Be[a].parentLocale:null!=Be[a]&&delete Be[a]);return Be[a]} -// returns locale data -function bb(a){var b;if(a&&a._locale&&a._locale._abbr&&(a=a._locale._abbr),!a)return xe;if(!c(a)){if( -//short-circuit everything else -b=Za(a))return b;a=[a]}return Ya(a)}function cb(){return wd(Be)}function db(a){var b,c=a._a;return c&&m(a).overflow===-2&&(b=c[be]<0||c[be]>11?be:c[ce]<1||c[ce]>ea(c[ae],c[be])?ce:c[de]<0||c[de]>24||24===c[de]&&(0!==c[ee]||0!==c[fe]||0!==c[ge])?de:c[ee]<0||c[ee]>59?ee:c[fe]<0||c[fe]>59?fe:c[ge]<0||c[ge]>999?ge:-1,m(a)._overflowDayOfYear&&(b<ae||b>ce)&&(b=ce),m(a)._overflowWeeks&&b===-1&&(b=he),m(a)._overflowWeekday&&b===-1&&(b=ie),m(a).overflow=b),a} -// date from iso format -function eb(a){var b,c,d,e,f,g,h=a._i,i=De.exec(h)||Ee.exec(h);if(i){for(m(a).iso=!0,b=0,c=Ge.length;b<c;b++)if(Ge[b][1].exec(i[1])){e=Ge[b][0],d=Ge[b][2]!==!1;break}if(null==e)return void(a._isValid=!1);if(i[3]){for(b=0,c=He.length;b<c;b++)if(He[b][1].exec(i[3])){ -// match[2] should be 'T' or space -f=(i[2]||" ")+He[b][0];break}if(null==f)return void(a._isValid=!1)}if(!d&&null!=f)return void(a._isValid=!1);if(i[4]){if(!Fe.exec(i[4]))return void(a._isValid=!1);g="Z"}a._f=e+(f||"")+(g||""),kb(a)}else a._isValid=!1} -// date from iso format or fallback -function fb(b){var c=Ie.exec(b._i);return null!==c?void(b._d=new Date(+c[1])):(eb(b),void(b._isValid===!1&&(delete b._isValid,a.createFromInputFallback(b))))} -// Pick the first defined of two or three arguments. -function gb(a,b,c){return null!=a?a:null!=b?b:c}function hb(b){ -// hooks is actually the exported moment object -var c=new Date(a.now());return b._useUTC?[c.getUTCFullYear(),c.getUTCMonth(),c.getUTCDate()]:[c.getFullYear(),c.getMonth(),c.getDate()]} -// convert an array to a date. -// the array should mirror the parameters below -// note: all values past the year are optional and will default to the lowest possible value. -// [year, month, day , hour, minute, second, millisecond] -function ib(a){var b,c,d,e,f=[];if(!a._d){ -// Default to current date. -// * if no year, month, day of month are given, default to today -// * if day of month is given, default month and year -// * if month is given, default only year -// * if year is given, don't default anything -for(d=hb(a), -//compute day of the year from weeks and weekdays -a._w&&null==a._a[ce]&&null==a._a[be]&&jb(a), -//if the day of the year is set, figure out what it is -a._dayOfYear&&(e=gb(a._a[ae],d[ae]),a._dayOfYear>pa(e)&&(m(a)._overflowDayOfYear=!0),c=ta(e,0,a._dayOfYear),a._a[be]=c.getUTCMonth(),a._a[ce]=c.getUTCDate()),b=0;b<3&&null==a._a[b];++b)a._a[b]=f[b]=d[b]; -// Zero out whatever was not defaulted, including time -for(;b<7;b++)a._a[b]=f[b]=null==a._a[b]?2===b?1:0:a._a[b]; -// Check for 24:00:00.000 -24===a._a[de]&&0===a._a[ee]&&0===a._a[fe]&&0===a._a[ge]&&(a._nextDay=!0,a._a[de]=0),a._d=(a._useUTC?ta:sa).apply(null,f), -// Apply timezone offset from input. The actual utcOffset can be changed -// with parseZone. -null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()-a._tzm),a._nextDay&&(a._a[de]=24)}}function jb(a){var b,c,d,e,f,g,h,i;if(b=a._w,null!=b.GG||null!=b.W||null!=b.E)f=1,g=4, -// TODO: We need to take the current isoWeekYear, but that depends on -// how we interpret now (local, utc, fixed offset). So create -// a now version of current config (take local/utc/offset flags, and -// create now). -c=gb(b.GG,a._a[ae],wa(sb(),1,4).year),d=gb(b.W,1),e=gb(b.E,1),(e<1||e>7)&&(i=!0);else{f=a._locale._week.dow,g=a._locale._week.doy;var j=wa(sb(),f,g);c=gb(b.gg,a._a[ae],j.year), -// Default to current week. -d=gb(b.w,j.week),null!=b.d?( -// weekday -- low day numbers are considered next week -e=b.d,(e<0||e>6)&&(i=!0)):null!=b.e?( -// local weekday -- counting starts from begining of week -e=b.e+f,(b.e<0||b.e>6)&&(i=!0)): -// default to begining of week -e=f}d<1||d>xa(c,f,g)?m(a)._overflowWeeks=!0:null!=i?m(a)._overflowWeekday=!0:(h=va(c,d,e,f,g),a._a[ae]=h.year,a._dayOfYear=h.dayOfYear)} -// date from string and format string -function kb(b){ -// TODO: Move this to another part of the creation flow to prevent circular deps -if(b._f===a.ISO_8601)return void eb(b);b._a=[],m(b).empty=!0; -// This array is used to make a Date, either with `new Date` or `Date.UTC` -var c,d,e,f,g,h=""+b._i,i=h.length,j=0;for(e=Y(b._f,b._locale).match(Fd)||[],c=0;c<e.length;c++)f=e[c],d=(h.match($(f,b))||[])[0], -// console.log('token', token, 'parsedInput', parsedInput, -// 'regex', getParseRegexForToken(token, config)); -d&&(g=h.substr(0,h.indexOf(d)),g.length>0&&m(b).unusedInput.push(g),h=h.slice(h.indexOf(d)+d.length),j+=d.length), -// don't parse if it's not a known token -Id[f]?(d?m(b).empty=!1:m(b).unusedTokens.push(f),da(f,d,b)):b._strict&&!d&&m(b).unusedTokens.push(f); -// add remaining unparsed input length to the string -m(b).charsLeftOver=i-j,h.length>0&&m(b).unusedInput.push(h), -// clear _12h flag if hour is <= 12 -b._a[de]<=12&&m(b).bigHour===!0&&b._a[de]>0&&(m(b).bigHour=void 0),m(b).parsedDateParts=b._a.slice(0),m(b).meridiem=b._meridiem, -// handle meridiem -b._a[de]=lb(b._locale,b._a[de],b._meridiem),ib(b),db(b)}function lb(a,b,c){var d; -// Fallback -return null==c?b:null!=a.meridiemHour?a.meridiemHour(b,c):null!=a.isPM?(d=a.isPM(c),d&&b<12&&(b+=12),d||12!==b||(b=0),b):b} -// date from string and array of format strings -function mb(a){var b,c,d,e,f;if(0===a._f.length)return m(a).invalidFormat=!0,void(a._d=new Date(NaN));for(e=0;e<a._f.length;e++)f=0,b=q({},a),null!=a._useUTC&&(b._useUTC=a._useUTC),b._f=a._f[e],kb(b),n(b)&&( -// if there is any input that was not parsed add a penalty for that format -f+=m(b).charsLeftOver, -//or tokens -f+=10*m(b).unusedTokens.length,m(b).score=f,(null==d||f<d)&&(d=f,c=b));j(a,c||b)}function nb(a){if(!a._d){var b=L(a._i);a._a=h([b.year,b.month,b.day||b.date,b.hour,b.minute,b.second,b.millisecond],function(a){return a&&parseInt(a,10)}),ib(a)}}function ob(a){var b=new r(db(pb(a))); -// Adding is smart enough around DST -return b._nextDay&&(b.add(1,"d"),b._nextDay=void 0),b}function pb(a){var b=a._i,d=a._f;return a._locale=a._locale||bb(a._l),null===b||void 0===d&&""===b?o({nullInput:!0}):("string"==typeof b&&(a._i=b=a._locale.preparse(b)),s(b)?new r(db(b)):(g(b)?a._d=b:c(d)?mb(a):d?kb(a):qb(a),n(a)||(a._d=null),a))}function qb(b){var d=b._i;void 0===d?b._d=new Date(a.now()):g(d)?b._d=new Date(d.valueOf()):"string"==typeof d?fb(b):c(d)?(b._a=h(d.slice(0),function(a){return parseInt(a,10)}),ib(b)):"object"==typeof d?nb(b):f(d)? -// from milliseconds -b._d=new Date(d):a.createFromInputFallback(b)}function rb(a,b,f,g,h){var i={}; -// object construction must be done this way. -// https://github.com/moment/moment/issues/1423 -return f!==!0&&f!==!1||(g=f,f=void 0),(d(a)&&e(a)||c(a)&&0===a.length)&&(a=void 0),i._isAMomentObject=!0,i._useUTC=i._isUTC=h,i._l=f,i._i=a,i._f=b,i._strict=g,ob(i)}function sb(a,b,c,d){return rb(a,b,c,d,!1)} -// Pick a moment m from moments so that m[fn](other) is true for all -// other. This relies on the function fn to be transitive. -// -// moments should either be an array of moment objects or an array, whose -// first element is an array of moment objects. -function tb(a,b){var d,e;if(1===b.length&&c(b[0])&&(b=b[0]),!b.length)return sb();for(d=b[0],e=1;e<b.length;++e)b[e].isValid()&&!b[e][a](d)||(d=b[e]);return d} -// TODO: Use [].sort instead? -function ub(){var a=[].slice.call(arguments,0);return tb("isBefore",a)}function vb(){var a=[].slice.call(arguments,0);return tb("isAfter",a)}function wb(a){var b=L(a),c=b.year||0,d=b.quarter||0,e=b.month||0,f=b.week||0,g=b.day||0,h=b.hour||0,i=b.minute||0,j=b.second||0,k=b.millisecond||0; -// representation for dateAddRemove -this._milliseconds=+k+1e3*j+// 1000 -6e4*i+// 1000 * 60 -1e3*h*60*60,//using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 -// Because of dateAddRemove treats 24 hours as different from a -// day when working around DST, we need to store them separately -this._days=+g+7*f, -// It is impossible translate months into days without knowing -// which months you are are talking about, so we have to store -// it separately. -this._months=+e+3*d+12*c,this._data={},this._locale=bb(),this._bubble()}function xb(a){return a instanceof wb}function yb(a){return a<0?Math.round(-1*a)*-1:Math.round(a)} -// FORMATTING -function zb(a,b){U(a,0,0,function(){var a=this.utcOffset(),c="+";return a<0&&(a=-a,c="-"),c+T(~~(a/60),2)+b+T(~~a%60,2)})}function Ab(a,b){var c=(b||"").match(a);if(null===c)return null;var d=c[c.length-1]||[],e=(d+"").match(Me)||["-",0,0],f=+(60*e[1])+u(e[2]);return 0===f?0:"+"===e[0]?f:-f} -// Return a moment from input, that is local/utc/zone equivalent to model. -function Bb(b,c){var d,e; -// Use low-level api, because this fn is low-level api. -return c._isUTC?(d=c.clone(),e=(s(b)||g(b)?b.valueOf():sb(b).valueOf())-d.valueOf(),d._d.setTime(d._d.valueOf()+e),a.updateOffset(d,!1),d):sb(b).local()}function Cb(a){ -// On Firefox.24 Date#getTimezoneOffset returns a floating point. -// https://github.com/moment/moment/pull/1871 -return 15*-Math.round(a._d.getTimezoneOffset()/15)} -// MOMENTS -// keepLocalTime = true means only change the timezone, without -// affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> -// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset -// +0200, so we adjust the time as needed, to be valid. -// -// Keeping the time actually adds/subtracts (one hour) -// from the actual represented time. That is why we call updateOffset -// a second time. In case it wants us to change the offset again -// _changeInProgress == true case, then we have to adjust, because -// there is no such time in the given timezone. -function Db(b,c){var d,e=this._offset||0;if(!this.isValid())return null!=b?this:NaN;if(null!=b){if("string"==typeof b){if(b=Ab(Xd,b),null===b)return this}else Math.abs(b)<16&&(b=60*b);return!this._isUTC&&c&&(d=Cb(this)),this._offset=b,this._isUTC=!0,null!=d&&this.add(d,"m"),e!==b&&(!c||this._changeInProgress?Tb(this,Ob(b-e,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,a.updateOffset(this,!0),this._changeInProgress=null)),this}return this._isUTC?e:Cb(this)}function Eb(a,b){return null!=a?("string"!=typeof a&&(a=-a),this.utcOffset(a,b),this):-this.utcOffset()}function Fb(a){return this.utcOffset(0,a)}function Gb(a){return this._isUTC&&(this.utcOffset(0,a),this._isUTC=!1,a&&this.subtract(Cb(this),"m")),this}function Hb(){if(null!=this._tzm)this.utcOffset(this._tzm);else if("string"==typeof this._i){var a=Ab(Wd,this._i);null!=a?this.utcOffset(a):this.utcOffset(0,!0)}return this}function Ib(a){return!!this.isValid()&&(a=a?sb(a).utcOffset():0,(this.utcOffset()-a)%60===0)}function Jb(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function Kb(){if(!p(this._isDSTShifted))return this._isDSTShifted;var a={};if(q(a,this),a=pb(a),a._a){var b=a._isUTC?k(a._a):sb(a._a);this._isDSTShifted=this.isValid()&&v(a._a,b.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}function Lb(){return!!this.isValid()&&!this._isUTC}function Mb(){return!!this.isValid()&&this._isUTC}function Nb(){return!!this.isValid()&&(this._isUTC&&0===this._offset)}function Ob(a,b){var c,d,e,g=a, -// matching against regexp is expensive, do it on demand -h=null;// checks for null or undefined -return xb(a)?g={ms:a._milliseconds,d:a._days,M:a._months}:f(a)?(g={},b?g[b]=a:g.milliseconds=a):(h=Ne.exec(a))?(c="-"===h[1]?-1:1,g={y:0,d:u(h[ce])*c,h:u(h[de])*c,m:u(h[ee])*c,s:u(h[fe])*c,ms:u(yb(1e3*h[ge]))*c}):(h=Oe.exec(a))?(c="-"===h[1]?-1:1,g={y:Pb(h[2],c),M:Pb(h[3],c),w:Pb(h[4],c),d:Pb(h[5],c),h:Pb(h[6],c),m:Pb(h[7],c),s:Pb(h[8],c)}):null==g?g={}:"object"==typeof g&&("from"in g||"to"in g)&&(e=Rb(sb(g.from),sb(g.to)),g={},g.ms=e.milliseconds,g.M=e.months),d=new wb(g),xb(a)&&i(a,"_locale")&&(d._locale=a._locale),d}function Pb(a,b){ -// We'd normally use ~~inp for this, but unfortunately it also -// converts floats to ints. -// inp may be undefined, so careful calling replace on it. -var c=a&&parseFloat(a.replace(",",".")); -// apply sign while we're at it -return(isNaN(c)?0:c)*b}function Qb(a,b){var c={milliseconds:0,months:0};return c.months=b.month()-a.month()+12*(b.year()-a.year()),a.clone().add(c.months,"M").isAfter(b)&&--c.months,c.milliseconds=+b-+a.clone().add(c.months,"M"),c}function Rb(a,b){var c;return a.isValid()&&b.isValid()?(b=Bb(b,a),a.isBefore(b)?c=Qb(a,b):(c=Qb(b,a),c.milliseconds=-c.milliseconds,c.months=-c.months),c):{milliseconds:0,months:0}} -// TODO: remove 'name' arg after deprecation is removed -function Sb(a,b){return function(c,d){var e,f; -//invert the arguments, but complain about it -return null===d||isNaN(+d)||(y(b,"moment()."+b+"(period, number) is deprecated. Please use moment()."+b+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),f=c,c=d,d=f),c="string"==typeof c?+c:c,e=Ob(c,d),Tb(this,e,a),this}}function Tb(b,c,d,e){var f=c._milliseconds,g=yb(c._days),h=yb(c._months);b.isValid()&&(e=null==e||e,f&&b._d.setTime(b._d.valueOf()+f*d),g&&Q(b,"Date",P(b,"Date")+g*d),h&&ja(b,P(b,"Month")+h*d),e&&a.updateOffset(b,g||h))}function Ub(a,b){var c=a.diff(b,"days",!0);return c<-6?"sameElse":c<-1?"lastWeek":c<0?"lastDay":c<1?"sameDay":c<2?"nextDay":c<7?"nextWeek":"sameElse"}function Vb(b,c){ -// We want to compare the start of today, vs this. -// Getting start-of-today depends on whether we're local/utc/offset or not. -var d=b||sb(),e=Bb(d,this).startOf("day"),f=a.calendarFormat(this,e)||"sameElse",g=c&&(z(c[f])?c[f].call(this,d):c[f]);return this.format(g||this.localeData().calendar(f,this,sb(d)))}function Wb(){return new r(this)}function Xb(a,b){var c=s(a)?a:sb(a);return!(!this.isValid()||!c.isValid())&&(b=K(p(b)?"millisecond":b),"millisecond"===b?this.valueOf()>c.valueOf():c.valueOf()<this.clone().startOf(b).valueOf())}function Yb(a,b){var c=s(a)?a:sb(a);return!(!this.isValid()||!c.isValid())&&(b=K(p(b)?"millisecond":b),"millisecond"===b?this.valueOf()<c.valueOf():this.clone().endOf(b).valueOf()<c.valueOf())}function Zb(a,b,c,d){return d=d||"()",("("===d[0]?this.isAfter(a,c):!this.isBefore(a,c))&&(")"===d[1]?this.isBefore(b,c):!this.isAfter(b,c))}function $b(a,b){var c,d=s(a)?a:sb(a);return!(!this.isValid()||!d.isValid())&&(b=K(b||"millisecond"),"millisecond"===b?this.valueOf()===d.valueOf():(c=d.valueOf(),this.clone().startOf(b).valueOf()<=c&&c<=this.clone().endOf(b).valueOf()))}function _b(a,b){return this.isSame(a,b)||this.isAfter(a,b)}function ac(a,b){return this.isSame(a,b)||this.isBefore(a,b)}function bc(a,b,c){var d,e,f,g;// 1000 -// 1000 * 60 -// 1000 * 60 * 60 -// 1000 * 60 * 60 * 24, negate dst -// 1000 * 60 * 60 * 24 * 7, negate dst -return this.isValid()?(d=Bb(a,this),d.isValid()?(e=6e4*(d.utcOffset()-this.utcOffset()),b=K(b),"year"===b||"month"===b||"quarter"===b?(g=cc(this,d),"quarter"===b?g/=3:"year"===b&&(g/=12)):(f=this-d,g="second"===b?f/1e3:"minute"===b?f/6e4:"hour"===b?f/36e5:"day"===b?(f-e)/864e5:"week"===b?(f-e)/6048e5:f),c?g:t(g)):NaN):NaN}function cc(a,b){ -// difference in months -var c,d,e=12*(b.year()-a.year())+(b.month()-a.month()), -// b is in (anchor - 1 month, anchor + 1 month) -f=a.clone().add(e,"months"); -//check for negative zero, return zero if negative zero -// linear across the month -// linear across the month -return b-f<0?(c=a.clone().add(e-1,"months"),d=(b-f)/(f-c)):(c=a.clone().add(e+1,"months"),d=(b-f)/(c-f)),-(e+d)||0}function dc(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")}function ec(){var a=this.clone().utc();return 0<a.year()&&a.year()<=9999?z(Date.prototype.toISOString)?this.toDate().toISOString():X(a,"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]"):X(a,"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]")}/** - * Return a human readable representation of a moment that can - * also be evaluated to get a new moment which is the same - * - * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects - */ -function fc(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var a="moment",b="";this.isLocal()||(a=0===this.utcOffset()?"moment.utc":"moment.parseZone",b="Z");var c="["+a+'("]',d=0<this.year()&&this.year()<=9999?"YYYY":"YYYYYY",e="-MM-DD[T]HH:mm:ss.SSS",f=b+'[")]';return this.format(c+d+e+f)}function gc(b){b||(b=this.isUtc()?a.defaultFormatUtc:a.defaultFormat);var c=X(this,b);return this.localeData().postformat(c)}function hc(a,b){return this.isValid()&&(s(a)&&a.isValid()||sb(a).isValid())?Ob({to:this,from:a}).locale(this.locale()).humanize(!b):this.localeData().invalidDate()}function ic(a){return this.from(sb(),a)}function jc(a,b){return this.isValid()&&(s(a)&&a.isValid()||sb(a).isValid())?Ob({from:this,to:a}).locale(this.locale()).humanize(!b):this.localeData().invalidDate()}function kc(a){return this.to(sb(),a)} -// If passed a locale key, it will set the locale for this -// instance. Otherwise, it will return the locale configuration -// variables for this instance. -function lc(a){var b;return void 0===a?this._locale._abbr:(b=bb(a),null!=b&&(this._locale=b),this)}function mc(){return this._locale}function nc(a){ -// the following switch intentionally omits break keywords -// to utilize falling through the cases. -switch(a=K(a)){case"year":this.month(0);/* falls through */ -case"quarter":case"month":this.date(1);/* falls through */ -case"week":case"isoWeek":case"day":case"date":this.hours(0);/* falls through */ -case"hour":this.minutes(0);/* falls through */ -case"minute":this.seconds(0);/* falls through */ -case"second":this.milliseconds(0)} -// weeks are a special case -// quarters are also special -return"week"===a&&this.weekday(0),"isoWeek"===a&&this.isoWeekday(1),"quarter"===a&&this.month(3*Math.floor(this.month()/3)),this}function oc(a){ -// 'date' is an alias for 'day', so it should be considered as such. -return a=K(a),void 0===a||"millisecond"===a?this:("date"===a&&(a="day"),this.startOf(a).add(1,"isoWeek"===a?"week":a).subtract(1,"ms"))}function pc(){return this._d.valueOf()-6e4*(this._offset||0)}function qc(){return Math.floor(this.valueOf()/1e3)}function rc(){return new Date(this.valueOf())}function sc(){var a=this;return[a.year(),a.month(),a.date(),a.hour(),a.minute(),a.second(),a.millisecond()]}function tc(){var a=this;return{years:a.year(),months:a.month(),date:a.date(),hours:a.hours(),minutes:a.minutes(),seconds:a.seconds(),milliseconds:a.milliseconds()}}function uc(){ -// new Date(NaN).toJSON() === null -return this.isValid()?this.toISOString():null}function vc(){return n(this)}function wc(){return j({},m(this))}function xc(){return m(this).overflow}function yc(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}}function zc(a,b){U(0,[a,a.length],0,b)} -// MOMENTS -function Ac(a){return Ec.call(this,a,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)}function Bc(a){return Ec.call(this,a,this.isoWeek(),this.isoWeekday(),1,4)}function Cc(){return xa(this.year(),1,4)}function Dc(){var a=this.localeData()._week;return xa(this.year(),a.dow,a.doy)}function Ec(a,b,c,d,e){var f;return null==a?wa(this,d,e).year:(f=xa(a,d,e),b>f&&(b=f),Fc.call(this,a,b,c,d,e))}function Fc(a,b,c,d,e){var f=va(a,b,c,d,e),g=ta(f.year,0,f.dayOfYear);return this.year(g.getUTCFullYear()),this.month(g.getUTCMonth()),this.date(g.getUTCDate()),this} -// MOMENTS -function Gc(a){return null==a?Math.ceil((this.month()+1)/3):this.month(3*(a-1)+this.month()%3)} -// HELPERS -// MOMENTS -function Hc(a){var b=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==a?b:this.add(a-b,"d")}function Ic(a,b){b[ge]=u(1e3*("0."+a))} -// MOMENTS -function Jc(){return this._isUTC?"UTC":""}function Kc(){return this._isUTC?"Coordinated Universal Time":""}function Lc(a){return sb(1e3*a)}function Mc(){return sb.apply(null,arguments).parseZone()}function Nc(a){return a}function Oc(a,b,c,d){var e=bb(),f=k().set(d,b);return e[c](f,a)}function Pc(a,b,c){if(f(a)&&(b=a,a=void 0),a=a||"",null!=b)return Oc(a,b,c,"month");var d,e=[];for(d=0;d<12;d++)e[d]=Oc(a,d,c,"month");return e} -// () -// (5) -// (fmt, 5) -// (fmt) -// (true) -// (true, 5) -// (true, fmt, 5) -// (true, fmt) -function Qc(a,b,c,d){"boolean"==typeof a?(f(b)&&(c=b,b=void 0),b=b||""):(b=a,c=b,a=!1,f(b)&&(c=b,b=void 0),b=b||"");var e=bb(),g=a?e._week.dow:0;if(null!=c)return Oc(b,(c+g)%7,d,"day");var h,i=[];for(h=0;h<7;h++)i[h]=Oc(b,(h+g)%7,d,"day");return i}function Rc(a,b){return Pc(a,b,"months")}function Sc(a,b){return Pc(a,b,"monthsShort")}function Tc(a,b,c){return Qc(a,b,c,"weekdays")}function Uc(a,b,c){return Qc(a,b,c,"weekdaysShort")}function Vc(a,b,c){return Qc(a,b,c,"weekdaysMin")}function Wc(){var a=this._data;return this._milliseconds=Ze(this._milliseconds),this._days=Ze(this._days),this._months=Ze(this._months),a.milliseconds=Ze(a.milliseconds),a.seconds=Ze(a.seconds),a.minutes=Ze(a.minutes),a.hours=Ze(a.hours),a.months=Ze(a.months),a.years=Ze(a.years),this}function Xc(a,b,c,d){var e=Ob(b,c);return a._milliseconds+=d*e._milliseconds,a._days+=d*e._days,a._months+=d*e._months,a._bubble()} -// supports only 2.0-style add(1, 's') or add(duration) -function Yc(a,b){return Xc(this,a,b,1)} -// supports only 2.0-style subtract(1, 's') or subtract(duration) -function Zc(a,b){return Xc(this,a,b,-1)}function $c(a){return a<0?Math.floor(a):Math.ceil(a)}function _c(){var a,b,c,d,e,f=this._milliseconds,g=this._days,h=this._months,i=this._data; -// if we have a mix of positive and negative values, bubble down first -// check: https://github.com/moment/moment/issues/2166 -// The following code bubbles up values, see the tests for -// examples of what that means. -// convert days to months -// 12 months -> 1 year -return f>=0&&g>=0&&h>=0||f<=0&&g<=0&&h<=0||(f+=864e5*$c(bd(h)+g),g=0,h=0),i.milliseconds=f%1e3,a=t(f/1e3),i.seconds=a%60,b=t(a/60),i.minutes=b%60,c=t(b/60),i.hours=c%24,g+=t(c/24),e=t(ad(g)),h+=e,g-=$c(bd(e)),d=t(h/12),h%=12,i.days=g,i.months=h,i.years=d,this}function ad(a){ -// 400 years have 146097 days (taking into account leap year rules) -// 400 years have 12 months === 4800 -return 4800*a/146097}function bd(a){ -// the reverse of daysToMonths -return 146097*a/4800}function cd(a){var b,c,d=this._milliseconds;if(a=K(a),"month"===a||"year"===a)return b=this._days+d/864e5,c=this._months+ad(b),"month"===a?c:c/12;switch( -// handle milliseconds separately because of floating point math errors (issue #1867) -b=this._days+Math.round(bd(this._months)),a){case"week":return b/7+d/6048e5;case"day":return b+d/864e5;case"hour":return 24*b+d/36e5;case"minute":return 1440*b+d/6e4;case"second":return 86400*b+d/1e3; -// Math.floor prevents floating point math errors here -case"millisecond":return Math.floor(864e5*b)+d;default:throw new Error("Unknown unit "+a)}} -// TODO: Use this.as('ms')? -function dd(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*u(this._months/12)}function ed(a){return function(){return this.as(a)}}function fd(a){return a=K(a),this[a+"s"]()}function gd(a){return function(){return this._data[a]}}function hd(){return t(this.days()/7)} -// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize -function id(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function jd(a,b,c){var d=Ob(a).abs(),e=of(d.as("s")),f=of(d.as("m")),g=of(d.as("h")),h=of(d.as("d")),i=of(d.as("M")),j=of(d.as("y")),k=e<pf.s&&["s",e]||f<=1&&["m"]||f<pf.m&&["mm",f]||g<=1&&["h"]||g<pf.h&&["hh",g]||h<=1&&["d"]||h<pf.d&&["dd",h]||i<=1&&["M"]||i<pf.M&&["MM",i]||j<=1&&["y"]||["yy",j];return k[2]=b,k[3]=+a>0,k[4]=c,id.apply(null,k)} -// This function allows you to set the rounding function for relative time strings -function kd(a){return void 0===a?of:"function"==typeof a&&(of=a,!0)} -// This function allows you to set a threshold for relative time strings -function ld(a,b){return void 0!==pf[a]&&(void 0===b?pf[a]:(pf[a]=b,!0))}function md(a){var b=this.localeData(),c=jd(this,!a,b);return a&&(c=b.pastFuture(+this,c)),b.postformat(c)}function nd(){ -// for ISO strings we do not use the normal bubbling rules: -// * milliseconds bubble up until they become hours -// * days do not bubble at all -// * months bubble up until they become years -// This is because there is no context-free conversion between hours and days -// (think of clock changes) -// and also not between days and months (28-31 days per month) -var a,b,c,d=qf(this._milliseconds)/1e3,e=qf(this._days),f=qf(this._months); -// 3600 seconds -> 60 minutes -> 1 hour -a=t(d/60),b=t(a/60),d%=60,a%=60, -// 12 months -> 1 year -c=t(f/12),f%=12; -// inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js -var g=c,h=f,i=e,j=b,k=a,l=d,m=this.asSeconds();return m?(m<0?"-":"")+"P"+(g?g+"Y":"")+(h?h+"M":"")+(i?i+"D":"")+(j||k||l?"T":"")+(j?j+"H":"")+(k?k+"M":"")+(l?l+"S":""):"P0D"}var od,pd;pd=Array.prototype.some?Array.prototype.some:function(a){for(var b=Object(this),c=b.length>>>0,d=0;d<c;d++)if(d in b&&a.call(this,b[d],d,b))return!0;return!1};var qd=pd,rd=a.momentProperties=[],sd=!1,td={};a.suppressDeprecationWarnings=!1,a.deprecationHandler=null;var ud;ud=Object.keys?Object.keys:function(a){var b,c=[];for(b in a)i(a,b)&&c.push(b);return c};var vd,wd=ud,xd={sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},yd={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},zd="Invalid date",Ad="%d",Bd=/\d{1,2}/,Cd={future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},Dd={},Ed={},Fd=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,Gd=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,Hd={},Id={},Jd=/\d/,Kd=/\d\d/,Ld=/\d{3}/,Md=/\d{4}/,Nd=/[+-]?\d{6}/,Od=/\d\d?/,Pd=/\d\d\d\d?/,Qd=/\d\d\d\d\d\d?/,Rd=/\d{1,3}/,Sd=/\d{1,4}/,Td=/[+-]?\d{1,6}/,Ud=/\d+/,Vd=/[+-]?\d+/,Wd=/Z|[+-]\d\d:?\d\d/gi,Xd=/Z|[+-]\d\d(?::?\d\d)?/gi,Yd=/[+-]?\d+(\.\d{1,3})?/,Zd=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,$d={},_d={},ae=0,be=1,ce=2,de=3,ee=4,fe=5,ge=6,he=7,ie=8;vd=Array.prototype.indexOf?Array.prototype.indexOf:function(a){ -// I know -var b;for(b=0;b<this.length;++b)if(this[b]===a)return b;return-1};var je=vd; -// FORMATTING -U("M",["MM",2],"Mo",function(){return this.month()+1}),U("MMM",0,0,function(a){return this.localeData().monthsShort(this,a)}),U("MMMM",0,0,function(a){return this.localeData().months(this,a)}), -// ALIASES -J("month","M"), -// PRIORITY -M("month",8), -// PARSING -Z("M",Od),Z("MM",Od,Kd),Z("MMM",function(a,b){return b.monthsShortRegex(a)}),Z("MMMM",function(a,b){return b.monthsRegex(a)}),ba(["M","MM"],function(a,b){b[be]=u(a)-1}),ba(["MMM","MMMM"],function(a,b,c,d){var e=c._locale.monthsParse(a,d,c._strict); -// if we didn't find a month name, mark the date as invalid. -null!=e?b[be]=e:m(c).invalidMonth=a}); -// LOCALES -var ke=/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/,le="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),me="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),ne=Zd,oe=Zd; -// FORMATTING -U("Y",0,0,function(){var a=this.year();return a<=9999?""+a:"+"+a}),U(0,["YY",2],0,function(){return this.year()%100}),U(0,["YYYY",4],0,"year"),U(0,["YYYYY",5],0,"year"),U(0,["YYYYYY",6,!0],0,"year"), -// ALIASES -J("year","y"), -// PRIORITIES -M("year",1), -// PARSING -Z("Y",Vd),Z("YY",Od,Kd),Z("YYYY",Sd,Md),Z("YYYYY",Td,Nd),Z("YYYYYY",Td,Nd),ba(["YYYYY","YYYYYY"],ae),ba("YYYY",function(b,c){c[ae]=2===b.length?a.parseTwoDigitYear(b):u(b)}),ba("YY",function(b,c){c[ae]=a.parseTwoDigitYear(b)}),ba("Y",function(a,b){b[ae]=parseInt(a,10)}), -// HOOKS -a.parseTwoDigitYear=function(a){return u(a)+(u(a)>68?1900:2e3)}; -// MOMENTS -var pe=O("FullYear",!0); -// FORMATTING -U("w",["ww",2],"wo","week"),U("W",["WW",2],"Wo","isoWeek"), -// ALIASES -J("week","w"),J("isoWeek","W"), -// PRIORITIES -M("week",5),M("isoWeek",5), -// PARSING -Z("w",Od),Z("ww",Od,Kd),Z("W",Od),Z("WW",Od,Kd),ca(["w","ww","W","WW"],function(a,b,c,d){b[d.substr(0,1)]=u(a)});var qe={dow:0,// Sunday is the first day of the week. -doy:6}; -// FORMATTING -U("d",0,"do","day"),U("dd",0,0,function(a){return this.localeData().weekdaysMin(this,a)}),U("ddd",0,0,function(a){return this.localeData().weekdaysShort(this,a)}),U("dddd",0,0,function(a){return this.localeData().weekdays(this,a)}),U("e",0,0,"weekday"),U("E",0,0,"isoWeekday"), -// ALIASES -J("day","d"),J("weekday","e"),J("isoWeekday","E"), -// PRIORITY -M("day",11),M("weekday",11),M("isoWeekday",11), -// PARSING -Z("d",Od),Z("e",Od),Z("E",Od),Z("dd",function(a,b){return b.weekdaysMinRegex(a)}),Z("ddd",function(a,b){return b.weekdaysShortRegex(a)}),Z("dddd",function(a,b){return b.weekdaysRegex(a)}),ca(["dd","ddd","dddd"],function(a,b,c,d){var e=c._locale.weekdaysParse(a,d,c._strict); -// if we didn't get a weekday name, mark the date as invalid -null!=e?b.d=e:m(c).invalidWeekday=a}),ca(["d","e","E"],function(a,b,c,d){b[d]=u(a)}); -// LOCALES -var re="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),se="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),te="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),ue=Zd,ve=Zd,we=Zd;U("H",["HH",2],0,"hour"),U("h",["hh",2],0,Ra),U("k",["kk",2],0,Sa),U("hmm",0,0,function(){return""+Ra.apply(this)+T(this.minutes(),2)}),U("hmmss",0,0,function(){return""+Ra.apply(this)+T(this.minutes(),2)+T(this.seconds(),2)}),U("Hmm",0,0,function(){return""+this.hours()+T(this.minutes(),2)}),U("Hmmss",0,0,function(){return""+this.hours()+T(this.minutes(),2)+T(this.seconds(),2)}),Ta("a",!0),Ta("A",!1), -// ALIASES -J("hour","h"), -// PRIORITY -M("hour",13),Z("a",Ua),Z("A",Ua),Z("H",Od),Z("h",Od),Z("HH",Od,Kd),Z("hh",Od,Kd),Z("hmm",Pd),Z("hmmss",Qd),Z("Hmm",Pd),Z("Hmmss",Qd),ba(["H","HH"],de),ba(["a","A"],function(a,b,c){c._isPm=c._locale.isPM(a),c._meridiem=a}),ba(["h","hh"],function(a,b,c){b[de]=u(a),m(c).bigHour=!0}),ba("hmm",function(a,b,c){var d=a.length-2;b[de]=u(a.substr(0,d)),b[ee]=u(a.substr(d)),m(c).bigHour=!0}),ba("hmmss",function(a,b,c){var d=a.length-4,e=a.length-2;b[de]=u(a.substr(0,d)),b[ee]=u(a.substr(d,2)),b[fe]=u(a.substr(e)),m(c).bigHour=!0}),ba("Hmm",function(a,b,c){var d=a.length-2;b[de]=u(a.substr(0,d)),b[ee]=u(a.substr(d))}),ba("Hmmss",function(a,b,c){var d=a.length-4,e=a.length-2;b[de]=u(a.substr(0,d)),b[ee]=u(a.substr(d,2)),b[fe]=u(a.substr(e))});var xe,ye=/[ap]\.?m?\.?/i,ze=O("Hours",!0),Ae={calendar:xd,longDateFormat:yd,invalidDate:zd,ordinal:Ad,ordinalParse:Bd,relativeTime:Cd,months:le,monthsShort:me,week:qe,weekdays:re,weekdaysMin:te,weekdaysShort:se,meridiemParse:ye},Be={},Ce={},De=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Ee=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Fe=/Z|[+-]\d\d(?::?\d\d)?/,Ge=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/], -// YYYYMM is NOT allowed by the standard -["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],He=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],Ie=/^\/?Date\((\-?\d+)/i;a.createFromInputFallback=x("value provided is not in a recognized ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",function(a){a._d=new Date(a._i+(a._useUTC?" UTC":""))}), -// constant that refers to the ISO standard -a.ISO_8601=function(){};var Je=x("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var a=sb.apply(null,arguments);return this.isValid()&&a.isValid()?a<this?this:a:o()}),Ke=x("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var a=sb.apply(null,arguments);return this.isValid()&&a.isValid()?a>this?this:a:o()}),Le=function(){return Date.now?Date.now():+new Date};zb("Z",":"),zb("ZZ",""), -// PARSING -Z("Z",Xd),Z("ZZ",Xd),ba(["Z","ZZ"],function(a,b,c){c._useUTC=!0,c._tzm=Ab(Xd,a)}); -// HELPERS -// timezone chunker -// '+10:00' > ['10', '00'] -// '-1530' > ['-15', '30'] -var Me=/([\+\-]|\d\d)/gi; -// HOOKS -// This function will be called whenever a moment is mutated. -// It is intended to keep the offset in sync with the timezone. -a.updateOffset=function(){}; -// ASP.NET json date format regex -var Ne=/^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,Oe=/^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/;Ob.fn=wb.prototype;var Pe=Sb(1,"add"),Qe=Sb(-1,"subtract");a.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",a.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var Re=x("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(a){return void 0===a?this.localeData():this.locale(a)}); -// FORMATTING -U(0,["gg",2],0,function(){return this.weekYear()%100}),U(0,["GG",2],0,function(){return this.isoWeekYear()%100}),zc("gggg","weekYear"),zc("ggggg","weekYear"),zc("GGGG","isoWeekYear"),zc("GGGGG","isoWeekYear"), -// ALIASES -J("weekYear","gg"),J("isoWeekYear","GG"), -// PRIORITY -M("weekYear",1),M("isoWeekYear",1), -// PARSING -Z("G",Vd),Z("g",Vd),Z("GG",Od,Kd),Z("gg",Od,Kd),Z("GGGG",Sd,Md),Z("gggg",Sd,Md),Z("GGGGG",Td,Nd),Z("ggggg",Td,Nd),ca(["gggg","ggggg","GGGG","GGGGG"],function(a,b,c,d){b[d.substr(0,2)]=u(a)}),ca(["gg","GG"],function(b,c,d,e){c[e]=a.parseTwoDigitYear(b)}), -// FORMATTING -U("Q",0,"Qo","quarter"), -// ALIASES -J("quarter","Q"), -// PRIORITY -M("quarter",7), -// PARSING -Z("Q",Jd),ba("Q",function(a,b){b[be]=3*(u(a)-1)}), -// FORMATTING -U("D",["DD",2],"Do","date"), -// ALIASES -J("date","D"), -// PRIOROITY -M("date",9), -// PARSING -Z("D",Od),Z("DD",Od,Kd),Z("Do",function(a,b){return a?b._ordinalParse:b._ordinalParseLenient}),ba(["D","DD"],ce),ba("Do",function(a,b){b[ce]=u(a.match(Od)[0],10)}); -// MOMENTS -var Se=O("Date",!0); -// FORMATTING -U("DDD",["DDDD",3],"DDDo","dayOfYear"), -// ALIASES -J("dayOfYear","DDD"), -// PRIORITY -M("dayOfYear",4), -// PARSING -Z("DDD",Rd),Z("DDDD",Ld),ba(["DDD","DDDD"],function(a,b,c){c._dayOfYear=u(a)}), -// FORMATTING -U("m",["mm",2],0,"minute"), -// ALIASES -J("minute","m"), -// PRIORITY -M("minute",14), -// PARSING -Z("m",Od),Z("mm",Od,Kd),ba(["m","mm"],ee); -// MOMENTS -var Te=O("Minutes",!1); -// FORMATTING -U("s",["ss",2],0,"second"), -// ALIASES -J("second","s"), -// PRIORITY -M("second",15), -// PARSING -Z("s",Od),Z("ss",Od,Kd),ba(["s","ss"],fe); -// MOMENTS -var Ue=O("Seconds",!1); -// FORMATTING -U("S",0,0,function(){return~~(this.millisecond()/100)}),U(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),U(0,["SSS",3],0,"millisecond"),U(0,["SSSS",4],0,function(){return 10*this.millisecond()}),U(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),U(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),U(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),U(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),U(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}), -// ALIASES -J("millisecond","ms"), -// PRIORITY -M("millisecond",16), -// PARSING -Z("S",Rd,Jd),Z("SS",Rd,Kd),Z("SSS",Rd,Ld);var Ve;for(Ve="SSSS";Ve.length<=9;Ve+="S")Z(Ve,Ud);for(Ve="S";Ve.length<=9;Ve+="S")ba(Ve,Ic); -// MOMENTS -var We=O("Milliseconds",!1); -// FORMATTING -U("z",0,0,"zoneAbbr"),U("zz",0,0,"zoneName");var Xe=r.prototype;Xe.add=Pe,Xe.calendar=Vb,Xe.clone=Wb,Xe.diff=bc,Xe.endOf=oc,Xe.format=gc,Xe.from=hc,Xe.fromNow=ic,Xe.to=jc,Xe.toNow=kc,Xe.get=R,Xe.invalidAt=xc,Xe.isAfter=Xb,Xe.isBefore=Yb,Xe.isBetween=Zb,Xe.isSame=$b,Xe.isSameOrAfter=_b,Xe.isSameOrBefore=ac,Xe.isValid=vc,Xe.lang=Re,Xe.locale=lc,Xe.localeData=mc,Xe.max=Ke,Xe.min=Je,Xe.parsingFlags=wc,Xe.set=S,Xe.startOf=nc,Xe.subtract=Qe,Xe.toArray=sc,Xe.toObject=tc,Xe.toDate=rc,Xe.toISOString=ec,Xe.inspect=fc,Xe.toJSON=uc,Xe.toString=dc,Xe.unix=qc,Xe.valueOf=pc,Xe.creationData=yc, -// Year -Xe.year=pe,Xe.isLeapYear=ra, -// Week Year -Xe.weekYear=Ac,Xe.isoWeekYear=Bc, -// Quarter -Xe.quarter=Xe.quarters=Gc, -// Month -Xe.month=ka,Xe.daysInMonth=la, -// Week -Xe.week=Xe.weeks=Ba,Xe.isoWeek=Xe.isoWeeks=Ca,Xe.weeksInYear=Dc,Xe.isoWeeksInYear=Cc, -// Day -Xe.date=Se,Xe.day=Xe.days=Ka,Xe.weekday=La,Xe.isoWeekday=Ma,Xe.dayOfYear=Hc, -// Hour -Xe.hour=Xe.hours=ze, -// Minute -Xe.minute=Xe.minutes=Te, -// Second -Xe.second=Xe.seconds=Ue, -// Millisecond -Xe.millisecond=Xe.milliseconds=We, -// Offset -Xe.utcOffset=Db,Xe.utc=Fb,Xe.local=Gb,Xe.parseZone=Hb,Xe.hasAlignedHourOffset=Ib,Xe.isDST=Jb,Xe.isLocal=Lb,Xe.isUtcOffset=Mb,Xe.isUtc=Nb,Xe.isUTC=Nb, -// Timezone -Xe.zoneAbbr=Jc,Xe.zoneName=Kc, -// Deprecations -Xe.dates=x("dates accessor is deprecated. Use date instead.",Se),Xe.months=x("months accessor is deprecated. Use month instead",ka),Xe.years=x("years accessor is deprecated. Use year instead",pe),Xe.zone=x("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",Eb),Xe.isDSTShifted=x("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",Kb);var Ye=C.prototype;Ye.calendar=D,Ye.longDateFormat=E,Ye.invalidDate=F,Ye.ordinal=G,Ye.preparse=Nc,Ye.postformat=Nc,Ye.relativeTime=H,Ye.pastFuture=I,Ye.set=A, -// Month -Ye.months=fa,Ye.monthsShort=ga,Ye.monthsParse=ia,Ye.monthsRegex=na,Ye.monthsShortRegex=ma, -// Week -Ye.week=ya,Ye.firstDayOfYear=Aa,Ye.firstDayOfWeek=za, -// Day of Week -Ye.weekdays=Fa,Ye.weekdaysMin=Ha,Ye.weekdaysShort=Ga,Ye.weekdaysParse=Ja,Ye.weekdaysRegex=Na,Ye.weekdaysShortRegex=Oa,Ye.weekdaysMinRegex=Pa, -// Hours -Ye.isPM=Va,Ye.meridiem=Wa,$a("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(a){var b=a%10,c=1===u(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}), -// Side effect imports -a.lang=x("moment.lang is deprecated. Use moment.locale instead.",$a),a.langData=x("moment.langData is deprecated. Use moment.localeData instead.",bb);var Ze=Math.abs,$e=ed("ms"),_e=ed("s"),af=ed("m"),bf=ed("h"),cf=ed("d"),df=ed("w"),ef=ed("M"),ff=ed("y"),gf=gd("milliseconds"),hf=gd("seconds"),jf=gd("minutes"),kf=gd("hours"),lf=gd("days"),mf=gd("months"),nf=gd("years"),of=Math.round,pf={s:45,// seconds to minute -m:45,// minutes to hour -h:22,// hours to day -d:26,// days to month -M:11},qf=Math.abs,rf=wb.prototype; -// Deprecations -// Side effect imports -// FORMATTING -// PARSING -// Side effect imports -return rf.abs=Wc,rf.add=Yc,rf.subtract=Zc,rf.as=cd,rf.asMilliseconds=$e,rf.asSeconds=_e,rf.asMinutes=af,rf.asHours=bf,rf.asDays=cf,rf.asWeeks=df,rf.asMonths=ef,rf.asYears=ff,rf.valueOf=dd,rf._bubble=_c,rf.get=fd,rf.milliseconds=gf,rf.seconds=hf,rf.minutes=jf,rf.hours=kf,rf.days=lf,rf.weeks=hd,rf.months=mf,rf.years=nf,rf.humanize=md,rf.toISOString=nd,rf.toString=nd,rf.toJSON=nd,rf.locale=lc,rf.localeData=mc,rf.toIsoString=x("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",nd),rf.lang=Re,U("X",0,0,"unix"),U("x",0,0,"valueOf"),Z("x",Vd),Z("X",Yd),ba("X",function(a,b,c){c._d=new Date(1e3*parseFloat(a,10))}),ba("x",function(a,b,c){c._d=new Date(u(a))}),a.version="2.17.1",b(sb),a.fn=Xe,a.min=ub,a.max=vb,a.now=Le,a.utc=k,a.unix=Lc,a.months=Rc,a.isDate=g,a.locale=$a,a.invalid=o,a.duration=Ob,a.isMoment=s,a.weekdays=Tc,a.parseZone=Mc,a.localeData=bb,a.isDuration=xb,a.monthsShort=Sc,a.weekdaysMin=Vc,a.defineLocale=_a,a.updateLocale=ab,a.locales=cb,a.weekdaysShort=Uc,a.normalizeUnits=K,a.relativeTimeRounding=kd,a.relativeTimeThreshold=ld,a.calendarFormat=Ub,a.prototype=Xe,a}); -/*! - * FullCalendar v3.1.0 - * Docs & License: http://fullcalendar.io/ - * (c) 2016 Adam Shaw - */ -!function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):"object"==typeof exports?module.exports=t(require("jquery"),require("moment")):t(jQuery,moment)}(function(t,e){function n(t){return q(t,$t)}function i(t,e){e.left&&t.css({"border-left-width":1,"margin-left":e.left-1}),e.right&&t.css({"border-right-width":1,"margin-right":e.right-1})}function r(t){t.css({"margin-left":"","margin-right":"","border-left-width":"","border-right-width":""})}function s(){t("body").addClass("fc-not-allowed")}function o(){t("body").removeClass("fc-not-allowed")}function l(e,n,i){var r=Math.floor(n/e.length),s=Math.floor(n-r*(e.length-1)),o=[],l=[],u=[],c=0;a(e),e.each(function(n,i){var a=n===e.length-1?s:r,d=t(i).outerHeight(!0);d<a?(o.push(i),l.push(d),u.push(t(i).height())):c+=d}),i&&(n-=c,r=Math.floor(n/o.length),s=Math.floor(n-r*(o.length-1))),t(o).each(function(e,n){var i=e===o.length-1?s:r,a=l[e],c=u[e],d=i-(a-c);a<i&&t(n).height(d)})}function a(t){t.height("")}function u(e){var n=0;return e.find("> *").each(function(e,i){var r=t(i).outerWidth();r>n&&(n=r)}),n++,e.width(n),n}function c(t,e){var n,i=t.add(e);return i.css({position:"relative",left:-1}),n=t.outerHeight()-e.outerHeight(),i.css({position:"",left:""}),n}function d(e){var n=e.css("position"),i=e.parents().filter(function(){var e=t(this);return/(auto|scroll)/.test(e.css("overflow")+e.css("overflow-y")+e.css("overflow-x"))}).eq(0);return"fixed"!==n&&i.length?i:t(e[0].ownerDocument||document)}function h(t,e){var n=t.offset(),i=n.left-(e?e.left:0),r=n.top-(e?e.top:0);return{left:i,right:i+t.outerWidth(),top:r,bottom:r+t.outerHeight()}}function f(t,e){var n=t.offset(),i=p(t),r=n.left+y(t,"border-left-width")+i.left-(e?e.left:0),s=n.top+y(t,"border-top-width")+i.top-(e?e.top:0);return{left:r,right:r+t[0].clientWidth,top:s,bottom:s+t[0].clientHeight}}function g(t,e){var n=t.offset(),i=n.left+y(t,"border-left-width")+y(t,"padding-left")-(e?e.left:0),r=n.top+y(t,"border-top-width")+y(t,"padding-top")-(e?e.top:0);return{left:i,right:i+t.width(),top:r,bottom:r+t.height()}}function p(t){var e=t.innerWidth()-t[0].clientWidth,n={left:0,right:0,top:0,bottom:t.innerHeight()-t[0].clientHeight};return v()&&"rtl"==t.css("direction")?n.left=e:n.right=e,n}function v(){return null===Qt&&(Qt=m()),Qt}function m(){var e=t("<div><div/></div>").css({position:"absolute",top:-1e3,left:0,border:0,padding:0,overflow:"scroll",direction:"rtl"}).appendTo("body"),n=e.children(),i=n.offset().left>e.offset().left;return e.remove(),i}function y(t,e){return parseFloat(t.css(e))||0}function S(t){return 1==t.which&&!t.ctrlKey}function w(t){if(void 0!==t.pageX)return t.pageX;var e=t.originalEvent.touches;return e?e[0].pageX:void 0}function E(t){if(void 0!==t.pageY)return t.pageY;var e=t.originalEvent.touches;return e?e[0].pageY:void 0}function b(t){return/^touch/.test(t.type)}function D(t){t.addClass("fc-unselectable").on("selectstart",T)}function T(t){t.preventDefault()}function C(t){return!!window.addEventListener&&(window.addEventListener("scroll",t,!0),!0)}function H(t){return!!window.removeEventListener&&(window.removeEventListener("scroll",t,!0),!0)}function R(t,e){var n={left:Math.max(t.left,e.left),right:Math.min(t.right,e.right),top:Math.max(t.top,e.top),bottom:Math.min(t.bottom,e.bottom)};return n.left<n.right&&n.top<n.bottom&&n}function x(t,e){return{left:Math.min(Math.max(t.left,e.left),e.right),top:Math.min(Math.max(t.top,e.top),e.bottom)}}function I(t){return{left:(t.left+t.right)/2,top:(t.top+t.bottom)/2}}function k(t,e){return{left:t.left-e.left,top:t.top-e.top}}function L(e){var n,i,r=[],s=[];for("string"==typeof e?s=e.split(/\s*,\s*/):"function"==typeof e?s=[e]:t.isArray(e)&&(s=e),n=0;n<s.length;n++)i=s[n],"string"==typeof i?r.push("-"==i.charAt(0)?{field:i.substring(1),order:-1}:{field:i,order:1}):"function"==typeof i&&r.push({func:i});return r}function M(t,e,n){var i,r;for(i=0;i<n.length;i++)if(r=B(t,e,n[i]))return r;return 0}function B(t,e,n){return n.func?n.func(t,e):z(t[n.field],e[n.field])*(n.order||1)}function z(e,n){return e||n?null==n?-1:null==e?1:"string"===t.type(e)||"string"===t.type(n)?String(e).localeCompare(String(n)):e-n:0}function F(t,e){var n,i,r,s,o=t.start,l=t.end,a=e.start,u=e.end;if(l>a&&o<u)return o>=a?(n=o.clone(),r=!0):(n=a.clone(),r=!1),l<=u?(i=l.clone(),s=!0):(i=u.clone(),s=!1),{start:n,end:i,isStart:r,isEnd:s}}function N(t,n){return e.duration({days:t.clone().stripTime().diff(n.clone().stripTime(),"days"),ms:t.time()-n.time()})}function G(t,n){return e.duration({days:t.clone().stripTime().diff(n.clone().stripTime(),"days")})}function O(t,n,i){return e.duration(Math.round(t.diff(n,i,!0)),i)}function A(t,e){var n,i,r;for(n=0;n<Kt.length&&(i=Kt[n],r=V(i,t,e),!(r>=1&&ot(r)));n++);return i}function V(t,n,i){return null!=i?i.diff(n,t,!0):e.isDuration(n)?n.as(t):n.end.diff(n.start,t,!0)}function P(t,e,n){var i;return W(n)?(e-t)/n:(i=n.asMonths(),Math.abs(i)>=1&&ot(i)?e.diff(t,"months",!0)/i:e.diff(t,"days",!0)/n.asDays())}function _(t,e){var n,i;return W(t)||W(e)?t/e:(n=t.asMonths(),i=e.asMonths(),Math.abs(n)>=1&&ot(n)&&Math.abs(i)>=1&&ot(i)?n/i:t.asDays()/e.asDays())}function Y(t,n){var i;return W(t)?e.duration(t*n):(i=t.asMonths(),Math.abs(i)>=1&&ot(i)?e.duration({months:i*n}):e.duration({days:t.asDays()*n}))}function W(t){return Boolean(t.hours()||t.minutes()||t.seconds()||t.milliseconds())}function U(t){return"[object Date]"===Object.prototype.toString.call(t)||t instanceof Date}function j(t){return/^\d+\:\d+(?:\:\d+\.?(?:\d{3})?)?$/.test(t)}function q(t,e){var n,i,r,s,o,l,a={};if(e)for(n=0;n<e.length;n++){for(i=e[n],r=[],s=t.length-1;s>=0;s--)if(o=t[s][i],"object"==typeof o)r.unshift(o);else if(void 0!==o){a[i]=o;break}r.length&&(a[i]=q(r))}for(n=t.length-1;n>=0;n--){l=t[n];for(i in l)i in a||(a[i]=l[i])}return a}function Z(t){var e=function(){};return e.prototype=t,new e}function $(t,e){for(var n in t)Q(t,n)&&(e[n]=t[n])}function Q(t,e){return Jt.call(t,e)}function X(e){return/undefined|null|boolean|number|string/.test(t.type(e))}function K(e,n,i){if(t.isFunction(e)&&(e=[e]),e){var r,s;for(r=0;r<e.length;r++)s=e[r].apply(n,i)||s;return s}}function J(){for(var t=0;t<arguments.length;t++)if(void 0!==arguments[t])return arguments[t]}function tt(t){return(t+"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/'/g,"'").replace(/"/g,""").replace(/\n/g,"<br />")}function et(t){return t.replace(/&.*?;/g,"")}function nt(e){var n=[];return t.each(e,function(t,e){null!=e&&n.push(t+":"+e)}),n.join(";")}function it(e){var n=[];return t.each(e,function(t,e){null!=e&&n.push(t+'="'+tt(e)+'"')}),n.join(" ")}function rt(t){return t.charAt(0).toUpperCase()+t.slice(1)}function st(t,e){return t-e}function ot(t){return t%1===0}function lt(t,e){var n=t[e];return function(){return n.apply(t,arguments)}}function at(t,e,n){var i,r,s,o,l,a=function(){var u=+new Date-o;u<e?i=setTimeout(a,e-u):(i=null,n||(l=t.apply(s,r),s=r=null))};return function(){s=this,r=arguments,o=+new Date;var u=n&&!i;return i||(i=setTimeout(a,e)),u&&(l=t.apply(s,r),s=r=null),l}}function ut(n,i,r){var s,o,l,a,u=n[0],c=1==n.length&&"string"==typeof u;return e.isMoment(u)||U(u)||void 0===u?a=e.apply(null,n):(s=!1,o=!1,c?te.test(u)?(u+="-01",n=[u],s=!0,o=!0):(l=ee.exec(u))&&(s=!l[5],o=!0):t.isArray(u)&&(o=!0),a=i||s?e.utc.apply(e,n):e.apply(null,n),s?(a._ambigTime=!0,a._ambigZone=!0):r&&(o?a._ambigZone=!0:c&&a.utcOffset(u))),a._fullCalendar=!0,a}function ct(t,e){return ie.format.call(t,e)}function dt(t,e){return ht(t,mt(e))}function ht(t,e){var n,i="";for(n=0;n<e.length;n++)i+=ft(t,e[n]);return i}function ft(t,e){var n,i;return"string"==typeof e?e:(n=e.token)?se[n]?se[n](t):ct(t,n):e.maybe&&(i=ht(t,e.maybe),i.match(/[1-9]/))?i:""}function gt(t,e,n,i,r){var s;return t=qt.moment.parseZone(t),e=qt.moment.parseZone(e),s=t.localeData(),n=s.longDateFormat(n)||n,i=i||" - ",pt(t,e,mt(n),i,r)}function pt(t,e,n,i,r){var s,o,l,a,u=t.clone().stripZone(),c=e.clone().stripZone(),d="",h="",f="",g="",p="";for(o=0;o<n.length&&(s=vt(t,e,u,c,n[o]),s!==!1);o++)d+=s;for(l=n.length-1;l>o&&(s=vt(t,e,u,c,n[l]),s!==!1);l--)h=s+h;for(a=o;a<=l;a++)f+=ft(t,n[a]),g+=ft(e,n[a]);return(f||g)&&(p=r?g+i+f:f+i+g),d+p+h}function vt(t,e,n,i,r){var s,o;return"string"==typeof r?r:!!((s=r.token)&&(o=oe[s.charAt(0)],o&&n.isSame(i,o)))&&ct(t,s)}function mt(t){return t in le?le[t]:le[t]=yt(t)}function yt(t){for(var e,n=[],i=/\[([^\]]*)\]|\(([^\)]*)\)|(LTS|LT|(\w)\4*o?)|([^\w\[\(]+)/g;e=i.exec(t);)e[1]?n.push(e[1]):e[2]?n.push({maybe:yt(e[2])}):e[3]?n.push({token:e[3]}):e[5]&&n.push(e[5]);return n}function St(){}function wt(t,e){var n;return Q(e,"constructor")&&(n=e.constructor),"function"!=typeof n&&(n=e.constructor=function(){t.apply(this,arguments)}),n.prototype=Z(t.prototype),$(e,n.prototype),$(t,n),n}function Et(t,e){$(e,t.prototype)}function bt(e){var n=t.Deferred(),i=n.promise();if("function"==typeof e&&e(function(t){bt.immediate&&(i._value=t),n.resolve(t)},function(){n.reject()}),bt.immediate){var r=i.then;i.then=function(t,e){var n=i.state();if("resolved"===n){if("function"==typeof t)return bt.resolve(t(i._value))}else if("rejected"===n&&"function"==typeof e)return e(),i;return r.call(i,t,e)}}return i}function Dt(t){function e(t){return new bt(function(e){var i=function(){bt.resolve(t()).then(e).then(function(){n.shift(),n.length&&n[0]()})};n.push(i),1===n.length&&i()})}var n=[];this.add="number"==typeof t?at(e,t):e,this.addQuickly=e}function Tt(t,e){return!t&&!e||!(!t||!e)&&(t.component===e.component&&Ct(t,e)&&Ct(e,t))}function Ct(t,e){for(var n in t)if(!/^(component|left|right|top|bottom)$/.test(n)&&t[n]!==e[n])return!1;return!0}function Ht(t){return{start:t.start.clone(),end:t.end?t.end.clone():null,allDay:t.allDay}}function Rt(t){var e=It(t);return"background"===e||"inverse-background"===e}function xt(t){return"inverse-background"===It(t)}function It(t){return J((t.source||{}).rendering,t.rendering)}function kt(t){var e,n,i={};for(e=0;e<t.length;e++)n=t[e],(i[n._id]||(i[n._id]=[])).push(n);return i}function Lt(t,e){return t.start-e.start}function Mt(n){var i,r,s,o,l=qt.dataAttrPrefix;return l&&(l+="-"),i=n.data(l+"event")||null,i&&(i="object"==typeof i?t.extend({},i):{},r=i.start,null==r&&(r=i.time),s=i.duration,o=i.stick,delete i.start,delete i.time,delete i.duration,delete i.stick),null==r&&(r=n.data(l+"start")),null==r&&(r=n.data(l+"time")),null==s&&(s=n.data(l+"duration")),null==o&&(o=n.data(l+"stick")),r=null!=r?e.duration(r):null,s=null!=s?e.duration(s):null,o=Boolean(o),{eventProps:i,startTime:r,duration:s,stick:o}}function Bt(t,e){var n,i;for(n=0;n<e.length;n++)if(i=e[n],i.leftCol<=t.rightCol&&i.rightCol>=t.leftCol)return!0;return!1}function zt(t,e){return t.leftCol-e.leftCol}function Ft(t){var e,n,i,r=[];for(e=0;e<t.length;e++){for(n=t[e],i=0;i<r.length&&Ot(n,r[i]).length;i++);n.level=i,(r[i]||(r[i]=[])).push(n)}return r}function Nt(t){var e,n,i,r,s;for(e=0;e<t.length;e++)for(n=t[e],i=0;i<n.length;i++)for(r=n[i],r.forwardSegs=[],s=e+1;s<t.length;s++)Ot(r,t[s],r.forwardSegs)}function Gt(t){var e,n,i=t.forwardSegs,r=0;if(void 0===t.forwardPressure){for(e=0;e<i.length;e++)n=i[e],Gt(n),r=Math.max(r,1+n.forwardPressure);t.forwardPressure=r}}function Ot(t,e,n){n=n||[];for(var i=0;i<e.length;i++)At(t,e[i])&&n.push(e[i]);return n}function At(t,e){return t.bottom>e.top&&t.top<e.bottom}function Vt(t){this.items=t||[]}function Pt(e,n){function i(t){n=t}function r(){var i=n.layout;p=e.options.theme?"ui":"fc",i?(g?g.empty():g=this.el=t("<div class='fc-toolbar "+n.extraClasses+"'/>"),g.append(o("left")).append(o("right")).append(o("center")).append('<div class="fc-clear"/>')):s()}function s(){g&&(g.remove(),g=f.el=null)}function o(i){var r=t('<div class="fc-'+i+'"/>'),s=n.layout[i];return s&&t.each(s.split(" "),function(n){var i,s=t(),o=!0;t.each(this.split(","),function(n,i){var r,l,a,u,c,d,h,f,g,m;"title"==i?(s=s.add(t("<h2> </h2>")),o=!1):((r=(e.options.customButtons||{})[i])?(a=function(t){r.click&&r.click.call(m[0],t)},u="",c=r.text):(l=e.getViewSpec(i))?(a=function(){e.changeView(i)},v.push(i),u=l.buttonTextOverride,c=l.buttonTextDefault):e[i]&&(a=function(){e[i]()},u=(e.overrides.buttonText||{})[i],c=e.options.buttonText[i]),a&&(d=r?r.themeIcon:e.options.themeButtonIcons[i],h=r?r.icon:e.options.buttonIcons[i],f=u?tt(u):d&&e.options.theme?"<span class='ui-icon ui-icon-"+d+"'></span>":h&&!e.options.theme?"<span class='fc-icon fc-icon-"+h+"'></span>":tt(c),g=["fc-"+i+"-button",p+"-button",p+"-state-default"],m=t('<button type="button" class="'+g.join(" ")+'">'+f+"</button>").click(function(t){m.hasClass(p+"-state-disabled")||(a(t),(m.hasClass(p+"-state-active")||m.hasClass(p+"-state-disabled"))&&m.removeClass(p+"-state-hover"))}).mousedown(function(){m.not("."+p+"-state-active").not("."+p+"-state-disabled").addClass(p+"-state-down")}).mouseup(function(){m.removeClass(p+"-state-down")}).hover(function(){m.not("."+p+"-state-active").not("."+p+"-state-disabled").addClass(p+"-state-hover")},function(){m.removeClass(p+"-state-hover").removeClass(p+"-state-down")}),s=s.add(m)))}),o&&s.first().addClass(p+"-corner-left").end().last().addClass(p+"-corner-right").end(),s.length>1?(i=t("<div/>"),o&&i.addClass("fc-button-group"),i.append(s),r.append(i)):r.append(s)}),r}function l(t){g&&g.find("h2").text(t)}function a(t){g&&g.find(".fc-"+t+"-button").addClass(p+"-state-active")}function u(t){g&&g.find(".fc-"+t+"-button").removeClass(p+"-state-active")}function c(t){g&&g.find(".fc-"+t+"-button").prop("disabled",!0).addClass(p+"-state-disabled")}function d(t){g&&g.find(".fc-"+t+"-button").prop("disabled",!1).removeClass(p+"-state-disabled")}function h(){return v}var f=this;f.setToolbarOptions=i,f.render=r,f.removeElement=s,f.updateTitle=l,f.activateButton=a,f.deactivateButton=u,f.disableButton=c,f.enableButton=d,f.getViewsWithButtons=h,f.el=null;var g,p,v=[]}function _t(n,i){function r(t){t._locale=Y}function s(){q?a()&&(f(),u()):o()}function o(){n.addClass("fc"),n.on("click.fc","a[data-goto]",function(e){var n=t(this),i=n.data("goto"),r=_.moment(i.date),s=i.type,o=Q.opt("navLink"+rt(s)+"Click");"function"==typeof o?o(r,e):("string"==typeof o&&(s=o),B(r,s))}),_.bindOption("theme",function(t){$=t?"ui":"fc",n.toggleClass("ui-widget",t),n.toggleClass("fc-unthemed",!t)}),_.bindOptions(["isRTL","locale"],function(t){n.toggleClass("fc-ltr",!t),n.toggleClass("fc-rtl",t)}),q=t("<div class='fc-view-container'/>").prependTo(n);var e=y();W=new Vt(e),U=_.header=e[0],j=_.footer=e[1],E(),b(),u(_.options.defaultView),_.options.handleWindowResize&&(K=at(v,_.options.windowResizeDelay),t(window).resize(K))}function l(){Q&&Q.removeElement(),W.proxyCall("removeElement"),q.remove(),n.removeClass("fc fc-ltr fc-rtl fc-unthemed ui-widget"),n.off(".fc"),K&&t(window).unbind("resize",K)}function a(){return n.is(":visible")}function u(e,n){nt++;var i=Q&&e&&Q.type!==e;i&&(F(),c()),!Q&&e&&(Q=_.view=et[e]||(et[e]=_.instantiateView(e)),Q.setElement(t("<div class='fc-view fc-"+e+"-view' />").appendTo(q)),W.proxyCall("activateButton",e)),Q&&(J=Q.massageCurrentDate(J),Q.isDateSet&&J>=Q.intervalStart&&J<Q.intervalEnd||a()&&(n&&Q.captureInitialScroll(n),Q.setDate(J,n),n&&Q.releaseScroll(),D())),i&&N(),nt--}function c(){W.proxyCall("deactivateButton",Q.type),Q.removeElement(),Q=_.view=null}function d(){nt++,F();var t=Q.type,e=Q.queryScroll();c(),f(),u(t,e),N(),nt--}function h(t){if(a())return t&&g(),nt++,Q.updateSize(!0),nt--,!0}function f(){a()&&g()}function g(){var t=_.options.contentHeight,e=_.options.height;X="number"==typeof t?t:"function"==typeof t?t():"number"==typeof e?e-p():"function"==typeof e?e()-p():"parent"===e?n.parent().height()-p():Math.round(q.width()/Math.max(_.options.aspectRatio,.5))}function p(){return W.items.reduce(function(t,e){var n=e.el?e.el.outerHeight(!0):0;return t+n},0)}function v(t){!nt&&t.target===window&&Q.start&&h(!0)&&Q.publiclyTrigger("windowResize",tt)}function m(){a()&&_.reportEventChange()}function y(){return[new Pt(_,S()),new Pt(_,w())]}function S(){return{extraClasses:"fc-header-toolbar",layout:_.options.header}}function w(){return{extraClasses:"fc-footer-toolbar",layout:_.options.footer}}function E(){U.setToolbarOptions(S()),U.render(),U.el&&n.prepend(U.el)}function b(){j.setToolbarOptions(w()),j.render(),j.el&&n.append(j.el)}function D(){var t=_.getNow();t>=Q.intervalStart&&t<Q.intervalEnd?W.proxyCall("disableButton","today"):W.proxyCall("enableButton","today")}function T(t,e){Q.select(_.buildSelectSpan.apply(_,arguments))}function C(){Q&&Q.unselect()}function H(){J=Q.computePrevDate(J),u()}function R(){J=Q.computeNextDate(J),u()}function x(){J.add(-1,"years"),u()}function I(){J.add(1,"years"),u()}function k(){J=_.getNow(),u()}function L(t){J=_.moment(t).stripZone(),u()}function M(t){J.add(e.duration(t)),u()}function B(t,e){var n;e=e||"day",n=_.getViewSpec(e)||_.getUnitViewSpec(e),J=t.clone(),u(n?n.type:null)}function z(){return _.applyTimezone(J)}function F(){it++||q.css({width:"100%",height:q.height(),overflow:"hidden"})}function N(){--it||q.css({width:"",height:"",overflow:""})}function G(){return _}function O(){return Q}function A(t,e){var n;if("string"==typeof t){if(void 0===e)return _.options[t];n={},n[t]=e,V(n)}else"object"==typeof t&&V(t)}function V(t){var e,n=0;for(e in t)_.dynamicOverrides[e]=t[e];_.viewSpecCache={},_.populateOptionsHash();for(e in t)_.triggerOptionHandlers(e),n++;if(1===n){if("height"===e||"contentHeight"===e||"aspectRatio"===e)return void h(!0);if("defaultDate"===e)return;if("businessHours"===e)return void(Q&&(Q.unrenderBusinessHours(),Q.renderBusinessHours()));if("timezone"===e)return _.rezoneArrayEventSources(),void _.refetchEvents()}E(),b(),et={},d()}function P(t,e){var n=Array.prototype.slice.call(arguments,2);if(e=e||tt,this.triggerWith(t,e,n),_.options[t])return _.options[t].apply(e,n)}var _=this;_.render=s,_.destroy=l,_.rerenderEvents=m,_.changeView=u,_.select=T,_.unselect=C,_.prev=H,_.next=R,_.prevYear=x,_.nextYear=I,_.today=k,_.gotoDate=L,_.incrementDate=M,_.zoomTo=B,_.getDate=z,_.getCalendar=G,_.getView=O,_.option=A,_.publiclyTrigger=P,_.dynamicOverrides={},_.viewSpecCache={},_.optionHandlers={},_.overrides=t.extend({},i),_.populateOptionsHash();var Y;_.bindOptions(["locale","monthNames","monthNamesShort","dayNames","dayNamesShort","firstDay","weekNumberCalculation"],function(t,e,n,i,s,o,l){if("iso"===l&&(l="ISO"),Y=Z(Wt(t)),e&&(Y._months=e),n&&(Y._monthsShort=n),i&&(Y._weekdays=i),s&&(Y._weekdaysShort=s),null==o&&"ISO"===l&&(o=1),null!=o){var a=Z(Y._week);a.dow=o,Y._week=a}"ISO"!==l&&"local"!==l&&"function"!=typeof l||(Y._fullCalendar_weekCalc=l),J&&r(J)}),_.defaultAllDayEventDuration=e.duration(_.options.defaultAllDayEventDuration),_.defaultTimedEventDuration=e.duration(_.options.defaultTimedEventDuration),_.moment=function(){var t;return"local"===_.options.timezone?(t=qt.moment.apply(null,arguments),t.hasTime()&&t.local()):t="UTC"===_.options.timezone?qt.moment.utc.apply(null,arguments):qt.moment.parseZone.apply(null,arguments),r(t),t},_.localizeMoment=r,_.getIsAmbigTimezone=function(){return"local"!==_.options.timezone&&"UTC"!==_.options.timezone},_.applyTimezone=function(t){if(!t.hasTime())return t.clone();var e,n=_.moment(t.toArray()),i=t.time()-n.time();return i&&(e=n.clone().add(i),t.time()-e.time()===0&&(n=e)),n},_.getNow=function(){var t=_.options.now;return"function"==typeof t&&(t=t()),_.moment(t).stripZone()},_.getEventEnd=function(t){return t.end?t.end.clone():_.getDefaultEventEnd(t.allDay,t.start)},_.getDefaultEventEnd=function(t,e){var n=e.clone();return t?n.stripTime().add(_.defaultAllDayEventDuration):n.add(_.defaultTimedEventDuration),_.getIsAmbigTimezone()&&n.stripZone(),n},_.humanizeDuration=function(t){return t.locale(_.options.locale).humanize()},Ut.call(_);var W,U,j,q,$,Q,X,K,J,tt=n[0],et={},nt=0;J=null!=_.options.defaultDate?_.moment(_.options.defaultDate).stripZone():_.getNow(),_.getSuggestedViewHeight=function(){return void 0===X&&f(),X},_.isHeightAuto=function(){return"auto"===_.options.contentHeight||"auto"===_.options.height},_.setToolbarsTitle=function(t){W.proxyCall("updateTitle",t)},_.freezeContentHeight=F,_.thawContentHeight=N;var it=0;_.initialize()}function Yt(e){t.each(Re,function(t,n){null==e[t]&&(e[t]=n(e))})}function Wt(t){return e.localeData(t)||e.localeData("en")}function Ut(){function n(t,e){return!U.options.lazyFetching||s(t,e)?o(t,e):bt.resolve($)}function i(){$=r(nt),U.trigger("eventsReset",$)}function r(t){var e,n,i=[];for(e=0;e<t.length;e++)n=t[e],n.start.clone().stripZone()<Z&&U.getEventEnd(n).stripZone()>q&&i.push(n);return i}function s(t,e){return!q||t<q||e>Z}function o(t,e){return q=t,Z=e,l()}function l(){return u(tt,"reset")}function a(t){return u(E(t))}function u(t,e){var n,i;for("reset"===e?nt=[]:"add"!==e&&(nt=C(nt,t)),n=0;n<t.length;n++)i=t[n],"pending"!==i._status&&et++,i._fetchId=(i._fetchId||0)+1,i._status="pending";for(n=0;n<t.length;n++)i=t[n],c(i,i._fetchId);return et?new bt(function(t){U.one("eventsReceived",t)}):bt.resolve($)}function c(e,n){f(e,function(i){var r,s,o,l=t.isArray(e.events);if(n===e._fetchId&&"rejected"!==e._status){if(e._status="resolved",i)for(r=0;r<i.length;r++)s=i[r],o=l?s:F(s,e),o&&nt.push.apply(nt,_(o));h()}})}function d(t){var e="pending"===t._status;t._status="rejected",e&&h()}function h(){et--,et||(i(nt),U.trigger("eventsReceived",$))}function f(e,n){var i,r,s=qt.sourceFetchers;for(i=0;i<s.length;i++){if(r=s[i].call(U,e,q.clone(),Z.clone(),U.options.timezone,n),r===!0)return;if("object"==typeof r)return void f(r,n)}var o=e.events;if(o)t.isFunction(o)?(U.pushLoading(),o.call(U,q.clone(),Z.clone(),U.options.timezone,function(t){n(t),U.popLoading()})):t.isArray(o)?n(o):n();else{var l=e.url;if(l){var a,u=e.success,c=e.error,d=e.complete;a=t.isFunction(e.data)?e.data():e.data;var h=t.extend({},a||{}),g=J(e.startParam,U.options.startParam),p=J(e.endParam,U.options.endParam),v=J(e.timezoneParam,U.options.timezoneParam);g&&(h[g]=q.format()),p&&(h[p]=Z.format()),U.options.timezone&&"local"!=U.options.timezone&&(h[v]=U.options.timezone),U.pushLoading(),t.ajax(t.extend({},xe,e,{data:h,success:function(e){e=e||[];var i=K(u,this,arguments);t.isArray(i)&&(e=i),n(e)},error:function(){K(c,this,arguments),n()},complete:function(){K(d,this,arguments),U.popLoading()}}))}else n()}}function g(t){var e=p(t);e&&(tt.push(e),u([e],"add"))}function p(e){var n,i,r=qt.sourceNormalizers;if(t.isFunction(e)||t.isArray(e)?n={events:e}:"string"==typeof e?n={url:e}:"object"==typeof e&&(n=t.extend({},e)),n){for(n.className?"string"==typeof n.className&&(n.className=n.className.split(/\s+/)):n.className=[],t.isArray(n.events)&&(n.origArray=n.events,n.events=t.map(n.events,function(t){return F(t,n)})),i=0;i<r.length;i++)r[i].call(U,n);return n}}function v(t){y(b(t))}function m(t){null==t?y(tt,!0):y(E(t))}function y(e,n){var r;for(r=0;r<e.length;r++)d(e[r]);n?(tt=[],nt=[]):(tt=t.grep(tt,function(t){for(r=0;r<e.length;r++)if(t===e[r])return!1;return!0}),nt=C(nt,e)),i()}function S(){return tt.slice(1)}function w(e){return t.grep(tt,function(t){return t.id&&t.id===e})[0]}function E(e){e?t.isArray(e)||(e=[e]):e=[];var n,i=[];for(n=0;n<e.length;n++)i.push.apply(i,b(e[n]));return i}function b(e){var n,i;for(n=0;n<tt.length;n++)if(i=tt[n],i===e)return[i];return i=w(e),i?[i]:t.grep(tt,function(t){return D(e,t)})}function D(t,e){return t&&e&&T(t)==T(e)}function T(t){return("object"==typeof t?t.origArray||t.googleCalendarId||t.url||t.events:null)||t}function C(e,n){return t.grep(e,function(t){for(var e=0;e<n.length;e++)if(t.source===n[e])return!1;return!0})}function H(t){R([t])}function R(t){var e,n;for(e=0;e<t.length;e++)n=t[e],n.start=U.moment(n.start),n.end?n.end=U.moment(n.end):n.end=null,Y(n,x(n));i()}function x(e){var n={};return t.each(e,function(t,e){I(t)&&void 0!==e&&X(e)&&(n[t]=e)}),n}function I(t){return!/^_|^(id|allDay|start|end)$/.test(t)}function k(t,e){return L([t],e)}function L(t,e){var n,r,s,o,l,a=[];for(s=0;s<t.length;s++)if(r=F(t[s])){for(n=_(r),o=0;o<n.length;o++)l=n[o],l.source||(e&&(Q.events.push(l),l.source=Q),nt.push(l));a=a.concat(n)}return a.length&&i(),a}function M(e){var n,r;for(null==e?e=function(){return!0}:t.isFunction(e)||(n=e+"",e=function(t){return t._id==n}),nt=t.grep(nt,e,!0),r=0;r<tt.length;r++)t.isArray(tt[r].events)&&(tt[r].events=t.grep(tt[r].events,e,!0));i()}function B(e){return t.isFunction(e)?t.grep(nt,e):null!=e?(e+="",t.grep(nt,function(t){return t._id==e})):nt}function z(t){t.start=U.moment(t.start),t.end&&(t.end=U.moment(t.end)),jt(t)}function F(n,i){var r,s,o,l={};if(U.options.eventDataTransform&&(n=U.options.eventDataTransform(n)),i&&i.eventDataTransform&&(n=i.eventDataTransform(n)),t.extend(l,n),i&&(l.source=i),l._id=n._id||(void 0===n.id?"_fc"+Ie++:n.id+""),n.className?"string"==typeof n.className?l.className=n.className.split(/\s+/):l.className=n.className:l.className=[],r=n.start||n.date,s=n.end,j(r)&&(r=e.duration(r)),j(s)&&(s=e.duration(s)),n.dow||e.isDuration(r)||e.isDuration(s))l.start=r?e.duration(r):null,l.end=s?e.duration(s):null,l._recurring=!0;else{if(r&&(r=U.moment(r),!r.isValid()))return!1;s&&(s=U.moment(s),s.isValid()||(s=null)),o=n.allDay,void 0===o&&(o=J(i?i.allDayDefault:void 0,U.options.allDayDefault)),A(r,s,o,l)}return U.normalizeEvent(l),l}function A(t,e,n,i){i.start=t,i.end=e,i.allDay=n,V(i),jt(i)}function V(t){P(t),t.end&&!t.end.isAfter(t.start)&&(t.end=null),t.end||(U.options.forceEventDuration?t.end=U.getDefaultEventEnd(t.allDay,t.start):t.end=null)}function P(t){null==t.allDay&&(t.allDay=!(t.start.hasTime()||t.end&&t.end.hasTime())),t.allDay?(t.start.stripTime(),t.end&&t.end.stripTime()):(t.start.hasTime()||(t.start=U.applyTimezone(t.start.time(0))),t.end&&!t.end.hasTime()&&(t.end=U.applyTimezone(t.end.time(0))))}function _(e,n,i){var r,s,o,l,a,u,c,d,h,f=[];if(n=n||q,i=i||Z,e)if(e._recurring){if(s=e.dow)for(r={},o=0;o<s.length;o++)r[s[o]]=!0;for(l=n.clone().stripTime();l.isBefore(i);)r&&!r[l.day()]||(a=e.start,u=e.end,c=l.clone(),d=null,a&&(c=c.time(a)),u&&(d=l.clone().time(u)),h=t.extend({},e),A(c,d,!a&&!u,h),f.push(h)),l.add(1,"days")}else f.push(e);return f}function Y(e,n,i){function r(t,e){return i?O(t,e,i):n.allDay?G(t,e):N(t,e)}var s,o,l,a,u,c,d={};return n=n||{},n.start||(n.start=e.start.clone()),void 0===n.end&&(n.end=e.end?e.end.clone():null),null==n.allDay&&(n.allDay=e.allDay),V(n),s={start:e._start.clone(),end:e._end?e._end.clone():U.getDefaultEventEnd(e._allDay,e._start),allDay:n.allDay},V(s),o=null!==e._end&&null===n.end,l=r(n.start,s.start),n.end?(a=r(n.end,s.end),u=a.subtract(l)):u=null,t.each(n,function(t,e){I(t)&&void 0!==e&&(d[t]=e)}),c=W(B(e._id),o,n.allDay,l,u,d),{dateDelta:l,durationDelta:u,undo:c}}function W(e,n,i,r,s,o){var l=U.getIsAmbigTimezone(),a=[];return r&&!r.valueOf()&&(r=null),s&&!s.valueOf()&&(s=null),t.each(e,function(e,u){var c,d;c={start:u.start.clone(),end:u.end?u.end.clone():null,allDay:u.allDay},t.each(o,function(t){c[t]=u[t]}),d={start:u._start,end:u._end,allDay:i},V(d),n?d.end=null:s&&!d.end&&(d.end=U.getDefaultEventEnd(d.allDay,d.start)),r&&(d.start.add(r),d.end&&d.end.add(r)),s&&d.end.add(s),l&&!d.allDay&&(r||s)&&(d.start.stripZone(),d.end&&d.end.stripZone()),t.extend(u,o,d),jt(u),a.push(function(){t.extend(u,c),jt(u)})}),function(){for(var t=0;t<a.length;t++)a[t]()}}var U=this;U.requestEvents=n,U.reportEventChange=i,U.isFetchNeeded=s,U.fetchEvents=o,U.fetchEventSources=u,U.refetchEvents=l,U.refetchEventSources=a,U.getEventSources=S,U.getEventSourceById=w,U.addEventSource=g,U.removeEventSource=v,U.removeEventSources=m,U.updateEvent=H,U.updateEvents=R,U.renderEvent=k,U.renderEvents=L,U.removeEvents=M,U.clientEvents=B,U.mutateEvent=Y,U.normalizeEventDates=V,U.normalizeEventTimes=P;var q,Z,$,Q={events:[]},tt=[Q],et=0,nt=[];t.each((U.options.events?[U.options.events]:[]).concat(U.options.eventSources||[]),function(t,e){var n=p(e);n&&tt.push(n)}),U.getEventCache=function(){return nt},U.getPrunedEventCache=function(){return $},U.rezoneArrayEventSources=function(){var e,n,i;for(e=0;e<tt.length;e++)if(n=tt[e].events,t.isArray(n))for(i=0;i<n.length;i++)z(n[i])},U.buildEventFromInput=F,U.expandEvent=_}function jt(t){t._allDay=t.allDay,t._start=t.start.clone(),t._end=t.end?t.end.clone():null}var qt=t.fullCalendar={version:"3.1.0",internalApiVersion:7},Zt=qt.views={};t.fn.fullCalendar=function(e){var n=Array.prototype.slice.call(arguments,1),i=this;return this.each(function(r,s){var o,l=t(s),a=l.data("fullCalendar");"string"==typeof e?a&&t.isFunction(a[e])&&(o=a[e].apply(a,n),r||(i=o),"destroy"===e&&l.removeData("fullCalendar")):a||(a=new De(l,e),l.data("fullCalendar",a),a.render())}),i};var $t=["header","footer","buttonText","buttonIcons","themeButtonIcons"];qt.intersectRanges=F,qt.applyAll=K,qt.debounce=at,qt.isInt=ot,qt.htmlEscape=tt,qt.cssToStr=nt,qt.proxy=lt,qt.capitaliseFirstLetter=rt,qt.getOuterRect=h,qt.getClientRect=f,qt.getContentRect=g,qt.getScrollbarWidths=p;var Qt=null;qt.preventDefault=T,qt.intersectRects=R,qt.parseFieldSpecs=L,qt.compareByFieldSpecs=M,qt.compareByFieldSpec=B,qt.flexibleCompare=z,qt.computeIntervalUnit=A,qt.divideRangeByDuration=P,qt.divideDurationByDuration=_,qt.multiplyDuration=Y,qt.durationHasTime=W;var Xt=["sun","mon","tue","wed","thu","fri","sat"],Kt=["year","month","week","day","hour","minute","second","millisecond"];qt.log=function(){var t=window.console;if(t&&t.log)return t.log.apply(t,arguments)},qt.warn=function(){var t=window.console;return t&&t.warn?t.warn.apply(t,arguments):qt.log.apply(qt,arguments)};var Jt={}.hasOwnProperty;qt.createObject=Z;var te=/^\s*\d{4}-\d\d$/,ee=/^\s*\d{4}-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?)?$/,ne=e.fn,ie=t.extend({},ne),re=e.momentProperties;re.push("_fullCalendar"),re.push("_ambigTime"),re.push("_ambigZone"),qt.moment=function(){return ut(arguments)},qt.moment.utc=function(){var t=ut(arguments,!0);return t.hasTime()&&t.utc(),t},qt.moment.parseZone=function(){return ut(arguments,!0,!0)},ne.week=ne.weeks=function(t){var e=this._locale._fullCalendar_weekCalc;return null==t&&"function"==typeof e?e(this):"ISO"===e?ie.isoWeek.apply(this,arguments):ie.week.apply(this,arguments)},ne.time=function(t){if(!this._fullCalendar)return ie.time.apply(this,arguments);if(null==t)return e.duration({hours:this.hours(),minutes:this.minutes(),seconds:this.seconds(),milliseconds:this.milliseconds()});this._ambigTime=!1,e.isDuration(t)||e.isMoment(t)||(t=e.duration(t));var n=0;return e.isDuration(t)&&(n=24*Math.floor(t.asDays())),this.hours(n+t.hours()).minutes(t.minutes()).seconds(t.seconds()).milliseconds(t.milliseconds())},ne.stripTime=function(){return this._ambigTime||(this.utc(!0),this.set({hours:0,minutes:0,seconds:0,ms:0}),this._ambigTime=!0,this._ambigZone=!0),this},ne.hasTime=function(){return!this._ambigTime},ne.stripZone=function(){var t;return this._ambigZone||(t=this._ambigTime,this.utc(!0),this._ambigTime=t||!1,this._ambigZone=!0),this},ne.hasZone=function(){return!this._ambigZone},ne.local=function(t){return ie.local.call(this,this._ambigZone||t),this._ambigTime=!1,this._ambigZone=!1,this},ne.utc=function(t){return ie.utc.call(this,t),this._ambigTime=!1,this._ambigZone=!1,this},ne.utcOffset=function(t){return null!=t&&(this._ambigTime=!1,this._ambigZone=!1),ie.utcOffset.apply(this,arguments)},ne.format=function(){return this._fullCalendar&&arguments[0]?dt(this,arguments[0]):this._ambigTime?ct(this,"YYYY-MM-DD"):this._ambigZone?ct(this,"YYYY-MM-DD[T]HH:mm:ss"):ie.format.apply(this,arguments)},ne.toISOString=function(){return this._ambigTime?ct(this,"YYYY-MM-DD"):this._ambigZone?ct(this,"YYYY-MM-DD[T]HH:mm:ss"):ie.toISOString.apply(this,arguments)};var se={t:function(t){return ct(t,"a").charAt(0)},T:function(t){return ct(t,"A").charAt(0)}};qt.formatRange=gt;var oe={Y:"year",M:"month",D:"day",d:"day",A:"second",a:"second",T:"second",t:"second",H:"second",h:"second",m:"second",s:"second"},le={},ae={Y:{value:1,unit:"year"},M:{value:2,unit:"month"},W:{value:3,unit:"week"},w:{value:3,unit:"week"},D:{value:4,unit:"day"},d:{value:4,unit:"day"}};qt.queryMostGranularFormatUnit=function(t){ -var e,n,i,r,s=mt(t);for(e=0;e<s.length;e++)n=s[e],n.token&&(i=ae[n.token.charAt(0)],i&&(!r||i.value>r.value)&&(r=i));return r?r.unit:null},qt.Class=St,St.extend=function(){var t,e,n=arguments.length;for(t=0;t<n;t++)e=arguments[t],t<n-1&&Et(this,e);return wt(this,e||{})},St.mixin=function(t){Et(this,t)},qt.Promise=bt,bt.immediate=!0,bt.resolve=function(e){if(e&&"function"==typeof e.resolve)return e.promise();if(e&&"function"==typeof e.then)return e;var n=t.Deferred().resolve(e),i=n.promise();if(bt.immediate){var r=i.then;i._value=e,i.then=function(t,n){return"function"==typeof t?bt.resolve(t(e)):r.call(i,t,n)}}return i},bt.reject=function(){return t.Deferred().reject().promise()},bt.all=function(e){var n,i,r,s=!1;if(bt.immediate)for(s=!0,n=[],i=0;i<e.length;i++)if(r=e[i],r&&"function"==typeof r.state&&"resolved"===r.state()&&"_value"in r)n.push(r._value);else{if(r&&"function"==typeof r.then){s=!1;break}n.push(r)}return s?bt.resolve(n):t.when.apply(t.when,e).then(function(){return t.when(t.makeArray(arguments))})},qt.TaskQueue=Dt;var ue=qt.EmitterMixin={on:function(e,n){return t(this).on(e,this._prepareIntercept(n)),this},one:function(e,n){return t(this).one(e,this._prepareIntercept(n)),this},_prepareIntercept:function(e){var n=function(t,n){return e.apply(n.context||this,n.args||[])};return e.guid||(e.guid=t.guid++),n.guid=e.guid,n},off:function(e,n){return t(this).off(e,n),this},trigger:function(e){var n=Array.prototype.slice.call(arguments,1);return t(this).triggerHandler(e,{args:n}),this},triggerWith:function(e,n,i){return t(this).triggerHandler(e,{context:n,args:i}),this}},ce=qt.ListenerMixin=function(){var e=0,n={listenerId:null,listenTo:function(e,n,i){if("object"==typeof n)for(var r in n)n.hasOwnProperty(r)&&this.listenTo(e,r,n[r]);else"string"==typeof n&&e.on(n+"."+this.getListenerNamespace(),t.proxy(i,this))},stopListeningTo:function(t,e){t.off((e||"")+"."+this.getListenerNamespace())},getListenerNamespace:function(){return null==this.listenerId&&(this.listenerId=e++),"_listener"+this.listenerId}};return n}(),de={isIgnoringMouse:!1,delayUnignoreMouse:null,initMouseIgnoring:function(t){this.delayUnignoreMouse=at(lt(this,"unignoreMouse"),t||1e3)},tempIgnoreMouse:function(){this.isIgnoringMouse=!0,this.delayUnignoreMouse()},unignoreMouse:function(){this.isIgnoringMouse=!1}},he=St.extend(ce,{isHidden:!0,options:null,el:null,margin:10,constructor:function(t){this.options=t||{}},show:function(){this.isHidden&&(this.el||this.render(),this.el.show(),this.position(),this.isHidden=!1,this.trigger("show"))},hide:function(){this.isHidden||(this.el.hide(),this.isHidden=!0,this.trigger("hide"))},render:function(){var e=this,n=this.options;this.el=t('<div class="fc-popover"/>').addClass(n.className||"").css({top:0,left:0}).append(n.content).appendTo(n.parentEl),this.el.on("click",".fc-close",function(){e.hide()}),n.autoHide&&this.listenTo(t(document),"mousedown",this.documentMousedown)},documentMousedown:function(e){this.el&&!t(e.target).closest(this.el).length&&this.hide()},removeElement:function(){this.hide(),this.el&&(this.el.remove(),this.el=null),this.stopListeningTo(t(document),"mousedown")},position:function(){var e,n,i,r,s,o=this.options,l=this.el.offsetParent().offset(),a=this.el.outerWidth(),u=this.el.outerHeight(),c=t(window),h=d(this.el);r=o.top||0,s=void 0!==o.left?o.left:void 0!==o.right?o.right-a:0,h.is(window)||h.is(document)?(h=c,e=0,n=0):(i=h.offset(),e=i.top,n=i.left),e+=c.scrollTop(),n+=c.scrollLeft(),o.viewportConstrain!==!1&&(r=Math.min(r,e+h.outerHeight()-u-this.margin),r=Math.max(r,e+this.margin),s=Math.min(s,n+h.outerWidth()-a-this.margin),s=Math.max(s,n+this.margin)),this.el.css({top:r-l.top,left:s-l.left})},trigger:function(t){this.options[t]&&this.options[t].apply(this,Array.prototype.slice.call(arguments,1))}}),fe=qt.CoordCache=St.extend({els:null,forcedOffsetParentEl:null,origin:null,boundingRect:null,isHorizontal:!1,isVertical:!1,lefts:null,rights:null,tops:null,bottoms:null,constructor:function(e){this.els=t(e.els),this.isHorizontal=e.isHorizontal,this.isVertical=e.isVertical,this.forcedOffsetParentEl=e.offsetParent?t(e.offsetParent):null},build:function(){var t=this.forcedOffsetParentEl;!t&&this.els.length>0&&(t=this.els.eq(0).offsetParent()),this.origin=t?t.offset():null,this.boundingRect=this.queryBoundingRect(),this.isHorizontal&&this.buildElHorizontals(),this.isVertical&&this.buildElVerticals()},clear:function(){this.origin=null,this.boundingRect=null,this.lefts=null,this.rights=null,this.tops=null,this.bottoms=null},ensureBuilt:function(){this.origin||this.build()},buildElHorizontals:function(){var e=[],n=[];this.els.each(function(i,r){var s=t(r),o=s.offset().left,l=s.outerWidth();e.push(o),n.push(o+l)}),this.lefts=e,this.rights=n},buildElVerticals:function(){var e=[],n=[];this.els.each(function(i,r){var s=t(r),o=s.offset().top,l=s.outerHeight();e.push(o),n.push(o+l)}),this.tops=e,this.bottoms=n},getHorizontalIndex:function(t){this.ensureBuilt();var e,n=this.lefts,i=this.rights,r=n.length;for(e=0;e<r;e++)if(t>=n[e]&&t<i[e])return e},getVerticalIndex:function(t){this.ensureBuilt();var e,n=this.tops,i=this.bottoms,r=n.length;for(e=0;e<r;e++)if(t>=n[e]&&t<i[e])return e},getLeftOffset:function(t){return this.ensureBuilt(),this.lefts[t]},getLeftPosition:function(t){return this.ensureBuilt(),this.lefts[t]-this.origin.left},getRightOffset:function(t){return this.ensureBuilt(),this.rights[t]},getRightPosition:function(t){return this.ensureBuilt(),this.rights[t]-this.origin.left},getWidth:function(t){return this.ensureBuilt(),this.rights[t]-this.lefts[t]},getTopOffset:function(t){return this.ensureBuilt(),this.tops[t]},getTopPosition:function(t){return this.ensureBuilt(),this.tops[t]-this.origin.top},getBottomOffset:function(t){return this.ensureBuilt(),this.bottoms[t]},getBottomPosition:function(t){return this.ensureBuilt(),this.bottoms[t]-this.origin.top},getHeight:function(t){return this.ensureBuilt(),this.bottoms[t]-this.tops[t]},queryBoundingRect:function(){var t;return this.els.length>0&&(t=d(this.els.eq(0)),!t.is(document))?f(t):null},isPointInBounds:function(t,e){return this.isLeftInBounds(t)&&this.isTopInBounds(e)},isLeftInBounds:function(t){return!this.boundingRect||t>=this.boundingRect.left&&t<this.boundingRect.right},isTopInBounds:function(t){return!this.boundingRect||t>=this.boundingRect.top&&t<this.boundingRect.bottom}}),ge=qt.DragListener=St.extend(ce,de,{options:null,subjectEl:null,originX:null,originY:null,scrollEl:null,isInteracting:!1,isDistanceSurpassed:!1,isDelayEnded:!1,isDragging:!1,isTouch:!1,delay:null,delayTimeoutId:null,minDistance:null,handleTouchScrollProxy:null,constructor:function(t){this.options=t||{},this.handleTouchScrollProxy=lt(this,"handleTouchScroll"),this.initMouseIgnoring(500)},startInteraction:function(e,n){var i=b(e);if("mousedown"===e.type){if(this.isIgnoringMouse)return;if(!S(e))return;e.preventDefault()}this.isInteracting||(n=n||{},this.delay=J(n.delay,this.options.delay,0),this.minDistance=J(n.distance,this.options.distance,0),this.subjectEl=this.options.subjectEl,this.isInteracting=!0,this.isTouch=i,this.isDelayEnded=!1,this.isDistanceSurpassed=!1,this.originX=w(e),this.originY=E(e),this.scrollEl=d(t(e.target)),this.bindHandlers(),this.initAutoScroll(),this.handleInteractionStart(e),this.startDelay(e),this.minDistance||this.handleDistanceSurpassed(e))},handleInteractionStart:function(t){this.trigger("interactionStart",t)},endInteraction:function(t,e){this.isInteracting&&(this.endDrag(t),this.delayTimeoutId&&(clearTimeout(this.delayTimeoutId),this.delayTimeoutId=null),this.destroyAutoScroll(),this.unbindHandlers(),this.isInteracting=!1,this.handleInteractionEnd(t,e),this.isTouch&&this.tempIgnoreMouse())},handleInteractionEnd:function(t,e){this.trigger("interactionEnd",t,e||!1)},bindHandlers:function(){var e=this,n=1;this.isTouch?(this.listenTo(t(document),{touchmove:this.handleTouchMove,touchend:this.endInteraction,touchcancel:this.endInteraction,touchstart:function(t){n?n--:e.endInteraction(t,!0)}}),!C(this.handleTouchScrollProxy)&&this.scrollEl&&this.listenTo(this.scrollEl,"scroll",this.handleTouchScroll)):this.listenTo(t(document),{mousemove:this.handleMouseMove,mouseup:this.endInteraction}),this.listenTo(t(document),{selectstart:T,contextmenu:T})},unbindHandlers:function(){this.stopListeningTo(t(document)),H(this.handleTouchScrollProxy),this.scrollEl&&this.stopListeningTo(this.scrollEl,"scroll")},startDrag:function(t,e){this.startInteraction(t,e),this.isDragging||(this.isDragging=!0,this.handleDragStart(t))},handleDragStart:function(t){this.trigger("dragStart",t)},handleMove:function(t){var e,n=w(t)-this.originX,i=E(t)-this.originY,r=this.minDistance;this.isDistanceSurpassed||(e=n*n+i*i,e>=r*r&&this.handleDistanceSurpassed(t)),this.isDragging&&this.handleDrag(n,i,t)},handleDrag:function(t,e,n){this.trigger("drag",t,e,n),this.updateAutoScroll(n)},endDrag:function(t){this.isDragging&&(this.isDragging=!1,this.handleDragEnd(t))},handleDragEnd:function(t){this.trigger("dragEnd",t)},startDelay:function(t){var e=this;this.delay?this.delayTimeoutId=setTimeout(function(){e.handleDelayEnd(t)},this.delay):this.handleDelayEnd(t)},handleDelayEnd:function(t){this.isDelayEnded=!0,this.isDistanceSurpassed&&this.startDrag(t)},handleDistanceSurpassed:function(t){this.isDistanceSurpassed=!0,this.isDelayEnded&&this.startDrag(t)},handleTouchMove:function(t){this.isDragging&&t.preventDefault(),this.handleMove(t)},handleMouseMove:function(t){this.handleMove(t)},handleTouchScroll:function(t){this.isDragging||this.endInteraction(t,!0)},trigger:function(t){this.options[t]&&this.options[t].apply(this,Array.prototype.slice.call(arguments,1)),this["_"+t]&&this["_"+t].apply(this,Array.prototype.slice.call(arguments,1))}});ge.mixin({isAutoScroll:!1,scrollBounds:null,scrollTopVel:null,scrollLeftVel:null,scrollIntervalId:null,scrollSensitivity:30,scrollSpeed:200,scrollIntervalMs:50,initAutoScroll:function(){var t=this.scrollEl;this.isAutoScroll=this.options.scroll&&t&&!t.is(window)&&!t.is(document),this.isAutoScroll&&this.listenTo(t,"scroll",at(this.handleDebouncedScroll,100))},destroyAutoScroll:function(){this.endAutoScroll(),this.isAutoScroll&&this.stopListeningTo(this.scrollEl,"scroll")},computeScrollBounds:function(){this.isAutoScroll&&(this.scrollBounds=h(this.scrollEl))},updateAutoScroll:function(t){var e,n,i,r,s=this.scrollSensitivity,o=this.scrollBounds,l=0,a=0;o&&(e=(s-(E(t)-o.top))/s,n=(s-(o.bottom-E(t)))/s,i=(s-(w(t)-o.left))/s,r=(s-(o.right-w(t)))/s,e>=0&&e<=1?l=e*this.scrollSpeed*-1:n>=0&&n<=1&&(l=n*this.scrollSpeed),i>=0&&i<=1?a=i*this.scrollSpeed*-1:r>=0&&r<=1&&(a=r*this.scrollSpeed)),this.setScrollVel(l,a)},setScrollVel:function(t,e){this.scrollTopVel=t,this.scrollLeftVel=e,this.constrainScrollVel(),!this.scrollTopVel&&!this.scrollLeftVel||this.scrollIntervalId||(this.scrollIntervalId=setInterval(lt(this,"scrollIntervalFunc"),this.scrollIntervalMs))},constrainScrollVel:function(){var t=this.scrollEl;this.scrollTopVel<0?t.scrollTop()<=0&&(this.scrollTopVel=0):this.scrollTopVel>0&&t.scrollTop()+t[0].clientHeight>=t[0].scrollHeight&&(this.scrollTopVel=0),this.scrollLeftVel<0?t.scrollLeft()<=0&&(this.scrollLeftVel=0):this.scrollLeftVel>0&&t.scrollLeft()+t[0].clientWidth>=t[0].scrollWidth&&(this.scrollLeftVel=0)},scrollIntervalFunc:function(){var t=this.scrollEl,e=this.scrollIntervalMs/1e3;this.scrollTopVel&&t.scrollTop(t.scrollTop()+this.scrollTopVel*e),this.scrollLeftVel&&t.scrollLeft(t.scrollLeft()+this.scrollLeftVel*e),this.constrainScrollVel(),this.scrollTopVel||this.scrollLeftVel||this.endAutoScroll()},endAutoScroll:function(){this.scrollIntervalId&&(clearInterval(this.scrollIntervalId),this.scrollIntervalId=null,this.handleScrollEnd())},handleDebouncedScroll:function(){this.scrollIntervalId||this.handleScrollEnd()},handleScrollEnd:function(){}});var pe=ge.extend({component:null,origHit:null,hit:null,coordAdjust:null,constructor:function(t,e){ge.call(this,e),this.component=t},handleInteractionStart:function(t){var e,n,i,r=this.subjectEl;this.computeCoords(),t?(n={left:w(t),top:E(t)},i=n,r&&(e=h(r),i=x(i,e)),this.origHit=this.queryHit(i.left,i.top),r&&this.options.subjectCenter&&(this.origHit&&(e=R(this.origHit,e)||e),i=I(e)),this.coordAdjust=k(i,n)):(this.origHit=null,this.coordAdjust=null),ge.prototype.handleInteractionStart.apply(this,arguments)},computeCoords:function(){this.component.prepareHits(),this.computeScrollBounds()},handleDragStart:function(t){var e;ge.prototype.handleDragStart.apply(this,arguments),e=this.queryHit(w(t),E(t)),e&&this.handleHitOver(e)},handleDrag:function(t,e,n){var i;ge.prototype.handleDrag.apply(this,arguments),i=this.queryHit(w(n),E(n)),Tt(i,this.hit)||(this.hit&&this.handleHitOut(),i&&this.handleHitOver(i))},handleDragEnd:function(){this.handleHitDone(),ge.prototype.handleDragEnd.apply(this,arguments)},handleHitOver:function(t){var e=Tt(t,this.origHit);this.hit=t,this.trigger("hitOver",this.hit,e,this.origHit)},handleHitOut:function(){this.hit&&(this.trigger("hitOut",this.hit),this.handleHitDone(),this.hit=null)},handleHitDone:function(){this.hit&&this.trigger("hitDone",this.hit)},handleInteractionEnd:function(){ge.prototype.handleInteractionEnd.apply(this,arguments),this.origHit=null,this.hit=null,this.component.releaseHits()},handleScrollEnd:function(){ge.prototype.handleScrollEnd.apply(this,arguments),this.computeCoords()},queryHit:function(t,e){return this.coordAdjust&&(t+=this.coordAdjust.left,e+=this.coordAdjust.top),this.component.queryHit(t,e)}}),ve=St.extend(ce,{options:null,sourceEl:null,el:null,parentEl:null,top0:null,left0:null,y0:null,x0:null,topDelta:null,leftDelta:null,isFollowing:!1,isHidden:!1,isAnimating:!1,constructor:function(e,n){this.options=n=n||{},this.sourceEl=e,this.parentEl=n.parentEl?t(n.parentEl):e.parent()},start:function(e){this.isFollowing||(this.isFollowing=!0,this.y0=E(e),this.x0=w(e),this.topDelta=0,this.leftDelta=0,this.isHidden||this.updatePosition(),b(e)?this.listenTo(t(document),"touchmove",this.handleMove):this.listenTo(t(document),"mousemove",this.handleMove))},stop:function(e,n){function i(){r.isAnimating=!1,r.removeElement(),r.top0=r.left0=null,n&&n()}var r=this,s=this.options.revertDuration;this.isFollowing&&!this.isAnimating&&(this.isFollowing=!1,this.stopListeningTo(t(document)),e&&s&&!this.isHidden?(this.isAnimating=!0,this.el.animate({top:this.top0,left:this.left0},{duration:s,complete:i})):i())},getEl:function(){var t=this.el;return t||(t=this.el=this.sourceEl.clone().addClass(this.options.additionalClass||"").css({position:"absolute",visibility:"",display:this.isHidden?"none":"",margin:0,right:"auto",bottom:"auto",width:this.sourceEl.width(),height:this.sourceEl.height(),opacity:this.options.opacity||"",zIndex:this.options.zIndex}),t.addClass("fc-unselectable"),t.appendTo(this.parentEl)),t},removeElement:function(){this.el&&(this.el.remove(),this.el=null)},updatePosition:function(){var t,e;this.getEl(),null===this.top0&&(t=this.sourceEl.offset(),e=this.el.offsetParent().offset(),this.top0=t.top-e.top,this.left0=t.left-e.left),this.el.css({top:this.top0+this.topDelta,left:this.left0+this.leftDelta})},handleMove:function(t){this.topDelta=E(t)-this.y0,this.leftDelta=w(t)-this.x0,this.isHidden||this.updatePosition()},hide:function(){this.isHidden||(this.isHidden=!0,this.el&&this.el.hide())},show:function(){this.isHidden&&(this.isHidden=!1,this.updatePosition(),this.getEl().show())}}),me=qt.Grid=St.extend(ce,de,{hasDayInteractions:!0,view:null,isRTL:null,start:null,end:null,el:null,elsByFill:null,eventTimeFormat:null,displayEventTime:null,displayEventEnd:null,minResizeDuration:null,largeUnit:null,dayDragListener:null,segDragListener:null,segResizeListener:null,externalDragListener:null,constructor:function(t){this.view=t,this.isRTL=t.opt("isRTL"),this.elsByFill={},this.dayDragListener=this.buildDayDragListener(),this.initMouseIgnoring()},computeEventTimeFormat:function(){return this.view.opt("smallTimeFormat")},computeDisplayEventTime:function(){return!0},computeDisplayEventEnd:function(){return!0},setRange:function(t){this.start=t.start.clone(),this.end=t.end.clone(),this.rangeUpdated(),this.processRangeOptions()},rangeUpdated:function(){},processRangeOptions:function(){var t,e,n=this.view;this.eventTimeFormat=n.opt("eventTimeFormat")||n.opt("timeFormat")||this.computeEventTimeFormat(),t=n.opt("displayEventTime"),null==t&&(t=this.computeDisplayEventTime()),e=n.opt("displayEventEnd"),null==e&&(e=this.computeDisplayEventEnd()),this.displayEventTime=t,this.displayEventEnd=e},spanToSegs:function(t){},diffDates:function(t,e){return this.largeUnit?O(t,e,this.largeUnit):N(t,e)},prepareHits:function(){},releaseHits:function(){},queryHit:function(t,e){},getHitSpan:function(t){},getHitEl:function(t){},setElement:function(t){this.el=t,this.hasDayInteractions&&(D(t),this.bindDayHandler("touchstart",this.dayTouchStart),this.bindDayHandler("mousedown",this.dayMousedown)),this.bindSegHandlers(),this.bindGlobalHandlers()},bindDayHandler:function(e,n){var i=this;this.el.on(e,function(e){if(!t(e.target).is(i.segSelector+","+i.segSelector+" *,.fc-more,a[data-goto]"))return n.call(i,e)})},removeElement:function(){this.unbindGlobalHandlers(),this.clearDragListeners(),this.el.remove()},renderSkeleton:function(){},renderDates:function(){},unrenderDates:function(){},bindGlobalHandlers:function(){this.listenTo(t(document),{dragstart:this.externalDragStart,sortstart:this.externalDragStart})},unbindGlobalHandlers:function(){this.stopListeningTo(t(document))},dayMousedown:function(t){this.isIgnoringMouse||this.dayDragListener.startInteraction(t,{})},dayTouchStart:function(t){var e=this.view,n=e.opt("selectLongPressDelay");(e.isSelected||e.selectedEvent)&&this.tempIgnoreMouse(),null==n&&(n=e.opt("longPressDelay")),this.dayDragListener.startInteraction(t,{delay:n})},buildDayDragListener:function(){var t,e,n=this,i=this.view,r=i.opt("selectable"),l=new pe(this,{scroll:i.opt("dragScroll"),interactionStart:function(){t=l.origHit,e=null},dragStart:function(){i.unselect()},hitOver:function(i,o,l){l&&(o||(t=null),r&&(e=n.computeSelection(n.getHitSpan(l),n.getHitSpan(i)),e?n.renderSelection(e):e===!1&&s()))},hitOut:function(){t=null,e=null,n.unrenderSelection()},hitDone:function(){o()},interactionEnd:function(r,s){s||(t&&!n.isIgnoringMouse&&i.triggerDayClick(n.getHitSpan(t),n.getHitEl(t),r),e&&i.reportSelection(e,r))}});return l},clearDragListeners:function(){this.dayDragListener.endInteraction(),this.segDragListener&&this.segDragListener.endInteraction(),this.segResizeListener&&this.segResizeListener.endInteraction(),this.externalDragListener&&this.externalDragListener.endInteraction()},renderEventLocationHelper:function(t,e){var n=this.fabricateHelperEvent(t,e);return this.renderHelper(n,e)},fabricateHelperEvent:function(t,e){var n=e?Z(e.event):{};return n.start=t.start.clone(),n.end=t.end?t.end.clone():null,n.allDay=null,this.view.calendar.normalizeEventDates(n),n.className=(n.className||[]).concat("fc-helper"),e||(n.editable=!1),n},renderHelper:function(t,e){},unrenderHelper:function(){},renderSelection:function(t){this.renderHighlight(t)},unrenderSelection:function(){this.unrenderHighlight()},computeSelection:function(t,e){var n=this.computeSelectionSpan(t,e);return!(n&&!this.view.calendar.isSelectionSpanAllowed(n))&&n},computeSelectionSpan:function(t,e){var n=[t.start,t.end,e.start,e.end];return n.sort(st),{start:n[0].clone(),end:n[3].clone()}},renderHighlight:function(t){this.renderFill("highlight",this.spanToSegs(t))},unrenderHighlight:function(){this.unrenderFill("highlight")},highlightSegClasses:function(){return["fc-highlight"]},renderBusinessHours:function(){},unrenderBusinessHours:function(){},getNowIndicatorUnit:function(){},renderNowIndicator:function(t){},unrenderNowIndicator:function(){},renderFill:function(t,e){},unrenderFill:function(t){var e=this.elsByFill[t];e&&(e.remove(),delete this.elsByFill[t])},renderFillSegEls:function(e,n){var i,r=this,s=this[e+"SegEl"],o="",l=[];if(n.length){for(i=0;i<n.length;i++)o+=this.fillSegHtml(e,n[i]);t(o).each(function(e,i){var o=n[e],a=t(i);s&&(a=s.call(r,o,a)),a&&(a=t(a),a.is(r.fillSegTag)&&(o.el=a,l.push(o)))})}return l},fillSegTag:"div",fillSegHtml:function(t,e){var n=this[t+"SegClasses"],i=this[t+"SegCss"],r=n?n.call(this,e):[],s=nt(i?i.call(this,e):{});return"<"+this.fillSegTag+(r.length?' class="'+r.join(" ")+'"':"")+(s?' style="'+s+'"':"")+" />"},getDayClasses:function(t,e){var n=this.view,i=n.calendar.getNow(),r=["fc-"+Xt[t.day()]];return 1==n.intervalDuration.as("months")&&t.month()!=n.intervalStart.month()&&r.push("fc-other-month"),t.isSame(i,"day")?(r.push("fc-today"),e!==!0&&r.push(n.highlightStateClass)):t<i?r.push("fc-past"):r.push("fc-future"),r}});me.mixin({segSelector:".fc-event-container > *",mousedOverSeg:null,isDraggingSeg:!1,isResizingSeg:!1,isDraggingExternal:!1,segs:null,renderEvents:function(t){var e,n=[],i=[];for(e=0;e<t.length;e++)(Rt(t[e])?n:i).push(t[e]);this.segs=[].concat(this.renderBgEvents(n),this.renderFgEvents(i))},renderBgEvents:function(t){var e=this.eventsToSegs(t);return this.renderBgSegs(e)||e},renderFgEvents:function(t){var e=this.eventsToSegs(t);return this.renderFgSegs(e)||e},unrenderEvents:function(){this.handleSegMouseout(),this.clearDragListeners(),this.unrenderFgSegs(),this.unrenderBgSegs(),this.segs=null},getEventSegs:function(){return this.segs||[]},renderFgSegs:function(t){},unrenderFgSegs:function(){},renderFgSegEls:function(e,n){var i,r=this.view,s="",o=[];if(e.length){for(i=0;i<e.length;i++)s+=this.fgSegHtml(e[i],n);t(s).each(function(n,i){var s=e[n],l=r.resolveEventEl(s.event,t(i));l&&(l.data("fc-seg",s),s.el=l,o.push(s))})}return o},fgSegHtml:function(t,e){},renderBgSegs:function(t){return this.renderFill("bgEvent",t)},unrenderBgSegs:function(){this.unrenderFill("bgEvent")},bgEventSegEl:function(t,e){return this.view.resolveEventEl(t.event,e)},bgEventSegClasses:function(t){var e=t.event,n=e.source||{};return["fc-bgevent"].concat(e.className,n.className||[])},bgEventSegCss:function(t){return{"background-color":this.getSegSkinCss(t)["background-color"]}},businessHoursSegClasses:function(t){return["fc-nonbusiness","fc-bgevent"]},buildBusinessHourSegs:function(t,e){return this.eventsToSegs(this.buildBusinessHourEvents(t,e))},buildBusinessHourEvents:function(e,n){var i,r=this.view.calendar;return null==n&&(n=r.options.businessHours),i=r.computeBusinessHourEvents(e,n),!i.length&&n&&(i=[t.extend({},ke,{start:this.view.end,end:this.view.end,dow:null})]),i},bindSegHandlers:function(){this.bindSegHandlersToEl(this.el)},bindSegHandlersToEl:function(t){this.bindSegHandlerToEl(t,"touchstart",this.handleSegTouchStart),this.bindSegHandlerToEl(t,"touchend",this.handleSegTouchEnd),this.bindSegHandlerToEl(t,"mouseenter",this.handleSegMouseover),this.bindSegHandlerToEl(t,"mouseleave",this.handleSegMouseout),this.bindSegHandlerToEl(t,"mousedown",this.handleSegMousedown),this.bindSegHandlerToEl(t,"click",this.handleSegClick)},bindSegHandlerToEl:function(e,n,i){var r=this;e.on(n,this.segSelector,function(e){var n=t(this).data("fc-seg");if(n&&!r.isDraggingSeg&&!r.isResizingSeg)return i.call(r,n,e)})},handleSegClick:function(t,e){var n=this.view.publiclyTrigger("eventClick",t.el[0],t.event,e);n===!1&&e.preventDefault()},handleSegMouseover:function(t,e){this.isIgnoringMouse||this.mousedOverSeg||(this.mousedOverSeg=t,this.view.isEventResizable(t.event)&&t.el.addClass("fc-allow-mouse-resize"),this.view.publiclyTrigger("eventMouseover",t.el[0],t.event,e))},handleSegMouseout:function(t,e){e=e||{},this.mousedOverSeg&&(t=t||this.mousedOverSeg,this.mousedOverSeg=null,this.view.isEventResizable(t.event)&&t.el.removeClass("fc-allow-mouse-resize"),this.view.publiclyTrigger("eventMouseout",t.el[0],t.event,e))},handleSegMousedown:function(t,e){var n=this.startSegResize(t,e,{distance:5});!n&&this.view.isEventDraggable(t.event)&&this.buildSegDragListener(t).startInteraction(e,{distance:5})},handleSegTouchStart:function(t,e){var n,i,r=this.view,s=t.event,o=r.isEventSelected(s),l=r.isEventDraggable(s),a=r.isEventResizable(s),u=!1;o&&a&&(u=this.startSegResize(t,e)),u||!l&&!a||(i=r.opt("eventLongPressDelay"),null==i&&(i=r.opt("longPressDelay")),n=l?this.buildSegDragListener(t):this.buildSegSelectListener(t),n.startInteraction(e,{delay:o?0:i})),this.tempIgnoreMouse()},handleSegTouchEnd:function(t,e){this.tempIgnoreMouse()},startSegResize:function(e,n,i){return!!t(n.target).is(".fc-resizer")&&(this.buildSegResizeListener(e,t(n.target).is(".fc-start-resizer")).startInteraction(n,i),!0)},buildSegDragListener:function(t){var e,n,i,r=this,l=this.view,a=l.calendar,u=t.el,c=t.event;if(this.segDragListener)return this.segDragListener;var d=this.segDragListener=new pe(l,{scroll:l.opt("dragScroll"),subjectEl:u,subjectCenter:!0,interactionStart:function(i){t.component=r,e=!1,n=new ve(t.el,{additionalClass:"fc-dragging",parentEl:l.el,opacity:d.isTouch?null:l.opt("dragOpacity"),revertDuration:l.opt("dragRevertDuration"),zIndex:2}),n.hide(),n.start(i)},dragStart:function(n){d.isTouch&&!l.isEventSelected(c)&&l.selectEvent(c),e=!0,r.handleSegMouseout(t,n),r.segDragStart(t,n),l.hideEvent(c)},hitOver:function(e,o,u){var h;t.hit&&(u=t.hit),i=r.computeEventDrop(u.component.getHitSpan(u),e.component.getHitSpan(e),c),i&&!a.isEventSpanAllowed(r.eventToSpan(i),c)&&(s(),i=null),i&&(h=l.renderDrag(i,t))?(h.addClass("fc-dragging"),d.isTouch||r.applyDragOpacity(h),n.hide()):n.show(),o&&(i=null)},hitOut:function(){l.unrenderDrag(),n.show(),i=null},hitDone:function(){o()},interactionEnd:function(s){delete t.component,n.stop(!i,function(){e&&(l.unrenderDrag(),r.segDragStop(t,s)),i?l.reportEventDrop(c,i,r.largeUnit,u,s):l.showEvent(c)}),r.segDragListener=null}});return d},buildSegSelectListener:function(t){var e=this,n=this.view,i=t.event;if(this.segDragListener)return this.segDragListener;var r=this.segDragListener=new ge({dragStart:function(t){r.isTouch&&!n.isEventSelected(i)&&n.selectEvent(i)},interactionEnd:function(t){e.segDragListener=null}});return r},segDragStart:function(t,e){this.isDraggingSeg=!0,this.view.publiclyTrigger("eventDragStart",t.el[0],t.event,e,{})},segDragStop:function(t,e){this.isDraggingSeg=!1,this.view.publiclyTrigger("eventDragStop",t.el[0],t.event,e,{})},computeEventDrop:function(t,e,n){var i,r,s=this.view.calendar,o=t.start,l=e.start;return o.hasTime()===l.hasTime()?(i=this.diffDates(l,o),n.allDay&&W(i)?(r={start:n.start.clone(),end:s.getEventEnd(n),allDay:!1},s.normalizeEventTimes(r)):r=Ht(n),r.start.add(i),r.end&&r.end.add(i)):r={start:l.clone(),end:null,allDay:!l.hasTime()},r},applyDragOpacity:function(t){var e=this.view.opt("dragOpacity");null!=e&&t.css("opacity",e)},externalDragStart:function(e,n){var i,r,s=this.view;s.opt("droppable")&&(i=t((n?n.item:null)||e.target),r=s.opt("dropAccept"),(t.isFunction(r)?r.call(i[0],i):i.is(r))&&(this.isDraggingExternal||this.listenToExternalDrag(i,e,n)))},listenToExternalDrag:function(t,e,n){var i,r=this,l=this.view.calendar,a=Mt(t),u=r.externalDragListener=new pe(this,{interactionStart:function(){r.isDraggingExternal=!0},hitOver:function(t){i=r.computeExternalDrop(t.component.getHitSpan(t),a),i&&!l.isExternalSpanAllowed(r.eventToSpan(i),i,a.eventProps)&&(s(),i=null),i&&r.renderDrag(i)},hitOut:function(){i=null},hitDone:function(){o(),r.unrenderDrag()},interactionEnd:function(e){i&&r.view.reportExternalDrop(a,i,t,e,n),r.isDraggingExternal=!1,r.externalDragListener=null}});u.startDrag(e)},computeExternalDrop:function(t,e){var n=this.view.calendar,i={start:n.applyTimezone(t.start),end:null};return e.startTime&&!i.start.hasTime()&&i.start.time(e.startTime),e.duration&&(i.end=i.start.clone().add(e.duration)),i},renderDrag:function(t,e){},unrenderDrag:function(){},buildSegResizeListener:function(t,e){var n,i,r=this,l=this.view,a=l.calendar,u=t.el,c=t.event,d=a.getEventEnd(c),h=this.segResizeListener=new pe(this,{scroll:l.opt("dragScroll"),subjectEl:u,interactionStart:function(){n=!1},dragStart:function(e){n=!0,r.handleSegMouseout(t,e),r.segResizeStart(t,e)},hitOver:function(n,o,u){var h=r.getHitSpan(u),f=r.getHitSpan(n);i=e?r.computeEventStartResize(h,f,c):r.computeEventEndResize(h,f,c),i&&(a.isEventSpanAllowed(r.eventToSpan(i),c)?i.start.isSame(c.start.clone().stripZone())&&i.end.isSame(d.clone().stripZone())&&(i=null):(s(),i=null)),i&&(l.hideEvent(c),r.renderEventResize(i,t))},hitOut:function(){i=null,l.showEvent(c)},hitDone:function(){r.unrenderEventResize(),o()},interactionEnd:function(e){n&&r.segResizeStop(t,e),i?l.reportEventResize(c,i,r.largeUnit,u,e):l.showEvent(c),r.segResizeListener=null}});return h},segResizeStart:function(t,e){this.isResizingSeg=!0,this.view.publiclyTrigger("eventResizeStart",t.el[0],t.event,e,{})},segResizeStop:function(t,e){this.isResizingSeg=!1,this.view.publiclyTrigger("eventResizeStop",t.el[0],t.event,e,{})},computeEventStartResize:function(t,e,n){return this.computeEventResize("start",t,e,n)},computeEventEndResize:function(t,e,n){return this.computeEventResize("end",t,e,n)},computeEventResize:function(t,e,n,i){var r,s,o=this.view.calendar,l=this.diffDates(n[t],e[t]);return r={start:i.start.clone(),end:o.getEventEnd(i),allDay:i.allDay},r.allDay&&W(l)&&(r.allDay=!1,o.normalizeEventTimes(r)),r[t].add(l),r.start.isBefore(r.end)||(s=this.minResizeDuration||(i.allDay?o.defaultAllDayEventDuration:o.defaultTimedEventDuration),"start"==t?r.start=r.end.clone().subtract(s):r.end=r.start.clone().add(s)),r},renderEventResize:function(t,e){},unrenderEventResize:function(){},getEventTimeText:function(t,e,n){return null==e&&(e=this.eventTimeFormat),null==n&&(n=this.displayEventEnd),this.displayEventTime&&t.start.hasTime()?n&&t.end?this.view.formatRange(t,e):t.start.format(e):""},getSegClasses:function(t,e,n){var i=this.view,r=["fc-event",t.isStart?"fc-start":"fc-not-start",t.isEnd?"fc-end":"fc-not-end"].concat(this.getSegCustomClasses(t));return e&&r.push("fc-draggable"),n&&r.push("fc-resizable"),i.isEventSelected(t.event)&&r.push("fc-selected"),r},getSegCustomClasses:function(t){var e=t.event;return[].concat(e.className,e.source?e.source.className:[])},getSegSkinCss:function(t){return{"background-color":this.getSegBackgroundColor(t),"border-color":this.getSegBorderColor(t),color:this.getSegTextColor(t)}},getSegBackgroundColor:function(t){return t.event.backgroundColor||t.event.color||this.getSegDefaultBackgroundColor(t)},getSegDefaultBackgroundColor:function(t){var e=t.event.source||{};return e.backgroundColor||e.color||this.view.opt("eventBackgroundColor")||this.view.opt("eventColor")},getSegBorderColor:function(t){return t.event.borderColor||t.event.color||this.getSegDefaultBorderColor(t)},getSegDefaultBorderColor:function(t){var e=t.event.source||{};return e.borderColor||e.color||this.view.opt("eventBorderColor")||this.view.opt("eventColor")},getSegTextColor:function(t){return t.event.textColor||this.getSegDefaultTextColor(t)},getSegDefaultTextColor:function(t){var e=t.event.source||{};return e.textColor||this.view.opt("eventTextColor")},eventToSegs:function(t){return this.eventsToSegs([t])},eventToSpan:function(t){return this.eventToSpans(t)[0]},eventToSpans:function(t){var e=this.eventToRange(t);return this.eventRangeToSpans(e,t)},eventsToSegs:function(e,n){var i=this,r=kt(e),s=[];return t.each(r,function(t,e){var r,o=[];for(r=0;r<e.length;r++)o.push(i.eventToRange(e[r]));if(xt(e[0]))for(o=i.invertRanges(o),r=0;r<o.length;r++)s.push.apply(s,i.eventRangeToSegs(o[r],e[0],n));else for(r=0;r<o.length;r++)s.push.apply(s,i.eventRangeToSegs(o[r],e[r],n))}),s},eventToRange:function(t){var e=this.view.calendar,n=t.start.clone().stripZone(),i=(t.end?t.end.clone():e.getDefaultEventEnd(null!=t.allDay?t.allDay:!t.start.hasTime(),t.start)).stripZone();return e.localizeMoment(n),e.localizeMoment(i),{start:n,end:i}},eventRangeToSegs:function(t,e,n){var i,r=this.eventRangeToSpans(t,e),s=[];for(i=0;i<r.length;i++)s.push.apply(s,this.eventSpanToSegs(r[i],e,n));return s},eventRangeToSpans:function(e,n){return[t.extend({},e)]},eventSpanToSegs:function(t,e,n){var i,r,s=n?n(t):this.spanToSegs(t); -for(i=0;i<s.length;i++)r=s[i],r.event=e,r.eventStartMS=+t.start,r.eventDurationMS=t.end-t.start;return s},invertRanges:function(t){var e,n,i=this.view,r=i.start.clone(),s=i.end.clone(),o=[],l=r;for(t.sort(Lt),e=0;e<t.length;e++)n=t[e],n.start>l&&o.push({start:l,end:n.start}),l=n.end;return l<s&&o.push({start:l,end:s}),o},sortEventSegs:function(t){t.sort(lt(this,"compareEventSegs"))},compareEventSegs:function(t,e){return t.eventStartMS-e.eventStartMS||e.eventDurationMS-t.eventDurationMS||e.event.allDay-t.event.allDay||M(t.event,e.event,this.view.eventOrderSpecs)}}),qt.pluckEventDateProps=Ht,qt.isBgEvent=Rt,qt.dataAttrPrefix="";var ye=qt.DayTableMixin={breakOnWeeks:!1,dayDates:null,dayIndices:null,daysPerRow:null,rowCnt:null,colCnt:null,colHeadFormat:null,updateDayTable:function(){for(var t,e,n,i=this.view,r=this.start.clone(),s=-1,o=[],l=[];r.isBefore(this.end);)i.isHiddenDay(r)?o.push(s+.5):(s++,o.push(s),l.push(r.clone())),r.add(1,"days");if(this.breakOnWeeks){for(e=l[0].day(),t=1;t<l.length&&l[t].day()!=e;t++);n=Math.ceil(l.length/t)}else n=1,t=l.length;this.dayDates=l,this.dayIndices=o,this.daysPerRow=t,this.rowCnt=n,this.updateDayTableCols()},updateDayTableCols:function(){this.colCnt=this.computeColCnt(),this.colHeadFormat=this.view.opt("columnFormat")||this.computeColHeadFormat()},computeColCnt:function(){return this.daysPerRow},getCellDate:function(t,e){return this.dayDates[this.getCellDayIndex(t,e)].clone()},getCellRange:function(t,e){var n=this.getCellDate(t,e),i=n.clone().add(1,"days");return{start:n,end:i}},getCellDayIndex:function(t,e){return t*this.daysPerRow+this.getColDayIndex(e)},getColDayIndex:function(t){return this.isRTL?this.colCnt-1-t:t},getDateDayIndex:function(t){var e=this.dayIndices,n=t.diff(this.start,"days");return n<0?e[0]-1:n>=e.length?e[e.length-1]+1:e[n]},computeColHeadFormat:function(){return this.rowCnt>1||this.colCnt>10?"ddd":this.colCnt>1?this.view.opt("dayOfMonthFormat"):"dddd"},sliceRangeByRow:function(t){var e,n,i,r,s,o=this.daysPerRow,l=this.view.computeDayRange(t),a=this.getDateDayIndex(l.start),u=this.getDateDayIndex(l.end.clone().subtract(1,"days")),c=[];for(e=0;e<this.rowCnt;e++)n=e*o,i=n+o-1,r=Math.max(a,n),s=Math.min(u,i),r=Math.ceil(r),s=Math.floor(s),r<=s&&c.push({row:e,firstRowDayIndex:r-n,lastRowDayIndex:s-n,isStart:r===a,isEnd:s===u});return c},sliceRangeByDay:function(t){var e,n,i,r,s,o,l=this.daysPerRow,a=this.view.computeDayRange(t),u=this.getDateDayIndex(a.start),c=this.getDateDayIndex(a.end.clone().subtract(1,"days")),d=[];for(e=0;e<this.rowCnt;e++)for(n=e*l,i=n+l-1,r=n;r<=i;r++)s=Math.max(u,r),o=Math.min(c,r),s=Math.ceil(s),o=Math.floor(o),s<=o&&d.push({row:e,firstRowDayIndex:s-n,lastRowDayIndex:o-n,isStart:s===u,isEnd:o===c});return d},renderHeadHtml:function(){var t=this.view;return'<div class="fc-row '+t.widgetHeaderClass+'"><table><thead>'+this.renderHeadTrHtml()+"</thead></table></div>"},renderHeadIntroHtml:function(){return this.renderIntroHtml()},renderHeadTrHtml:function(){return"<tr>"+(this.isRTL?"":this.renderHeadIntroHtml())+this.renderHeadDateCellsHtml()+(this.isRTL?this.renderHeadIntroHtml():"")+"</tr>"},renderHeadDateCellsHtml:function(){var t,e,n=[];for(t=0;t<this.colCnt;t++)e=this.getCellDate(0,t),n.push(this.renderHeadDateCellHtml(e));return n.join("")},renderHeadDateCellHtml:function(t,e,n){var i=this.view,r=["fc-day-header",i.widgetHeaderClass];return 1===this.rowCnt?r=r.concat(this.getDayClasses(t,!0)):r.push("fc-"+Xt[t.day()]),'<th class="'+r.join(" ")+'"'+(1===this.rowCnt?' data-date="'+t.format("YYYY-MM-DD")+'"':"")+(e>1?' colspan="'+e+'"':"")+(n?" "+n:"")+">"+i.buildGotoAnchorHtml({date:t,forceOff:this.rowCnt>1||1===this.colCnt},tt(t.format(this.colHeadFormat)))+"</th>"},renderBgTrHtml:function(t){return"<tr>"+(this.isRTL?"":this.renderBgIntroHtml(t))+this.renderBgCellsHtml(t)+(this.isRTL?this.renderBgIntroHtml(t):"")+"</tr>"},renderBgIntroHtml:function(t){return this.renderIntroHtml()},renderBgCellsHtml:function(t){var e,n,i=[];for(e=0;e<this.colCnt;e++)n=this.getCellDate(t,e),i.push(this.renderBgCellHtml(n));return i.join("")},renderBgCellHtml:function(t,e){var n=this.view,i=this.getDayClasses(t);return i.unshift("fc-day",n.widgetContentClass),'<td class="'+i.join(" ")+'" data-date="'+t.format("YYYY-MM-DD")+'"'+(e?" "+e:"")+"></td>"},renderIntroHtml:function(){},bookendCells:function(t){var e=this.renderIntroHtml();e&&(this.isRTL?t.append(e):t.prepend(e))}},Se=qt.DayGrid=me.extend(ye,{numbersVisible:!1,bottomCoordPadding:0,rowEls:null,cellEls:null,helperEls:null,rowCoordCache:null,colCoordCache:null,renderDates:function(t){var e,n,i=this.view,r=this.rowCnt,s=this.colCnt,o="";for(e=0;e<r;e++)o+=this.renderDayRowHtml(e,t);for(this.el.html(o),this.rowEls=this.el.find(".fc-row"),this.cellEls=this.el.find(".fc-day"),this.rowCoordCache=new fe({els:this.rowEls,isVertical:!0}),this.colCoordCache=new fe({els:this.cellEls.slice(0,this.colCnt),isHorizontal:!0}),e=0;e<r;e++)for(n=0;n<s;n++)i.publiclyTrigger("dayRender",null,this.getCellDate(e,n),this.getCellEl(e,n))},unrenderDates:function(){this.removeSegPopover()},renderBusinessHours:function(){var t=this.buildBusinessHourSegs(!0);this.renderFill("businessHours",t,"bgevent")},unrenderBusinessHours:function(){this.unrenderFill("businessHours")},renderDayRowHtml:function(t,e){var n=this.view,i=["fc-row","fc-week",n.widgetContentClass];return e&&i.push("fc-rigid"),'<div class="'+i.join(" ")+'"><div class="fc-bg"><table>'+this.renderBgTrHtml(t)+'</table></div><div class="fc-content-skeleton"><table>'+(this.numbersVisible?"<thead>"+this.renderNumberTrHtml(t)+"</thead>":"")+"</table></div></div>"},renderNumberTrHtml:function(t){return"<tr>"+(this.isRTL?"":this.renderNumberIntroHtml(t))+this.renderNumberCellsHtml(t)+(this.isRTL?this.renderNumberIntroHtml(t):"")+"</tr>"},renderNumberIntroHtml:function(t){return this.renderIntroHtml()},renderNumberCellsHtml:function(t){var e,n,i=[];for(e=0;e<this.colCnt;e++)n=this.getCellDate(t,e),i.push(this.renderNumberCellHtml(n));return i.join("")},renderNumberCellHtml:function(t){var e,n,i="";return this.view.dayNumbersVisible||this.view.cellWeekNumbersVisible?(e=this.getDayClasses(t),e.unshift("fc-day-top"),this.view.cellWeekNumbersVisible&&(n="ISO"===t._locale._fullCalendar_weekCalc?1:t._locale.firstDayOfWeek()),i+='<td class="'+e.join(" ")+'" data-date="'+t.format()+'">',this.view.cellWeekNumbersVisible&&t.day()==n&&(i+=this.view.buildGotoAnchorHtml({date:t,type:"week"},{class:"fc-week-number"},t.format("w"))),this.view.dayNumbersVisible&&(i+=this.view.buildGotoAnchorHtml(t,{class:"fc-day-number"},t.date())),i+="</td>"):"<td/>"},computeEventTimeFormat:function(){return this.view.opt("extraSmallTimeFormat")},computeDisplayEventEnd:function(){return 1==this.colCnt},rangeUpdated:function(){this.updateDayTable()},spanToSegs:function(t){var e,n,i=this.sliceRangeByRow(t);for(e=0;e<i.length;e++)n=i[e],this.isRTL?(n.leftCol=this.daysPerRow-1-n.lastRowDayIndex,n.rightCol=this.daysPerRow-1-n.firstRowDayIndex):(n.leftCol=n.firstRowDayIndex,n.rightCol=n.lastRowDayIndex);return i},prepareHits:function(){this.colCoordCache.build(),this.rowCoordCache.build(),this.rowCoordCache.bottoms[this.rowCnt-1]+=this.bottomCoordPadding},releaseHits:function(){this.colCoordCache.clear(),this.rowCoordCache.clear()},queryHit:function(t,e){if(this.colCoordCache.isLeftInBounds(t)&&this.rowCoordCache.isTopInBounds(e)){var n=this.colCoordCache.getHorizontalIndex(t),i=this.rowCoordCache.getVerticalIndex(e);if(null!=i&&null!=n)return this.getCellHit(i,n)}},getHitSpan:function(t){return this.getCellRange(t.row,t.col)},getHitEl:function(t){return this.getCellEl(t.row,t.col)},getCellHit:function(t,e){return{row:t,col:e,component:this,left:this.colCoordCache.getLeftOffset(e),right:this.colCoordCache.getRightOffset(e),top:this.rowCoordCache.getTopOffset(t),bottom:this.rowCoordCache.getBottomOffset(t)}},getCellEl:function(t,e){return this.cellEls.eq(t*this.colCnt+e)},renderDrag:function(t,e){if(this.renderHighlight(this.eventToSpan(t)),e&&e.component!==this)return this.renderEventLocationHelper(t,e)},unrenderDrag:function(){this.unrenderHighlight(),this.unrenderHelper()},renderEventResize:function(t,e){return this.renderHighlight(this.eventToSpan(t)),this.renderEventLocationHelper(t,e)},unrenderEventResize:function(){this.unrenderHighlight(),this.unrenderHelper()},renderHelper:function(e,n){var i,r=[],s=this.eventToSegs(e);return s=this.renderFgSegEls(s),i=this.renderSegRows(s),this.rowEls.each(function(e,s){var o,l=t(s),a=t('<div class="fc-helper-skeleton"><table/></div>');o=n&&n.row===e?n.el.position().top:l.find(".fc-content-skeleton tbody").position().top,a.css("top",o).find("table").append(i[e].tbodyEl),l.append(a),r.push(a[0])}),this.helperEls=t(r)},unrenderHelper:function(){this.helperEls&&(this.helperEls.remove(),this.helperEls=null)},fillSegTag:"td",renderFill:function(e,n,i){var r,s,o,l=[];for(n=this.renderFillSegEls(e,n),r=0;r<n.length;r++)s=n[r],o=this.renderFillRow(e,s,i),this.rowEls.eq(s.row).append(o),l.push(o[0]);return this.elsByFill[e]=t(l),n},renderFillRow:function(e,n,i){var r,s,o=this.colCnt,l=n.leftCol,a=n.rightCol+1;return i=i||e.toLowerCase(),r=t('<div class="fc-'+i+'-skeleton"><table><tr/></table></div>'),s=r.find("tr"),l>0&&s.append('<td colspan="'+l+'"/>'),s.append(n.el.attr("colspan",a-l)),a<o&&s.append('<td colspan="'+(o-a)+'"/>'),this.bookendCells(s),r}});Se.mixin({rowStructs:null,unrenderEvents:function(){this.removeSegPopover(),me.prototype.unrenderEvents.apply(this,arguments)},getEventSegs:function(){return me.prototype.getEventSegs.call(this).concat(this.popoverSegs||[])},renderBgSegs:function(e){var n=t.grep(e,function(t){return t.event.allDay});return me.prototype.renderBgSegs.call(this,n)},renderFgSegs:function(e){var n;return e=this.renderFgSegEls(e),n=this.rowStructs=this.renderSegRows(e),this.rowEls.each(function(e,i){t(i).find(".fc-content-skeleton > table").append(n[e].tbodyEl)}),e},unrenderFgSegs:function(){for(var t,e=this.rowStructs||[];t=e.pop();)t.tbodyEl.remove();this.rowStructs=null},renderSegRows:function(t){var e,n,i=[];for(e=this.groupSegRows(t),n=0;n<e.length;n++)i.push(this.renderSegRow(n,e[n]));return i},fgSegHtml:function(t,e){var n,i,r=this.view,s=t.event,o=r.isEventDraggable(s),l=!e&&s.allDay&&t.isStart&&r.isEventResizableFromStart(s),a=!e&&s.allDay&&t.isEnd&&r.isEventResizableFromEnd(s),u=this.getSegClasses(t,o,l||a),c=nt(this.getSegSkinCss(t)),d="";return u.unshift("fc-day-grid-event","fc-h-event"),t.isStart&&(n=this.getEventTimeText(s),n&&(d='<span class="fc-time">'+tt(n)+"</span>")),i='<span class="fc-title">'+(tt(s.title||"")||" ")+"</span>",'<a class="'+u.join(" ")+'"'+(s.url?' href="'+tt(s.url)+'"':"")+(c?' style="'+c+'"':"")+'><div class="fc-content">'+(this.isRTL?i+" "+d:d+" "+i)+"</div>"+(l?'<div class="fc-resizer fc-start-resizer" />':"")+(a?'<div class="fc-resizer fc-end-resizer" />':"")+"</a>"},renderSegRow:function(e,n){function i(e){for(;o<e;)c=(m[r-1]||[])[o],c?c.attr("rowspan",parseInt(c.attr("rowspan")||1,10)+1):(c=t("<td/>"),l.append(c)),v[r][o]=c,m[r][o]=c,o++}var r,s,o,l,a,u,c,d=this.colCnt,h=this.buildSegLevels(n),f=Math.max(1,h.length),g=t("<tbody/>"),p=[],v=[],m=[];for(r=0;r<f;r++){if(s=h[r],o=0,l=t("<tr/>"),p.push([]),v.push([]),m.push([]),s)for(a=0;a<s.length;a++){for(u=s[a],i(u.leftCol),c=t('<td class="fc-event-container"/>').append(u.el),u.leftCol!=u.rightCol?c.attr("colspan",u.rightCol-u.leftCol+1):m[r][o]=c;o<=u.rightCol;)v[r][o]=c,p[r][o]=u,o++;l.append(c)}i(d),this.bookendCells(l),g.append(l)}return{row:e,tbodyEl:g,cellMatrix:v,segMatrix:p,segLevels:h,segs:n}},buildSegLevels:function(t){var e,n,i,r=[];for(this.sortEventSegs(t),e=0;e<t.length;e++){for(n=t[e],i=0;i<r.length&&Bt(n,r[i]);i++);n.level=i,(r[i]||(r[i]=[])).push(n)}for(i=0;i<r.length;i++)r[i].sort(zt);return r},groupSegRows:function(t){var e,n=[];for(e=0;e<this.rowCnt;e++)n.push([]);for(e=0;e<t.length;e++)n[t[e].row].push(t[e]);return n}}),Se.mixin({segPopover:null,popoverSegs:null,removeSegPopover:function(){this.segPopover&&this.segPopover.hide()},limitRows:function(t){var e,n,i=this.rowStructs||[];for(e=0;e<i.length;e++)this.unlimitRow(e),n=!!t&&("number"==typeof t?t:this.computeRowLevelLimit(e)),n!==!1&&this.limitRow(e,n)},computeRowLevelLimit:function(e){function n(e,n){s=Math.max(s,t(n).outerHeight())}var i,r,s,o=this.rowEls.eq(e),l=o.height(),a=this.rowStructs[e].tbodyEl.children();for(i=0;i<a.length;i++)if(r=a.eq(i).removeClass("fc-limited"),s=0,r.find("> td > :first-child").each(n),r.position().top+s>l)return i;return!1},limitRow:function(e,n){function i(i){for(;b<i;)u=S.getCellSegs(e,b,n),u.length&&(h=s[n-1][b],y=S.renderMoreLink(e,b,u),m=t("<div/>").append(y),h.append(m),E.push(m[0])),b++}var r,s,o,l,a,u,c,d,h,f,g,p,v,m,y,S=this,w=this.rowStructs[e],E=[],b=0;if(n&&n<w.segLevels.length){for(r=w.segLevels[n-1],s=w.cellMatrix,o=w.tbodyEl.children().slice(n).addClass("fc-limited").get(),l=0;l<r.length;l++){for(a=r[l],i(a.leftCol),d=[],c=0;b<=a.rightCol;)u=this.getCellSegs(e,b,n),d.push(u),c+=u.length,b++;if(c){for(h=s[n-1][a.leftCol],f=h.attr("rowspan")||1,g=[],p=0;p<d.length;p++)v=t('<td class="fc-more-cell"/>').attr("rowspan",f),u=d[p],y=this.renderMoreLink(e,a.leftCol+p,[a].concat(u)),m=t("<div/>").append(y),v.append(m),g.push(v[0]),E.push(v[0]);h.addClass("fc-limited").after(t(g)),o.push(h[0])}}i(this.colCnt),w.moreEls=t(E),w.limitedEls=t(o)}},unlimitRow:function(t){var e=this.rowStructs[t];e.moreEls&&(e.moreEls.remove(),e.moreEls=null),e.limitedEls&&(e.limitedEls.removeClass("fc-limited"),e.limitedEls=null)},renderMoreLink:function(e,n,i){var r=this,s=this.view;return t('<a class="fc-more"/>').text(this.getMoreLinkText(i.length)).on("click",function(o){var l=s.opt("eventLimitClick"),a=r.getCellDate(e,n),u=t(this),c=r.getCellEl(e,n),d=r.getCellSegs(e,n),h=r.resliceDaySegs(d,a),f=r.resliceDaySegs(i,a);"function"==typeof l&&(l=s.publiclyTrigger("eventLimitClick",null,{date:a,dayEl:c,moreEl:u,segs:h,hiddenSegs:f},o)),"popover"===l?r.showSegPopover(e,n,u,h):"string"==typeof l&&s.calendar.zoomTo(a,l)})},showSegPopover:function(t,e,n,i){var r,s,o=this,l=this.view,a=n.parent();r=1==this.rowCnt?l.el:this.rowEls.eq(t),s={className:"fc-more-popover",content:this.renderSegPopoverContent(t,e,i),parentEl:this.view.el,top:r.offset().top,autoHide:!0,viewportConstrain:l.opt("popoverViewportConstrain"),hide:function(){if(o.popoverSegs)for(var t,e=0;e<o.popoverSegs.length;++e)t=o.popoverSegs[e],l.publiclyTrigger("eventDestroy",t.event,t.event,t.el);o.segPopover.removeElement(),o.segPopover=null,o.popoverSegs=null}},this.isRTL?s.right=a.offset().left+a.outerWidth()+1:s.left=a.offset().left-1,this.segPopover=new he(s),this.segPopover.show(),this.bindSegHandlersToEl(this.segPopover.el)},renderSegPopoverContent:function(e,n,i){var r,s=this.view,o=s.opt("theme"),l=this.getCellDate(e,n).format(s.opt("dayPopoverFormat")),a=t('<div class="fc-header '+s.widgetHeaderClass+'"><span class="fc-close '+(o?"ui-icon ui-icon-closethick":"fc-icon fc-icon-x")+'"></span><span class="fc-title">'+tt(l)+'</span><div class="fc-clear"/></div><div class="fc-body '+s.widgetContentClass+'"><div class="fc-event-container"></div></div>'),u=a.find(".fc-event-container");for(i=this.renderFgSegEls(i,!0),this.popoverSegs=i,r=0;r<i.length;r++)this.prepareHits(),i[r].hit=this.getCellHit(e,n),this.releaseHits(),u.append(i[r].el);return a},resliceDaySegs:function(e,n){var i=t.map(e,function(t){return t.event}),r=n.clone(),s=r.clone().add(1,"days"),o={start:r,end:s};return e=this.eventsToSegs(i,function(t){var e=F(t,o);return e?[e]:[]}),this.sortEventSegs(e),e},getMoreLinkText:function(t){var e=this.view.opt("eventLimitText");return"function"==typeof e?e(t):"+"+t+" "+e},getCellSegs:function(t,e,n){for(var i,r=this.rowStructs[t].segMatrix,s=n||0,o=[];s<r.length;)i=r[s][e],i&&o.push(i),s++;return o}});var we=qt.TimeGrid=me.extend(ye,{slotDuration:null,snapDuration:null,snapsPerSlot:null,minTime:null,maxTime:null,labelFormat:null,labelInterval:null,colEls:null,slatContainerEl:null,slatEls:null,nowIndicatorEls:null,colCoordCache:null,slatCoordCache:null,constructor:function(){me.apply(this,arguments),this.processOptions()},renderDates:function(){this.el.html(this.renderHtml()),this.colEls=this.el.find(".fc-day"),this.slatContainerEl=this.el.find(".fc-slats"),this.slatEls=this.slatContainerEl.find("tr"),this.colCoordCache=new fe({els:this.colEls,isHorizontal:!0}),this.slatCoordCache=new fe({els:this.slatEls,isVertical:!0}),this.renderContentSkeleton()},renderHtml:function(){return'<div class="fc-bg"><table>'+this.renderBgTrHtml(0)+'</table></div><div class="fc-slats"><table>'+this.renderSlatRowHtml()+"</table></div>"},renderSlatRowHtml:function(){for(var t,n,i,r=this.view,s=this.isRTL,o="",l=e.duration(+this.minTime);l<this.maxTime;)t=this.start.clone().time(l),n=ot(_(l,this.labelInterval)),i='<td class="fc-axis fc-time '+r.widgetContentClass+'" '+r.axisStyleAttr()+">"+(n?"<span>"+tt(t.format(this.labelFormat))+"</span>":"")+"</td>",o+='<tr data-time="'+t.format("HH:mm:ss")+'"'+(n?"":' class="fc-minor"')+">"+(s?"":i)+'<td class="'+r.widgetContentClass+'"/>'+(s?i:"")+"</tr>",l.add(this.slotDuration);return o},processOptions:function(){var n,i=this.view,r=i.opt("slotDuration"),s=i.opt("snapDuration");r=e.duration(r),s=s?e.duration(s):r,this.slotDuration=r,this.snapDuration=s,this.snapsPerSlot=r/s,this.minResizeDuration=s,this.minTime=e.duration(i.opt("minTime")),this.maxTime=e.duration(i.opt("maxTime")),n=i.opt("slotLabelFormat"),t.isArray(n)&&(n=n[n.length-1]),this.labelFormat=n||i.opt("smallTimeFormat"),n=i.opt("slotLabelInterval"),this.labelInterval=n?e.duration(n):this.computeLabelInterval(r)},computeLabelInterval:function(t){var n,i,r;for(n=Oe.length-1;n>=0;n--)if(i=e.duration(Oe[n]),r=_(i,t),ot(r)&&r>1)return i;return e.duration(t)},computeEventTimeFormat:function(){return this.view.opt("noMeridiemTimeFormat")},computeDisplayEventEnd:function(){return!0},prepareHits:function(){this.colCoordCache.build(),this.slatCoordCache.build()},releaseHits:function(){this.colCoordCache.clear()},queryHit:function(t,e){var n=this.snapsPerSlot,i=this.colCoordCache,r=this.slatCoordCache;if(i.isLeftInBounds(t)&&r.isTopInBounds(e)){var s=i.getHorizontalIndex(t),o=r.getVerticalIndex(e);if(null!=s&&null!=o){var l=r.getTopOffset(o),a=r.getHeight(o),u=(e-l)/a,c=Math.floor(u*n),d=o*n+c,h=l+c/n*a,f=l+(c+1)/n*a;return{col:s,snap:d,component:this,left:i.getLeftOffset(s),right:i.getRightOffset(s),top:h,bottom:f}}}},getHitSpan:function(t){var e,n=this.getCellDate(0,t.col),i=this.computeSnapTime(t.snap);return n.time(i),e=n.clone().add(this.snapDuration),{start:n,end:e}},getHitEl:function(t){return this.colEls.eq(t.col)},rangeUpdated:function(){this.updateDayTable()},computeSnapTime:function(t){return e.duration(this.minTime+this.snapDuration*t)},spanToSegs:function(t){var e,n=this.sliceRangeByTimes(t);for(e=0;e<n.length;e++)this.isRTL?n[e].col=this.daysPerRow-1-n[e].dayIndex:n[e].col=n[e].dayIndex;return n},sliceRangeByTimes:function(t){var e,n,i,r,s=[];for(n=0;n<this.daysPerRow;n++)i=this.dayDates[n].clone(),r={start:i.clone().time(this.minTime),end:i.clone().time(this.maxTime)},e=F(t,r),e&&(e.dayIndex=n,s.push(e));return s},updateSize:function(t){this.slatCoordCache.build(),t&&this.updateSegVerticals([].concat(this.fgSegs||[],this.bgSegs||[],this.businessSegs||[]))},getTotalSlatHeight:function(){return this.slatContainerEl.outerHeight()},computeDateTop:function(t,n){return this.computeTimeTop(e.duration(t-n.clone().stripTime()))},computeTimeTop:function(t){var e,n,i=this.slatEls.length,r=(t-this.minTime)/this.slotDuration;return r=Math.max(0,r),r=Math.min(i,r),e=Math.floor(r),e=Math.min(e,i-1),n=r-e,this.slatCoordCache.getTopPosition(e)+this.slatCoordCache.getHeight(e)*n},renderDrag:function(t,e){return e?this.renderEventLocationHelper(t,e):void this.renderHighlight(this.eventToSpan(t))},unrenderDrag:function(){this.unrenderHelper(),this.unrenderHighlight()},renderEventResize:function(t,e){return this.renderEventLocationHelper(t,e)},unrenderEventResize:function(){this.unrenderHelper()},renderHelper:function(t,e){return this.renderHelperSegs(this.eventToSegs(t),e)},unrenderHelper:function(){this.unrenderHelperSegs()},renderBusinessHours:function(){this.renderBusinessSegs(this.buildBusinessHourSegs())},unrenderBusinessHours:function(){this.unrenderBusinessSegs()},getNowIndicatorUnit:function(){return"minute"},renderNowIndicator:function(e){var n,i=this.spanToSegs({start:e,end:e}),r=this.computeDateTop(e,e),s=[];for(n=0;n<i.length;n++)s.push(t('<div class="fc-now-indicator fc-now-indicator-line"></div>').css("top",r).appendTo(this.colContainerEls.eq(i[n].col))[0]);i.length>0&&s.push(t('<div class="fc-now-indicator fc-now-indicator-arrow"></div>').css("top",r).appendTo(this.el.find(".fc-content-skeleton"))[0]),this.nowIndicatorEls=t(s)},unrenderNowIndicator:function(){this.nowIndicatorEls&&(this.nowIndicatorEls.remove(),this.nowIndicatorEls=null)},renderSelection:function(t){this.view.opt("selectHelper")?this.renderEventLocationHelper(t):this.renderHighlight(t)},unrenderSelection:function(){this.unrenderHelper(),this.unrenderHighlight()},renderHighlight:function(t){this.renderHighlightSegs(this.spanToSegs(t))},unrenderHighlight:function(){this.unrenderHighlightSegs()}});we.mixin({colContainerEls:null,fgContainerEls:null,bgContainerEls:null,helperContainerEls:null,highlightContainerEls:null,businessContainerEls:null,fgSegs:null,bgSegs:null,helperSegs:null,highlightSegs:null,businessSegs:null,renderContentSkeleton:function(){var e,n,i="";for(e=0;e<this.colCnt;e++)i+='<td><div class="fc-content-col"><div class="fc-event-container fc-helper-container"></div><div class="fc-event-container"></div><div class="fc-highlight-container"></div><div class="fc-bgevent-container"></div><div class="fc-business-container"></div></div></td>';n=t('<div class="fc-content-skeleton"><table><tr>'+i+"</tr></table></div>"),this.colContainerEls=n.find(".fc-content-col"),this.helperContainerEls=n.find(".fc-helper-container"),this.fgContainerEls=n.find(".fc-event-container:not(.fc-helper-container)"),this.bgContainerEls=n.find(".fc-bgevent-container"),this.highlightContainerEls=n.find(".fc-highlight-container"),this.businessContainerEls=n.find(".fc-business-container"),this.bookendCells(n.find("tr")),this.el.append(n)},renderFgSegs:function(t){return t=this.renderFgSegsIntoContainers(t,this.fgContainerEls),this.fgSegs=t,t},unrenderFgSegs:function(){this.unrenderNamedSegs("fgSegs")},renderHelperSegs:function(e,n){var i,r,s,o=[];for(e=this.renderFgSegsIntoContainers(e,this.helperContainerEls),i=0;i<e.length;i++)r=e[i],n&&n.col===r.col&&(s=n.el,r.el.css({left:s.css("left"),right:s.css("right"),"margin-left":s.css("margin-left"),"margin-right":s.css("margin-right")})),o.push(r.el[0]);return this.helperSegs=e,t(o)},unrenderHelperSegs:function(){this.unrenderNamedSegs("helperSegs")},renderBgSegs:function(t){return t=this.renderFillSegEls("bgEvent",t),this.updateSegVerticals(t),this.attachSegsByCol(this.groupSegsByCol(t),this.bgContainerEls),this.bgSegs=t,t},unrenderBgSegs:function(){this.unrenderNamedSegs("bgSegs")},renderHighlightSegs:function(t){t=this.renderFillSegEls("highlight",t),this.updateSegVerticals(t),this.attachSegsByCol(this.groupSegsByCol(t),this.highlightContainerEls),this.highlightSegs=t},unrenderHighlightSegs:function(){this.unrenderNamedSegs("highlightSegs")},renderBusinessSegs:function(t){t=this.renderFillSegEls("businessHours",t),this.updateSegVerticals(t),this.attachSegsByCol(this.groupSegsByCol(t),this.businessContainerEls),this.businessSegs=t},unrenderBusinessSegs:function(){this.unrenderNamedSegs("businessSegs")},groupSegsByCol:function(t){var e,n=[];for(e=0;e<this.colCnt;e++)n.push([]);for(e=0;e<t.length;e++)n[t[e].col].push(t[e]);return n},attachSegsByCol:function(t,e){var n,i,r;for(n=0;n<this.colCnt;n++)for(i=t[n],r=0;r<i.length;r++)e.eq(n).append(i[r].el)},unrenderNamedSegs:function(t){var e,n=this[t];if(n){for(e=0;e<n.length;e++)n[e].el.remove();this[t]=null}},renderFgSegsIntoContainers:function(t,e){var n,i;for(t=this.renderFgSegEls(t),n=this.groupSegsByCol(t),i=0;i<this.colCnt;i++)this.updateFgSegCoords(n[i]);return this.attachSegsByCol(n,e),t},fgSegHtml:function(t,e){var n,i,r,s=this.view,o=t.event,l=s.isEventDraggable(o),a=!e&&t.isStart&&s.isEventResizableFromStart(o),u=!e&&t.isEnd&&s.isEventResizableFromEnd(o),c=this.getSegClasses(t,l,a||u),d=nt(this.getSegSkinCss(t));return c.unshift("fc-time-grid-event","fc-v-event"),s.isMultiDayEvent(o)?(t.isStart||t.isEnd)&&(n=this.getEventTimeText(t),i=this.getEventTimeText(t,"LT"),r=this.getEventTimeText(t,null,!1)):(n=this.getEventTimeText(o),i=this.getEventTimeText(o,"LT"),r=this.getEventTimeText(o,null,!1)),'<a class="'+c.join(" ")+'"'+(o.url?' href="'+tt(o.url)+'"':"")+(d?' style="'+d+'"':"")+'><div class="fc-content">'+(n?'<div class="fc-time" data-start="'+tt(r)+'" data-full="'+tt(i)+'"><span>'+tt(n)+"</span></div>":"")+(o.title?'<div class="fc-title">'+tt(o.title)+"</div>":"")+'</div><div class="fc-bg"/>'+(u?'<div class="fc-resizer fc-end-resizer" />':"")+"</a>"},updateSegVerticals:function(t){this.computeSegVerticals(t),this.assignSegVerticals(t)},computeSegVerticals:function(t){var e,n;for(e=0;e<t.length;e++)n=t[e],n.top=this.computeDateTop(n.start,n.start),n.bottom=this.computeDateTop(n.end,n.start)},assignSegVerticals:function(t){var e,n;for(e=0;e<t.length;e++)n=t[e],n.el.css(this.generateSegVerticalCss(n))},generateSegVerticalCss:function(t){return{top:t.top,bottom:-t.bottom}},updateFgSegCoords:function(t){this.computeSegVerticals(t),this.computeFgSegHorizontals(t),this.assignSegVerticals(t),this.assignFgSegHorizontals(t)},computeFgSegHorizontals:function(t){var e,n,i;if(this.sortEventSegs(t),e=Ft(t),Nt(e),n=e[0]){for(i=0;i<n.length;i++)Gt(n[i]);for(i=0;i<n.length;i++)this.computeFgSegForwardBack(n[i],0,0)}},computeFgSegForwardBack:function(t,e,n){var i,r=t.forwardSegs;if(void 0===t.forwardCoord)for(r.length?(this.sortForwardSegs(r),this.computeFgSegForwardBack(r[0],e+1,n),t.forwardCoord=r[0].backwardCoord):t.forwardCoord=1,t.backwardCoord=t.forwardCoord-(t.forwardCoord-n)/(e+1),i=0;i<r.length;i++)this.computeFgSegForwardBack(r[i],0,t.forwardCoord)},sortForwardSegs:function(t){t.sort(lt(this,"compareForwardSegs"))},compareForwardSegs:function(t,e){return e.forwardPressure-t.forwardPressure||(t.backwardCoord||0)-(e.backwardCoord||0)||this.compareEventSegs(t,e)},assignFgSegHorizontals:function(t){var e,n;for(e=0;e<t.length;e++)n=t[e],n.el.css(this.generateFgSegHorizontalCss(n)),n.bottom-n.top<30&&n.el.addClass("fc-short")},generateFgSegHorizontalCss:function(t){var e,n,i=this.view.opt("slotEventOverlap"),r=t.backwardCoord,s=t.forwardCoord,o=this.generateSegVerticalCss(t);return i&&(s=Math.min(1,r+2*(s-r))),this.isRTL?(e=1-s,n=r):(e=r,n=1-s),o.zIndex=t.level+1,o.left=100*e+"%",o.right=100*n+"%",i&&t.forwardPressure&&(o[this.isRTL?"marginLeft":"marginRight"]=20),o}});var Ee=qt.View=St.extend(ue,ce,{type:null,name:null,title:null,calendar:null,options:null,el:null,isDateSet:!1,isDateRendered:!1,dateRenderQueue:null,isEventsBound:!1,isEventsSet:!1,isEventsRendered:!1,eventRenderQueue:null,start:null,end:null,intervalStart:null,intervalEnd:null,intervalDuration:null,intervalUnit:null,isRTL:!1,isSelected:!1,selectedEvent:null,eventOrderSpecs:null,widgetHeaderClass:null,widgetContentClass:null,highlightStateClass:null,nextDayThreshold:null,isHiddenDayHash:null,isNowIndicatorRendered:null,initialNowDate:null,initialNowQueriedMs:null,nowIndicatorTimeoutID:null,nowIndicatorIntervalID:null,constructor:function(t,n,i,r){this.calendar=t,this.type=this.name=n,this.options=i,this.intervalDuration=r||e.duration(1,"day"),this.nextDayThreshold=e.duration(this.opt("nextDayThreshold")),this.initThemingProps(),this.initHiddenDays(),this.isRTL=this.opt("isRTL"),this.eventOrderSpecs=L(this.opt("eventOrder")),this.dateRenderQueue=new Dt,this.eventRenderQueue=new Dt(this.opt("eventRenderWait")),this.initialize()},initialize:function(){},opt:function(t){return this.options[t]},publiclyTrigger:function(t,e){var n=this.calendar;return n.publiclyTrigger.apply(n,[t,e||this].concat(Array.prototype.slice.call(arguments,2),[this]))},rejectOn:function(t,e){var n=this;return new bt(function(i,r){function s(){n.off(t,r)}n.one(t,r),e.then(function(t){s(),i(t)},function(){s(),r()})})},setRange:function(e){t.extend(this,e),this.updateTitle()},computeRange:function(t){var e,n,i=A(this.intervalDuration),r=t.clone().startOf(i),s=r.clone().add(this.intervalDuration);return/year|month|week|day/.test(i)?(r.stripTime(),s.stripTime()):(r.hasTime()||(r=this.calendar.time(0)),s.hasTime()||(s=this.calendar.time(0))),e=r.clone(),e=this.skipHiddenDays(e),n=s.clone(),n=this.skipHiddenDays(n,-1,!0),{intervalUnit:i,intervalStart:r,intervalEnd:s,start:e,end:n}},computePrevDate:function(t){return this.massageCurrentDate(t.clone().startOf(this.intervalUnit).subtract(this.intervalDuration),-1)},computeNextDate:function(t){return this.massageCurrentDate(t.clone().startOf(this.intervalUnit).add(this.intervalDuration))},massageCurrentDate:function(t,e){return this.intervalDuration.as("days")<=1&&this.isHiddenDay(t)&&(t=this.skipHiddenDays(t,e),t.startOf("day")),t},updateTitle:function(){this.title=this.computeTitle(),this.calendar.setToolbarsTitle(this.title)},computeTitle:function(){return this.formatRange({start:this.calendar.applyTimezone(this.intervalStart),end:this.calendar.applyTimezone(this.intervalEnd)},this.opt("titleFormat")||this.computeTitleFormat(),this.opt("titleRangeSeparator"))},computeTitleFormat:function(){return"year"==this.intervalUnit?"YYYY":"month"==this.intervalUnit?this.opt("monthYearFormat"):this.intervalDuration.as("days")>1?"ll":"LL"},formatRange:function(t,e,n){var i=t.end;return i.hasTime()||(i=i.clone().subtract(1)),gt(t.start,i,e,n,this.opt("isRTL"))},getAllDayHtml:function(){return this.opt("allDayHtml")||tt(this.opt("allDayText"))},buildGotoAnchorHtml:function(e,n,i){var r,s,o,l;return t.isPlainObject(e)?(r=e.date,s=e.type,o=e.forceOff):r=e,r=qt.moment(r),l={date:r.format("YYYY-MM-DD"),type:s||"day"},"string"==typeof n&&(i=n,n=null),n=n?" "+it(n):"",i=i||"",!o&&this.opt("navLinks")?"<a"+n+' data-goto="'+tt(JSON.stringify(l))+'">'+i+"</a>":"<span"+n+">"+i+"</span>"},setElement:function(t){this.el=t,this.bindGlobalHandlers(),this.renderSkeleton()},removeElement:function(){this.unsetDate(),this.unrenderSkeleton(),this.unbindGlobalHandlers(),this.el.remove()},renderSkeleton:function(){},unrenderSkeleton:function(){},setDate:function(t){var e=this.isDateSet;this.isDateSet=!0,this.handleDate(t,e),this.trigger(e?"dateReset":"dateSet",t)},unsetDate:function(){this.isDateSet&&(this.isDateSet=!1,this.handleDateUnset(),this.trigger("dateUnset"))},handleDate:function(t,e){var n=this;this.unbindEvents(),this.requestDateRender(t).then(function(){n.bindEvents()})},handleDateUnset:function(){this.unbindEvents(),this.requestDateUnrender()},requestDateRender:function(t){var e=this;return this.dateRenderQueue.add(function(){return e.executeDateRender(t)})},requestDateUnrender:function(){var t=this;return this.dateRenderQueue.add(function(){return t.executeDateUnrender()})},executeDateRender:function(t){var e=this;return t?this.captureInitialScroll():this.captureScroll(),this.freezeHeight(),this.executeDateUnrender().then(function(){t&&e.setRange(e.computeRange(t)),e.render&&e.render(),e.renderDates(),e.updateSize(),e.renderBusinessHours(),e.startNowIndicator(),e.thawHeight(),e.releaseScroll(),e.isDateRendered=!0,e.onDateRender(),e.trigger("dateRender")})},executeDateUnrender:function(){var t=this;return t.isDateRendered?this.requestEventsUnrender().then(function(){t.unselect(),t.stopNowIndicator(),t.triggerUnrender(),t.unrenderBusinessHours(),t.unrenderDates(),t.destroy&&t.destroy(),t.isDateRendered=!1,t.trigger("dateUnrender")}):bt.resolve()},onDateRender:function(){this.triggerRender()},renderDates:function(){},unrenderDates:function(){},triggerRender:function(){this.publiclyTrigger("viewRender",this,this,this.el); -},triggerUnrender:function(){this.publiclyTrigger("viewDestroy",this,this,this.el)},bindGlobalHandlers:function(){this.listenTo(t(document),"mousedown",this.handleDocumentMousedown),this.listenTo(t(document),"touchstart",this.processUnselect)},unbindGlobalHandlers:function(){this.stopListeningTo(t(document))},initThemingProps:function(){var t=this.opt("theme")?"ui":"fc";this.widgetHeaderClass=t+"-widget-header",this.widgetContentClass=t+"-widget-content",this.highlightStateClass=t+"-state-highlight"},renderBusinessHours:function(){},unrenderBusinessHours:function(){},startNowIndicator:function(){var t,n,i,r=this;this.opt("nowIndicator")&&(t=this.getNowIndicatorUnit(),t&&(n=lt(this,"updateNowIndicator"),this.initialNowDate=this.calendar.getNow(),this.initialNowQueriedMs=+new Date,this.renderNowIndicator(this.initialNowDate),this.isNowIndicatorRendered=!0,i=this.initialNowDate.clone().startOf(t).add(1,t)-this.initialNowDate,this.nowIndicatorTimeoutID=setTimeout(function(){r.nowIndicatorTimeoutID=null,n(),i=+e.duration(1,t),i=Math.max(100,i),r.nowIndicatorIntervalID=setInterval(n,i)},i)))},updateNowIndicator:function(){this.isNowIndicatorRendered&&(this.unrenderNowIndicator(),this.renderNowIndicator(this.initialNowDate.clone().add(new Date-this.initialNowQueriedMs)))},stopNowIndicator:function(){this.isNowIndicatorRendered&&(this.nowIndicatorTimeoutID&&(clearTimeout(this.nowIndicatorTimeoutID),this.nowIndicatorTimeoutID=null),this.nowIndicatorIntervalID&&(clearTimeout(this.nowIndicatorIntervalID),this.nowIndicatorIntervalID=null),this.unrenderNowIndicator(),this.isNowIndicatorRendered=!1)},getNowIndicatorUnit:function(){},renderNowIndicator:function(t){},unrenderNowIndicator:function(){},updateSize:function(t){t&&this.captureScroll(),this.updateHeight(t),this.updateWidth(t),this.updateNowIndicator(),t&&this.releaseScroll()},updateWidth:function(t){},updateHeight:function(t){var e=this.calendar;this.setHeight(e.getSuggestedViewHeight(),e.isHeightAuto())},setHeight:function(t,e){},capturedScroll:null,capturedScrollDepth:0,captureScroll:function(){return!this.capturedScrollDepth++&&(this.capturedScroll=this.isDateRendered?this.queryScroll():{},!0)},captureInitialScroll:function(e){this.captureScroll()&&(this.capturedScroll.isInitial=!0,e?t.extend(this.capturedScroll,e):this.capturedScroll.isComputed=!0)},releaseScroll:function(){var e=this.capturedScroll,n=this.discardScroll();e.isComputed&&(n?t.extend(e,this.computeInitialScroll()):e=null),e&&(e.isInitial?this.hardSetScroll(e):this.setScroll(e))},discardScroll:function(){return!--this.capturedScrollDepth&&(this.capturedScroll=null,!0)},computeInitialScroll:function(){return{}},queryScroll:function(){return{}},hardSetScroll:function(t){var e=this,n=function(){e.setScroll(t)};n(),setTimeout(n,0)},setScroll:function(t){},freezeHeight:function(){this.calendar.freezeContentHeight()},thawHeight:function(){this.calendar.thawContentHeight()},bindEvents:function(){var t=this;this.isEventsBound||(this.isEventsBound=!0,this.rejectOn("eventsUnbind",this.requestEvents()).then(function(e){t.listenTo(t.calendar,"eventsReset",t.setEvents),t.setEvents(e)}))},unbindEvents:function(){this.isEventsBound&&(this.isEventsBound=!1,this.stopListeningTo(this.calendar,"eventsReset"),this.unsetEvents(),this.trigger("eventsUnbind"))},setEvents:function(t){var e=this.isEventSet;this.isEventsSet=!0,this.handleEvents(t,e),this.trigger(e?"eventsReset":"eventsSet",t)},unsetEvents:function(){this.isEventsSet&&(this.isEventsSet=!1,this.handleEventsUnset(),this.trigger("eventsUnset"))},whenEventsSet:function(){var t=this;return this.isEventsSet?bt.resolve(this.getCurrentEvents()):new bt(function(e){t.one("eventsSet",e)})},handleEvents:function(t,e){this.requestEventsRender(t)},handleEventsUnset:function(){this.requestEventsUnrender()},requestEventsRender:function(t){var e=this;return this.eventRenderQueue.add(function(){return e.executeEventsRender(t)})},requestEventsUnrender:function(){var t=this;return this.isEventsRendered?this.eventRenderQueue.addQuickly(function(){return t.executeEventsUnrender()}):bt.resolve()},requestCurrentEventsRender:function(){return this.isEventsSet?void this.requestEventsRender(this.getCurrentEvents()):bt.reject()},executeEventsRender:function(t){var e=this;return this.captureScroll(),this.freezeHeight(),this.executeEventsUnrender().then(function(){e.renderEvents(t),e.thawHeight(),e.releaseScroll(),e.isEventsRendered=!0,e.onEventsRender(),e.trigger("eventsRender")})},executeEventsUnrender:function(){return this.isEventsRendered&&(this.onBeforeEventsUnrender(),this.captureScroll(),this.freezeHeight(),this.destroyEvents&&this.destroyEvents(),this.unrenderEvents(),this.thawHeight(),this.releaseScroll(),this.isEventsRendered=!1,this.trigger("eventsUnrender")),bt.resolve()},onEventsRender:function(){this.renderedEventSegEach(function(t){this.publiclyTrigger("eventAfterRender",t.event,t.event,t.el)}),this.publiclyTrigger("eventAfterAllRender")},onBeforeEventsUnrender:function(){this.renderedEventSegEach(function(t){this.publiclyTrigger("eventDestroy",t.event,t.event,t.el)})},renderEvents:function(t){},unrenderEvents:function(){},requestEvents:function(){return this.calendar.requestEvents(this.start,this.end)},getCurrentEvents:function(){return this.calendar.getPrunedEventCache()},resolveEventEl:function(e,n){var i=this.publiclyTrigger("eventRender",e,e,n);return i===!1?n=null:i&&i!==!0&&(n=t(i)),n},showEvent:function(t){this.renderedEventSegEach(function(t){t.el.css("visibility","")},t)},hideEvent:function(t){this.renderedEventSegEach(function(t){t.el.css("visibility","hidden")},t)},renderedEventSegEach:function(t,e){var n,i=this.getEventSegs();for(n=0;n<i.length;n++)e&&i[n].event._id!==e._id||i[n].el&&t.call(this,i[n])},getEventSegs:function(){return[]},isEventDraggable:function(t){return this.isEventStartEditable(t)},isEventStartEditable:function(t){return J(t.startEditable,(t.source||{}).startEditable,this.opt("eventStartEditable"),this.isEventGenerallyEditable(t))},isEventGenerallyEditable:function(t){return J(t.editable,(t.source||{}).editable,this.opt("editable"))},reportEventDrop:function(t,e,n,i,r){var s=this.calendar,o=s.mutateEvent(t,e,n),l=function(){o.undo(),s.reportEventChange()};this.triggerEventDrop(t,o.dateDelta,l,i,r),s.reportEventChange()},triggerEventDrop:function(t,e,n,i,r){this.publiclyTrigger("eventDrop",i[0],t,e,n,r,{})},reportExternalDrop:function(e,n,i,r,s){var o,l,a=e.eventProps;a&&(o=t.extend({},a,n),l=this.calendar.renderEvent(o,e.stick)[0]),this.triggerExternalDrop(l,n,i,r,s)},triggerExternalDrop:function(t,e,n,i,r){this.publiclyTrigger("drop",n[0],e.start,i,r),t&&this.publiclyTrigger("eventReceive",null,t)},renderDrag:function(t,e){},unrenderDrag:function(){},isEventResizableFromStart:function(t){return this.opt("eventResizableFromStart")&&this.isEventResizable(t)},isEventResizableFromEnd:function(t){return this.isEventResizable(t)},isEventResizable:function(t){var e=t.source||{};return J(t.durationEditable,e.durationEditable,this.opt("eventDurationEditable"),t.editable,e.editable,this.opt("editable"))},reportEventResize:function(t,e,n,i,r){var s=this.calendar,o=s.mutateEvent(t,e,n),l=function(){o.undo(),s.reportEventChange()};this.triggerEventResize(t,o.durationDelta,l,i,r),s.reportEventChange()},triggerEventResize:function(t,e,n,i,r){this.publiclyTrigger("eventResize",i[0],t,e,n,r,{})},select:function(t,e){this.unselect(e),this.renderSelection(t),this.reportSelection(t,e)},renderSelection:function(t){},reportSelection:function(t,e){this.isSelected=!0,this.triggerSelect(t,e)},triggerSelect:function(t,e){this.publiclyTrigger("select",null,this.calendar.applyTimezone(t.start),this.calendar.applyTimezone(t.end),e)},unselect:function(t){this.isSelected&&(this.isSelected=!1,this.destroySelection&&this.destroySelection(),this.unrenderSelection(),this.publiclyTrigger("unselect",null,t))},unrenderSelection:function(){},selectEvent:function(t){this.selectedEvent&&this.selectedEvent===t||(this.unselectEvent(),this.renderedEventSegEach(function(t){t.el.addClass("fc-selected")},t),this.selectedEvent=t)},unselectEvent:function(){this.selectedEvent&&(this.renderedEventSegEach(function(t){t.el.removeClass("fc-selected")},this.selectedEvent),this.selectedEvent=null)},isEventSelected:function(t){return this.selectedEvent&&this.selectedEvent._id===t._id},handleDocumentMousedown:function(t){S(t)&&this.processUnselect(t)},processUnselect:function(t){this.processRangeUnselect(t),this.processEventUnselect(t)},processRangeUnselect:function(e){var n;this.isSelected&&this.opt("unselectAuto")&&(n=this.opt("unselectCancel"),n&&t(e.target).closest(n).length||this.unselect(e))},processEventUnselect:function(e){this.selectedEvent&&(t(e.target).closest(".fc-selected").length||this.unselectEvent())},triggerDayClick:function(t,e,n){this.publiclyTrigger("dayClick",e,this.calendar.applyTimezone(t.start),n)},initHiddenDays:function(){var e,n=this.opt("hiddenDays")||[],i=[],r=0;for(this.opt("weekends")===!1&&n.push(0,6),e=0;e<7;e++)(i[e]=t.inArray(e,n)!==-1)||r++;if(!r)throw"invalid hiddenDays";this.isHiddenDayHash=i},isHiddenDay:function(t){return e.isMoment(t)&&(t=t.day()),this.isHiddenDayHash[t]},skipHiddenDays:function(t,e,n){var i=t.clone();for(e=e||1;this.isHiddenDayHash[(i.day()+(n?e:0)+7)%7];)i.add(e,"days");return i},computeDayRange:function(t){var e,n=t.start.clone().stripTime(),i=t.end,r=null;return i&&(r=i.clone().stripTime(),e=+i.time(),e&&e>=this.nextDayThreshold&&r.add(1,"days")),(!i||r<=n)&&(r=n.clone().add(1,"days")),{start:n,end:r}},isMultiDayEvent:function(t){var e=this.computeDayRange(t);return e.end.diff(e.start,"days")>1}}),be=qt.Scroller=St.extend({el:null,scrollEl:null,overflowX:null,overflowY:null,constructor:function(t){t=t||{},this.overflowX=t.overflowX||t.overflow||"auto",this.overflowY=t.overflowY||t.overflow||"auto"},render:function(){this.el=this.renderEl(),this.applyOverflow()},renderEl:function(){return this.scrollEl=t('<div class="fc-scroller"></div>')},clear:function(){this.setHeight("auto"),this.applyOverflow()},destroy:function(){this.el.remove()},applyOverflow:function(){this.scrollEl.css({"overflow-x":this.overflowX,"overflow-y":this.overflowY})},lockOverflow:function(t){var e=this.overflowX,n=this.overflowY;t=t||this.getScrollbarWidths(),"auto"===e&&(e=t.top||t.bottom||this.scrollEl[0].scrollWidth-1>this.scrollEl[0].clientWidth?"scroll":"hidden"),"auto"===n&&(n=t.left||t.right||this.scrollEl[0].scrollHeight-1>this.scrollEl[0].clientHeight?"scroll":"hidden"),this.scrollEl.css({"overflow-x":e,"overflow-y":n})},setHeight:function(t){this.scrollEl.height(t)},getScrollTop:function(){return this.scrollEl.scrollTop()},setScrollTop:function(t){this.scrollEl.scrollTop(t)},getClientWidth:function(){return this.scrollEl[0].clientWidth},getClientHeight:function(){return this.scrollEl[0].clientHeight},getScrollbarWidths:function(){return p(this.scrollEl)}});Vt.prototype.proxyCall=function(t){var e=Array.prototype.slice.call(arguments,1),n=[];return this.items.forEach(function(i){n.push(i[t].apply(i,e))}),n};var De=qt.Calendar=St.extend({dirDefaults:null,localeDefaults:null,overrides:null,dynamicOverrides:null,options:null,viewSpecCache:null,view:null,header:null,footer:null,loadingLevel:0,constructor:_t,initialize:function(){},populateOptionsHash:function(){var t,e,i,r;t=J(this.dynamicOverrides.locale,this.overrides.locale),e=Te[t],e||(t=De.defaults.locale,e=Te[t]||{}),i=J(this.dynamicOverrides.isRTL,this.overrides.isRTL,e.isRTL,De.defaults.isRTL),r=i?De.rtlDefaults:{},this.dirDefaults=r,this.localeDefaults=e,this.options=n([De.defaults,r,e,this.overrides,this.dynamicOverrides]),Yt(this.options)},getViewSpec:function(t){var e=this.viewSpecCache;return e[t]||(e[t]=this.buildViewSpec(t))},getUnitViewSpec:function(e){var n,i,r;if(t.inArray(e,Kt)!=-1)for(n=this.header.getViewsWithButtons(),t.each(qt.views,function(t){n.push(t)}),i=0;i<n.length;i++)if(r=this.getViewSpec(n[i]),r&&r.singleUnit==e)return r},buildViewSpec:function(t){for(var i,r,s,o,l=this.overrides.views||{},a=[],u=[],c=[],d=t;d;)i=Zt[d],r=l[d],d=null,"function"==typeof i&&(i={class:i}),i&&(a.unshift(i),u.unshift(i.defaults||{}),s=s||i.duration,d=d||i.type),r&&(c.unshift(r),s=s||r.duration,d=d||r.type);return i=q(a),i.type=t,!!i.class&&(s&&(s=e.duration(s),s.valueOf()&&(i.duration=s,o=A(s),1===s.as(o)&&(i.singleUnit=o,c.unshift(l[o]||{})))),i.defaults=n(u),i.overrides=n(c),this.buildViewSpecOptions(i),this.buildViewSpecButtonText(i,t),i)},buildViewSpecOptions:function(t){t.options=n([De.defaults,t.defaults,this.dirDefaults,this.localeDefaults,this.overrides,t.overrides,this.dynamicOverrides]),Yt(t.options)},buildViewSpecButtonText:function(t,e){function n(n){var i=n.buttonText||{};return i[e]||(t.buttonTextKey?i[t.buttonTextKey]:null)||(t.singleUnit?i[t.singleUnit]:null)}t.buttonTextOverride=n(this.dynamicOverrides)||n(this.overrides)||t.overrides.buttonText,t.buttonTextDefault=n(this.localeDefaults)||n(this.dirDefaults)||t.defaults.buttonText||n(De.defaults)||(t.duration?this.humanizeDuration(t.duration):null)||e},instantiateView:function(t){var e=this.getViewSpec(t);return new e.class(this,t,e.options,e.duration)},isValidViewType:function(t){return Boolean(this.getViewSpec(t))},pushLoading:function(){this.loadingLevel++||this.publiclyTrigger("loading",null,!0,this.view)},popLoading:function(){--this.loadingLevel||this.publiclyTrigger("loading",null,!1,this.view)},buildSelectSpan:function(t,e){var n,i=this.moment(t).stripZone();return n=e?this.moment(e).stripZone():i.hasTime()?i.clone().add(this.defaultTimedEventDuration):i.clone().add(this.defaultAllDayEventDuration),{start:i,end:n}}});De.mixin(ue),De.mixin({optionHandlers:null,bindOption:function(t,e){this.bindOptions([t],e)},bindOptions:function(t,e){var n,i={func:e,names:t};for(n=0;n<t.length;n++)this.registerOptionHandlerObj(t[n],i);this.triggerOptionHandlerObj(i)},registerOptionHandlerObj:function(t,e){(this.optionHandlers[t]||(this.optionHandlers[t]=[])).push(e)},triggerOptionHandlers:function(t){var e,n=this.optionHandlers[t]||[];for(e=0;e<n.length;e++)this.triggerOptionHandlerObj(n[e])},triggerOptionHandlerObj:function(t){var e,n=t.names,i=[];for(e=0;e<n.length;e++)i.push(this.options[n[e]]);t.func.apply(this,i)}}),De.defaults={titleRangeSeparator:" – ",monthYearFormat:"MMMM YYYY",defaultTimedEventDuration:"02:00:00",defaultAllDayEventDuration:{days:1},forceEventDuration:!1,nextDayThreshold:"09:00:00",defaultView:"month",aspectRatio:1.35,header:{left:"title",center:"",right:"today prev,next"},weekends:!0,weekNumbers:!1,weekNumberTitle:"W",weekNumberCalculation:"local",scrollTime:"06:00:00",lazyFetching:!0,startParam:"start",endParam:"end",timezoneParam:"timezone",timezone:!1,isRTL:!1,buttonText:{prev:"prev",next:"next",prevYear:"prev year",nextYear:"next year",year:"year",today:"today",month:"month",week:"week",day:"day"},buttonIcons:{prev:"left-single-arrow",next:"right-single-arrow",prevYear:"left-double-arrow",nextYear:"right-double-arrow"},allDayText:"all-day",theme:!1,themeButtonIcons:{prev:"circle-triangle-w",next:"circle-triangle-e",prevYear:"seek-prev",nextYear:"seek-next"},dragOpacity:.75,dragRevertDuration:500,dragScroll:!0,unselectAuto:!0,dropAccept:"*",eventOrder:"title",eventLimit:!1,eventLimitText:"more",eventLimitClick:"popover",dayPopoverFormat:"LL",handleWindowResize:!0,windowResizeDelay:100,longPressDelay:1e3},De.englishDefaults={dayPopoverFormat:"dddd, MMMM D"},De.rtlDefaults={header:{left:"next,prev today",center:"",right:"title"},buttonIcons:{prev:"right-single-arrow",next:"left-single-arrow",prevYear:"right-double-arrow",nextYear:"left-double-arrow"},themeButtonIcons:{prev:"circle-triangle-e",next:"circle-triangle-w",nextYear:"seek-prev",prevYear:"seek-next"}};var Te=qt.locales={};qt.datepickerLocale=function(e,n,i){var r=Te[e]||(Te[e]={});r.isRTL=i.isRTL,r.weekNumberTitle=i.weekHeader,t.each(Ce,function(t,e){r[t]=e(i)}),t.datepicker&&(t.datepicker.regional[n]=t.datepicker.regional[e]=i,t.datepicker.regional.en=t.datepicker.regional[""],t.datepicker.setDefaults(i))},qt.locale=function(e,i){var r,s;r=Te[e]||(Te[e]={}),i&&(r=Te[e]=n([r,i])),s=Wt(e),t.each(He,function(t,e){null==r[t]&&(r[t]=e(s,r))}),De.defaults.locale=e};var Ce={buttonText:function(t){return{prev:et(t.prevText),next:et(t.nextText),today:et(t.currentText)}},monthYearFormat:function(t){return t.showMonthAfterYear?"YYYY["+t.yearSuffix+"] MMMM":"MMMM YYYY["+t.yearSuffix+"]"}},He={dayOfMonthFormat:function(t,e){var n=t.longDateFormat("l");return n=n.replace(/^Y+[^\w\s]*|[^\w\s]*Y+$/g,""),e.isRTL?n+=" ddd":n="ddd "+n,n},mediumTimeFormat:function(t){return t.longDateFormat("LT").replace(/\s*a$/i,"a")},smallTimeFormat:function(t){return t.longDateFormat("LT").replace(":mm","(:mm)").replace(/(\Wmm)$/,"($1)").replace(/\s*a$/i,"a")},extraSmallTimeFormat:function(t){return t.longDateFormat("LT").replace(":mm","(:mm)").replace(/(\Wmm)$/,"($1)").replace(/\s*a$/i,"t")},hourFormat:function(t){return t.longDateFormat("LT").replace(":mm","").replace(/(\Wmm)$/,"").replace(/\s*a$/i,"a")},noMeridiemTimeFormat:function(t){return t.longDateFormat("LT").replace(/\s*a$/i,"")}},Re={smallDayDateFormat:function(t){return t.isRTL?"D dd":"dd D"},weekFormat:function(t){return t.isRTL?"w[ "+t.weekNumberTitle+"]":"["+t.weekNumberTitle+" ]w"},smallWeekFormat:function(t){return t.isRTL?"w["+t.weekNumberTitle+"]":"["+t.weekNumberTitle+"]w"}};qt.locale("en",De.englishDefaults),qt.sourceNormalizers=[],qt.sourceFetchers=[];var xe={dataType:"json",cache:!1},Ie=1;De.prototype.normalizeEvent=function(t){},De.prototype.spanContainsSpan=function(t,e){var n=t.start.clone().stripZone(),i=this.getEventEnd(t).stripZone();return e.start>=n&&e.end<=i},De.prototype.getPeerEvents=function(t,e){var n,i,r=this.getEventCache(),s=[];for(n=0;n<r.length;n++)i=r[n],e&&e._id===i._id||s.push(i);return s},De.prototype.isEventSpanAllowed=function(t,e){var n=e.source||{},i=J(e.constraint,n.constraint,this.options.eventConstraint),r=J(e.overlap,n.overlap,this.options.eventOverlap);return this.isSpanAllowed(t,i,r,e)&&(!this.options.eventAllow||this.options.eventAllow(t,e)!==!1)},De.prototype.isExternalSpanAllowed=function(e,n,i){var r,s;return i&&(r=t.extend({},i,n),s=this.expandEvent(this.buildEventFromInput(r))[0]),s?this.isEventSpanAllowed(e,s):this.isSelectionSpanAllowed(e)},De.prototype.isSelectionSpanAllowed=function(t){return this.isSpanAllowed(t,this.options.selectConstraint,this.options.selectOverlap)&&(!this.options.selectAllow||this.options.selectAllow(t)!==!1)},De.prototype.isSpanAllowed=function(t,e,n,i){var r,s,o,l,a,u;if(null!=e&&(r=this.constraintToEvents(e))){for(s=!1,l=0;l<r.length;l++)if(this.spanContainsSpan(r[l],t)){s=!0;break}if(!s)return!1}for(o=this.getPeerEvents(t,i),l=0;l<o.length;l++)if(a=o[l],this.eventIntersectsRange(a,t)){if(n===!1)return!1;if("function"==typeof n&&!n(a,i))return!1;if(i){if(u=J(a.overlap,(a.source||{}).overlap),u===!1)return!1;if("function"==typeof u&&!u(i,a))return!1}}return!0},De.prototype.constraintToEvents=function(t){return"businessHours"===t?this.getCurrentBusinessHourEvents():"object"==typeof t?null!=t.start?this.expandEvent(this.buildEventFromInput(t)):null:this.clientEvents(t)},De.prototype.eventIntersectsRange=function(t,e){var n=t.start.clone().stripZone(),i=this.getEventEnd(t).stripZone();return e.start<i&&e.end>n};var ke={id:"_fcBusinessHours",start:"09:00",end:"17:00",dow:[1,2,3,4,5],rendering:"inverse-background"};De.prototype.getCurrentBusinessHourEvents=function(t){return this.computeBusinessHourEvents(t,this.options.businessHours)},De.prototype.computeBusinessHourEvents=function(e,n){return n===!0?this.expandBusinessHourEvents(e,[{}]):t.isPlainObject(n)?this.expandBusinessHourEvents(e,[n]):t.isArray(n)?this.expandBusinessHourEvents(e,n,!0):[]},De.prototype.expandBusinessHourEvents=function(e,n,i){var r,s,o=this.getView(),l=[];for(r=0;r<n.length;r++)s=n[r],i&&!s.dow||(s=t.extend({},ke,s),e&&(s.start=null,s.end=null),l.push.apply(l,this.expandEvent(this.buildEventFromInput(s),o.start,o.end)));return l};var Le=qt.BasicView=Ee.extend({scroller:null,dayGridClass:Se,dayGrid:null,dayNumbersVisible:!1,colWeekNumbersVisible:!1,cellWeekNumbersVisible:!1,weekNumberWidth:null,headContainerEl:null,headRowEl:null,initialize:function(){this.dayGrid=this.instantiateDayGrid(),this.scroller=new be({overflowX:"hidden",overflowY:"auto"})},instantiateDayGrid:function(){var t=this.dayGridClass.extend(Me);return new t(this)},setRange:function(t){Ee.prototype.setRange.call(this,t),this.dayGrid.breakOnWeeks=/year|month|week/.test(this.intervalUnit),this.dayGrid.setRange(t)},computeRange:function(t){var e=Ee.prototype.computeRange.call(this,t);return/year|month/.test(e.intervalUnit)&&(e.start.startOf("week"),e.start=this.skipHiddenDays(e.start),e.end.weekday()&&(e.end.add(1,"week").startOf("week"),e.end=this.skipHiddenDays(e.end,-1,!0))),e},renderDates:function(){this.dayNumbersVisible=this.dayGrid.rowCnt>1,this.opt("weekNumbers")&&(this.opt("weekNumbersWithinDays")?(this.cellWeekNumbersVisible=!0,this.colWeekNumbersVisible=!1):(this.cellWeekNumbersVisible=!1,this.colWeekNumbersVisible=!0)),this.dayGrid.numbersVisible=this.dayNumbersVisible||this.cellWeekNumbersVisible||this.colWeekNumbersVisible,this.el.addClass("fc-basic-view").html(this.renderSkeletonHtml()),this.renderHead(),this.scroller.render();var e=this.scroller.el.addClass("fc-day-grid-container"),n=t('<div class="fc-day-grid" />').appendTo(e);this.el.find(".fc-body > tr > td").append(e),this.dayGrid.setElement(n),this.dayGrid.renderDates(this.hasRigidRows())},renderHead:function(){this.headContainerEl=this.el.find(".fc-head-container").html(this.dayGrid.renderHeadHtml()),this.headRowEl=this.headContainerEl.find(".fc-row")},unrenderDates:function(){this.dayGrid.unrenderDates(),this.dayGrid.removeElement(),this.scroller.destroy()},renderBusinessHours:function(){this.dayGrid.renderBusinessHours()},unrenderBusinessHours:function(){this.dayGrid.unrenderBusinessHours()},renderSkeletonHtml:function(){return'<table><thead class="fc-head"><tr><td class="fc-head-container '+this.widgetHeaderClass+'"></td></tr></thead><tbody class="fc-body"><tr><td class="'+this.widgetContentClass+'"></td></tr></tbody></table>'},weekNumberStyleAttr:function(){return null!==this.weekNumberWidth?'style="width:'+this.weekNumberWidth+'px"':""},hasRigidRows:function(){var t=this.opt("eventLimit");return t&&"number"!=typeof t},updateWidth:function(){this.colWeekNumbersVisible&&(this.weekNumberWidth=u(this.el.find(".fc-week-number")))},setHeight:function(t,e){var n,s,o=this.opt("eventLimit");this.scroller.clear(),r(this.headRowEl),this.dayGrid.removeSegPopover(),o&&"number"==typeof o&&this.dayGrid.limitRows(o),n=this.computeScrollerHeight(t),this.setGridHeight(n,e),o&&"number"!=typeof o&&this.dayGrid.limitRows(o),e||(this.scroller.setHeight(n),s=this.scroller.getScrollbarWidths(),(s.left||s.right)&&(i(this.headRowEl,s),n=this.computeScrollerHeight(t),this.scroller.setHeight(n)),this.scroller.lockOverflow(s))},computeScrollerHeight:function(t){return t-c(this.el,this.scroller.el)},setGridHeight:function(t,e){e?a(this.dayGrid.rowEls):l(this.dayGrid.rowEls,t,!0)},computeInitialScroll:function(){return{top:0}},queryScroll:function(){return{top:this.scroller.getScrollTop()}},setScroll:function(t){this.scroller.setScrollTop(t.top)},prepareHits:function(){this.dayGrid.prepareHits()},releaseHits:function(){this.dayGrid.releaseHits()},queryHit:function(t,e){return this.dayGrid.queryHit(t,e)},getHitSpan:function(t){return this.dayGrid.getHitSpan(t)},getHitEl:function(t){return this.dayGrid.getHitEl(t)},renderEvents:function(t){this.dayGrid.renderEvents(t),this.updateHeight()},getEventSegs:function(){return this.dayGrid.getEventSegs()},unrenderEvents:function(){this.dayGrid.unrenderEvents()},renderDrag:function(t,e){return this.dayGrid.renderDrag(t,e)},unrenderDrag:function(){this.dayGrid.unrenderDrag()},renderSelection:function(t){this.dayGrid.renderSelection(t)},unrenderSelection:function(){this.dayGrid.unrenderSelection()}}),Me={renderHeadIntroHtml:function(){var t=this.view;return t.colWeekNumbersVisible?'<th class="fc-week-number '+t.widgetHeaderClass+'" '+t.weekNumberStyleAttr()+"><span>"+tt(t.opt("weekNumberTitle"))+"</span></th>":""},renderNumberIntroHtml:function(t){var e=this.view,n=this.getCellDate(t,0);return e.colWeekNumbersVisible?'<td class="fc-week-number" '+e.weekNumberStyleAttr()+">"+e.buildGotoAnchorHtml({date:n,type:"week",forceOff:1===this.colCnt},n.format("w"))+"</td>":""},renderBgIntroHtml:function(){var t=this.view;return t.colWeekNumbersVisible?'<td class="fc-week-number '+t.widgetContentClass+'" '+t.weekNumberStyleAttr()+"></td>":""},renderIntroHtml:function(){var t=this.view;return t.colWeekNumbersVisible?'<td class="fc-week-number" '+t.weekNumberStyleAttr()+"></td>":""}},Be=qt.MonthView=Le.extend({computeRange:function(t){var e,n=Le.prototype.computeRange.call(this,t);return this.isFixedWeeks()&&(e=Math.ceil(n.end.diff(n.start,"weeks",!0)),n.end.add(6-e,"weeks")),n},setGridHeight:function(t,e){e&&(t*=this.rowCnt/6),l(this.dayGrid.rowEls,t,!e)},isFixedWeeks:function(){return this.opt("fixedWeekCount")}});Zt.basic={class:Le},Zt.basicDay={type:"basic",duration:{days:1}},Zt.basicWeek={type:"basic",duration:{weeks:1}},Zt.month={class:Be,duration:{months:1},defaults:{fixedWeekCount:!0}};var ze=qt.AgendaView=Ee.extend({scroller:null,timeGridClass:we,timeGrid:null,dayGridClass:Se,dayGrid:null,axisWidth:null,headContainerEl:null,noScrollRowEls:null,bottomRuleEl:null,initialize:function(){this.timeGrid=this.instantiateTimeGrid(),this.opt("allDaySlot")&&(this.dayGrid=this.instantiateDayGrid()),this.scroller=new be({overflowX:"hidden",overflowY:"auto"})},instantiateTimeGrid:function(){var t=this.timeGridClass.extend(Fe);return new t(this)},instantiateDayGrid:function(){var t=this.dayGridClass.extend(Ne);return new t(this)},setRange:function(t){Ee.prototype.setRange.call(this,t),this.timeGrid.setRange(t),this.dayGrid&&this.dayGrid.setRange(t)},renderDates:function(){this.el.addClass("fc-agenda-view").html(this.renderSkeletonHtml()),this.renderHead(),this.scroller.render();var e=this.scroller.el.addClass("fc-time-grid-container"),n=t('<div class="fc-time-grid" />').appendTo(e);this.el.find(".fc-body > tr > td").append(e),this.timeGrid.setElement(n),this.timeGrid.renderDates(),this.bottomRuleEl=t('<hr class="fc-divider '+this.widgetHeaderClass+'"/>').appendTo(this.timeGrid.el),this.dayGrid&&(this.dayGrid.setElement(this.el.find(".fc-day-grid")),this.dayGrid.renderDates(),this.dayGrid.bottomCoordPadding=this.dayGrid.el.next("hr").outerHeight()),this.noScrollRowEls=this.el.find(".fc-row:not(.fc-scroller *)")},renderHead:function(){this.headContainerEl=this.el.find(".fc-head-container").html(this.timeGrid.renderHeadHtml())},unrenderDates:function(){this.timeGrid.unrenderDates(),this.timeGrid.removeElement(),this.dayGrid&&(this.dayGrid.unrenderDates(),this.dayGrid.removeElement()),this.scroller.destroy()},renderSkeletonHtml:function(){return'<table><thead class="fc-head"><tr><td class="fc-head-container '+this.widgetHeaderClass+'"></td></tr></thead><tbody class="fc-body"><tr><td class="'+this.widgetContentClass+'">'+(this.dayGrid?'<div class="fc-day-grid"/><hr class="fc-divider '+this.widgetHeaderClass+'"/>':"")+"</td></tr></tbody></table>"},axisStyleAttr:function(){return null!==this.axisWidth?'style="width:'+this.axisWidth+'px"':""},renderBusinessHours:function(){this.timeGrid.renderBusinessHours(),this.dayGrid&&this.dayGrid.renderBusinessHours()},unrenderBusinessHours:function(){this.timeGrid.unrenderBusinessHours(),this.dayGrid&&this.dayGrid.unrenderBusinessHours()},getNowIndicatorUnit:function(){return this.timeGrid.getNowIndicatorUnit()},renderNowIndicator:function(t){this.timeGrid.renderNowIndicator(t)},unrenderNowIndicator:function(){this.timeGrid.unrenderNowIndicator()},updateSize:function(t){this.timeGrid.updateSize(t),Ee.prototype.updateSize.call(this,t)},updateWidth:function(){this.axisWidth=u(this.el.find(".fc-axis"))},setHeight:function(t,e){var n,s,o;this.bottomRuleEl.hide(),this.scroller.clear(),r(this.noScrollRowEls),this.dayGrid&&(this.dayGrid.removeSegPopover(),n=this.opt("eventLimit"),n&&"number"!=typeof n&&(n=Ge),n&&this.dayGrid.limitRows(n)),e||(s=this.computeScrollerHeight(t),this.scroller.setHeight(s),o=this.scroller.getScrollbarWidths(),(o.left||o.right)&&(i(this.noScrollRowEls,o),s=this.computeScrollerHeight(t),this.scroller.setHeight(s)),this.scroller.lockOverflow(o),this.timeGrid.getTotalSlatHeight()<s&&this.bottomRuleEl.show())},computeScrollerHeight:function(t){return t-c(this.el,this.scroller.el)},computeInitialScroll:function(){var t=e.duration(this.opt("scrollTime")),n=this.timeGrid.computeTimeTop(t);return n=Math.ceil(n),n&&n++,{top:n}},queryScroll:function(){return{top:this.scroller.getScrollTop()}},setScroll:function(t){this.scroller.setScrollTop(t.top)},prepareHits:function(){this.timeGrid.prepareHits(),this.dayGrid&&this.dayGrid.prepareHits()},releaseHits:function(){this.timeGrid.releaseHits(),this.dayGrid&&this.dayGrid.releaseHits()},queryHit:function(t,e){var n=this.timeGrid.queryHit(t,e);return!n&&this.dayGrid&&(n=this.dayGrid.queryHit(t,e)),n},getHitSpan:function(t){return t.component.getHitSpan(t)},getHitEl:function(t){return t.component.getHitEl(t)},renderEvents:function(t){var e,n,i=[],r=[],s=[];for(n=0;n<t.length;n++)t[n].allDay?i.push(t[n]):r.push(t[n]);e=this.timeGrid.renderEvents(r),this.dayGrid&&(s=this.dayGrid.renderEvents(i)),this.updateHeight()},getEventSegs:function(){return this.timeGrid.getEventSegs().concat(this.dayGrid?this.dayGrid.getEventSegs():[])},unrenderEvents:function(){this.timeGrid.unrenderEvents(),this.dayGrid&&this.dayGrid.unrenderEvents()},renderDrag:function(t,e){return t.start.hasTime()?this.timeGrid.renderDrag(t,e):this.dayGrid?this.dayGrid.renderDrag(t,e):void 0},unrenderDrag:function(){this.timeGrid.unrenderDrag(),this.dayGrid&&this.dayGrid.unrenderDrag()},renderSelection:function(t){t.start.hasTime()||t.end.hasTime()?this.timeGrid.renderSelection(t):this.dayGrid&&this.dayGrid.renderSelection(t)},unrenderSelection:function(){this.timeGrid.unrenderSelection(),this.dayGrid&&this.dayGrid.unrenderSelection()}}),Fe={renderHeadIntroHtml:function(){var t,e=this.view;return e.opt("weekNumbers")?(t=this.start.format(e.opt("smallWeekFormat")),'<th class="fc-axis fc-week-number '+e.widgetHeaderClass+'" '+e.axisStyleAttr()+">"+e.buildGotoAnchorHtml({date:this.start,type:"week",forceOff:this.colCnt>1},tt(t))+"</th>"):'<th class="fc-axis '+e.widgetHeaderClass+'" '+e.axisStyleAttr()+"></th>"},renderBgIntroHtml:function(){var t=this.view;return'<td class="fc-axis '+t.widgetContentClass+'" '+t.axisStyleAttr()+"></td>"},renderIntroHtml:function(){var t=this.view;return'<td class="fc-axis" '+t.axisStyleAttr()+"></td>"}},Ne={renderBgIntroHtml:function(){var t=this.view;return'<td class="fc-axis '+t.widgetContentClass+'" '+t.axisStyleAttr()+"><span>"+t.getAllDayHtml()+"</span></td>"},renderIntroHtml:function(){var t=this.view;return'<td class="fc-axis" '+t.axisStyleAttr()+"></td>"}},Ge=5,Oe=[{hours:1},{minutes:30},{minutes:15},{seconds:30},{seconds:15}];Zt.agenda={class:ze,defaults:{allDaySlot:!0,slotDuration:"00:30:00",minTime:"00:00:00",maxTime:"24:00:00",slotEventOverlap:!0}},Zt.agendaDay={type:"agenda",duration:{days:1}},Zt.agendaWeek={type:"agenda",duration:{weeks:1}};var Ae=Ee.extend({grid:null,scroller:null,initialize:function(){this.grid=new Ve(this),this.scroller=new be({overflowX:"hidden",overflowY:"auto"})},setRange:function(t){Ee.prototype.setRange.call(this,t),this.grid.setRange(t)},renderSkeleton:function(){this.el.addClass("fc-list-view "+this.widgetContentClass),this.scroller.render(),this.scroller.el.appendTo(this.el),this.grid.setElement(this.scroller.scrollEl)},unrenderSkeleton:function(){this.scroller.destroy()},setHeight:function(t,e){this.scroller.setHeight(this.computeScrollerHeight(t))},computeScrollerHeight:function(t){return t-c(this.el,this.scroller.el); -},renderEvents:function(t){this.grid.renderEvents(t)},unrenderEvents:function(){this.grid.unrenderEvents()},isEventResizable:function(t){return!1},isEventDraggable:function(t){return!1}}),Ve=me.extend({segSelector:".fc-list-item",hasDayInteractions:!1,spanToSegs:function(t){for(var e,n=this.view,i=n.start.clone().time(0),r=0,s=[];i<n.end;)if(e=F(t,{start:i,end:i.clone().add(1,"day")}),e&&(e.dayIndex=r,s.push(e)),i.add(1,"day"),r++,e&&!e.isEnd&&t.end.hasTime()&&t.end<i.clone().add(this.view.nextDayThreshold)){e.end=t.end.clone(),e.isEnd=!0;break}return s},computeEventTimeFormat:function(){return this.view.opt("mediumTimeFormat")},handleSegClick:function(e,n){var i;me.prototype.handleSegClick.apply(this,arguments),t(n.target).closest("a[href]").length||(i=e.event.url,i&&!n.isDefaultPrevented()&&(window.location.href=i))},renderFgSegs:function(t){return t=this.renderFgSegEls(t),t.length?this.renderSegList(t):this.renderEmptyMessage(),t},renderEmptyMessage:function(){this.el.html('<div class="fc-list-empty-wrap2"><div class="fc-list-empty-wrap1"><div class="fc-list-empty">'+tt(this.view.opt("noEventsMessage"))+"</div></div></div>")},renderSegList:function(e){var n,i,r,s=this.groupSegsByDay(e),o=t('<table class="fc-list-table"><tbody/></table>'),l=o.find("tbody");for(n=0;n<s.length;n++)if(i=s[n])for(l.append(this.dayHeaderHtml(this.view.start.clone().add(n,"days"))),this.sortEventSegs(i),r=0;r<i.length;r++)l.append(i[r].el);this.el.empty().append(o)},groupSegsByDay:function(t){var e,n,i=[];for(e=0;e<t.length;e++)n=t[e],(i[n.dayIndex]||(i[n.dayIndex]=[])).push(n);return i},dayHeaderHtml:function(t){var e=this.view,n=e.opt("listDayFormat"),i=e.opt("listDayAltFormat");return'<tr class="fc-list-heading" data-date="'+t.format("YYYY-MM-DD")+'"><td class="'+e.widgetHeaderClass+'" colspan="3">'+(n?e.buildGotoAnchorHtml(t,{class:"fc-list-heading-main"},tt(t.format(n))):"")+(i?e.buildGotoAnchorHtml(t,{class:"fc-list-heading-alt"},tt(t.format(i))):"")+"</td></tr>"},fgSegHtml:function(t){var e,n=this.view,i=["fc-list-item"].concat(this.getSegCustomClasses(t)),r=this.getSegBackgroundColor(t),s=t.event,o=s.url;return e=s.allDay?n.getAllDayHtml():n.isMultiDayEvent(s)?t.isStart||t.isEnd?tt(this.getEventTimeText(t)):n.getAllDayHtml():tt(this.getEventTimeText(s)),o&&i.push("fc-has-url"),'<tr class="'+i.join(" ")+'">'+(this.displayEventTime?'<td class="fc-list-item-time '+n.widgetContentClass+'">'+(e||"")+"</td>":"")+'<td class="fc-list-item-marker '+n.widgetContentClass+'"><span class="fc-event-dot"'+(r?' style="background-color:'+r+'"':"")+'></span></td><td class="fc-list-item-title '+n.widgetContentClass+'"><a'+(o?' href="'+tt(o)+'"':"")+">"+tt(t.event.title||"")+"</a></td></tr>"}});return Zt.list={class:Ae,buttonTextKey:"list",defaults:{buttonText:"list",listDayFormat:"LL",noEventsMessage:"No events to display"}},Zt.listDay={type:"list",duration:{days:1},defaults:{listDayFormat:"dddd"}},Zt.listWeek={type:"list",duration:{weeks:1},defaults:{listDayFormat:"dddd",listDayAltFormat:"LL"}},Zt.listMonth={type:"list",duration:{month:1},defaults:{listDayAltFormat:"dddd"}},Zt.listYear={type:"list",duration:{year:1},defaults:{listDayAltFormat:"dddd"}},qt}); -!function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):"object"==typeof exports?module.exports=e(require("jquery"),require("moment")):e(jQuery,moment)}(function(e,a){!function(){!function(){var e=a.defineLocale("af",{months:"Januarie_Februarie_Maart_April_Mei_Junie_Julie_Augustus_September_Oktober_November_Desember".split("_"),monthsShort:"Jan_Feb_Mrt_Apr_Mei_Jun_Jul_Aug_Sep_Okt_Nov_Des".split("_"),weekdays:"Sondag_Maandag_Dinsdag_Woensdag_Donderdag_Vrydag_Saterdag".split("_"),weekdaysShort:"Son_Maa_Din_Woe_Don_Vry_Sat".split("_"),weekdaysMin:"So_Ma_Di_Wo_Do_Vr_Sa".split("_"),meridiemParse:/vm|nm/i,isPM:function(e){return/^nm$/i.test(e)},meridiem:function(e,a,t){return e<12?t?"vm":"VM":t?"nm":"NM"},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Vandag om] LT",nextDay:"[Môre om] LT",nextWeek:"dddd [om] LT",lastDay:"[Gister om] LT",lastWeek:"[Laas] dddd [om] LT",sameElse:"L"},relativeTime:{future:"oor %s",past:"%s gelede",s:"'n paar sekondes",m:"'n minuut",mm:"%d minute",h:"'n uur",hh:"%d ure",d:"'n dag",dd:"%d dae",M:"'n maand",MM:"%d maande",y:"'n jaar",yy:"%d jaar"},ordinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(1===e||8===e||e>=20?"ste":"de")},week:{dow:1,doy:4}});return e}(),e.fullCalendar.datepickerLocale("af","af",{closeText:"Selekteer",prevText:"Vorige",nextText:"Volgende",currentText:"Vandag",monthNames:["Januarie","Februarie","Maart","April","Mei","Junie","Julie","Augustus","September","Oktober","November","Desember"],monthNamesShort:["Jan","Feb","Mrt","Apr","Mei","Jun","Jul","Aug","Sep","Okt","Nov","Des"],dayNames:["Sondag","Maandag","Dinsdag","Woensdag","Donderdag","Vrydag","Saterdag"],dayNamesShort:["Son","Maa","Din","Woe","Don","Vry","Sat"],dayNamesMin:["So","Ma","Di","Wo","Do","Vr","Sa"],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("af",{buttonText:{year:"Jaar",month:"Maand",week:"Week",day:"Dag",list:"Agenda"},allDayHtml:"Heeldag",eventLimitText:"Addisionele",noEventsMessage:"Daar is geen gebeurtenis"})}(),function(){!function(){var e={1:"Ù¡",2:"Ù¢",3:"Ù£",4:"Ù¤",5:"Ù¥",6:"Ù¦",7:"Ù§",8:"Ù¨",9:"Ù©",0:"Ù "},t={"Ù¡":"1","Ù¢":"2","Ù£":"3","Ù¤":"4","Ù¥":"5","Ù¦":"6","Ù§":"7","Ù¨":"8","Ù©":"9","Ù ":"0"},n=function(e){return 0===e?0:1===e?1:2===e?2:e%100>=3&&e%100<=10?3:e%100>=11?4:5},r={s:["أقل من ثانية","ثانية ÙˆØ§ØØ¯Ø©",["ثانيتان","ثانيتين"],"%d ثوان","%d ثانية","%d ثانية"],m:["أقل من دقيقة","دقيقة ÙˆØ§ØØ¯Ø©",["دقيقتان","دقيقتين"],"%d دقائق","%d دقيقة","%d دقيقة"],h:["أقل من ساعة","ساعة ÙˆØ§ØØ¯Ø©",["ساعتان","ساعتين"],"%d ساعات","%d ساعة","%d ساعة"],d:["أقل من يوم","يوم ÙˆØ§ØØ¯",["يومان","يومين"],"%d أيام","%d يومًا","%d يوم"],M:["أقل من شهر","شهر ÙˆØ§ØØ¯",["شهران","شهرين"],"%d أشهر","%d شهرا","%d شهر"],y:["أقل من عام","عام ÙˆØ§ØØ¯",["عامان","عامين"],"%d أعوام","%d عامًا","%d عام"]},s=function(e){return function(a,t,s,d){var i=n(a),o=r[e][n(a)];return 2===i&&(o=o[t?0:1]),o.replace(/%d/i,a)}},d=["كانون الثاني يناير","شباط ÙØ¨Ø±Ø§ÙŠØ±","آذار مارس","نيسان أبريل","أيار مايو","ØØ²ÙŠØ±Ø§Ù† يونيو","تموز يوليو","آب أغسطس","أيلول سبتمبر","تشرين الأول أكتوبر","تشرين الثاني نوÙمبر","كانون الأول ديسمبر"],i=a.defineLocale("ar",{months:d,monthsShort:d,weekdays:"Ø§Ù„Ø£ØØ¯_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"Ø£ØØ¯_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"Ø_Ù†_Ø«_ر_Ø®_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/â€M/â€YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/ص|Ù…/,isPM:function(e){return"Ù…"===e},meridiem:function(e,a,t){return e<12?"ص":"Ù…"},calendar:{sameDay:"[اليوم عند الساعة] LT",nextDay:"[غدًا عند الساعة] LT",nextWeek:"dddd [عند الساعة] LT",lastDay:"[أمس عند الساعة] LT",lastWeek:"dddd [عند الساعة] LT",sameElse:"L"},relativeTime:{future:"بعد %s",past:"منذ %s",s:s("s"),m:s("m"),mm:s("m"),h:s("h"),hh:s("h"),d:s("d"),dd:s("d"),M:s("M"),MM:s("M"),y:s("y"),yy:s("y")},preparse:function(e){return e.replace(/\u200f/g,"").replace(/[١٢٣٤٥٦٧٨٩٠]/g,function(e){return t[e]}).replace(/ØŒ/g,",")},postformat:function(a){return a.replace(/\d/g,function(a){return e[a]}).replace(/,/g,"ØŒ")},week:{dow:6,doy:12}});return i}(),e.fullCalendar.datepickerLocale("ar","ar",{closeText:"إغلاق",prevText:"<السابق",nextText:"التالي>",currentText:"اليوم",monthNames:["يناير","ÙØ¨Ø±Ø§ÙŠØ±","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوÙمبر","ديسمبر"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["Ø§Ù„Ø£ØØ¯","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesShort:["Ø£ØØ¯","اثنين","ثلاثاء","أربعاء","خميس","جمعة","سبت"],dayNamesMin:["Ø","Ù†","Ø«","ر","Ø®","ج","س"],weekHeader:"أسبوع",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!0,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("ar",{buttonText:{month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},allDayText:"اليوم كله",eventLimitText:"أخرى",noEventsMessage:"أي Ø£ØØ¯Ø§Ø« لعرض"})}(),function(){!function(){var e=a.defineLocale("ar-dz",{months:"جانÙÙŠ_ÙÙŠÙØ±ÙŠ_مارس_Ø£ÙØ±ÙŠÙ„_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوÙمبر_ديسمبر".split("_"),monthsShort:"جانÙÙŠ_ÙÙŠÙØ±ÙŠ_مارس_Ø£ÙØ±ÙŠÙ„_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوÙمبر_ديسمبر".split("_"),weekdays:"Ø§Ù„Ø£ØØ¯_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"Ø§ØØ¯_اثنين_ثلاثاء_اربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"Ø£Ø_إث_ثلا_أر_خم_جم_سب".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"ÙÙŠ %s",past:"منذ %s",s:"ثوان",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},week:{dow:0,doy:4}});return e}(),e.fullCalendar.datepickerLocale("ar-dz","ar-DZ",{closeText:"إغلاق",prevText:"<السابق",nextText:"التالي>",currentText:"اليوم",monthNames:["جانÙÙŠ","ÙÙŠÙØ±ÙŠ","مارس","Ø£ÙØ±ÙŠÙ„","ماي","جوان","جويلية","أوت","سبتمبر","أكتوبر","نوÙمبر","ديسمبر"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["Ø§Ù„Ø£ØØ¯","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesShort:["Ø§Ù„Ø£ØØ¯","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesMin:["Ø","Ù†","Ø«","ر","Ø®","ج","س"],weekHeader:"أسبوع",dateFormat:"dd/mm/yy",firstDay:6,isRTL:!0,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("ar-dz",{buttonText:{month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},allDayText:"اليوم كله",eventLimitText:"أخرى",noEventsMessage:"أي Ø£ØØ¯Ø§Ø« لعرض"})}(),function(){!function(){var e={1:"1",2:"2",3:"3",4:"4",5:"5",6:"6",7:"7",8:"8",9:"9",0:"0"},t=function(e){return 0===e?0:1===e?1:2===e?2:e%100>=3&&e%100<=10?3:e%100>=11?4:5},n={s:["أقل من ثانية","ثانية ÙˆØ§ØØ¯Ø©",["ثانيتان","ثانيتين"],"%d ثوان","%d ثانية","%d ثانية"],m:["أقل من دقيقة","دقيقة ÙˆØ§ØØ¯Ø©",["دقيقتان","دقيقتين"],"%d دقائق","%d دقيقة","%d دقيقة"],h:["أقل من ساعة","ساعة ÙˆØ§ØØ¯Ø©",["ساعتان","ساعتين"],"%d ساعات","%d ساعة","%d ساعة"],d:["أقل من يوم","يوم ÙˆØ§ØØ¯",["يومان","يومين"],"%d أيام","%d يومًا","%d يوم"],M:["أقل من شهر","شهر ÙˆØ§ØØ¯",["شهران","شهرين"],"%d أشهر","%d شهرا","%d شهر"],y:["أقل من عام","عام ÙˆØ§ØØ¯",["عامان","عامين"],"%d أعوام","%d عامًا","%d عام"]},r=function(e){return function(a,r,s,d){var i=t(a),o=n[e][t(a)];return 2===i&&(o=o[r?0:1]),o.replace(/%d/i,a)}},s=["يناير","ÙØ¨Ø±Ø§ÙŠØ±","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوÙمبر","ديسمبر"],d=a.defineLocale("ar-ly",{months:s,monthsShort:s,weekdays:"Ø§Ù„Ø£ØØ¯_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"Ø£ØØ¯_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"Ø_Ù†_Ø«_ر_Ø®_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/â€M/â€YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/ص|Ù…/,isPM:function(e){return"Ù…"===e},meridiem:function(e,a,t){return e<12?"ص":"Ù…"},calendar:{sameDay:"[اليوم عند الساعة] LT",nextDay:"[غدًا عند الساعة] LT",nextWeek:"dddd [عند الساعة] LT",lastDay:"[أمس عند الساعة] LT",lastWeek:"dddd [عند الساعة] LT",sameElse:"L"},relativeTime:{future:"بعد %s",past:"منذ %s",s:r("s"),m:r("m"),mm:r("m"),h:r("h"),hh:r("h"),d:r("d"),dd:r("d"),M:r("M"),MM:r("M"),y:r("y"),yy:r("y")},preparse:function(e){return e.replace(/\u200f/g,"").replace(/ØŒ/g,",")},postformat:function(a){return a.replace(/\d/g,function(a){return e[a]}).replace(/,/g,"ØŒ")},week:{dow:6,doy:12}});return d}(),e.fullCalendar.datepickerLocale("ar-ly","ar",{closeText:"إغلاق",prevText:"<السابق",nextText:"التالي>",currentText:"اليوم",monthNames:["يناير","ÙØ¨Ø±Ø§ÙŠØ±","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوÙمبر","ديسمبر"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["Ø§Ù„Ø£ØØ¯","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesShort:["Ø£ØØ¯","اثنين","ثلاثاء","أربعاء","خميس","جمعة","سبت"],dayNamesMin:["Ø","Ù†","Ø«","ر","Ø®","ج","س"],weekHeader:"أسبوع",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!0,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("ar-ly",{buttonText:{month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},allDayText:"اليوم كله",eventLimitText:"أخرى",noEventsMessage:"أي Ø£ØØ¯Ø§Ø« لعرض"})}(),function(){!function(){var e=a.defineLocale("ar-ma",{months:"يناير_ÙØ¨Ø±Ø§ÙŠØ±_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),monthsShort:"يناير_ÙØ¨Ø±Ø§ÙŠØ±_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),weekdays:"Ø§Ù„Ø£ØØ¯_الإتنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"Ø§ØØ¯_اتنين_ثلاثاء_اربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"Ø_Ù†_Ø«_ر_Ø®_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"ÙÙŠ %s",past:"منذ %s",s:"ثوان",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},week:{dow:6,doy:12}});return e}(),e.fullCalendar.datepickerLocale("ar-ma","ar",{closeText:"إغلاق",prevText:"<السابق",nextText:"التالي>",currentText:"اليوم",monthNames:["يناير","ÙØ¨Ø±Ø§ÙŠØ±","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوÙمبر","ديسمبر"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["Ø§Ù„Ø£ØØ¯","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesShort:["Ø£ØØ¯","اثنين","ثلاثاء","أربعاء","خميس","جمعة","سبت"],dayNamesMin:["Ø","Ù†","Ø«","ر","Ø®","ج","س"],weekHeader:"أسبوع",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!0,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("ar-ma",{buttonText:{month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},allDayText:"اليوم كله",eventLimitText:"أخرى",noEventsMessage:"أي Ø£ØØ¯Ø§Ø« لعرض"})}(),function(){!function(){var e={1:"Ù¡",2:"Ù¢",3:"Ù£",4:"Ù¤",5:"Ù¥",6:"Ù¦",7:"Ù§",8:"Ù¨",9:"Ù©",0:"Ù "},t={"Ù¡":"1","Ù¢":"2","Ù£":"3","Ù¤":"4","Ù¥":"5","Ù¦":"6","Ù§":"7","Ù¨":"8","Ù©":"9","Ù ":"0"},n=a.defineLocale("ar-sa",{months:"يناير_ÙØ¨Ø±Ø§ÙŠØ±_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوÙمبر_ديسمبر".split("_"),monthsShort:"يناير_ÙØ¨Ø±Ø§ÙŠØ±_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوÙمبر_ديسمبر".split("_"),weekdays:"Ø§Ù„Ø£ØØ¯_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"Ø£ØØ¯_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"Ø_Ù†_Ø«_ر_Ø®_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/ص|Ù…/,isPM:function(e){return"Ù…"===e},meridiem:function(e,a,t){return e<12?"ص":"Ù…"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"ÙÙŠ %s",past:"منذ %s",s:"ثوان",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},preparse:function(e){return e.replace(/[١٢٣٤٥٦٧٨٩٠]/g,function(e){return t[e]}).replace(/ØŒ/g,",")},postformat:function(a){return a.replace(/\d/g,function(a){return e[a]}).replace(/,/g,"ØŒ")},week:{dow:0,doy:6}});return n}(),e.fullCalendar.datepickerLocale("ar-sa","ar",{closeText:"إغلاق",prevText:"<السابق",nextText:"التالي>",currentText:"اليوم",monthNames:["يناير","ÙØ¨Ø±Ø§ÙŠØ±","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوÙمبر","ديسمبر"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["Ø§Ù„Ø£ØØ¯","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesShort:["Ø£ØØ¯","اثنين","ثلاثاء","أربعاء","خميس","جمعة","سبت"],dayNamesMin:["Ø","Ù†","Ø«","ر","Ø®","ج","س"],weekHeader:"أسبوع",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!0,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("ar-sa",{buttonText:{month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},allDayText:"اليوم كله",eventLimitText:"أخرى",noEventsMessage:"أي Ø£ØØ¯Ø§Ø« لعرض"})}(),function(){!function(){var e=a.defineLocale("ar-tn",{months:"جانÙÙŠ_ÙÙŠÙØ±ÙŠ_مارس_Ø£ÙØ±ÙŠÙ„_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوÙمبر_ديسمبر".split("_"),monthsShort:"جانÙÙŠ_ÙÙŠÙØ±ÙŠ_مارس_Ø£ÙØ±ÙŠÙ„_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوÙمبر_ديسمبر".split("_"),weekdays:"Ø§Ù„Ø£ØØ¯_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"Ø£ØØ¯_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"Ø_Ù†_Ø«_ر_Ø®_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"ÙÙŠ %s",past:"منذ %s",s:"ثوان",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},week:{dow:1,doy:4}});return e}(),e.fullCalendar.datepickerLocale("ar-tn","ar",{closeText:"إغلاق",prevText:"<السابق",nextText:"التالي>",currentText:"اليوم",monthNames:["يناير","ÙØ¨Ø±Ø§ÙŠØ±","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوÙمبر","ديسمبر"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["Ø§Ù„Ø£ØØ¯","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesShort:["Ø£ØØ¯","اثنين","ثلاثاء","أربعاء","خميس","جمعة","سبت"],dayNamesMin:["Ø","Ù†","Ø«","ر","Ø®","ج","س"],weekHeader:"أسبوع",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!0,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("ar-tn",{buttonText:{month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},allDayText:"اليوم كله",eventLimitText:"أخرى",noEventsMessage:"أي Ø£ØØ¯Ø§Ø« لعرض"})}(),function(){!function(){var e=a.defineLocale("bg",{months:"Ñнуари_февруари_март_април_май_юни_юли_авгуÑÑ‚_Ñептември_октомври_ноември_декември".split("_"),monthsShort:"Ñнр_фев_мар_апр_май_юни_юли_авг_Ñеп_окт_ное_дек".split("_"),weekdays:"неделÑ_понеделник_вторник_ÑÑ€Ñда_четвъртък_петък_Ñъбота".split("_"),weekdaysShort:"нед_пон_вто_ÑÑ€Ñ_чет_пет_Ñъб".split("_"),weekdaysMin:"нд_пн_вт_ÑÑ€_чт_пт_Ñб".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[Ð”Ð½ÐµÑ Ð²] LT",nextDay:"[Утре в] LT",nextWeek:"dddd [в] LT",lastDay:"[Вчера в] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[Ð’ изминалата] dddd [в] LT";case 1:case 2:case 4:case 5:return"[Ð’ изминалиÑ] dddd [в] LT"}},sameElse:"L"},relativeTime:{future:"Ñлед %s",past:"преди %s",s:"нÑколко Ñекунди",m:"минута",mm:"%d минути",h:"чаÑ",hh:"%d чаÑа",d:"ден",dd:"%d дни",M:"меÑец",MM:"%d меÑеца",y:"година",yy:"%d години"},ordinalParse:/\d{1,2}-(ев|ен|ти|ви|ри|ми)/,ordinal:function(e){var a=e%10,t=e%100;return 0===e?e+"-ев":0===t?e+"-ен":t>10&&t<20?e+"-ти":1===a?e+"-ви":2===a?e+"-ри":7===a||8===a?e+"-ми":e+"-ти"},week:{dow:1,doy:7}});return e}(),e.fullCalendar.datepickerLocale("bg","bg",{closeText:"затвори",prevText:"<назад",nextText:"напред>",nextBigText:">>",currentText:"днеÑ",monthNames:["Януари","Февруари","Март","Ðприл","Май","Юни","Юли","ÐвгуÑÑ‚","Септември","Октомври","Ðоември","Декември"],monthNamesShort:["Яну","Фев","Мар","Ðпр","Май","Юни","Юли","Ðвг","Сеп","Окт","Ðов","Дек"],dayNames:["ÐеделÑ","Понеделник","Вторник","СрÑда","Четвъртък","Петък","Събота"],dayNamesShort:["Ðед","Пон","Вто","СрÑ","Чет","Пет","Съб"],dayNamesMin:["Ðе","По","Ð’Ñ‚","Ср","Че","Пе","Съ"],weekHeader:"Wk",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("bg",{buttonText:{month:"МеÑец",week:"Седмица",day:"Ден",list:"График"},allDayText:"ЦÑл ден",eventLimitText:function(e){return"+още "+e},noEventsMessage:"ÐÑма ÑÑŠÐ±Ð¸Ñ‚Ð¸Ñ Ð·Ð° показване"})}(),function(){!function(){var e=a.defineLocale("ca",{months:"gener_febrer_març_abril_maig_juny_juliol_agost_setembre_octubre_novembre_desembre".split("_"),monthsShort:"gen._febr._mar._abr._mai._jun._jul._ag._set._oct._nov._des.".split("_"),monthsParseExact:!0,weekdays:"diumenge_dilluns_dimarts_dimecres_dijous_divendres_dissabte".split("_"),weekdaysShort:"dg._dl._dt._dc._dj._dv._ds.".split("_"),weekdaysMin:"Dg_Dl_Dt_Dc_Dj_Dv_Ds".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd D MMMM YYYY H:mm"},calendar:{sameDay:function(){return"[avui a "+(1!==this.hours()?"les":"la")+"] LT"},nextDay:function(){return"[demà a "+(1!==this.hours()?"les":"la")+"] LT"},nextWeek:function(){return"dddd [a "+(1!==this.hours()?"les":"la")+"] LT"},lastDay:function(){return"[ahir a "+(1!==this.hours()?"les":"la")+"] LT"},lastWeek:function(){return"[el] dddd [passat a "+(1!==this.hours()?"les":"la")+"] LT"},sameElse:"L"},relativeTime:{future:"d'aquà %s",past:"fa %s",s:"uns segons",m:"un minut",mm:"%d minuts",h:"una hora",hh:"%d hores",d:"un dia",dd:"%d dies",M:"un mes",MM:"%d mesos",y:"un any",yy:"%d anys"},ordinalParse:/\d{1,2}(r|n|t|è|a)/,ordinal:function(e,a){var t=1===e?"r":2===e?"n":3===e?"r":4===e?"t":"è";return"w"!==a&&"W"!==a||(t="a"),e+t},week:{dow:1,doy:4}});return e}(),e.fullCalendar.datepickerLocale("ca","ca",{closeText:"Tanca",prevText:"Anterior",nextText:"Següent",currentText:"Avui",monthNames:["gener","febrer","març","abril","maig","juny","juliol","agost","setembre","octubre","novembre","desembre"],monthNamesShort:["gen","feb","març","abr","maig","juny","jul","ag","set","oct","nov","des"],dayNames:["diumenge","dilluns","dimarts","dimecres","dijous","divendres","dissabte"],dayNamesShort:["dg","dl","dt","dc","dj","dv","ds"],dayNamesMin:["dg","dl","dt","dc","dj","dv","ds"],weekHeader:"Set",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("ca",{buttonText:{month:"Mes",week:"Setmana",day:"Dia",list:"Agenda"},allDayText:"Tot el dia",eventLimitText:"més",noEventsMessage:"No hi ha esdeveniments per mostrar"})}(),function(){!function(){function e(e){return e>1&&e<5&&1!==~~(e/10)}function t(a,t,n,r){var s=a+" ";switch(n){case"s":return t||r?"pár sekund":"pár sekundami";case"m":return t?"minuta":r?"minutu":"minutou";case"mm":return t||r?s+(e(a)?"minuty":"minut"):s+"minutami";case"h":return t?"hodina":r?"hodinu":"hodinou";case"hh":return t||r?s+(e(a)?"hodiny":"hodin"):s+"hodinami";case"d":return t||r?"den":"dnem";case"dd":return t||r?s+(e(a)?"dny":"dnÃ"):s+"dny";case"M":return t||r?"mÄ›sÃc":"mÄ›sÃcem";case"MM":return t||r?s+(e(a)?"mÄ›sÃce":"mÄ›sÃců"):s+"mÄ›sÃci";case"y":return t||r?"rok":"rokem";case"yy":return t||r?s+(e(a)?"roky":"let"):s+"lety"}}var n="leden_únor_bÅ™ezen_duben_kvÄ›ten_Äerven_Äervenec_srpen_zářÃ_Å™Ãjen_listopad_prosinec".split("_"),r="led_úno_bÅ™e_dub_kvÄ›_Ävn_Ävc_srp_zář_Å™Ãj_lis_pro".split("_"),s=a.defineLocale("cs",{months:n,monthsShort:r,monthsParse:function(e,a){var t,n=[];for(t=0;t<12;t++)n[t]=new RegExp("^"+e[t]+"$|^"+a[t]+"$","i");return n}(n,r),shortMonthsParse:function(e){var a,t=[];for(a=0;a<12;a++)t[a]=new RegExp("^"+e[a]+"$","i");return t}(r),longMonthsParse:function(e){var a,t=[];for(a=0;a<12;a++)t[a]=new RegExp("^"+e[a]+"$","i");return t}(n),weekdays:"nedÄ›le_pondÄ›lÃ_úterý_stÅ™eda_Ätvrtek_pátek_sobota".split("_"),weekdaysShort:"ne_po_út_st_Ät_pá_so".split("_"),weekdaysMin:"ne_po_út_st_Ät_pá_so".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd D. MMMM YYYY H:mm",l:"D. M. YYYY"},calendar:{sameDay:"[dnes v] LT",nextDay:"[zÃtra v] LT",nextWeek:function(){switch(this.day()){case 0:return"[v nedÄ›li v] LT";case 1:case 2:return"[v] dddd [v] LT";case 3:return"[ve stÅ™edu v] LT";case 4:return"[ve Ätvrtek v] LT";case 5:return"[v pátek v] LT";case 6:return"[v sobotu v] LT"}},lastDay:"[vÄera v] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulou nedÄ›li v] LT";case 1:case 2:return"[minulé] dddd [v] LT";case 3:return"[minulou stÅ™edu v] LT";case 4:case 5:return"[minulý] dddd [v] LT";case 6:return"[minulou sobotu v] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"pÅ™ed %s",s:t,m:t,mm:t,h:t,hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}});return s}(),e.fullCalendar.datepickerLocale("cs","cs",{closeText:"ZavÅ™Ãt",prevText:"<DÅ™Ãve",nextText:"PozdÄ›ji>",currentText:"NynÃ",monthNames:["leden","únor","bÅ™ezen","duben","kvÄ›ten","Äerven","Äervenec","srpen","zářÃ","Å™Ãjen","listopad","prosinec"],monthNamesShort:["led","úno","bÅ™e","dub","kvÄ›","Äer","Ävc","srp","zář","Å™Ãj","lis","pro"],dayNames:["nedÄ›le","pondÄ›lÃ","úterý","stÅ™eda","Ätvrtek","pátek","sobota"],dayNamesShort:["ne","po","út","st","Ät","pá","so"],dayNamesMin:["ne","po","út","st","Ät","pá","so"],weekHeader:"Týd",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("cs",{buttonText:{month:"MÄ›sÃc",week:"Týden",day:"Den",list:"Agenda"},allDayText:"Celý den",eventLimitText:function(e){return"+dalÅ¡Ã: "+e},noEventsMessage:"Žádné akce k zobrazenÃ"})}(),function(){!function(){var e=a.defineLocale("da",{months:"januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),weekdaysShort:"søn_man_tir_ons_tor_fre_lør".split("_"),weekdaysMin:"sø_ma_ti_on_to_fr_lø".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd [d.] D. MMMM YYYY HH:mm"},calendar:{sameDay:"[I dag kl.] LT",nextDay:"[I morgen kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[I gÃ¥r kl.] LT",lastWeek:"[sidste] dddd [kl] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"fÃ¥ sekunder",m:"et minut",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dage",M:"en mÃ¥ned",MM:"%d mÃ¥neder",y:"et Ã¥r",yy:"%d Ã¥r"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}});return e}(),e.fullCalendar.datepickerLocale("da","da",{closeText:"Luk",prevText:"<Forrige",nextText:"Næste>",currentText:"Idag",monthNames:["Januar","Februar","Marts","April","Maj","Juni","Juli","August","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],dayNames:["Søndag","Mandag","Tirsdag","Onsdag","Torsdag","Fredag","Lørdag"],dayNamesShort:["Søn","Man","Tir","Ons","Tor","Fre","Lør"],dayNamesMin:["Sø","Ma","Ti","On","To","Fr","Lø"],weekHeader:"Uge",dateFormat:"dd-mm-yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("da",{buttonText:{month:"MÃ¥ned",week:"Uge",day:"Dag",list:"Agenda"},allDayText:"Hele dagen",eventLimitText:"flere",noEventsMessage:"Ingen arrangementer at vise"})}(),function(){!function(){function e(e,a,t,n){var r={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[e+" Tage",e+" Tagen"],M:["ein Monat","einem Monat"],MM:[e+" Monate",e+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[e+" Jahre",e+" Jahren"]};return a?r[t][0]:r[t][1]}var t=a.defineLocale("de",{months:"Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),monthsParseExact:!0,weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd, D. MMMM YYYY HH:mm"},calendar:{sameDay:"[heute um] LT [Uhr]",sameElse:"L",nextDay:"[morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",m:e,mm:"%d Minuten",h:e,hh:"%d Stunden",d:e,dd:e,M:e,MM:e,y:e,yy:e},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}});return t}(),e.fullCalendar.datepickerLocale("de","de",{closeText:"Schließen",prevText:"<Zurück",nextText:"Vor>",currentText:"Heute",monthNames:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthNamesShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],dayNames:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],dayNamesShort:["So","Mo","Di","Mi","Do","Fr","Sa"],dayNamesMin:["So","Mo","Di","Mi","Do","Fr","Sa"],weekHeader:"KW",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("de",{buttonText:{month:"Monat",week:"Woche",day:"Tag",list:"Terminübersicht"},allDayText:"Ganztägig",eventLimitText:function(e){return"+ weitere "+e},noEventsMessage:"Keine Ereignisse anzuzeigen"})}(),function(){!function(){function e(e,a,t,n){var r={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[e+" Tage",e+" Tagen"],M:["ein Monat","einem Monat"],MM:[e+" Monate",e+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[e+" Jahre",e+" Jahren"]};return a?r[t][0]:r[t][1]}var t=a.defineLocale("de-at",{months:"Jänner_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jän._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),monthsParseExact:!0,weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd, D. MMMM YYYY HH:mm"},calendar:{sameDay:"[heute um] LT [Uhr]",sameElse:"L",nextDay:"[morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",m:e,mm:"%d Minuten",h:e,hh:"%d Stunden",d:e,dd:e,M:e,MM:e,y:e,yy:e},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}});return t}(),e.fullCalendar.datepickerLocale("de-at","de",{closeText:"Schließen",prevText:"<Zurück",nextText:"Vor>",currentText:"Heute",monthNames:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthNamesShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],dayNames:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],dayNamesShort:["So","Mo","Di","Mi","Do","Fr","Sa"],dayNamesMin:["So","Mo","Di","Mi","Do","Fr","Sa"],weekHeader:"KW",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("de-at",{buttonText:{month:"Monat",week:"Woche",day:"Tag",list:"Terminübersicht"},allDayText:"Ganztägig",eventLimitText:function(e){return"+ weitere "+e},noEventsMessage:"Keine Ereignisse anzuzeigen"})}(),function(){!function(){function e(e){return e instanceof Function||"[object Function]"===Object.prototype.toString.call(e)}var t=a.defineLocale("el",{monthsNominativeEl:"ΙανουάÏιος_ΦεβÏουάÏιος_ΜάÏτιος_ΑπÏίλιος_Μάιος_ΙοÏνιος_ΙοÏλιος_ΑÏγουστος_ΣεπτÎμβÏιος_ΟκτώβÏιος_ÎοÎμβÏιος_ΔεκÎμβÏιος".split("_"),monthsGenitiveEl:"ΙανουαÏίου_ΦεβÏουαÏίου_ΜαÏτίου_ΑπÏιλίου_ΜαÎου_Ιουνίου_Ιουλίου_ΑυγοÏστου_ΣεπτεμβÏίου_ΟκτωβÏίου_ÎοεμβÏίου_ΔεκεμβÏίου".split("_"),months:function(e,a){return/D/.test(a.substring(0,a.indexOf("MMMM")))?this._monthsGenitiveEl[e.month()]:this._monthsNominativeEl[e.month()]},monthsShort:"Ιαν_Φεβ_ΜαÏ_ΑπÏ_Μαϊ_Ιουν_Ιουλ_Αυγ_Σεπ_Οκτ_Îοε_Δεκ".split("_"),weekdays:"ΚυÏιακή_ΔευτÎÏα_ΤÏίτη_ΤετάÏτη_Î Îμπτη_ΠαÏασκευή_Σάββατο".split("_"),weekdaysShort:"ΚυÏ_Δευ_ΤÏι_Τετ_Πεμ_ΠαÏ_Σαβ".split("_"),weekdaysMin:"Κυ_Δε_ΤÏ_Τε_Πε_Πα_Σα".split("_"),meridiem:function(e,a,t){return e>11?t?"μμ":"ΜΜ":t?"πμ":"ΠΜ"},isPM:function(e){return"μ"===(e+"").toLowerCase()[0]},meridiemParse:/[ΠΜ]\.?Μ?\.?/i,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendarEl:{sameDay:"[ΣήμεÏα {}] LT",nextDay:"[ΑÏÏιο {}] LT",nextWeek:"dddd [{}] LT",lastDay:"[Χθες {}] LT",lastWeek:function(){switch(this.day()){case 6:return"[το Ï€ÏοηγοÏμενο] dddd [{}] LT";default:return"[την Ï€ÏοηγοÏμενη] dddd [{}] LT"}},sameElse:"L"},calendar:function(a,t){var n=this._calendarEl[a],r=t&&t.hours();return e(n)&&(n=n.apply(t)),n.replace("{}",r%12===1?"στη":"στις")},relativeTime:{future:"σε %s",past:"%s Ï€Ïιν",s:"λίγα δευτεÏόλεπτα",m:"Îνα λεπτό",mm:"%d λεπτά",h:"μία ÏŽÏα",hh:"%d ÏŽÏες",d:"μία μÎÏα",dd:"%d μÎÏες",M:"Îνας μήνας",MM:"%d μήνες",y:"Îνας χÏόνος",yy:"%d χÏόνια"},ordinalParse:/\d{1,2}η/,ordinal:"%dη",week:{dow:1,doy:4}});return t}(),e.fullCalendar.datepickerLocale("el","el",{closeText:"Κλείσιμο",prevText:"Î ÏοηγοÏμενος",nextText:"Επόμενος",currentText:"ΣήμεÏα",monthNames:["ΙανουάÏιος","ΦεβÏουάÏιος","ΜάÏτιος","ΑπÏίλιος","Μάιος","ΙοÏνιος","ΙοÏλιος","ΑÏγουστος","ΣεπτÎμβÏιος","ΟκτώβÏιος","ÎοÎμβÏιος","ΔεκÎμβÏιος"],monthNamesShort:["Ιαν","Φεβ","ΜαÏ","ΑπÏ","Μαι","Ιουν","Ιουλ","Αυγ","Σεπ","Οκτ","Îοε","Δεκ"],dayNames:["ΚυÏιακή","ΔευτÎÏα","ΤÏίτη","ΤετάÏτη","Î Îμπτη","ΠαÏασκευή","Σάββατο"],dayNamesShort:["ΚυÏ","Δευ","ΤÏι","Τετ","Πεμ","ΠαÏ","Σαβ"],dayNamesMin:["Κυ","Δε","ΤÏ","Τε","Πε","Πα","Σα"],weekHeader:"Εβδ",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("el",{buttonText:{month:"Μήνας",week:"Εβδομάδα",day:"ΗμÎÏα",list:"ΑτζÎντα"},allDayText:"ΟλοήμεÏο",eventLimitText:"πεÏισσότεÏα",noEventsMessage:"Δεν υπάÏχουν γεγονότα για να εμφανιστεί"})}(),function(){!function(){var e=a.defineLocale("en-au",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var a=e%10,t=1===~~(e%100/10)?"th":1===a?"st":2===a?"nd":3===a?"rd":"th";return e+t},week:{ -dow:1,doy:4}});return e}(),e.fullCalendar.datepickerLocale("en-au","en-AU",{closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("en-au")}(),function(){!function(){var e=a.defineLocale("en-ca",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"YYYY-MM-DD",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var a=e%10,t=1===~~(e%100/10)?"th":1===a?"st":2===a?"nd":3===a?"rd":"th";return e+t}});return e}(),e.fullCalendar.locale("en-ca")}(),function(){!function(){var e=a.defineLocale("en-gb",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var a=e%10,t=1===~~(e%100/10)?"th":1===a?"st":2===a?"nd":3===a?"rd":"th";return e+t},week:{dow:1,doy:4}});return e}(),e.fullCalendar.datepickerLocale("en-gb","en-GB",{closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("en-gb")}(),function(){!function(){var e=a.defineLocale("en-ie",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var a=e%10,t=1===~~(e%100/10)?"th":1===a?"st":2===a?"nd":3===a?"rd":"th";return e+t},week:{dow:1,doy:4}});return e}(),e.fullCalendar.locale("en-ie")}(),function(){!function(){var e=a.defineLocale("en-nz",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var a=e%10,t=1===~~(e%100/10)?"th":1===a?"st":2===a?"nd":3===a?"rd":"th";return e+t},week:{dow:1,doy:4}});return e}(),e.fullCalendar.datepickerLocale("en-nz","en-NZ",{closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("en-nz")}(),function(){!function(){var e="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),t="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),n=a.defineLocale("es",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(a,n){return/-MMM-/.test(n)?t[a.month()]:e[a.month()]},monthsParseExact:!0,weekdays:"domingo_lunes_martes_miércoles_jueves_viernes_sábado".split("_"),weekdaysShort:"dom._lun._mar._mié._jue._vie._sáb.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_sá".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY H:mm",LLLL:"dddd, D [de] MMMM [de] YYYY H:mm"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[mañana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un dÃa",dd:"%d dÃas",M:"un mes",MM:"%d meses",y:"un año",yy:"%d años"},ordinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}});return n}(),e.fullCalendar.datepickerLocale("es","es",{closeText:"Cerrar",prevText:"<Ant",nextText:"Sig>",currentText:"Hoy",monthNames:["enero","febrero","marzo","abril","mayo","junio","julio","agosto","septiembre","octubre","noviembre","diciembre"],monthNamesShort:["ene","feb","mar","abr","may","jun","jul","ago","sep","oct","nov","dic"],dayNames:["domingo","lunes","martes","miércoles","jueves","viernes","sábado"],dayNamesShort:["dom","lun","mar","mié","jue","vie","sáb"],dayNamesMin:["D","L","M","X","J","V","S"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("es",{buttonText:{month:"Mes",week:"Semana",day:"DÃa",list:"Agenda"},allDayHtml:"Todo<br/>el dÃa",eventLimitText:"más",noEventsMessage:"No hay eventos para mostrar"})}(),function(){!function(){var e="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),t="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),n=a.defineLocale("es-do",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(a,n){return/-MMM-/.test(n)?t[a.month()]:e[a.month()]},monthsParseExact:!0,weekdays:"domingo_lunes_martes_miércoles_jueves_viernes_sábado".split("_"),weekdaysShort:"dom._lun._mar._mié._jue._vie._sáb.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_sá".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY h:mm A",LLLL:"dddd, D [de] MMMM [de] YYYY h:mm A"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[mañana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un dÃa",dd:"%d dÃas",M:"un mes",MM:"%d meses",y:"un año",yy:"%d años"},ordinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}});return n}(),e.fullCalendar.datepickerLocale("es-do","es",{closeText:"Cerrar",prevText:"<Ant",nextText:"Sig>",currentText:"Hoy",monthNames:["enero","febrero","marzo","abril","mayo","junio","julio","agosto","septiembre","octubre","noviembre","diciembre"],monthNamesShort:["ene","feb","mar","abr","may","jun","jul","ago","sep","oct","nov","dic"],dayNames:["domingo","lunes","martes","miércoles","jueves","viernes","sábado"],dayNamesShort:["dom","lun","mar","mié","jue","vie","sáb"],dayNamesMin:["D","L","M","X","J","V","S"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("es-do",{buttonText:{month:"Mes",week:"Semana",day:"DÃa",list:"Agenda"},allDayHtml:"Todo<br/>el dÃa",eventLimitText:"más",noEventsMessage:"No hay eventos para mostrar"})}(),function(){!function(){var e=a.defineLocale("eu",{months:"urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua".split("_"),monthsShort:"urt._ots._mar._api._mai._eka._uzt._abu._ira._urr._aza._abe.".split("_"),monthsParseExact:!0,weekdays:"igandea_astelehena_asteartea_asteazkena_osteguna_ostirala_larunbata".split("_"),weekdaysShort:"ig._al._ar._az._og._ol._lr.".split("_"),weekdaysMin:"ig_al_ar_az_og_ol_lr".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY[ko] MMMM[ren] D[a]",LLL:"YYYY[ko] MMMM[ren] D[a] HH:mm",LLLL:"dddd, YYYY[ko] MMMM[ren] D[a] HH:mm",l:"YYYY-M-D",ll:"YYYY[ko] MMM D[a]",lll:"YYYY[ko] MMM D[a] HH:mm",llll:"ddd, YYYY[ko] MMM D[a] HH:mm"},calendar:{sameDay:"[gaur] LT[etan]",nextDay:"[bihar] LT[etan]",nextWeek:"dddd LT[etan]",lastDay:"[atzo] LT[etan]",lastWeek:"[aurreko] dddd LT[etan]",sameElse:"L"},relativeTime:{future:"%s barru",past:"duela %s",s:"segundo batzuk",m:"minutu bat",mm:"%d minutu",h:"ordu bat",hh:"%d ordu",d:"egun bat",dd:"%d egun",M:"hilabete bat",MM:"%d hilabete",y:"urte bat",yy:"%d urte"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}});return e}(),e.fullCalendar.datepickerLocale("eu","eu",{closeText:"Egina",prevText:"<Aur",nextText:"Hur>",currentText:"Gaur",monthNames:["urtarrila","otsaila","martxoa","apirila","maiatza","ekaina","uztaila","abuztua","iraila","urria","azaroa","abendua"],monthNamesShort:["urt.","ots.","mar.","api.","mai.","eka.","uzt.","abu.","ira.","urr.","aza.","abe."],dayNames:["igandea","astelehena","asteartea","asteazkena","osteguna","ostirala","larunbata"],dayNamesShort:["ig.","al.","ar.","az.","og.","ol.","lr."],dayNamesMin:["ig","al","ar","az","og","ol","lr"],weekHeader:"As",dateFormat:"yy-mm-dd",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("eu",{buttonText:{month:"Hilabetea",week:"Astea",day:"Eguna",list:"Agenda"},allDayHtml:"Egun<br/>osoa",eventLimitText:"gehiago",noEventsMessage:"Ez dago ekitaldirik erakusteko"})}(),function(){!function(){var e={1:"Û±",2:"Û²",3:"Û³",4:"Û´",5:"Ûµ",6:"Û¶",7:"Û·",8:"Û¸",9:"Û¹",0:"Û°"},t={"Û±":"1","Û²":"2","Û³":"3","Û´":"4","Ûµ":"5","Û¶":"6","Û·":"7","Û¸":"8","Û¹":"9","Û°":"0"},n=a.defineLocale("fa",{months:"ژانویه_Ùوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر".split("_"),monthsShort:"ژانویه_Ùوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر".split("_"),weekdays:"یک‌شنبه_دوشنبه_سه‌شنبه_چهارشنبه_پنج‌شنبه_جمعه_شنبه".split("_"),weekdaysShort:"یک‌شنبه_دوشنبه_سه‌شنبه_چهارشنبه_پنج‌شنبه_جمعه_شنبه".split("_"),weekdaysMin:"ÛŒ_د_س_Ú†_Ù¾_ج_Ø´".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},meridiemParse:/قبل از ظهر|بعد از ظهر/,isPM:function(e){return/بعد از ظهر/.test(e)},meridiem:function(e,a,t){return e<12?"قبل از ظهر":"بعد از ظهر"},calendar:{sameDay:"[امروز ساعت] LT",nextDay:"[ÙØ±Ø¯Ø§ ساعت] LT",nextWeek:"dddd [ساعت] LT",lastDay:"[دیروز ساعت] LT",lastWeek:"dddd [پیش] [ساعت] LT",sameElse:"L"},relativeTime:{future:"در %s",past:"%s پیش",s:"چندین ثانیه",m:"یک دقیقه",mm:"%d دقیقه",h:"یک ساعت",hh:"%d ساعت",d:"یک روز",dd:"%d روز",M:"یک ماه",MM:"%d ماه",y:"یک سال",yy:"%d سال"},preparse:function(e){return e.replace(/[Û°-Û¹]/g,function(e){return t[e]}).replace(/ØŒ/g,",")},postformat:function(a){return a.replace(/\d/g,function(a){return e[a]}).replace(/,/g,"ØŒ")},ordinalParse:/\d{1,2}Ù…/,ordinal:"%dÙ…",week:{dow:6,doy:12}});return n}(),e.fullCalendar.datepickerLocale("fa","fa",{closeText:"بستن",prevText:"<قبلی",nextText:"بعدی>",currentText:"امروز",monthNames:["ژانویه","Ùوریه","مارس","آوریل","مه","ژوئن","ژوئیه","اوت","سپتامبر","اکتبر","نوامبر","دسامبر"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["يکشنبه","دوشنبه","سه‌شنبه","چهارشنبه","پنجشنبه","جمعه","شنبه"],dayNamesShort:["ÛŒ","د","س","Ú†","Ù¾","ج","Ø´"],dayNamesMin:["ÛŒ","د","س","Ú†","Ù¾","ج","Ø´"],weekHeader:"Ù‡Ù",dateFormat:"yy/mm/dd",firstDay:6,isRTL:!0,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("fa",{buttonText:{month:"ماه",week:"Ù‡ÙØªÙ‡",day:"روز",list:"برنامه"},allDayText:"تمام روز",eventLimitText:function(e){return"بیش از "+e},noEventsMessage:"هیچ رویدادی به نمایش"})}(),function(){!function(){function e(e,a,n,r){var s="";switch(n){case"s":return r?"muutaman sekunnin":"muutama sekunti";case"m":return r?"minuutin":"minuutti";case"mm":s=r?"minuutin":"minuuttia";break;case"h":return r?"tunnin":"tunti";case"hh":s=r?"tunnin":"tuntia";break;case"d":return r?"päivän":"päivä";case"dd":s=r?"päivän":"päivää";break;case"M":return r?"kuukauden":"kuukausi";case"MM":s=r?"kuukauden":"kuukautta";break;case"y":return r?"vuoden":"vuosi";case"yy":s=r?"vuoden":"vuotta"}return s=t(e,r)+" "+s}function t(e,a){return e<10?a?r[e]:n[e]:e}var n="nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän".split(" "),r=["nolla","yhden","kahden","kolmen","neljän","viiden","kuuden",n[7],n[8],n[9]],s=a.defineLocale("fi",{months:"tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesäkuu_heinäkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu".split("_"),monthsShort:"tammi_helmi_maalis_huhti_touko_kesä_heinä_elo_syys_loka_marras_joulu".split("_"),weekdays:"sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai".split("_"),weekdaysShort:"su_ma_ti_ke_to_pe_la".split("_"),weekdaysMin:"su_ma_ti_ke_to_pe_la".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD.MM.YYYY",LL:"Do MMMM[ta] YYYY",LLL:"Do MMMM[ta] YYYY, [klo] HH.mm",LLLL:"dddd, Do MMMM[ta] YYYY, [klo] HH.mm",l:"D.M.YYYY",ll:"Do MMM YYYY",lll:"Do MMM YYYY, [klo] HH.mm",llll:"ddd, Do MMM YYYY, [klo] HH.mm"},calendar:{sameDay:"[tänään] [klo] LT",nextDay:"[huomenna] [klo] LT",nextWeek:"dddd [klo] LT",lastDay:"[eilen] [klo] LT",lastWeek:"[viime] dddd[na] [klo] LT",sameElse:"L"},relativeTime:{future:"%s päästä",past:"%s sitten",s:e,m:e,mm:e,h:e,hh:e,d:e,dd:e,M:e,MM:e,y:e,yy:e},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}});return s}(),e.fullCalendar.datepickerLocale("fi","fi",{closeText:"Sulje",prevText:"«Edellinen",nextText:"Seuraava»",currentText:"Tänään",monthNames:["Tammikuu","Helmikuu","Maaliskuu","Huhtikuu","Toukokuu","Kesäkuu","Heinäkuu","Elokuu","Syyskuu","Lokakuu","Marraskuu","Joulukuu"],monthNamesShort:["Tammi","Helmi","Maalis","Huhti","Touko","Kesä","Heinä","Elo","Syys","Loka","Marras","Joulu"],dayNamesShort:["Su","Ma","Ti","Ke","To","Pe","La"],dayNames:["Sunnuntai","Maanantai","Tiistai","Keskiviikko","Torstai","Perjantai","Lauantai"],dayNamesMin:["Su","Ma","Ti","Ke","To","Pe","La"],weekHeader:"Vk",dateFormat:"d.m.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("fi",{buttonText:{month:"Kuukausi",week:"Viikko",day:"Päivä",list:"Tapahtumat"},allDayText:"Koko päivä",eventLimitText:"lisää",noEventsMessage:"Ei tapahtumia näytettäviä"})}(),function(){!function(){var e=a.defineLocale("fr",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),monthsParseExact:!0,weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Aujourd'hui à ] LT",nextDay:"[Demain à ] LT",nextWeek:"dddd [à ] LT",lastDay:"[Hier à ] LT",lastWeek:"dddd [dernier à ] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},ordinalParse:/\d{1,2}(er|)/,ordinal:function(e){return e+(1===e?"er":"")},week:{dow:1,doy:4}});return e}(),e.fullCalendar.datepickerLocale("fr","fr",{closeText:"Fermer",prevText:"Précédent",nextText:"Suivant",currentText:"Aujourd'hui",monthNames:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],monthNamesShort:["janv.","févr.","mars","avr.","mai","juin","juil.","août","sept.","oct.","nov.","déc."],dayNames:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],dayNamesShort:["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],dayNamesMin:["D","L","M","M","J","V","S"],weekHeader:"Sem.",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("fr",{buttonText:{year:"Année",month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},allDayHtml:"Toute la<br/>journée",eventLimitText:"en plus",noEventsMessage:"Aucun événement à afficher"})}(),function(){!function(){var e=a.defineLocale("fr-ca",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),monthsParseExact:!0,weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Aujourd'hui à ] LT",nextDay:"[Demain à ] LT",nextWeek:"dddd [à ] LT",lastDay:"[Hier à ] LT",lastWeek:"dddd [dernier à ] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},ordinalParse:/\d{1,2}(er|e)/,ordinal:function(e){return e+(1===e?"er":"e")}});return e}(),e.fullCalendar.datepickerLocale("fr-ca","fr-CA",{closeText:"Fermer",prevText:"Précédent",nextText:"Suivant",currentText:"Aujourd'hui",monthNames:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],monthNamesShort:["janv.","févr.","mars","avril","mai","juin","juil.","août","sept.","oct.","nov.","déc."],dayNames:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],dayNamesShort:["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],dayNamesMin:["D","L","M","M","J","V","S"],weekHeader:"Sem.",dateFormat:"yy-mm-dd",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("fr-ca",{buttonText:{year:"Année",month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},allDayHtml:"Toute la<br/>journée",eventLimitText:"en plus",noEventsMessage:"Aucun événement à afficher"})}(),function(){!function(){var e=a.defineLocale("fr-ch",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),monthsParseExact:!0,weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Aujourd'hui à ] LT",nextDay:"[Demain à ] LT",nextWeek:"dddd [à ] LT",lastDay:"[Hier à ] LT",lastWeek:"dddd [dernier à ] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},ordinalParse:/\d{1,2}(er|e)/,ordinal:function(e){return e+(1===e?"er":"e")},week:{dow:1,doy:4}});return e}(),e.fullCalendar.datepickerLocale("fr-ch","fr-CH",{closeText:"Fermer",prevText:"<Préc",nextText:"Suiv>",currentText:"Courant",monthNames:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],monthNamesShort:["janv.","févr.","mars","avril","mai","juin","juil.","août","sept.","oct.","nov.","déc."],dayNames:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],dayNamesShort:["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],dayNamesMin:["D","L","M","M","J","V","S"],weekHeader:"Sm",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("fr-ch",{buttonText:{year:"Année",month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},allDayHtml:"Toute la<br/>journée",eventLimitText:"en plus",noEventsMessage:"Aucun événement à afficher"})}(),function(){!function(){var e=a.defineLocale("gl",{months:"xaneiro_febreiro_marzo_abril_maio_xuño_xullo_agosto_setembro_outubro_novembro_decembro".split("_"),monthsShort:"xan._feb._mar._abr._mai._xuñ._xul._ago._set._out._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"domingo_luns_martes_mércores_xoves_venres_sábado".split("_"),weekdaysShort:"dom._lun._mar._mér._xov._ven._sáb.".split("_"),weekdaysMin:"do_lu_ma_mé_xo_ve_sá".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY H:mm",LLLL:"dddd, D [de] MMMM [de] YYYY H:mm"},calendar:{sameDay:function(){return"[hoxe "+(1!==this.hours()?"ás":"á")+"] LT"},nextDay:function(){return"[mañá "+(1!==this.hours()?"ás":"á")+"] LT"},nextWeek:function(){return"dddd ["+(1!==this.hours()?"ás":"a")+"] LT"},lastDay:function(){return"[onte "+(1!==this.hours()?"á":"a")+"] LT"},lastWeek:function(){return"[o] dddd [pasado "+(1!==this.hours()?"ás":"a")+"] LT"},sameElse:"L"},relativeTime:{future:function(e){return 0===e.indexOf("un")?"n"+e:"en "+e},past:"hai %s",s:"uns segundos",m:"un minuto",mm:"%d minutos",h:"unha hora",hh:"%d horas",d:"un dÃa",dd:"%d dÃas",M:"un mes",MM:"%d meses",y:"un ano",yy:"%d anos"},ordinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}});return e}(),e.fullCalendar.datepickerLocale("gl","gl",{closeText:"Pechar",prevText:"<Ant",nextText:"Seg>",currentText:"Hoxe",monthNames:["Xaneiro","Febreiro","Marzo","Abril","Maio","Xuño","Xullo","Agosto","Setembro","Outubro","Novembro","Decembro"],monthNamesShort:["Xan","Feb","Mar","Abr","Mai","Xuñ","Xul","Ago","Set","Out","Nov","Dec"],dayNames:["Domingo","Luns","Martes","Mércores","Xoves","Venres","Sábado"],dayNamesShort:["Dom","Lun","Mar","Mér","Xov","Ven","Sáb"],dayNamesMin:["Do","Lu","Ma","Mé","Xo","Ve","Sá"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("gl",{buttonText:{month:"Mes",week:"Semana",day:"DÃa",list:"Axenda"},allDayHtml:"Todo<br/>o dÃa",eventLimitText:"máis",noEventsMessage:"Non hai eventos para amosar"})}(),function(){!function(){var e=a.defineLocale("he",{months:"×™× ×•×ר_פברו×ר_מרץ_×פריל_מ××™_×™×•× ×™_יולי_×וגוסט_ספטמבר_×וקטובר_× ×•×‘×ž×‘×¨_דצמבר".split("_"),monthsShort:"×™× ×•×³_פבר׳_מרץ_×פר׳_מ××™_×™×•× ×™_יולי_×וג׳_ספט׳_×וק׳_× ×•×‘×³_דצמ׳".split("_"),weekdays:"ר×שון_×©× ×™_שלישי_רביעי_חמישי_שישי_שבת".split("_"),weekdaysShort:"×׳_ב׳_ג׳_ד׳_ה׳_ו׳_ש׳".split("_"),weekdaysMin:"×_ב_×’_ד_×”_ו_ש".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [ב]MMMM YYYY",LLL:"D [ב]MMMM YYYY HH:mm",LLLL:"dddd, D [ב]MMMM YYYY HH:mm",l:"D/M/YYYY",ll:"D MMM YYYY",lll:"D MMM YYYY HH:mm",llll:"ddd, D MMM YYYY HH:mm"},calendar:{sameDay:"[×”×™×•× ×‘Ö¾]LT",nextDay:"[מחר ב־]LT",nextWeek:"dddd [בשעה] LT",lastDay:"[×תמול ב־]LT",lastWeek:"[ביו×] dddd [×”×חרון בשעה] LT",sameElse:"L"},relativeTime:{future:"בעוד %s",past:"×œ×¤× ×™ %s",s:"מספר ×©× ×™×•×ª",m:"דקה",mm:"%d דקות",h:"שעה",hh:function(e){return 2===e?"שעתיי×":e+" שעות"},d:"יו×",dd:function(e){return 2===e?"יומיי×":e+" ימי×"},M:"חודש",MM:function(e){return 2===e?"חודשיי×":e+" חודשי×"},y:"×©× ×”",yy:function(e){return 2===e?"×©× ×ª×™×™×":e%10===0&&10!==e?e+" ×©× ×”":e+" ×©× ×™×"}},meridiemParse:/××—×”"צ|×œ×¤× ×”"צ|×חרי הצהריי×|×œ×¤× ×™ הצהריי×|×œ×¤× ×•×ª בוקר|בבוקר|בערב/i,isPM:function(e){return/^(××—×”"צ|×חרי הצהריי×|בערב)$/.test(e)},meridiem:function(e,a,t){return e<5?"×œ×¤× ×•×ª בוקר":e<10?"בבוקר":e<12?t?'×œ×¤× ×”"צ':"×œ×¤× ×™ הצהריי×":e<18?t?'××—×”"צ':"×חרי הצהריי×":"בערב"}});return e}(),e.fullCalendar.datepickerLocale("he","he",{closeText:"סגור",prevText:"<הקוד×",nextText:"הב×>",currentText:"היו×",monthNames:["×™× ×•×ר","פברו×ר","מרץ","×פריל","מ××™","×™×•× ×™","יולי","×וגוסט","ספטמבר","×וקטובר","× ×•×‘×ž×‘×¨","דצמבר"],monthNamesShort:["×™× ×•","פבר","מרץ","×פר","מ××™","×™×•× ×™","יולי","×וג","ספט","×וק","× ×•×‘","דצמ"],dayNames:["ר×שון","×©× ×™","שלישי","רביעי","חמישי","שישי","שבת"],dayNamesShort:["×'","ב'","×’'","ד'","×”'","ו'","שבת"],dayNamesMin:["×'","ב'","×’'","ד'","×”'","ו'","שבת"],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!0,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("he",{buttonText:{month:"חודש",week:"שבוע",day:"יו×",list:"סדר יו×"},allDayText:"כל היו×",eventLimitText:"×חר",noEventsMessage:"×ין ××™×¨×•×¢×™× ×œ×”×¦×’×”",weekNumberTitle:"שבוע"})}(),function(){!function(){var e={1:"१",2:"२",3:"३",4:"४",5:"५",6:"६",7:"à¥",8:"८",9:"९",0:"०"},t={"१":"1","२":"2","३":"3","४":"4","५":"5","६":"6","à¥":"7","८":"8","९":"9","०":"0"},n=a.defineLocale("hi",{months:"जनवरी_फ़रवरी_मारà¥à¤š_अपà¥à¤°à¥ˆà¤²_मई_जून_जà¥à¤²à¤¾à¤ˆ_अगसà¥à¤¤_सितमà¥à¤¬à¤°_अकà¥à¤Ÿà¥‚बर_नवमà¥à¤¬à¤°_दिसमà¥à¤¬à¤°".split("_"),monthsShort:"जन._फ़र._मारà¥à¤š_अपà¥à¤°à¥ˆ._मई_जून_जà¥à¤²._अग._सित._अकà¥à¤Ÿà¥‚._नव._दिस.".split("_"),monthsParseExact:!0,weekdays:"रविवार_सोमवार_मंगलवार_बà¥à¤§à¤µà¤¾à¤°_गà¥à¤°à¥‚वार_शà¥à¤•à¥à¤°à¤µà¤¾à¤°_शनिवार".split("_"),weekdaysShort:"रवि_सोम_मंगल_बà¥à¤§_गà¥à¤°à¥‚_शà¥à¤•à¥à¤°_शनि".split("_"),weekdaysMin:"र_सो_मं_बà¥_गà¥_शà¥_श".split("_"),longDateFormat:{LT:"A h:mm बजे",LTS:"A h:mm:ss बजे",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm बजे",LLLL:"dddd, D MMMM YYYY, A h:mm बजे"},calendar:{sameDay:"[आज] LT",nextDay:"[कल] LT",nextWeek:"dddd, LT",lastDay:"[कल] LT",lastWeek:"[पिछले] dddd, LT",sameElse:"L"},relativeTime:{future:"%s में",past:"%s पहले",s:"कà¥à¤› ही कà¥à¤·à¤£",m:"à¤à¤• मिनट",mm:"%d मिनट",h:"à¤à¤• घंटा",hh:"%d घंटे",d:"à¤à¤• दिन",dd:"%d दिन",M:"à¤à¤• महीने",MM:"%d महीने",y:"à¤à¤• वरà¥à¤·",yy:"%d वरà¥à¤·"},preparse:function(e){return e.replace(/[१२३४५६à¥à¥®à¥¯à¥¦]/g,function(e){return t[e]})},postformat:function(a){return a.replace(/\d/g,function(a){return e[a]})},meridiemParse:/रात|सà¥à¤¬à¤¹|दोपहर|शाम/,meridiemHour:function(e,a){return 12===e&&(e=0),"रात"===a?e<4?e:e+12:"सà¥à¤¬à¤¹"===a?e:"दोपहर"===a?e>=10?e:e+12:"शाम"===a?e+12:void 0},meridiem:function(e,a,t){return e<4?"रात":e<10?"सà¥à¤¬à¤¹":e<17?"दोपहर":e<20?"शाम":"रात"},week:{dow:0,doy:6}});return n}(),e.fullCalendar.datepickerLocale("hi","hi",{closeText:"बंद",prevText:"पिछला",nextText:"अगला",currentText:"आज",monthNames:["जनवरी ","फरवरी","मारà¥à¤š","अपà¥à¤°à¥‡à¤²","मई","जून","जूलाई","अगसà¥à¤¤ ","सितमà¥à¤¬à¤°","अकà¥à¤Ÿà¥‚बर","नवमà¥à¤¬à¤°","दिसमà¥à¤¬à¤°"],monthNamesShort:["जन","फर","मारà¥à¤š","अपà¥à¤°à¥‡à¤²","मई","जून","जूलाई","अग","सित","अकà¥à¤Ÿ","नव","दि"],dayNames:["रविवार","सोमवार","मंगलवार","बà¥à¤§à¤µà¤¾à¤°","गà¥à¤°à¥à¤µà¤¾à¤°","शà¥à¤•à¥à¤°à¤µà¤¾à¤°","शनिवार"],dayNamesShort:["रवि","सोम","मंगल","बà¥à¤§","गà¥à¤°à¥","शà¥à¤•à¥à¤°","शनि"],dayNamesMin:["रवि","सोम","मंगल","बà¥à¤§","गà¥à¤°à¥","शà¥à¤•à¥à¤°","शनि"],weekHeader:"हफà¥à¤¤à¤¾",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("hi",{buttonText:{month:"महीना",week:"सपà¥à¤¤à¤¾à¤¹",day:"दिन",list:"कारà¥à¤¯à¤¸à¥‚ची"},allDayText:"सà¤à¥€ दिन",eventLimitText:function(e){return"+अधिक "+e},noEventsMessage:"कोई घटनाओं को पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤ करने के लिà¤"})}(),function(){!function(){function e(e,a,t){var n=e+" ";switch(t){case"m":return a?"jedna minuta":"jedne minute";case"mm":return n+=1===e?"minuta":2===e||3===e||4===e?"minute":"minuta";case"h":return a?"jedan sat":"jednog sata";case"hh":return n+=1===e?"sat":2===e||3===e||4===e?"sata":"sati";case"dd":return n+=1===e?"dan":"dana";case"MM":return n+=1===e?"mjesec":2===e||3===e||4===e?"mjeseca":"mjeseci";case"yy":return n+=1===e?"godina":2===e||3===e||4===e?"godine":"godina"}}var t=a.defineLocale("hr",{months:{format:"sijeÄnja_veljaÄe_ožujka_travnja_svibnja_lipnja_srpnja_kolovoza_rujna_listopada_studenoga_prosinca".split("_"),standalone:"sijeÄanj_veljaÄa_ožujak_travanj_svibanj_lipanj_srpanj_kolovoz_rujan_listopad_studeni_prosinac".split("_")},monthsShort:"sij._velj._ožu._tra._svi._lip._srp._kol._ruj._lis._stu._pro.".split("_"),monthsParseExact:!0,weekdays:"nedjelja_ponedjeljak_utorak_srijeda_Äetvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._Äet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_Äe_pe_su".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[juÄer u] LT",lastWeek:function(){switch(this.day()){case 0:case 3:return"[proÅ¡lu] dddd [u] LT";case 6:return"[proÅ¡le] [subote] [u] LT";case 1:case 2:case 4:case 5:return"[proÅ¡li] dddd [u] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"par sekundi",m:e,mm:e,h:e,hh:e,d:"dan",dd:e,M:"mjesec",MM:e,y:"godinu",yy:e},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}});return t}(),e.fullCalendar.datepickerLocale("hr","hr",{closeText:"Zatvori",prevText:"<",nextText:">",currentText:"Danas",monthNames:["SijeÄanj","VeljaÄa","Ožujak","Travanj","Svibanj","Lipanj","Srpanj","Kolovoz","Rujan","Listopad","Studeni","Prosinac"], -monthNamesShort:["Sij","Velj","Ožu","Tra","Svi","Lip","Srp","Kol","Ruj","Lis","Stu","Pro"],dayNames:["Nedjelja","Ponedjeljak","Utorak","Srijeda","ÄŒetvrtak","Petak","Subota"],dayNamesShort:["Ned","Pon","Uto","Sri","ÄŒet","Pet","Sub"],dayNamesMin:["Ne","Po","Ut","Sr","ÄŒe","Pe","Su"],weekHeader:"Tje",dateFormat:"dd.mm.yy.",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("hr",{buttonText:{month:"Mjesec",week:"Tjedan",day:"Dan",list:"Raspored"},allDayText:"Cijeli dan",eventLimitText:function(e){return"+ joÅ¡ "+e},noEventsMessage:"Nema dogaÄ‘aja za prikaz"})}(),function(){!function(){function e(e,a,t,n){var r=e;switch(t){case"s":return n||a?"néhány másodperc":"néhány másodperce";case"m":return"egy"+(n||a?" perc":" perce");case"mm":return r+(n||a?" perc":" perce");case"h":return"egy"+(n||a?" óra":" órája");case"hh":return r+(n||a?" óra":" órája");case"d":return"egy"+(n||a?" nap":" napja");case"dd":return r+(n||a?" nap":" napja");case"M":return"egy"+(n||a?" hónap":" hónapja");case"MM":return r+(n||a?" hónap":" hónapja");case"y":return"egy"+(n||a?" év":" éve");case"yy":return r+(n||a?" év":" éve")}return""}function t(e){return(e?"":"[múlt] ")+"["+n[this.day()]+"] LT[-kor]"}var n="vasárnap hétfÅ‘n kedden szerdán csütörtökön pénteken szombaton".split(" "),r=a.defineLocale("hu",{months:"január_február_március_április_május_június_július_augusztus_szeptember_október_november_december".split("_"),monthsShort:"jan_feb_márc_ápr_máj_jún_júl_aug_szept_okt_nov_dec".split("_"),weekdays:"vasárnap_hétfÅ‘_kedd_szerda_csütörtök_péntek_szombat".split("_"),weekdaysShort:"vas_hét_kedd_sze_csüt_pén_szo".split("_"),weekdaysMin:"v_h_k_sze_cs_p_szo".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"YYYY.MM.DD.",LL:"YYYY. MMMM D.",LLL:"YYYY. MMMM D. H:mm",LLLL:"YYYY. MMMM D., dddd H:mm"},meridiemParse:/de|du/i,isPM:function(e){return"u"===e.charAt(1).toLowerCase()},meridiem:function(e,a,t){return e<12?t===!0?"de":"DE":t===!0?"du":"DU"},calendar:{sameDay:"[ma] LT[-kor]",nextDay:"[holnap] LT[-kor]",nextWeek:function(){return t.call(this,!0)},lastDay:"[tegnap] LT[-kor]",lastWeek:function(){return t.call(this,!1)},sameElse:"L"},relativeTime:{future:"%s múlva",past:"%s",s:e,m:e,mm:e,h:e,hh:e,d:e,dd:e,M:e,MM:e,y:e,yy:e},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}});return r}(),e.fullCalendar.datepickerLocale("hu","hu",{closeText:"bezár",prevText:"vissza",nextText:"elÅ‘re",currentText:"ma",monthNames:["Január","Február","Március","Ãprilis","Május","Június","Július","Augusztus","Szeptember","Október","November","December"],monthNamesShort:["Jan","Feb","Már","Ãpr","Máj","Jún","Júl","Aug","Szep","Okt","Nov","Dec"],dayNames:["Vasárnap","HétfÅ‘","Kedd","Szerda","Csütörtök","Péntek","Szombat"],dayNamesShort:["Vas","Hét","Ked","Sze","Csü","Pén","Szo"],dayNamesMin:["V","H","K","Sze","Cs","P","Szo"],weekHeader:"Hét",dateFormat:"yy.mm.dd.",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:""}),e.fullCalendar.locale("hu",{buttonText:{month:"Hónap",week:"Hét",day:"Nap",list:"Napló"},allDayText:"Egész nap",eventLimitText:"további",noEventsMessage:"Nincs megjelenÃthetÅ‘ események"})}(),function(){!function(){var e=a.defineLocale("id",{months:"Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_November_Desember".split("_"),monthsShort:"Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nov_Des".split("_"),weekdays:"Minggu_Senin_Selasa_Rabu_Kamis_Jumat_Sabtu".split("_"),weekdaysShort:"Min_Sen_Sel_Rab_Kam_Jum_Sab".split("_"),weekdaysMin:"Mg_Sn_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|siang|sore|malam/,meridiemHour:function(e,a){return 12===e&&(e=0),"pagi"===a?e:"siang"===a?e>=11?e:e+12:"sore"===a||"malam"===a?e+12:void 0},meridiem:function(e,a,t){return e<11?"pagi":e<15?"siang":e<19?"sore":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Besok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kemarin pukul] LT",lastWeek:"dddd [lalu pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lalu",s:"beberapa detik",m:"semenit",mm:"%d menit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}});return e}(),e.fullCalendar.datepickerLocale("id","id",{closeText:"Tutup",prevText:"<mundur",nextText:"maju>",currentText:"hari ini",monthNames:["Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","Nopember","Desember"],monthNamesShort:["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Agus","Sep","Okt","Nop","Des"],dayNames:["Minggu","Senin","Selasa","Rabu","Kamis","Jumat","Sabtu"],dayNamesShort:["Min","Sen","Sel","Rab","kam","Jum","Sab"],dayNamesMin:["Mg","Sn","Sl","Rb","Km","jm","Sb"],weekHeader:"Mg",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("id",{buttonText:{month:"Bulan",week:"Minggu",day:"Hari",list:"Agenda"},allDayHtml:"Sehari<br/>penuh",eventLimitText:"lebih",noEventsMessage:"Tidak ada acara untuk ditampilkan"})}(),function(){!function(){function e(e){return e%100===11||e%10!==1}function t(a,t,n,r){var s=a+" ";switch(n){case"s":return t||r?"nokkrar sekúndur":"nokkrum sekúndum";case"m":return t?"mÃnúta":"mÃnútu";case"mm":return e(a)?s+(t||r?"mÃnútur":"mÃnútum"):t?s+"mÃnúta":s+"mÃnútu";case"hh":return e(a)?s+(t||r?"klukkustundir":"klukkustundum"):s+"klukkustund";case"d":return t?"dagur":r?"dag":"degi";case"dd":return e(a)?t?s+"dagar":s+(r?"daga":"dögum"):t?s+"dagur":s+(r?"dag":"degi");case"M":return t?"mánuður":r?"mánuð":"mánuði";case"MM":return e(a)?t?s+"mánuðir":s+(r?"mánuði":"mánuðum"):t?s+"mánuður":s+(r?"mánuð":"mánuði");case"y":return t||r?"ár":"ári";case"yy":return e(a)?s+(t||r?"ár":"árum"):s+(t||r?"ár":"ári")}}var n=a.defineLocale("is",{months:"janúar_febrúar_mars_aprÃl_maÃ_júnÃ_júlÃ_ágúst_september_október_nóvember_desember".split("_"),monthsShort:"jan_feb_mar_apr_maÃ_jún_júl_ágú_sep_okt_nóv_des".split("_"),weekdays:"sunnudagur_mánudagur_þriðjudagur_miðvikudagur_fimmtudagur_föstudagur_laugardagur".split("_"),weekdaysShort:"sun_mán_þri_mið_fim_fös_lau".split("_"),weekdaysMin:"Su_Má_Þr_Mi_Fi_Fö_La".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] H:mm",LLLL:"dddd, D. MMMM YYYY [kl.] H:mm"},calendar:{sameDay:"[à dag kl.] LT",nextDay:"[á morgun kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[à gær kl.] LT",lastWeek:"[sÃðasta] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"eftir %s",past:"fyrir %s sÃðan",s:t,m:t,mm:t,h:"klukkustund",hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}});return n}(),e.fullCalendar.datepickerLocale("is","is",{closeText:"Loka",prevText:"< Fyrri",nextText:"Næsti >",currentText:"à dag",monthNames:["Janúar","Febrúar","Mars","AprÃl","MaÃ","JúnÃ","JúlÃ","Ãgúst","September","Október","Nóvember","Desember"],monthNamesShort:["Jan","Feb","Mar","Apr","MaÃ","Jún","Júl","Ãgú","Sep","Okt","Nóv","Des"],dayNames:["Sunnudagur","Mánudagur","Þriðjudagur","Miðvikudagur","Fimmtudagur","Föstudagur","Laugardagur"],dayNamesShort:["Sun","Mán","Þri","Mið","Fim","Fös","Lau"],dayNamesMin:["Su","Má","Þr","Mi","Fi","Fö","La"],weekHeader:"Vika",dateFormat:"dd.mm.yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("is",{buttonText:{month:"Mánuður",week:"Vika",day:"Dagur",list:"Dagskrá"},allDayHtml:"Allan<br/>daginn",eventLimitText:"meira",noEventsMessage:"Engir viðburðir til að sýna"})}(),function(){!function(){var e=a.defineLocale("it",{months:"gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"),monthsShort:"gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"),weekdays:"Domenica_Lunedì_Martedì_Mercoledì_Giovedì_Venerdì_Sabato".split("_"),weekdaysShort:"Dom_Lun_Mar_Mer_Gio_Ven_Sab".split("_"),weekdaysMin:"Do_Lu_Ma_Me_Gi_Ve_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Oggi alle] LT",nextDay:"[Domani alle] LT",nextWeek:"dddd [alle] LT",lastDay:"[Ieri alle] LT",lastWeek:function(){switch(this.day()){case 0:return"[la scorsa] dddd [alle] LT";default:return"[lo scorso] dddd [alle] LT"}},sameElse:"L"},relativeTime:{future:function(e){return(/^[0-9].+$/.test(e)?"tra":"in")+" "+e},past:"%s fa",s:"alcuni secondi",m:"un minuto",mm:"%d minuti",h:"un'ora",hh:"%d ore",d:"un giorno",dd:"%d giorni",M:"un mese",MM:"%d mesi",y:"un anno",yy:"%d anni"},ordinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}});return e}(),e.fullCalendar.datepickerLocale("it","it",{closeText:"Chiudi",prevText:"<Prec",nextText:"Succ>",currentText:"Oggi",monthNames:["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"],monthNamesShort:["Gen","Feb","Mar","Apr","Mag","Giu","Lug","Ago","Set","Ott","Nov","Dic"],dayNames:["Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato"],dayNamesShort:["Dom","Lun","Mar","Mer","Gio","Ven","Sab"],dayNamesMin:["Do","Lu","Ma","Me","Gi","Ve","Sa"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("it",{buttonText:{month:"Mese",week:"Settimana",day:"Giorno",list:"Agenda"},allDayHtml:"Tutto il<br/>giorno",eventLimitText:function(e){return"+altri "+e},noEventsMessage:"Non ci sono eventi da visualizzare"})}(),function(){!function(){var e=a.defineLocale("ja",{months:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"日曜日_月曜日_ç«æ›œæ—¥_水曜日_木曜日_金曜日_土曜日".split("_"),weekdaysShort:"æ—¥_月_ç«_æ°´_木_金_土".split("_"),weekdaysMin:"æ—¥_月_ç«_æ°´_木_金_土".split("_"),longDateFormat:{LT:"Ah時m分",LTS:"Ah時m分sç§’",L:"YYYY/MM/DD",LL:"YYYYå¹´M月Dæ—¥",LLL:"YYYYå¹´M月Dæ—¥Ah時m分",LLLL:"YYYYå¹´M月Dæ—¥Ah時m分 dddd"},meridiemParse:/åˆå‰|åˆå¾Œ/i,isPM:function(e){return"åˆå¾Œ"===e},meridiem:function(e,a,t){return e<12?"åˆå‰":"åˆå¾Œ"},calendar:{sameDay:"[今日] LT",nextDay:"[明日] LT",nextWeek:"[æ¥é€±]dddd LT",lastDay:"[昨日] LT",lastWeek:"[å‰é€±]dddd LT",sameElse:"L"},ordinalParse:/\d{1,2}æ—¥/,ordinal:function(e,a){switch(a){case"d":case"D":case"DDD":return e+"æ—¥";default:return e}},relativeTime:{future:"%s後",past:"%så‰",s:"æ•°ç§’",m:"1分",mm:"%d分",h:"1時間",hh:"%d時間",d:"1æ—¥",dd:"%dæ—¥",M:"1ヶ月",MM:"%dヶ月",y:"1å¹´",yy:"%då¹´"}});return e}(),e.fullCalendar.datepickerLocale("ja","ja",{closeText:"é–‰ã˜ã‚‹",prevText:"<å‰",nextText:"次>",currentText:"今日",monthNames:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],monthNamesShort:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],dayNames:["日曜日","月曜日","ç«æ›œæ—¥","水曜日","木曜日","金曜日","土曜日"],dayNamesShort:["æ—¥","月","ç«","æ°´","木","金","土"],dayNamesMin:["æ—¥","月","ç«","æ°´","木","金","土"],weekHeader:"週",dateFormat:"yy/mm/dd",firstDay:0,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"å¹´"}),e.fullCalendar.locale("ja",{buttonText:{month:"月",week:"週",day:"æ—¥",list:"予定リスト"},allDayText:"終日",eventLimitText:function(e){return"ä»– "+e+" ä»¶"},noEventsMessage:"イベントãŒè¡¨ç¤ºã•れãªã„よã†ã«"})}(),function(){!function(){var e={0:"-ші",1:"-ші",2:"-ші",3:"-ші",4:"-ші",5:"-ші",6:"-шы",7:"-ші",8:"-ші",9:"-шы",10:"-шы",20:"-шы",30:"-шы",40:"-шы",50:"-ші",60:"-шы",70:"-ші",80:"-ші",90:"-шы",100:"-ші"},t=a.defineLocale("kk",{months:"қаңтар_ақпан_наурыз_Ñәуір_мамыр_мауÑым_шілде_тамыз_қыркүйек_қазан_қараша_желтоқÑан".split("_"),monthsShort:"қаң_ақп_нау_Ñәу_мам_мау_шіл_там_қыр_қаз_қар_жел".split("_"),weekdays:"жекÑенбі_дүйÑенбі_ÑейÑенбі_ÑәрÑенбі_бейÑенбі_жұма_Ñенбі".split("_"),weekdaysShort:"жек_дүй_Ñей_Ñәр_бей_жұм_Ñен".split("_"),weekdaysMin:"жк_дй_Ñй_ÑÑ€_бй_жм_Ñн".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Бүгін Ñағат] LT",nextDay:"[Ертең Ñағат] LT",nextWeek:"dddd [Ñағат] LT",lastDay:"[Кеше Ñағат] LT",lastWeek:"[Өткен аптаның] dddd [Ñағат] LT",sameElse:"L"},relativeTime:{future:"%s ішінде",past:"%s бұрын",s:"бірнеше Ñекунд",m:"бір минут",mm:"%d минут",h:"бір Ñағат",hh:"%d Ñағат",d:"бір күн",dd:"%d күн",M:"бір ай",MM:"%d ай",y:"бір жыл",yy:"%d жыл"},ordinalParse:/\d{1,2}-(ші|шы)/,ordinal:function(a){var t=a%10,n=a>=100?100:null;return a+(e[a]||e[t]||e[n])},week:{dow:1,doy:7}});return t}(),e.fullCalendar.datepickerLocale("kk","kk",{closeText:"Жабу",prevText:"<Ðлдыңғы",nextText:"КелеÑÑ–>",currentText:"Бүгін",monthNames:["Қаңтар","Ðқпан","Ðаурыз","Сәуір","Мамыр","МауÑым","Шілде","Тамыз","Қыркүйек","Қазан","Қараша","ЖелтоқÑан"],monthNamesShort:["Қаң","Ðқп","Ðау","Сәу","Мам","Мау","Шіл","Там","Қыр","Қаз","Қар","Жел"],dayNames:["ЖекÑенбі","ДүйÑенбі","СейÑенбі","СәрÑенбі","БейÑенбі","Жұма","Сенбі"],dayNamesShort:["жкÑ","дÑн","ÑÑн","ÑÑ€Ñ","бÑн","жма","Ñнб"],dayNamesMin:["Жк","ДÑ","СÑ","Ср","БÑ","Жм","Сн"],weekHeader:"Ðе",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("kk",{buttonText:{month:"Ðй",week:"Ðпта",day:"Күн",list:"Күн тәртібі"},allDayText:"Күні бойы",eventLimitText:function(e){return"+ тағы "+e},noEventsMessage:"КөрÑету үшін оқиғалар жоқ"})}(),function(){!function(){var e=a.defineLocale("ko",{months:"1ì›”_2ì›”_3ì›”_4ì›”_5ì›”_6ì›”_7ì›”_8ì›”_9ì›”_10ì›”_11ì›”_12ì›”".split("_"),monthsShort:"1ì›”_2ì›”_3ì›”_4ì›”_5ì›”_6ì›”_7ì›”_8ì›”_9ì›”_10ì›”_11ì›”_12ì›”".split("_"),weekdays:"ì¼ìš”ì¼_월요ì¼_화요ì¼_수요ì¼_목요ì¼_금요ì¼_í† ìš”ì¼".split("_"),weekdaysShort:"ì¼_ì›”_í™”_수_목_금_í† ".split("_"),weekdaysMin:"ì¼_ì›”_í™”_수_목_금_í† ".split("_"),longDateFormat:{LT:"A h시 më¶„",LTS:"A h시 më¶„ sì´ˆ",L:"YYYY.MM.DD",LL:"YYYYë…„ MMMM Dì¼",LLL:"YYYYë…„ MMMM Dì¼ A h시 më¶„",LLLL:"YYYYë…„ MMMM Dì¼ dddd A h시 më¶„"},calendar:{sameDay:"오늘 LT",nextDay:"ë‚´ì¼ LT",nextWeek:"dddd LT",lastDay:"ì–´ì œ LT",lastWeek:"지난주 dddd LT",sameElse:"L"},relativeTime:{future:"%s 후",past:"%s ì „",s:"몇 ì´ˆ",ss:"%dì´ˆ",m:"ì¼ë¶„",mm:"%dë¶„",h:"한 시간",hh:"%d시간",d:"하루",dd:"%dì¼",M:"한 달",MM:"%d달",y:"ì¼ ë…„",yy:"%dë…„"},ordinalParse:/\d{1,2}ì¼/,ordinal:"%dì¼",meridiemParse:/ì˜¤ì „|오후/,isPM:function(e){return"오후"===e},meridiem:function(e,a,t){return e<12?"ì˜¤ì „":"오후"}});return e}(),e.fullCalendar.datepickerLocale("ko","ko",{closeText:"닫기",prevText:"ì´ì „달",nextText:"다ìŒë‹¬",currentText:"오늘",monthNames:["1ì›”","2ì›”","3ì›”","4ì›”","5ì›”","6ì›”","7ì›”","8ì›”","9ì›”","10ì›”","11ì›”","12ì›”"],monthNamesShort:["1ì›”","2ì›”","3ì›”","4ì›”","5ì›”","6ì›”","7ì›”","8ì›”","9ì›”","10ì›”","11ì›”","12ì›”"],dayNames:["ì¼ìš”ì¼","월요ì¼","화요ì¼","수요ì¼","목요ì¼","금요ì¼","í† ìš”ì¼"],dayNamesShort:["ì¼","ì›”","í™”","수","목","금","í† "],dayNamesMin:["ì¼","ì›”","í™”","수","목","금","í† "],weekHeader:"주",dateFormat:"yy. m. d.",firstDay:0,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"ë…„"}),e.fullCalendar.locale("ko",{buttonText:{month:"ì›”",week:"주",day:"ì¼",list:"ì¼ì •목ë¡"},allDayText:"종ì¼",eventLimitText:"ê°œ",noEventsMessage:"ì¼ì •ì´ í‘œì‹œ 없습니다"})}(),function(){!function(){function e(e,a,t,n){var r={m:["eng Minutt","enger Minutt"],h:["eng Stonn","enger Stonn"],d:["een Dag","engem Dag"],M:["ee Mount","engem Mount"],y:["ee Joer","engem Joer"]};return a?r[t][0]:r[t][1]}function t(e){var a=e.substr(0,e.indexOf(" "));return r(a)?"a "+e:"an "+e}function n(e){var a=e.substr(0,e.indexOf(" "));return r(a)?"viru "+e:"virun "+e}function r(e){if(e=parseInt(e,10),isNaN(e))return!1;if(e<0)return!0;if(e<10)return 4<=e&&e<=7;if(e<100){var a=e%10,t=e/10;return r(0===a?t:a)}if(e<1e4){for(;e>=10;)e/=10;return r(e)}return e/=1e3,r(e)}var s=a.defineLocale("lb",{months:"Januar_Februar_Mäerz_Abrëll_Mee_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Febr._Mrz._Abr._Mee_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),monthsParseExact:!0,weekdays:"Sonndeg_Méindeg_Dënschdeg_Mëttwoch_Donneschdeg_Freideg_Samschdeg".split("_"),weekdaysShort:"So._Mé._Dë._Më._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mé_Dë_Më_Do_Fr_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm [Auer]",LTS:"H:mm:ss [Auer]",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm [Auer]",LLLL:"dddd, D. MMMM YYYY H:mm [Auer]"},calendar:{sameDay:"[Haut um] LT",sameElse:"L",nextDay:"[Muer um] LT",nextWeek:"dddd [um] LT",lastDay:"[Gëschter um] LT",lastWeek:function(){switch(this.day()){case 2:case 4:return"[Leschten] dddd [um] LT";default:return"[Leschte] dddd [um] LT"}}},relativeTime:{future:t,past:n,s:"e puer Sekonnen",m:e,mm:"%d Minutten",h:e,hh:"%d Stonnen",d:e,dd:"%d Deeg",M:e,MM:"%d Méint",y:e,yy:"%d Joer"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}});return s}(),e.fullCalendar.datepickerLocale("lb","lb",{closeText:"Fäerdeg",prevText:"Zréck",nextText:"Weider",currentText:"Haut",monthNames:["Januar","Februar","Mäerz","Abrëll","Mee","Juni","Juli","August","September","Oktober","November","Dezember"],monthNamesShort:["Jan","Feb","Mäe","Abr","Mee","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],dayNames:["Sonndeg","Méindeg","Dënschdeg","Mëttwoch","Donneschdeg","Freideg","Samschdeg"],dayNamesShort:["Son","Méi","Dën","Mët","Don","Fre","Sam"],dayNamesMin:["So","Mé","Dë","Më","Do","Fr","Sa"],weekHeader:"W",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("lb",{buttonText:{month:"Mount",week:"Woch",day:"Dag",list:"Terminiwwersiicht"},allDayText:"Ganzen Dag",eventLimitText:"méi",noEventsMessage:"Nee Evenementer ze affichéieren"})}(),function(){!function(){function e(e,a,t,n){return a?"kelios sekundÄ—s":n?"kelių sekundžių":"kelias sekundes"}function t(e,a,t,n){return a?r(t)[0]:n?r(t)[1]:r(t)[2]}function n(e){return e%10===0||e>10&&e<20}function r(e){return d[e].split("_")}function s(e,a,s,d){var i=e+" ";return 1===e?i+t(e,a,s[0],d):a?i+(n(e)?r(s)[1]:r(s)[0]):d?i+r(s)[1]:i+(n(e)?r(s)[1]:r(s)[2])}var d={m:"minutÄ—_minutÄ—s_minutÄ™",mm:"minutÄ—s_minuÄių_minutes",h:"valanda_valandos_valandÄ…",hh:"valandos_valandų_valandas",d:"diena_dienos_dienÄ…",dd:"dienos_dienų_dienas",M:"mÄ—nuo_mÄ—nesio_mÄ—nesį",MM:"mÄ—nesiai_mÄ—nesių_mÄ—nesius",y:"metai_metų_metus",yy:"metai_metų_metus"},i=a.defineLocale("lt",{months:{format:"sausio_vasario_kovo_balandžio_gegužės_birželio_liepos_rugpjÅ«Äio_rugsÄ—jo_spalio_lapkriÄio_gruodžio".split("_"),standalone:"sausis_vasaris_kovas_balandis_gegužė_birželis_liepa_rugpjÅ«tis_rugsÄ—jis_spalis_lapkritis_gruodis".split("_"),isFormat:/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?|MMMM?(\[[^\[\]]*\]|\s)+D[oD]?/},monthsShort:"sau_vas_kov_bal_geg_bir_lie_rgp_rgs_spa_lap_grd".split("_"),weekdays:{format:"sekmadienį_pirmadienį_antradienį_treÄiadienį_ketvirtadienį_penktadienį_Å¡eÅ¡tadienį".split("_"),standalone:"sekmadienis_pirmadienis_antradienis_treÄiadienis_ketvirtadienis_penktadienis_Å¡eÅ¡tadienis".split("_"),isFormat:/dddd HH:mm/},weekdaysShort:"Sek_Pir_Ant_Tre_Ket_Pen_Å eÅ¡".split("_"),weekdaysMin:"S_P_A_T_K_Pn_Å ".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY [m.] MMMM D [d.]",LLL:"YYYY [m.] MMMM D [d.], HH:mm [val.]",LLLL:"YYYY [m.] MMMM D [d.], dddd, HH:mm [val.]",l:"YYYY-MM-DD",ll:"YYYY [m.] MMMM D [d.]",lll:"YYYY [m.] MMMM D [d.], HH:mm [val.]",llll:"YYYY [m.] MMMM D [d.], ddd, HH:mm [val.]"},calendar:{sameDay:"[Å iandien] LT",nextDay:"[Rytoj] LT",nextWeek:"dddd LT",lastDay:"[Vakar] LT",lastWeek:"[PraÄ—jusį] dddd LT",sameElse:"L"},relativeTime:{future:"po %s",past:"prieÅ¡ %s",s:e,m:t,mm:s,h:t,hh:s,d:t,dd:s,M:t,MM:s,y:t,yy:s},ordinalParse:/\d{1,2}-oji/,ordinal:function(e){return e+"-oji"},week:{dow:1,doy:4}});return i}(),e.fullCalendar.datepickerLocale("lt","lt",{closeText:"Uždaryti",prevText:"<Atgal",nextText:"Pirmyn>",currentText:"Å iandien",monthNames:["Sausis","Vasaris","Kovas","Balandis","Gegužė","Birželis","Liepa","RugpjÅ«tis","RugsÄ—jis","Spalis","Lapkritis","Gruodis"],monthNamesShort:["Sau","Vas","Kov","Bal","Geg","Bir","Lie","Rugp","Rugs","Spa","Lap","Gru"],dayNames:["sekmadienis","pirmadienis","antradienis","treÄiadienis","ketvirtadienis","penktadienis","Å¡eÅ¡tadienis"],dayNamesShort:["sek","pir","ant","tre","ket","pen","Å¡eÅ¡"],dayNamesMin:["Se","Pr","An","Tr","Ke","Pe","Å e"],weekHeader:"SAV",dateFormat:"yy-mm-dd",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:""}),e.fullCalendar.locale("lt",{buttonText:{month:"MÄ—nuo",week:"SavaitÄ—",day:"Diena",list:"DarbotvarkÄ—"},allDayText:"VisÄ… dienÄ…",eventLimitText:"daugiau",noEventsMessage:"NÄ—ra įvykių rodyti"})}(),function(){!function(){function e(e,a,t){return t?a%10===1&&a%100!==11?e[2]:e[3]:a%10===1&&a%100!==11?e[0]:e[1]}function t(a,t,n){return a+" "+e(s[n],a,t)}function n(a,t,n){return e(s[n],a,t)}function r(e,a){return a?"dažas sekundes":"dažÄm sekundÄ“m"}var s={m:"minÅ«tes_minÅ«tÄ“m_minÅ«te_minÅ«tes".split("_"),mm:"minÅ«tes_minÅ«tÄ“m_minÅ«te_minÅ«tes".split("_"),h:"stundas_stundÄm_stunda_stundas".split("_"),hh:"stundas_stundÄm_stunda_stundas".split("_"),d:"dienas_dienÄm_diena_dienas".split("_"),dd:"dienas_dienÄm_diena_dienas".split("_"),M:"mÄ“neÅ¡a_mÄ“neÅ¡iem_mÄ“nesis_mÄ“neÅ¡i".split("_"),MM:"mÄ“neÅ¡a_mÄ“neÅ¡iem_mÄ“nesis_mÄ“neÅ¡i".split("_"),y:"gada_gadiem_gads_gadi".split("_"),yy:"gada_gadiem_gads_gadi".split("_")},d=a.defineLocale("lv",{months:"janvÄris_februÄris_marts_aprÄ«lis_maijs_jÅ«nijs_jÅ«lijs_augusts_septembris_oktobris_novembris_decembris".split("_"),monthsShort:"jan_feb_mar_apr_mai_jÅ«n_jÅ«l_aug_sep_okt_nov_dec".split("_"),weekdays:"svÄ“tdiena_pirmdiena_otrdiena_treÅ¡diena_ceturtdiena_piektdiena_sestdiena".split("_"),weekdaysShort:"Sv_P_O_T_C_Pk_S".split("_"),weekdaysMin:"Sv_P_O_T_C_Pk_S".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY.",LL:"YYYY. [gada] D. MMMM",LLL:"YYYY. [gada] D. MMMM, HH:mm",LLLL:"YYYY. [gada] D. MMMM, dddd, HH:mm"},calendar:{sameDay:"[Å odien pulksten] LT",nextDay:"[RÄ«t pulksten] LT",nextWeek:"dddd [pulksten] LT",lastDay:"[Vakar pulksten] LT",lastWeek:"[PagÄjuÅ¡Ä] dddd [pulksten] LT",sameElse:"L"},relativeTime:{future:"pÄ“c %s",past:"pirms %s",s:r,m:n,mm:t,h:n,hh:t,d:n,dd:t,M:n,MM:t,y:n,yy:t},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}});return d}(),e.fullCalendar.datepickerLocale("lv","lv",{closeText:"AizvÄ“rt",prevText:"Iepr.",nextText:"NÄk.",currentText:"Å odien",monthNames:["JanvÄris","FebruÄris","Marts","AprÄ«lis","Maijs","JÅ«nijs","JÅ«lijs","Augusts","Septembris","Oktobris","Novembris","Decembris"],monthNamesShort:["Jan","Feb","Mar","Apr","Mai","JÅ«n","JÅ«l","Aug","Sep","Okt","Nov","Dec"],dayNames:["svÄ“tdiena","pirmdiena","otrdiena","treÅ¡diena","ceturtdiena","piektdiena","sestdiena"],dayNamesShort:["svt","prm","otr","tre","ctr","pkt","sst"],dayNamesMin:["Sv","Pr","Ot","Tr","Ct","Pk","Ss"],weekHeader:"Ned.",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("lv",{buttonText:{month:"MÄ“nesis",week:"Nedēļa",day:"Diena",list:"Dienas kÄrtÄ«ba"},allDayText:"Visu dienu",eventLimitText:function(e){return"+vÄ“l "+e},noEventsMessage:"Nav notikumu, lai parÄdÄ«tu"})}(),function(){!function(){var e=a.defineLocale("mk",{months:"јануари_февруари_март_април_мај_јуни_јули_авгуÑÑ‚_Ñептември_октомври_ноември_декември".split("_"),monthsShort:"јан_фев_мар_апр_мај_јун_јул_авг_Ñеп_окт_ное_дек".split("_"),weekdays:"недела_понеделник_вторник_Ñреда_четврток_петок_Ñабота".split("_"),weekdaysShort:"нед_пон_вто_Ñре_чет_пет_Ñаб".split("_"),weekdaysMin:"нe_пo_вт_ÑÑ€_че_пе_Ña".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[Ð”ÐµÐ½ÐµÑ Ð²Ð¾] LT",nextDay:"[Утре во] LT",nextWeek:"[Во] dddd [во] LT",lastDay:"[Вчера во] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[Изминатата] dddd [во] LT";case 1:case 2:case 4:case 5:return"[Изминатиот] dddd [во] LT"}},sameElse:"L"},relativeTime:{future:"поÑле %s",past:"пред %s",s:"неколку Ñекунди",m:"минута",mm:"%d минути",h:"чаÑ",hh:"%d чаÑа",d:"ден",dd:"%d дена",M:"меÑец",MM:"%d меÑеци",y:"година",yy:"%d години"},ordinalParse:/\d{1,2}-(ев|ен|ти|ви|ри|ми)/,ordinal:function(e){var a=e%10,t=e%100;return 0===e?e+"-ев":0===t?e+"-ен":t>10&&t<20?e+"-ти":1===a?e+"-ви":2===a?e+"-ри":7===a||8===a?e+"-ми":e+"-ти"},week:{dow:1,doy:7}});return e}(),e.fullCalendar.datepickerLocale("mk","mk",{closeText:"Затвори",prevText:"<",nextText:">",currentText:"ДенеÑ",monthNames:["Јануари","Февруари","Март","Ðприл","Мај","Јуни","Јули","ÐвгуÑÑ‚","Септември","Октомври","Ðоември","Декември"],monthNamesShort:["Јан","Фев","Мар","Ðпр","Мај","Јун","Јул","Ðвг","Сеп","Окт","Ðое","Дек"],dayNames:["Ðедела","Понеделник","Вторник","Среда","Четврток","Петок","Сабота"],dayNamesShort:["Ðед","Пон","Вто","Сре","Чет","Пет","Саб"],dayNamesMin:["Ðе","По","Ð’Ñ‚","Ср","Че","Пе","Са"],weekHeader:"Сед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("mk",{buttonText:{month:"МеÑец",week:"Ðедела",day:"Ден",list:"График"},allDayText:"Цел ден",eventLimitText:function(e){return"+повеќе "+e},noEventsMessage:"Ðема наÑтани за прикажување"})}(),function(){!function(){var e=a.defineLocale("ms",{months:"Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis".split("_"),weekdays:"Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu".split("_"),weekdaysShort:"Ahd_Isn_Sel_Rab_Kha_Jum_Sab".split("_"),weekdaysMin:"Ah_Is_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|tengahari|petang|malam/,meridiemHour:function(e,a){return 12===e&&(e=0),"pagi"===a?e:"tengahari"===a?e>=11?e:e+12:"petang"===a||"malam"===a?e+12:void 0},meridiem:function(e,a,t){return e<11?"pagi":e<15?"tengahari":e<19?"petang":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Esok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kelmarin pukul] LT",lastWeek:"dddd [lepas pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lepas",s:"beberapa saat",m:"seminit",mm:"%d minit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}});return e}(),e.fullCalendar.datepickerLocale("ms","ms",{closeText:"Tutup",prevText:"<Sebelum",nextText:"Selepas>",currentText:"hari ini",monthNames:["Januari","Februari","Mac","April","Mei","Jun","Julai","Ogos","September","Oktober","November","Disember"],monthNamesShort:["Jan","Feb","Mac","Apr","Mei","Jun","Jul","Ogo","Sep","Okt","Nov","Dis"],dayNames:["Ahad","Isnin","Selasa","Rabu","Khamis","Jumaat","Sabtu"],dayNamesShort:["Aha","Isn","Sel","Rab","kha","Jum","Sab"],dayNamesMin:["Ah","Is","Se","Ra","Kh","Ju","Sa"],weekHeader:"Mg",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("ms",{buttonText:{month:"Bulan",week:"Minggu",day:"Hari",list:"Agenda"},allDayText:"Sepanjang hari",eventLimitText:function(e){return"masih ada "+e+" acara"},noEventsMessage:"Tiada peristiwa untuk dipaparkan"})}(),function(){!function(){var e=a.defineLocale("ms-my",{months:"Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis".split("_"),weekdays:"Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu".split("_"),weekdaysShort:"Ahd_Isn_Sel_Rab_Kha_Jum_Sab".split("_"),weekdaysMin:"Ah_Is_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|tengahari|petang|malam/,meridiemHour:function(e,a){return 12===e&&(e=0),"pagi"===a?e:"tengahari"===a?e>=11?e:e+12:"petang"===a||"malam"===a?e+12:void 0},meridiem:function(e,a,t){return e<11?"pagi":e<15?"tengahari":e<19?"petang":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Esok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kelmarin pukul] LT",lastWeek:"dddd [lepas pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lepas",s:"beberapa saat",m:"seminit",mm:"%d minit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}});return e}(),e.fullCalendar.datepickerLocale("ms-my","ms",{closeText:"Tutup",prevText:"<Sebelum",nextText:"Selepas>",currentText:"hari ini",monthNames:["Januari","Februari","Mac","April","Mei","Jun","Julai","Ogos","September","Oktober","November","Disember"],monthNamesShort:["Jan","Feb","Mac","Apr","Mei","Jun","Jul","Ogo","Sep","Okt","Nov","Dis"],dayNames:["Ahad","Isnin","Selasa","Rabu","Khamis","Jumaat","Sabtu"],dayNamesShort:["Aha","Isn","Sel","Rab","kha","Jum","Sab"],dayNamesMin:["Ah","Is","Se","Ra","Kh","Ju","Sa"],weekHeader:"Mg",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("ms-my",{buttonText:{month:"Bulan",week:"Minggu",day:"Hari",list:"Agenda"},allDayText:"Sepanjang hari",eventLimitText:function(e){return"masih ada "+e+" acara"},noEventsMessage:"Tiada peristiwa untuk dipaparkan"})}(),function(){!function(){var e=a.defineLocale("nb",{months:"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan._feb._mars_april_mai_juni_juli_aug._sep._okt._nov._des.".split("_"),monthsParseExact:!0,weekdays:"søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),weekdaysShort:"sø._ma._ti._on._to._fr._lø.".split("_"),weekdaysMin:"sø_ma_ti_on_to_fr_lø".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] HH:mm",LLLL:"dddd D. MMMM YYYY [kl.] HH:mm"},calendar:{sameDay:"[i dag kl.] LT",nextDay:"[i morgen kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[i gÃ¥r kl.] LT",lastWeek:"[forrige] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"noen sekunder",m:"ett minutt",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dager",M:"en mÃ¥ned",MM:"%d mÃ¥neder",y:"ett Ã¥r",yy:"%d Ã¥r"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}});return e}(),e.fullCalendar.datepickerLocale("nb","nb",{closeText:"Lukk",prevText:"«Forrige",nextText:"Neste»",currentText:"I dag",monthNames:["januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember"],monthNamesShort:["jan","feb","mar","apr","mai","jun","jul","aug","sep","okt","nov","des"],dayNamesShort:["søn","man","tir","ons","tor","fre","lør"],dayNames:["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"],dayNamesMin:["sø","ma","ti","on","to","fr","lø"],weekHeader:"Uke",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("nb",{buttonText:{month:"MÃ¥ned",week:"Uke",day:"Dag",list:"Agenda"},allDayText:"Hele dagen",eventLimitText:"til",noEventsMessage:"Ingen hendelser Ã¥ vise"})}(),function(){!function(){var e="jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),t="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_"),n=[/^jan/i,/^feb/i,/^maart|mrt.?$/i,/^apr/i,/^mei$/i,/^jun[i.]?$/i,/^jul[i.]?$/i,/^aug/i,/^sep/i,/^okt/i,/^nov/i,/^dec/i],r=/^(januari|februari|maart|april|mei|april|ju[nl]i|augustus|september|oktober|november|december|jan\.?|feb\.?|mrt\.?|apr\.?|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,s=a.defineLocale("nl",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(a,n){return/-MMM-/.test(n)?t[a.month()]:e[a.month()]},monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:/^(januari|februari|maart|mei|ju[nl]i|april|augustus|september|oktober|november|december)/i,monthsShortStrictRegex:/^(jan\.?|feb\.?|mrt\.?|apr\.?|mei|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,monthsParse:n,longMonthsParse:n,shortMonthsParse:n,weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"Zo_Ma_Di_Wo_Do_Vr_Za".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT", -lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",m:"één minuut",mm:"%d minuten",h:"één uur",hh:"%d uur",d:"één dag",dd:"%d dagen",M:"één maand",MM:"%d maanden",y:"één jaar",yy:"%d jaar"},ordinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(1===e||8===e||e>=20?"ste":"de")},week:{dow:1,doy:4}});return s}(),e.fullCalendar.datepickerLocale("nl","nl",{closeText:"Sluiten",prevText:"â†",nextText:"→",currentText:"Vandaag",monthNames:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],monthNamesShort:["jan","feb","mrt","apr","mei","jun","jul","aug","sep","okt","nov","dec"],dayNames:["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],dayNamesShort:["zon","maa","din","woe","don","vri","zat"],dayNamesMin:["zo","ma","di","wo","do","vr","za"],weekHeader:"Wk",dateFormat:"dd-mm-yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("nl",{buttonText:{month:"Maand",week:"Week",day:"Dag",list:"Agenda"},allDayText:"Hele dag",eventLimitText:"extra",noEventsMessage:"Geen evenementen om te laten zien"})}(),function(){!function(){var e="jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),t="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_"),n=[/^jan/i,/^feb/i,/^maart|mrt.?$/i,/^apr/i,/^mei$/i,/^jun[i.]?$/i,/^jul[i.]?$/i,/^aug/i,/^sep/i,/^okt/i,/^nov/i,/^dec/i],r=/^(januari|februari|maart|april|mei|april|ju[nl]i|augustus|september|oktober|november|december|jan\.?|feb\.?|mrt\.?|apr\.?|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,s=a.defineLocale("nl-be",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(a,n){return/-MMM-/.test(n)?t[a.month()]:e[a.month()]},monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:/^(januari|februari|maart|mei|ju[nl]i|april|augustus|september|oktober|november|december)/i,monthsShortStrictRegex:/^(jan\.?|feb\.?|mrt\.?|apr\.?|mei|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,monthsParse:n,longMonthsParse:n,shortMonthsParse:n,weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"Zo_Ma_Di_Wo_Do_Vr_Za".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT",lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",m:"één minuut",mm:"%d minuten",h:"één uur",hh:"%d uur",d:"één dag",dd:"%d dagen",M:"één maand",MM:"%d maanden",y:"één jaar",yy:"%d jaar"},ordinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(1===e||8===e||e>=20?"ste":"de")},week:{dow:1,doy:4}});return s}(),e.fullCalendar.datepickerLocale("nl-be","nl-BE",{closeText:"Sluiten",prevText:"â†",nextText:"→",currentText:"Vandaag",monthNames:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],monthNamesShort:["jan","feb","mrt","apr","mei","jun","jul","aug","sep","okt","nov","dec"],dayNames:["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],dayNamesShort:["zon","maa","din","woe","don","vri","zat"],dayNamesMin:["zo","ma","di","wo","do","vr","za"],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("nl-be",{buttonText:{month:"Maand",week:"Week",day:"Dag",list:"Agenda"},allDayText:"Hele dag",eventLimitText:"extra",noEventsMessage:"Geen evenementen om te laten zien"})}(),function(){!function(){var e=a.defineLocale("nn",{months:"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),weekdays:"sundag_mÃ¥ndag_tysdag_onsdag_torsdag_fredag_laurdag".split("_"),weekdaysShort:"sun_mÃ¥n_tys_ons_tor_fre_lau".split("_"),weekdaysMin:"su_mÃ¥_ty_on_to_fr_lø".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] H:mm",LLLL:"dddd D. MMMM YYYY [kl.] HH:mm"},calendar:{sameDay:"[I dag klokka] LT",nextDay:"[I morgon klokka] LT",nextWeek:"dddd [klokka] LT",lastDay:"[I gÃ¥r klokka] LT",lastWeek:"[FøregÃ¥ande] dddd [klokka] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s sidan",s:"nokre sekund",m:"eit minutt",mm:"%d minutt",h:"ein time",hh:"%d timar",d:"ein dag",dd:"%d dagar",M:"ein mÃ¥nad",MM:"%d mÃ¥nader",y:"eit Ã¥r",yy:"%d Ã¥r"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}});return e}(),e.fullCalendar.datepickerLocale("nn","nn",{closeText:"Lukk",prevText:"«Førre",nextText:"Neste»",currentText:"I dag",monthNames:["januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember"],monthNamesShort:["jan","feb","mar","apr","mai","jun","jul","aug","sep","okt","nov","des"],dayNamesShort:["sun","mÃ¥n","tys","ons","tor","fre","lau"],dayNames:["sundag","mÃ¥ndag","tysdag","onsdag","torsdag","fredag","laurdag"],dayNamesMin:["su","mÃ¥","ty","on","to","fr","la"],weekHeader:"Veke",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("nn",{buttonText:{month:"MÃ¥nad",week:"Veke",day:"Dag",list:"Agenda"},allDayText:"Heile dagen",eventLimitText:"til",noEventsMessage:"Ingen hendelser Ã¥ vise"})}(),function(){!function(){function e(e){return e%10<5&&e%10>1&&~~(e/10)%10!==1}function t(a,t,n){var r=a+" ";switch(n){case"m":return t?"minuta":"minutÄ™";case"mm":return r+(e(a)?"minuty":"minut");case"h":return t?"godzina":"godzinÄ™";case"hh":return r+(e(a)?"godziny":"godzin");case"MM":return r+(e(a)?"miesiÄ…ce":"miesiÄ™cy");case"yy":return r+(e(a)?"lata":"lat")}}var n="styczeÅ„_luty_marzec_kwiecieÅ„_maj_czerwiec_lipiec_sierpieÅ„_wrzesieÅ„_październik_listopad_grudzieÅ„".split("_"),r="stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_wrzeÅ›nia_października_listopada_grudnia".split("_"),s=a.defineLocale("pl",{months:function(e,a){return""===a?"("+r[e.month()]+"|"+n[e.month()]+")":/D MMMM/.test(a)?r[e.month()]:n[e.month()]},monthsShort:"sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru".split("_"),weekdays:"niedziela_poniedziaÅ‚ek_wtorek_Å›roda_czwartek_piÄ…tek_sobota".split("_"),weekdaysShort:"ndz_pon_wt_Å›r_czw_pt_sob".split("_"),weekdaysMin:"Nd_Pn_Wt_Åšr_Cz_Pt_So".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[DziÅ› o] LT",nextDay:"[Jutro o] LT",nextWeek:"[W] dddd [o] LT",lastDay:"[Wczoraj o] LT",lastWeek:function(){switch(this.day()){case 0:return"[W zeszłą niedzielÄ™ o] LT";case 3:return"[W zeszłą Å›rodÄ™ o] LT";case 6:return"[W zeszłą sobotÄ™ o] LT";default:return"[W zeszÅ‚y] dddd [o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"%s temu",s:"kilka sekund",m:t,mm:t,h:t,hh:t,d:"1 dzieÅ„",dd:"%d dni",M:"miesiÄ…c",MM:t,y:"rok",yy:t},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}});return s}(),e.fullCalendar.datepickerLocale("pl","pl",{closeText:"Zamknij",prevText:"<Poprzedni",nextText:"NastÄ™pny>",currentText:"DziÅ›",monthNames:["StyczeÅ„","Luty","Marzec","KwiecieÅ„","Maj","Czerwiec","Lipiec","SierpieÅ„","WrzesieÅ„","Październik","Listopad","GrudzieÅ„"],monthNamesShort:["Sty","Lu","Mar","Kw","Maj","Cze","Lip","Sie","Wrz","Pa","Lis","Gru"],dayNames:["Niedziela","PoniedziaÅ‚ek","Wtorek","Åšroda","Czwartek","PiÄ…tek","Sobota"],dayNamesShort:["Nie","Pn","Wt","Åšr","Czw","Pt","So"],dayNamesMin:["N","Pn","Wt","Åšr","Cz","Pt","So"],weekHeader:"Tydz",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("pl",{buttonText:{month:"MiesiÄ…c",week:"TydzieÅ„",day:"DzieÅ„",list:"Plan dnia"},allDayText:"CaÅ‚y dzieÅ„",eventLimitText:"wiÄ™cej",noEventsMessage:"Brak wydarzeÅ„ do wyÅ›wietlenia"})}(),function(){!function(){var e=a.defineLocale("pt",{months:"Janeiro_Fevereiro_Março_Abril_Maio_Junho_Julho_Agosto_Setembro_Outubro_Novembro_Dezembro".split("_"),monthsShort:"Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez".split("_"),weekdays:"Domingo_Segunda-Feira_Terça-Feira_Quarta-Feira_Quinta-Feira_Sexta-Feira_Sábado".split("_"),weekdaysShort:"Dom_Seg_Ter_Qua_Qui_Sex_Sáb".split("_"),weekdaysMin:"Dom_2ª_3ª_4ª_5ª_6ª_Sáb".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY HH:mm",LLLL:"dddd, D [de] MMMM [de] YYYY HH:mm"},calendar:{sameDay:"[Hoje à s] LT",nextDay:"[Amanhã à s] LT",nextWeek:"dddd [à s] LT",lastDay:"[Ontem à s] LT",lastWeek:function(){return 0===this.day()||6===this.day()?"[Último] dddd [à s] LT":"[Última] dddd [à s] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"há %s",s:"segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",M:"um mês",MM:"%d meses",y:"um ano",yy:"%d anos"},ordinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}});return e}(),e.fullCalendar.datepickerLocale("pt","pt",{closeText:"Fechar",prevText:"Anterior",nextText:"Seguinte",currentText:"Hoje",monthNames:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],monthNamesShort:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],dayNames:["Domingo","Segunda-feira","Terça-feira","Quarta-feira","Quinta-feira","Sexta-feira","Sábado"],dayNamesShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],dayNamesMin:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],weekHeader:"Sem",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("pt",{buttonText:{month:"Mês",week:"Semana",day:"Dia",list:"Agenda"},allDayText:"Todo o dia",eventLimitText:"mais",noEventsMessage:"Não há eventos para mostrar"})}(),function(){!function(){var e=a.defineLocale("pt-br",{months:"Janeiro_Fevereiro_Março_Abril_Maio_Junho_Julho_Agosto_Setembro_Outubro_Novembro_Dezembro".split("_"),monthsShort:"Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez".split("_"),weekdays:"Domingo_Segunda-feira_Terça-feira_Quarta-feira_Quinta-feira_Sexta-feira_Sábado".split("_"),weekdaysShort:"Dom_Seg_Ter_Qua_Qui_Sex_Sáb".split("_"),weekdaysMin:"Dom_2ª_3ª_4ª_5ª_6ª_Sáb".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY [à s] HH:mm",LLLL:"dddd, D [de] MMMM [de] YYYY [à s] HH:mm"},calendar:{sameDay:"[Hoje à s] LT",nextDay:"[Amanhã à s] LT",nextWeek:"dddd [à s] LT",lastDay:"[Ontem à s] LT",lastWeek:function(){return 0===this.day()||6===this.day()?"[Último] dddd [à s] LT":"[Última] dddd [à s] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"%s atrás",s:"poucos segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",M:"um mês",MM:"%d meses",y:"um ano",yy:"%d anos"},ordinalParse:/\d{1,2}º/,ordinal:"%dº"});return e}(),e.fullCalendar.datepickerLocale("pt-br","pt-BR",{closeText:"Fechar",prevText:"<Anterior",nextText:"Próximo>",currentText:"Hoje",monthNames:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],monthNamesShort:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],dayNames:["Domingo","Segunda-feira","Terça-feira","Quarta-feira","Quinta-feira","Sexta-feira","Sábado"],dayNamesShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],dayNamesMin:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("pt-br",{buttonText:{month:"Mês",week:"Semana",day:"Dia",list:"Compromissos"},allDayText:"dia inteiro",eventLimitText:function(e){return"mais +"+e},noEventsMessage:"Não há eventos para mostrar"})}(),function(){!function(){function e(e,a,t){var n={mm:"minute",hh:"ore",dd:"zile",MM:"luni",yy:"ani"},r=" ";return(e%100>=20||e>=100&&e%100===0)&&(r=" de "),e+r+n[t]}var t=a.defineLocale("ro",{months:"ianuarie_februarie_martie_aprilie_mai_iunie_iulie_august_septembrie_octombrie_noiembrie_decembrie".split("_"),monthsShort:"ian._febr._mart._apr._mai_iun._iul._aug._sept._oct._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"duminică_luni_marÈ›i_miercuri_joi_vineri_sâmbătă".split("_"),weekdaysShort:"Dum_Lun_Mar_Mie_Joi_Vin_Sâm".split("_"),weekdaysMin:"Du_Lu_Ma_Mi_Jo_Vi_Sâ".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[azi la] LT",nextDay:"[mâine la] LT",nextWeek:"dddd [la] LT",lastDay:"[ieri la] LT",lastWeek:"[fosta] dddd [la] LT",sameElse:"L"},relativeTime:{future:"peste %s",past:"%s în urmă",s:"câteva secunde",m:"un minut",mm:e,h:"o oră",hh:e,d:"o zi",dd:e,M:"o lună",MM:e,y:"un an",yy:e},week:{dow:1,doy:7}});return t}(),e.fullCalendar.datepickerLocale("ro","ro",{closeText:"ÃŽnchide",prevText:"« Luna precedentă",nextText:"Luna următoare »",currentText:"Azi",monthNames:["Ianuarie","Februarie","Martie","Aprilie","Mai","Iunie","Iulie","August","Septembrie","Octombrie","Noiembrie","Decembrie"],monthNamesShort:["Ian","Feb","Mar","Apr","Mai","Iun","Iul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Duminică","Luni","MarÅ£i","Miercuri","Joi","Vineri","Sâmbătă"],dayNamesShort:["Dum","Lun","Mar","Mie","Joi","Vin","Sâm"],dayNamesMin:["Du","Lu","Ma","Mi","Jo","Vi","Sâ"],weekHeader:"Săpt",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("ro",{buttonText:{prev:"precedentă",next:"următoare",month:"Lună",week:"Săptămână",day:"Zi",list:"Agendă"},allDayText:"Toată ziua",eventLimitText:function(e){return"+alte "+e},noEventsMessage:"Nu există evenimente de afiÈ™at"})}(),function(){!function(){function e(e,a){var t=e.split("_");return a%10===1&&a%100!==11?t[0]:a%10>=2&&a%10<=4&&(a%100<10||a%100>=20)?t[1]:t[2]}function t(a,t,n){var r={mm:t?"минута_минуты_минут":"минуту_минуты_минут",hh:"чаÑ_чаÑа_чаÑов",dd:"день_днÑ_дней",MM:"меÑÑц_меÑÑца_меÑÑцев",yy:"год_года_лет"};return"m"===n?t?"минута":"минуту":a+" "+e(r[n],+a)}var n=[/^Ñнв/i,/^фев/i,/^мар/i,/^апр/i,/^ма[йÑ]/i,/^июн/i,/^июл/i,/^авг/i,/^Ñен/i,/^окт/i,/^ноÑ/i,/^дек/i],r=a.defineLocale("ru",{months:{format:"ÑнварÑ_февралÑ_марта_апрелÑ_маÑ_июнÑ_июлÑ_авгуÑта_ÑентÑбрÑ_октÑбрÑ_ноÑбрÑ_декабрÑ".split("_"),standalone:"Ñнварь_февраль_март_апрель_май_июнь_июль_авгуÑÑ‚_ÑентÑбрь_октÑбрь_ноÑбрь_декабрь".split("_")},monthsShort:{format:"Ñнв._февр._мар._апр._маÑ_июнÑ_июлÑ_авг._Ñент._окт._ноÑб._дек.".split("_"),standalone:"Ñнв._февр._март_апр._май_июнь_июль_авг._Ñент._окт._ноÑб._дек.".split("_")},weekdays:{standalone:"воÑкреÑенье_понедельник_вторник_Ñреда_четверг_пÑтница_Ñуббота".split("_"),format:"воÑкреÑенье_понедельник_вторник_Ñреду_четверг_пÑтницу_Ñубботу".split("_"),isFormat:/\[ ?[Вв] ?(?:прошлую|Ñледующую|Ñту)? ?\] ?dddd/},weekdaysShort:"вÑ_пн_вт_ÑÑ€_чт_пт_Ñб".split("_"),weekdaysMin:"вÑ_пн_вт_ÑÑ€_чт_пт_Ñб".split("_"),monthsParse:n,longMonthsParse:n,shortMonthsParse:n,monthsRegex:/^(Ñнвар[ÑŒÑ]|Ñнв\.?|феврал[ÑŒÑ]|февр?\.?|марта?|мар\.?|апрел[ÑŒÑ]|апр\.?|ма[йÑ]|июн[ÑŒÑ]|июн\.?|июл[ÑŒÑ]|июл\.?|авгуÑта?|авг\.?|ÑентÑбр[ÑŒÑ]|Ñент?\.?|октÑбр[ÑŒÑ]|окт\.?|ноÑбр[ÑŒÑ]|ноÑб?\.?|декабр[ÑŒÑ]|дек\.?)/i,monthsShortRegex:/^(Ñнвар[ÑŒÑ]|Ñнв\.?|феврал[ÑŒÑ]|февр?\.?|марта?|мар\.?|апрел[ÑŒÑ]|апр\.?|ма[йÑ]|июн[ÑŒÑ]|июн\.?|июл[ÑŒÑ]|июл\.?|авгуÑта?|авг\.?|ÑентÑбр[ÑŒÑ]|Ñент?\.?|октÑбр[ÑŒÑ]|окт\.?|ноÑбр[ÑŒÑ]|ноÑб?\.?|декабр[ÑŒÑ]|дек\.?)/i,monthsStrictRegex:/^(Ñнвар[ÑÑŒ]|феврал[ÑÑŒ]|марта?|апрел[ÑÑŒ]|ма[Ñй]|июн[ÑÑŒ]|июл[ÑÑŒ]|авгуÑта?|ÑентÑбр[ÑÑŒ]|октÑбр[ÑÑŒ]|ноÑбр[ÑÑŒ]|декабр[ÑÑŒ])/i,monthsShortStrictRegex:/^(Ñнв\.|февр?\.|мар[Ñ‚.]|апр\.|ма[Ñй]|июн[ÑŒÑ.]|июл[ÑŒÑ.]|авг\.|Ñент?\.|окт\.|ноÑб?\.|дек\.)/i,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY г.",LLL:"D MMMM YYYY г., HH:mm",LLLL:"dddd, D MMMM YYYY г., HH:mm"},calendar:{sameDay:"[Ð¡ÐµÐ³Ð¾Ð´Ð½Ñ Ð²] LT",nextDay:"[Завтра в] LT",lastDay:"[Вчера в] LT",nextWeek:function(e){if(e.week()===this.week())return 2===this.day()?"[Во] dddd [в] LT":"[Ð’] dddd [в] LT";switch(this.day()){case 0:return"[Ð’ Ñледующее] dddd [в] LT";case 1:case 2:case 4:return"[Ð’ Ñледующий] dddd [в] LT";case 3:case 5:case 6:return"[Ð’ Ñледующую] dddd [в] LT"}},lastWeek:function(e){if(e.week()===this.week())return 2===this.day()?"[Во] dddd [в] LT":"[Ð’] dddd [в] LT";switch(this.day()){case 0:return"[Ð’ прошлое] dddd [в] LT";case 1:case 2:case 4:return"[Ð’ прошлый] dddd [в] LT";case 3:case 5:case 6:return"[Ð’ прошлую] dddd [в] LT"}},sameElse:"L"},relativeTime:{future:"через %s",past:"%s назад",s:"неÑколько Ñекунд",m:t,mm:t,h:"чаÑ",hh:t,d:"день",dd:t,M:"меÑÑц",MM:t,y:"год",yy:t},meridiemParse:/ночи|утра|днÑ|вечера/i,isPM:function(e){return/^(днÑ|вечера)$/.test(e)},meridiem:function(e,a,t){return e<4?"ночи":e<12?"утра":e<17?"днÑ":"вечера"},ordinalParse:/\d{1,2}-(й|го|Ñ)/,ordinal:function(e,a){switch(a){case"M":case"d":case"DDD":return e+"-й";case"D":return e+"-го";case"w":case"W":return e+"-Ñ";default:return e}},week:{dow:1,doy:7}});return r}(),e.fullCalendar.datepickerLocale("ru","ru",{closeText:"Закрыть",prevText:"<Пред",nextText:"След>",currentText:"СегоднÑ",monthNames:["Январь","Февраль","Март","Ðпрель","Май","Июнь","Июль","ÐвгуÑÑ‚","СентÑбрь","ОктÑбрь","ÐоÑбрь","Декабрь"],monthNamesShort:["Янв","Фев","Мар","Ðпр","Май","Июн","Июл","Ðвг","Сен","Окт","ÐоÑ","Дек"],dayNames:["воÑкреÑенье","понедельник","вторник","Ñреда","четверг","пÑтница","Ñуббота"],dayNamesShort:["вÑк","пнд","втр","Ñрд","чтв","птн","Ñбт"],dayNamesMin:["Ð’Ñ","Пн","Ð’Ñ‚","Ср","Чт","Пт","Сб"],weekHeader:"Ðед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("ru",{buttonText:{month:"МеÑÑц",week:"ÐеделÑ",day:"День",list:"ПовеÑтка днÑ"},allDayText:"ВеÑÑŒ день",eventLimitText:function(e){return"+ ещё "+e},noEventsMessage:"Ðет Ñобытий Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ"})}(),function(){!function(){function e(e){return e>1&&e<5}function t(a,t,n,r){var s=a+" ";switch(n){case"s":return t||r?"pár sekúnd":"pár sekundami";case"m":return t?"minúta":r?"minútu":"minútou";case"mm":return t||r?s+(e(a)?"minúty":"minút"):s+"minútami";case"h":return t?"hodina":r?"hodinu":"hodinou";case"hh":return t||r?s+(e(a)?"hodiny":"hodÃn"):s+"hodinami";case"d":return t||r?"deň":"dňom";case"dd":return t||r?s+(e(a)?"dni":"dnÃ"):s+"dňami";case"M":return t||r?"mesiac":"mesiacom";case"MM":return t||r?s+(e(a)?"mesiace":"mesiacov"):s+"mesiacmi";case"y":return t||r?"rok":"rokom";case"yy":return t||r?s+(e(a)?"roky":"rokov"):s+"rokmi"}}var n="január_február_marec_aprÃl_máj_jún_júl_august_september_október_november_december".split("_"),r="jan_feb_mar_apr_máj_jún_júl_aug_sep_okt_nov_dec".split("_"),s=a.defineLocale("sk",{months:n,monthsShort:r,weekdays:"nedeľa_pondelok_utorok_streda_Å¡tvrtok_piatok_sobota".split("_"),weekdaysShort:"ne_po_ut_st_Å¡t_pi_so".split("_"),weekdaysMin:"ne_po_ut_st_Å¡t_pi_so".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd D. MMMM YYYY H:mm"},calendar:{sameDay:"[dnes o] LT",nextDay:"[zajtra o] LT",nextWeek:function(){switch(this.day()){case 0:return"[v nedeľu o] LT";case 1:case 2:return"[v] dddd [o] LT";case 3:return"[v stredu o] LT";case 4:return"[vo Å¡tvrtok o] LT";case 5:return"[v piatok o] LT";case 6:return"[v sobotu o] LT"}},lastDay:"[vÄera o] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulú nedeľu o] LT";case 1:case 2:return"[minulý] dddd [o] LT";case 3:return"[minulú stredu o] LT";case 4:case 5:return"[minulý] dddd [o] LT";case 6:return"[minulú sobotu o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"pred %s",s:t,m:t,mm:t,h:t,hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}});return s}(),e.fullCalendar.datepickerLocale("sk","sk",{closeText:"ZavrieÅ¥",prevText:"<Predchádzajúci",nextText:"Nasledujúci>",currentText:"Dnes",monthNames:["január","február","marec","aprÃl","máj","jún","júl","august","september","október","november","december"],monthNamesShort:["Jan","Feb","Mar","Apr","Máj","Jún","Júl","Aug","Sep","Okt","Nov","Dec"],dayNames:["nedeľa","pondelok","utorok","streda","Å¡tvrtok","piatok","sobota"],dayNamesShort:["Ned","Pon","Uto","Str","Å tv","Pia","Sob"],dayNamesMin:["Ne","Po","Ut","St","Å t","Pia","So"],weekHeader:"Ty",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("sk",{buttonText:{month:"Mesiac",week:"Týždeň",day:"Deň",list:"Rozvrh"},allDayText:"Celý deň",eventLimitText:function(e){return"+ÄalÅ¡ie: "+e},noEventsMessage:"Žiadne akcie na zobrazenie"})}(),function(){!function(){function e(e,a,t,n){var r=e+" ";switch(t){case"s":return a||n?"nekaj sekund":"nekaj sekundami";case"m":return a?"ena minuta":"eno minuto";case"mm":return r+=1===e?a?"minuta":"minuto":2===e?a||n?"minuti":"minutama":e<5?a||n?"minute":"minutami":a||n?"minut":"minutami";case"h":return a?"ena ura":"eno uro";case"hh":return r+=1===e?a?"ura":"uro":2===e?a||n?"uri":"urama":e<5?a||n?"ure":"urami":a||n?"ur":"urami";case"d":return a||n?"en dan":"enim dnem";case"dd":return r+=1===e?a||n?"dan":"dnem":2===e?a||n?"dni":"dnevoma":a||n?"dni":"dnevi";case"M":return a||n?"en mesec":"enim mesecem";case"MM":return r+=1===e?a||n?"mesec":"mesecem":2===e?a||n?"meseca":"mesecema":e<5?a||n?"mesece":"meseci":a||n?"mesecev":"meseci";case"y":return a||n?"eno leto":"enim letom";case"yy":return r+=1===e?a||n?"leto":"letom":2===e?a||n?"leti":"letoma":e<5?a||n?"leta":"leti":a||n?"let":"leti"}}var t=a.defineLocale("sl",{months:"januar_februar_marec_april_maj_junij_julij_avgust_september_oktober_november_december".split("_"),monthsShort:"jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"nedelja_ponedeljek_torek_sreda_Äetrtek_petek_sobota".split("_"),weekdaysShort:"ned._pon._tor._sre._Äet._pet._sob.".split("_"),weekdaysMin:"ne_po_to_sr_Äe_pe_so".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danes ob] LT",nextDay:"[jutri ob] LT",nextWeek:function(){switch(this.day()){case 0:return"[v] [nedeljo] [ob] LT";case 3:return"[v] [sredo] [ob] LT";case 6:return"[v] [soboto] [ob] LT";case 1:case 2:case 4:case 5:return"[v] dddd [ob] LT"}},lastDay:"[vÄeraj ob] LT",lastWeek:function(){switch(this.day()){case 0:return"[prejÅ¡njo] [nedeljo] [ob] LT";case 3:return"[prejÅ¡njo] [sredo] [ob] LT";case 6:return"[prejÅ¡njo] [soboto] [ob] LT";case 1:case 2:case 4:case 5:return"[prejÅ¡nji] dddd [ob] LT"}},sameElse:"L"},relativeTime:{future:"Äez %s",past:"pred %s",s:e,m:e,mm:e,h:e,hh:e,d:e,dd:e,M:e,MM:e,y:e,yy:e},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}});return t}(),e.fullCalendar.datepickerLocale("sl","sl",{closeText:"Zapri",prevText:"<PrejÅ¡nji",nextText:"Naslednji>",currentText:"Trenutni",monthNames:["Januar","Februar","Marec","April","Maj","Junij","Julij","Avgust","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Avg","Sep","Okt","Nov","Dec"],dayNames:["Nedelja","Ponedeljek","Torek","Sreda","ÄŒetrtek","Petek","Sobota"],dayNamesShort:["Ned","Pon","Tor","Sre","ÄŒet","Pet","Sob"],dayNamesMin:["Ne","Po","To","Sr","ÄŒe","Pe","So"],weekHeader:"Teden",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("sl",{buttonText:{month:"Mesec",week:"Teden",day:"Dan",list:"Dnevni red"},allDayText:"Ves dan",eventLimitText:"veÄ",noEventsMessage:"Ni dogodkov za prikaz"})}(),function(){!function(){var e={words:{m:["jedan minut","jedne minute"],mm:["minut","minute","minuta"],h:["jedan sat","jednog sata"],hh:["sat","sata","sati"],dd:["dan","dana","dana"],MM:["mesec","meseca","meseci"],yy:["godina","godine","godina"]},correctGrammaticalCase:function(e,a){return 1===e?a[0]:e>=2&&e<=4?a[1]:a[2]},translate:function(a,t,n){var r=e.words[n];return 1===n.length?t?r[0]:r[1]:a+" "+e.correctGrammaticalCase(a,r)}},t=a.defineLocale("sr",{months:"januar_februar_mart_april_maj_jun_jul_avgust_septembar_oktobar_novembar_decembar".split("_"),monthsShort:"jan._feb._mar._apr._maj_jun_jul_avg._sep._okt._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"nedelja_ponedeljak_utorak_sreda_Äetvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sre._Äet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_Äe_pe_su".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedelju] [u] LT";case 3:return"[u] [sredu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[juÄe u] LT",lastWeek:function(){var e=["[proÅ¡le] [nedelje] [u] LT","[proÅ¡log] [ponedeljka] [u] LT","[proÅ¡log] [utorka] [u] LT","[proÅ¡le] [srede] [u] LT","[proÅ¡log] [Äetvrtka] [u] LT","[proÅ¡log] [petka] [u] LT","[proÅ¡le] [subote] [u] LT"];return e[this.day()]},sameElse:"L"},relativeTime:{future:"za %s",past:"pre %s",s:"nekoliko sekundi",m:e.translate,mm:e.translate,h:e.translate,hh:e.translate,d:"dan",dd:e.translate,M:"mesec",MM:e.translate,y:"godinu",yy:e.translate},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}});return t}(),e.fullCalendar.datepickerLocale("sr","sr",{closeText:"Затвори",prevText:"<",nextText:">",currentText:"ДанаÑ",monthNames:["Јануар","Фебруар","Март","Ðприл","Мај","Јун","Јул","ÐвгуÑÑ‚","Септембар","Октобар","Ðовембар","Децембар"],monthNamesShort:["Јан","Феб","Мар","Ðпр","Мај","Јун","Јул","Ðвг","Сеп","Окт","Ðов","Дец"],dayNames:["Ðедеља","Понедељак","Уторак","Среда","Четвртак","Петак","Субота"],dayNamesShort:["Ðед","Пон","Уто","Сре","Чет","Пет","Суб"],dayNamesMin:["Ðе","По","Ут","Ср","Че","Пе","Су"],weekHeader:"Сед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("sr",{buttonText:{month:"МеÑец",week:"Ðедеља",day:"Дан",list:"Планер"},allDayText:"Цео дан",eventLimitText:function(e){return"+ још "+e},noEventsMessage:"Ðема догађаја за приказ"})}(),function(){!function(){var e={words:{m:["један минут","једне минуте"],mm:["минут","минуте","минута"],h:["један Ñат","једног Ñата"],hh:["Ñат","Ñата","Ñати"],dd:["дан","дана","дана"],MM:["меÑец","меÑеца","меÑеци"],yy:["година","године","година"]},correctGrammaticalCase:function(e,a){return 1===e?a[0]:e>=2&&e<=4?a[1]:a[2]},translate:function(a,t,n){var r=e.words[n];return 1===n.length?t?r[0]:r[1]:a+" "+e.correctGrammaticalCase(a,r)}},t=a.defineLocale("sr-cyrl",{months:"јануар_фебруар_март_април_мај_јун_јул_авгуÑÑ‚_Ñептембар_октобар_новембар_децембар".split("_"),monthsShort:"јан._феб._мар._апр._мај_јун_јул_авг._Ñеп._окт._нов._дец.".split("_"),monthsParseExact:!0,weekdays:"недеља_понедељак_уторак_Ñреда_четвртак_петак_Ñубота".split("_"),weekdaysShort:"нед._пон._уто._Ñре._чет._пет._Ñуб.".split("_"),weekdaysMin:"не_по_ут_ÑÑ€_че_пе_Ñу".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[Ð´Ð°Ð½Ð°Ñ Ñƒ] LT",nextDay:"[Ñутра у] LT",nextWeek:function(){switch(this.day()){case 0:return"[у] [недељу] [у] LT";case 3:return"[у] [Ñреду] [у] LT";case 6:return"[у] [Ñуботу] [у] LT";case 1:case 2:case 4:case 5:return"[у] dddd [у] LT"}},lastDay:"[јуче у] LT",lastWeek:function(){var e=["[прошле] [недеље] [у] LT","[прошлог] [понедељка] [у] LT","[прошлог] [уторка] [у] LT","[прошле] [Ñреде] [у] LT","[прошлог] [четвртка] [у] LT","[прошлог] [петка] [у] LT","[прошле] [Ñуботе] [у] LT"];return e[this.day()]},sameElse:"L"},relativeTime:{future:"за %s",past:"пре %s",s:"неколико Ñекунди",m:e.translate,mm:e.translate,h:e.translate,hh:e.translate,d:"дан",dd:e.translate,M:"меÑец",MM:e.translate,y:"годину",yy:e.translate},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}});return t}(),e.fullCalendar.datepickerLocale("sr-cyrl","sr",{closeText:"Затвори",prevText:"<",nextText:">",currentText:"ДанаÑ",monthNames:["Јануар","Фебруар","Март","Ðприл","Мај","Јун","Јул","ÐвгуÑÑ‚","Септембар","Октобар","Ðовембар","Децембар"],monthNamesShort:["Јан","Феб","Мар","Ðпр","Мај","Јун","Јул","Ðвг","Сеп","Окт","Ðов","Дец"],dayNames:["Ðедеља","Понедељак","Уторак","Среда","Четвртак","Петак","Субота"],dayNamesShort:["Ðед","Пон","Уто","Сре","Чет","Пет","Суб"],dayNamesMin:["Ðе","По","Ут","Ср","Че","Пе","Су"],weekHeader:"Сед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("sr-cyrl",{buttonText:{month:"МеÑец",week:"Ðедеља",day:"Дан",list:"Планер"},allDayText:"Цео дан",eventLimitText:function(e){return"+ још "+e},noEventsMessage:"Ðема догађаја за приказ"})}(),function(){!function(){var e=a.defineLocale("sv",{months:"januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"söndag_mÃ¥ndag_tisdag_onsdag_torsdag_fredag_lördag".split("_"),weekdaysShort:"sön_mÃ¥n_tis_ons_tor_fre_lör".split("_"),weekdaysMin:"sö_mÃ¥_ti_on_to_fr_lö".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [kl.] HH:mm",LLLL:"dddd D MMMM YYYY [kl.] HH:mm",lll:"D MMM YYYY HH:mm",llll:"ddd D MMM YYYY HH:mm"},calendar:{sameDay:"[Idag] LT",nextDay:"[Imorgon] LT",lastDay:"[IgÃ¥r] LT",nextWeek:"[PÃ¥] dddd LT",lastWeek:"[I] dddd[s] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"för %s sedan",s:"nÃ¥gra sekunder",m:"en minut",mm:"%d minuter",h:"en timme",hh:"%d timmar",d:"en dag",dd:"%d dagar",M:"en mÃ¥nad",MM:"%d mÃ¥nader",y:"ett Ã¥r",yy:"%d Ã¥r"},ordinalParse:/\d{1,2}(e|a)/,ordinal:function(e){var a=e%10,t=1===~~(e%100/10)?"e":1===a?"a":2===a?"a":"e";return e+t},week:{dow:1,doy:4}});return e}(),e.fullCalendar.datepickerLocale("sv","sv",{closeText:"Stäng",prevText:"«Förra",nextText:"Nästa»",currentText:"Idag",monthNames:["Januari","Februari","Mars","April","Maj","Juni","Juli","Augusti","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],dayNamesShort:["Sön","MÃ¥n","Tis","Ons","Tor","Fre","Lör"],dayNames:["Söndag","MÃ¥ndag","Tisdag","Onsdag","Torsdag","Fredag","Lördag"],dayNamesMin:["Sö","MÃ¥","Ti","On","To","Fr","Lö"],weekHeader:"Ve",dateFormat:"yy-mm-dd",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("sv",{buttonText:{month:"MÃ¥nad",week:"Vecka",day:"Dag",list:"Program"},allDayText:"Heldag",eventLimitText:"till",noEventsMessage:"Inga händelser att visa"})}(),function(){!function(){var e=a.defineLocale("th",{months:"มà¸à¸£à¸²à¸„ม_à¸à¸¸à¸¡à¸ าพันธ์_มีนาคม_เมษายน_พฤษภาคม_มิถุนายน_à¸à¸£à¸à¸Žà¸²à¸„ม_สิงหาคม_à¸à¸±à¸™à¸¢à¸²à¸¢à¸™_ตุลาคม_พฤศจิà¸à¸²à¸¢à¸™_ธันวาคม".split("_"),monthsShort:"ม.ค._à¸.พ._มี.ค._เม.ย._พ.ค._มิ.ย._à¸.ค._ส.ค._à¸.ย._ต.ค._พ.ย._ธ.ค.".split("_"),monthsParseExact:!0,weekdays:"à¸à¸²à¸—ิตย์_จันทร์_à¸à¸±à¸‡à¸„าร_พุธ_พฤหัสบดี_ศุà¸à¸£à¹Œ_เสาร์".split("_"),weekdaysShort:"à¸à¸²à¸—ิตย์_จันทร์_à¸à¸±à¸‡à¸„าร_พุธ_พฤหัส_ศุà¸à¸£à¹Œ_เสาร์".split("_"),weekdaysMin:"à¸à¸²._จ._à¸._พ._พฤ._ศ._ส.".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"YYYY/MM/DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY เวลา H:mm",LLLL:"วันddddที่ D MMMM YYYY เวลา H:mm"},meridiemParse:/à¸à¹ˆà¸à¸™à¹€à¸—ี่ยง|หลังเที่ยง/,isPM:function(e){return"หลังเที่ยง"===e},meridiem:function(e,a,t){return e<12?"à¸à¹ˆà¸à¸™à¹€à¸—ี่ยง":"หลังเที่ยง"},calendar:{sameDay:"[วันนี้ เวลา] LT",nextDay:"[พรุ่งนี้ เวลา] LT",nextWeek:"dddd[หน้า เวลา] LT",lastDay:"[เมื่à¸à¸§à¸²à¸™à¸™à¸µà¹‰ เวลา] LT",lastWeek:"[วัน]dddd[ที่à¹à¸¥à¹‰à¸§ เวลา] LT",sameElse:"L"},relativeTime:{future:"à¸à¸µà¸ %s",past:"%sที่à¹à¸¥à¹‰à¸§",s:"ไม่à¸à¸µà¹ˆà¸§à¸´à¸™à¸²à¸—ี",m:"1 นาที",mm:"%d นาที",h:"1 ชั่วโมง",hh:"%d ชั่วโมง",d:"1 วัน",dd:"%d วัน",M:"1 เดืà¸à¸™",MM:"%d เดืà¸à¸™",y:"1 ปี",yy:"%d ปี" -}});return e}(),e.fullCalendar.datepickerLocale("th","th",{closeText:"ปิด",prevText:"« à¸¢à¹‰à¸à¸™",nextText:"ถัดไป »",currentText:"วันนี้",monthNames:["มà¸à¸£à¸²à¸„ม","à¸à¸¸à¸¡à¸ าพันธ์","มีนาคม","เมษายน","พฤษภาคม","มิถุนายน","à¸à¸£à¸à¸Žà¸²à¸„ม","สิงหาคม","à¸à¸±à¸™à¸¢à¸²à¸¢à¸™","ตุลาคม","พฤศจิà¸à¸²à¸¢à¸™","ธันวาคม"],monthNamesShort:["ม.ค.","à¸.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.","à¸.ค.","ส.ค.","à¸.ย.","ต.ค.","พ.ย.","ธ.ค."],dayNames:["à¸à¸²à¸—ิตย์","จันทร์","à¸à¸±à¸‡à¸„าร","พุธ","พฤหัสบดี","ศุà¸à¸£à¹Œ","เสาร์"],dayNamesShort:["à¸à¸².","จ.","à¸.","พ.","พฤ.","ศ.","ส."],dayNamesMin:["à¸à¸².","จ.","à¸.","พ.","พฤ.","ศ.","ส."],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("th",{buttonText:{month:"เดืà¸à¸™",week:"สัปดาห์",day:"วัน",list:"à¹à¸œà¸™à¸‡à¸²à¸™"},allDayText:"ตลà¸à¸”วัน",eventLimitText:"เพิ่มเติม",noEventsMessage:"ไม่มีà¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¸—ี่จะà¹à¸ªà¸”ง"})}(),function(){!function(){var e={1:"'inci",5:"'inci",8:"'inci",70:"'inci",80:"'inci",2:"'nci",7:"'nci",20:"'nci",50:"'nci",3:"'üncü",4:"'üncü",100:"'üncü",6:"'ncı",9:"'uncu",10:"'uncu",30:"'uncu",60:"'ıncı",90:"'ıncı"},t=a.defineLocale("tr",{months:"Ocak_Åžubat_Mart_Nisan_Mayıs_Haziran_Temmuz_AÄŸustos_Eylül_Ekim_Kasım_Aralık".split("_"),monthsShort:"Oca_Åžub_Mar_Nis_May_Haz_Tem_AÄŸu_Eyl_Eki_Kas_Ara".split("_"),weekdays:"Pazar_Pazartesi_Salı_ÇarÅŸamba_PerÅŸembe_Cuma_Cumartesi".split("_"),weekdaysShort:"Paz_Pts_Sal_Çar_Per_Cum_Cts".split("_"),weekdaysMin:"Pz_Pt_Sa_Ça_Pe_Cu_Ct".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[bugün saat] LT",nextDay:"[yarın saat] LT",nextWeek:"[haftaya] dddd [saat] LT",lastDay:"[dün] LT",lastWeek:"[geçen hafta] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s sonra",past:"%s önce",s:"birkaç saniye",m:"bir dakika",mm:"%d dakika",h:"bir saat",hh:"%d saat",d:"bir gün",dd:"%d gün",M:"bir ay",MM:"%d ay",y:"bir yıl",yy:"%d yıl"},ordinalParse:/\d{1,2}'(inci|nci|üncü|ncı|uncu|ıncı)/,ordinal:function(a){if(0===a)return a+"'ıncı";var t=a%10,n=a%100-t,r=a>=100?100:null;return a+(e[t]||e[n]||e[r])},week:{dow:1,doy:7}});return t}(),e.fullCalendar.datepickerLocale("tr","tr",{closeText:"kapat",prevText:"<geri",nextText:"ileri>",currentText:"bugün",monthNames:["Ocak","Åžubat","Mart","Nisan","Mayıs","Haziran","Temmuz","AÄŸustos","Eylül","Ekim","Kasım","Aralık"],monthNamesShort:["Oca","Åžub","Mar","Nis","May","Haz","Tem","AÄŸu","Eyl","Eki","Kas","Ara"],dayNames:["Pazar","Pazartesi","Salı","ÇarÅŸamba","PerÅŸembe","Cuma","Cumartesi"],dayNamesShort:["Pz","Pt","Sa","Ça","Pe","Cu","Ct"],dayNamesMin:["Pz","Pt","Sa","Ça","Pe","Cu","Ct"],weekHeader:"Hf",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("tr",{buttonText:{next:"ileri",month:"Ay",week:"Hafta",day:"Gün",list:"Ajanda"},allDayText:"Tüm gün",eventLimitText:"daha fazla",noEventsMessage:"Herhangi bir etkinlik görüntülemek için"})}(),function(){!function(){function e(e,a){var t=e.split("_");return a%10===1&&a%100!==11?t[0]:a%10>=2&&a%10<=4&&(a%100<10||a%100>=20)?t[1]:t[2]}function t(a,t,n){var r={mm:t?"хвилина_хвилини_хвилин":"хвилину_хвилини_хвилин",hh:t?"година_години_годин":"годину_години_годин",dd:"день_дні_днів",MM:"міÑÑць_міÑÑці_міÑÑців",yy:"рік_роки_років"};return"m"===n?t?"хвилина":"хвилину":"h"===n?t?"година":"годину":a+" "+e(r[n],+a)}function n(e,a){var t={nominative:"неділÑ_понеділок_вівторок_Ñереда_четвер_п’ÑтницÑ_Ñубота".split("_"),accusative:"неділю_понеділок_вівторок_Ñереду_четвер_п’Ñтницю_Ñуботу".split("_"),genitive:"неділі_понеділка_вівторка_Ñереди_четверга_п’Ñтниці_Ñуботи".split("_")},n=/(\[[ВвУу]\]) ?dddd/.test(a)?"accusative":/\[?(?:минулої|наÑтупної)? ?\] ?dddd/.test(a)?"genitive":"nominative";return t[n][e.day()]}function r(e){return function(){return e+"о"+(11===this.hours()?"б":"")+"] LT"}}var s=a.defineLocale("uk",{months:{format:"ÑічнÑ_лютого_березнÑ_квітнÑ_травнÑ_червнÑ_липнÑ_ÑерпнÑ_вереÑнÑ_жовтнÑ_лиÑтопада_груднÑ".split("_"),standalone:"Ñічень_лютий_березень_квітень_травень_червень_липень_Ñерпень_вереÑень_жовтень_лиÑтопад_грудень".split("_")},monthsShort:"Ñіч_лют_бер_квіт_трав_черв_лип_Ñерп_вер_жовт_лиÑÑ‚_груд".split("_"),weekdays:n,weekdaysShort:"нд_пн_вт_ÑÑ€_чт_пт_Ñб".split("_"),weekdaysMin:"нд_пн_вт_ÑÑ€_чт_пт_Ñб".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY Ñ€.",LLL:"D MMMM YYYY Ñ€., HH:mm",LLLL:"dddd, D MMMM YYYY Ñ€., HH:mm"},calendar:{sameDay:r("[Сьогодні "),nextDay:r("[Завтра "),lastDay:r("[Вчора "),nextWeek:r("[У] dddd ["),lastWeek:function(){switch(this.day()){case 0:case 3:case 5:case 6:return r("[Минулої] dddd [").call(this);case 1:case 2:case 4:return r("[Минулого] dddd [").call(this)}},sameElse:"L"},relativeTime:{future:"за %s",past:"%s тому",s:"декілька Ñекунд",m:t,mm:t,h:"годину",hh:t,d:"день",dd:t,M:"міÑÑць",MM:t,y:"рік",yy:t},meridiemParse:/ночі|ранку|днÑ|вечора/,isPM:function(e){return/^(днÑ|вечора)$/.test(e)},meridiem:function(e,a,t){return e<4?"ночі":e<12?"ранку":e<17?"днÑ":"вечора"},ordinalParse:/\d{1,2}-(й|го)/,ordinal:function(e,a){switch(a){case"M":case"d":case"DDD":case"w":case"W":return e+"-й";case"D":return e+"-го";default:return e}},week:{dow:1,doy:7}});return s}(),e.fullCalendar.datepickerLocale("uk","uk",{closeText:"Закрити",prevText:"<",nextText:">",currentText:"Сьогодні",monthNames:["Січень","Лютий","Березень","Квітень","Травень","Червень","Липень","Серпень","ВереÑень","Жовтень","ЛиÑтопад","Грудень"],monthNamesShort:["Січ","Лют","Бер","Кві","Тра","Чер","Лип","Сер","Вер","Жов","ЛиÑ","Гру"],dayNames:["неділÑ","понеділок","вівторок","Ñереда","четвер","п’ÑтницÑ","Ñубота"],dayNamesShort:["нед","пнд","вів","Ñрд","чтв","птн","Ñбт"],dayNamesMin:["Ðд","Пн","Ð’Ñ‚","Ср","Чт","Пт","Сб"],weekHeader:"Тиж",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("uk",{buttonText:{month:"МіÑÑць",week:"Тиждень",day:"День",list:"ПорÑдок денний"},allDayText:"УвеÑÑŒ день",eventLimitText:function(e){return"+ще "+e+"..."},noEventsMessage:"Ðемає подій Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ"})}(),function(){!function(){var e=a.defineLocale("vi",{months:"tháng 1_tháng 2_tháng 3_tháng 4_tháng 5_tháng 6_tháng 7_tháng 8_tháng 9_tháng 10_tháng 11_tháng 12".split("_"),monthsShort:"Th01_Th02_Th03_Th04_Th05_Th06_Th07_Th08_Th09_Th10_Th11_Th12".split("_"),monthsParseExact:!0,weekdays:"chá»§ nháºt_thứ hai_thứ ba_thứ tư_thứ năm_thứ sáu_thứ bảy".split("_"),weekdaysShort:"CN_T2_T3_T4_T5_T6_T7".split("_"),weekdaysMin:"CN_T2_T3_T4_T5_T6_T7".split("_"),weekdaysParseExact:!0,meridiemParse:/sa|ch/i,isPM:function(e){return/^ch$/i.test(e)},meridiem:function(e,a,t){return e<12?t?"sa":"SA":t?"ch":"CH"},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM [năm] YYYY",LLL:"D MMMM [năm] YYYY HH:mm",LLLL:"dddd, D MMMM [năm] YYYY HH:mm",l:"DD/M/YYYY",ll:"D MMM YYYY",lll:"D MMM YYYY HH:mm",llll:"ddd, D MMM YYYY HH:mm"},calendar:{sameDay:"[Hôm nay lúc] LT",nextDay:"[Ngà y mai lúc] LT",nextWeek:"dddd [tuần tá»›i lúc] LT",lastDay:"[Hôm qua lúc] LT",lastWeek:"dddd [tuần rồi lúc] LT",sameElse:"L"},relativeTime:{future:"%s tá»›i",past:"%s trước",s:"và i giây",m:"má»™t phút",mm:"%d phút",h:"má»™t giá»",hh:"%d giá»",d:"má»™t ngà y",dd:"%d ngà y",M:"má»™t tháng",MM:"%d tháng",y:"má»™t năm",yy:"%d năm"},ordinalParse:/\d{1,2}/,ordinal:function(e){return e},week:{dow:1,doy:4}});return e}(),e.fullCalendar.datepickerLocale("vi","vi",{closeText:"Äóng",prevText:"<Trước",nextText:"Tiếp>",currentText:"Hôm nay",monthNames:["Tháng Má»™t","Tháng Hai","Tháng Ba","Tháng Tư","Tháng Năm","Tháng Sáu","Tháng Bảy","Tháng Tám","Tháng ChÃn","Tháng Mưá»i","Tháng Mưá»i Má»™t","Tháng Mưá»i Hai"],monthNamesShort:["Tháng 1","Tháng 2","Tháng 3","Tháng 4","Tháng 5","Tháng 6","Tháng 7","Tháng 8","Tháng 9","Tháng 10","Tháng 11","Tháng 12"],dayNames:["Chá»§ Nháºt","Thứ Hai","Thứ Ba","Thứ Tư","Thứ Năm","Thứ Sáu","Thứ Bảy"],dayNamesShort:["CN","T2","T3","T4","T5","T6","T7"],dayNamesMin:["CN","T2","T3","T4","T5","T6","T7"],weekHeader:"Tu",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.locale("vi",{buttonText:{month:"Tháng",week:"Tuần",day:"Ngà y",list:"Lịch biểu"},allDayText:"Cả ngà y",eventLimitText:function(e){return"+ thêm "+e},noEventsMessage:"Không có sá»± kiện để hiển thị"})}(),function(){!function(){var e=a.defineLocale("zh-cn",{months:"一月_二月_三月_四月_五月_å…æœˆ_七月_八月_乿œˆ_åæœˆ_å一月_å二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期å…".split("_"),weekdaysShort:"周日_周一_周二_周三_周四_周五_周å…".split("_"),weekdaysMin:"æ—¥_一_二_三_å››_五_å…".split("_"),longDateFormat:{LT:"Ah点mm分",LTS:"Ah点m分sç§’",L:"YYYY-MM-DD",LL:"YYYYå¹´MMMDæ—¥",LLL:"YYYYå¹´MMMDæ—¥Ah点mm分",LLLL:"YYYYå¹´MMMDæ—¥ddddAh点mm分",l:"YYYY-MM-DD",ll:"YYYYå¹´MMMDæ—¥",lll:"YYYYå¹´MMMDæ—¥Ah点mm分",llll:"YYYYå¹´MMMDæ—¥ddddAh点mm分"},meridiemParse:/凌晨|早上|上åˆ|ä¸åˆ|下åˆ|晚上/,meridiemHour:function(e,a){return 12===e&&(e=0),"凌晨"===a||"早上"===a||"上åˆ"===a?e:"下åˆ"===a||"晚上"===a?e+12:e>=11?e:e+12},meridiem:function(e,a,t){var n=100*e+a;return n<600?"凌晨":n<900?"早上":n<1130?"上åˆ":n<1230?"ä¸åˆ":n<1800?"下åˆ":"晚上"},calendar:{sameDay:function(){return 0===this.minutes()?"[今天]Ah[点整]":"[今天]LT"},nextDay:function(){return 0===this.minutes()?"[明天]Ah[点整]":"[明天]LT"},lastDay:function(){return 0===this.minutes()?"[昨天]Ah[点整]":"[昨天]LT"},nextWeek:function(){var e,t;return e=a().startOf("week"),t=this.diff(e,"days")>=7?"[下]":"[本]",0===this.minutes()?t+"dddAh点整":t+"dddAh点mm"},lastWeek:function(){var e,t;return e=a().startOf("week"),t=this.unix()<e.unix()?"[上]":"[本]",0===this.minutes()?t+"dddAh点整":t+"dddAh点mm"},sameElse:"LL"},ordinalParse:/\d{1,2}(æ—¥|月|周)/,ordinal:function(e,a){switch(a){case"d":case"D":case"DDD":return e+"æ—¥";case"M":return e+"月";case"w":case"W":return e+"周";default:return e}},relativeTime:{future:"%s内",past:"%så‰",s:"å‡ ç§’",m:"1 分钟",mm:"%d 分钟",h:"1 å°æ—¶",hh:"%d å°æ—¶",d:"1 天",dd:"%d 天",M:"1 个月",MM:"%d 个月",y:"1 å¹´",yy:"%d å¹´"},week:{dow:1,doy:4}});return e}(),e.fullCalendar.datepickerLocale("zh-cn","zh-CN",{closeText:"å…³é—",prevText:"<上月",nextText:"下月>",currentText:"今天",monthNames:["一月","二月","三月","四月","五月","å…æœˆ","七月","八月","乿œˆ","åæœˆ","å一月","å二月"],monthNamesShort:["一月","二月","三月","四月","五月","å…æœˆ","七月","八月","乿œˆ","åæœˆ","å一月","å二月"],dayNames:["星期日","星期一","星期二","星期三","星期四","星期五","星期å…"],dayNamesShort:["周日","周一","周二","周三","周四","周五","周å…"],dayNamesMin:["æ—¥","一","二","三","å››","五","å…"],weekHeader:"周",dateFormat:"yy-mm-dd",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"å¹´"}),e.fullCalendar.locale("zh-cn",{buttonText:{month:"月",week:"周",day:"æ—¥",list:"日程"},allDayText:"全天",eventLimitText:function(e){return"å¦å¤– "+e+" 个"},noEventsMessage:"没有事件显示"})}(),function(){!function(){var e=a.defineLocale("zh-tw",{months:"一月_二月_三月_四月_五月_å…æœˆ_七月_八月_乿œˆ_åæœˆ_å一月_å二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期å…".split("_"),weekdaysShort:"週日_週一_週二_週三_週四_週五_週å…".split("_"),weekdaysMin:"æ—¥_一_二_三_å››_五_å…".split("_"),longDateFormat:{LT:"Ah點mm分",LTS:"Ah點m分sç§’",L:"YYYYå¹´MMMDæ—¥",LL:"YYYYå¹´MMMDæ—¥",LLL:"YYYYå¹´MMMDæ—¥Ah點mm分",LLLL:"YYYYå¹´MMMDæ—¥ddddAh點mm分",l:"YYYYå¹´MMMDæ—¥",ll:"YYYYå¹´MMMDæ—¥",lll:"YYYYå¹´MMMDæ—¥Ah點mm分",llll:"YYYYå¹´MMMDæ—¥ddddAh點mm分"},meridiemParse:/凌晨|早上|上åˆ|ä¸åˆ|下åˆ|晚上/,meridiemHour:function(e,a){return 12===e&&(e=0),"凌晨"===a||"早上"===a||"上åˆ"===a?e:"ä¸åˆ"===a?e>=11?e:e+12:"下åˆ"===a||"晚上"===a?e+12:void 0},meridiem:function(e,a,t){var n=100*e+a;return n<600?"凌晨":n<900?"早上":n<1130?"上åˆ":n<1230?"ä¸åˆ":n<1800?"下åˆ":"晚上"},calendar:{sameDay:"[今天]LT",nextDay:"[明天]LT",nextWeek:"[下]ddddLT",lastDay:"[昨天]LT",lastWeek:"[上]ddddLT",sameElse:"L"},ordinalParse:/\d{1,2}(æ—¥|月|週)/,ordinal:function(e,a){switch(a){case"d":case"D":case"DDD":return e+"æ—¥";case"M":return e+"月";case"w":case"W":return e+"週";default:return e}},relativeTime:{future:"%så…§",past:"%så‰",s:"幾秒",m:"1 分é˜",mm:"%d 分é˜",h:"1 å°æ™‚",hh:"%d å°æ™‚",d:"1 天",dd:"%d 天",M:"1 個月",MM:"%d 個月",y:"1 å¹´",yy:"%d å¹´"}});return e}(),e.fullCalendar.datepickerLocale("zh-tw","zh-TW",{closeText:"關閉",prevText:"<上月",nextText:"下月>",currentText:"今天",monthNames:["一月","二月","三月","四月","五月","å…æœˆ","七月","八月","乿œˆ","åæœˆ","å一月","å二月"],monthNamesShort:["一月","二月","三月","四月","五月","å…æœˆ","七月","八月","乿œˆ","åæœˆ","å一月","å二月"],dayNames:["星期日","星期一","星期二","星期三","星期四","星期五","星期å…"],dayNamesShort:["周日","周一","周二","周三","周四","周五","周å…"],dayNamesMin:["æ—¥","一","二","三","å››","五","å…"],weekHeader:"周",dateFormat:"yy/mm/dd",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"å¹´"}),e.fullCalendar.locale("zh-tw",{buttonText:{month:"月",week:"週",day:"天",list:"å¾…è¾¦äº‹é …"},allDayText:"全天",eventLimitText:"更多",noEventsMessage:"没有事件显示"})}(),a.locale("en"),e.fullCalendar.locale("en"),e.datepicker&&e.datepicker.setDefaults(e.datepicker.regional[""])}); !function(){function n(n){return n&&(n.ownerDocument||n.document||n).documentElement}function t(n){return n&&(n.ownerDocument&&n.ownerDocument.defaultView||n.document&&n||n.defaultView)}function e(n,t){return t>n?-1:n>t?1:n>=t?0:NaN}function r(n){return null===n?NaN:+n}function i(n){return!isNaN(n)}function u(n){return{left:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)<0?r=u+1:i=u}return r},right:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)>0?i=u:r=u+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function l(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function c(){this._=Object.create(null)}function f(n){return(n+="")===bo||n[0]===_o?_o+n:n}function s(n){return(n+="")[0]===_o?n.slice(1):n}function h(n){return f(n)in this._}function p(n){return(n=f(n))in this._&&delete this._[n]}function g(){var n=[];for(var t in this._)n.push(s(t));return n}function v(){var n=0;for(var t in this._)++n;return n}function d(){for(var n in this._)return!1;return!0}function y(){this._=Object.create(null)}function m(n){return n}function M(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e=0,r=wo.length;r>e;++e){var i=wo[e]+t;if(i in n)return i}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,i=-1,u=r.length;++i<u;)(t=r[i].on)&&t.apply(this,arguments);return n}var e=[],r=new c;return t.on=function(t,i){var u,o=r.get(t);return arguments.length<2?o&&o.on:(o&&(o.on=null,e=e.slice(0,u=e.indexOf(o)).concat(e.slice(u+1)),r.remove(t)),i&&e.push(r.set(t,{on:i})),n)},t}function S(){ao.event.preventDefault()}function k(){for(var n,t=ao.event;n=t.sourceEvent;)t=n;return t}function N(n){for(var t=new _,e=0,r=arguments.length;++e<r;)t[arguments[e]]=w(t);return t.of=function(e,r){return function(i){try{var u=i.sourceEvent=ao.event;i.target=n,ao.event=i,t[i.type].apply(e,r)}finally{ao.event=u}}},t}function E(n){return ko(n,Co),n}function A(n){return"function"==typeof n?n:function(){return No(n,this)}}function C(n){return"function"==typeof n?n:function(){return Eo(n,this)}}function z(n,t){function e(){this.removeAttribute(n)}function r(){this.removeAttributeNS(n.space,n.local)}function i(){this.setAttribute(n,t)}function u(){this.setAttributeNS(n.space,n.local,t)}function o(){var e=t.apply(this,arguments);null==e?this.removeAttribute(n):this.setAttribute(n,e)}function a(){var e=t.apply(this,arguments);null==e?this.removeAttributeNS(n.space,n.local):this.setAttributeNS(n.space,n.local,e)}return n=ao.ns.qualify(n),null==t?n.local?r:e:"function"==typeof t?n.local?a:o:n.local?u:i}function L(n){return n.trim().replace(/\s+/g," ")}function q(n){return new RegExp("(?:^|\\s+)"+ao.requote(n)+"(?:\\s+|$)","g")}function T(n){return(n+"").trim().split(/^|\s+/)}function R(n,t){function e(){for(var e=-1;++e<i;)n[e](this,t)}function r(){for(var e=-1,r=t.apply(this,arguments);++e<i;)n[e](this,r)}n=T(n).map(D);var i=n.length;return"function"==typeof t?r:e}function D(n){var t=q(n);return function(e,r){if(i=e.classList)return r?i.add(n):i.remove(n);var i=e.getAttribute("class")||"";r?(t.lastIndex=0,t.test(i)||e.setAttribute("class",L(i+" "+n))):e.setAttribute("class",L(i.replace(t," ")))}}function P(n,t,e){function r(){this.style.removeProperty(n)}function i(){this.style.setProperty(n,t,e)}function u(){var r=t.apply(this,arguments);null==r?this.style.removeProperty(n):this.style.setProperty(n,r,e)}return null==t?r:"function"==typeof t?u:i}function U(n,t){function e(){delete this[n]}function r(){this[n]=t}function i(){var e=t.apply(this,arguments);null==e?delete this[n]:this[n]=e}return null==t?e:"function"==typeof t?i:r}function j(n){function t(){var t=this.ownerDocument,e=this.namespaceURI;return e===zo&&t.documentElement.namespaceURI===zo?t.createElement(n):t.createElementNS(e,n)}function e(){return this.ownerDocument.createElementNS(n.space,n.local)}return"function"==typeof n?n:(n=ao.ns.qualify(n)).local?e:t}function F(){var n=this.parentNode;n&&n.removeChild(this)}function H(n){return{__data__:n}}function O(n){return function(){return Ao(this,n)}}function I(n){return arguments.length||(n=e),function(t,e){return t&&e?n(t.__data__,e.__data__):!t-!e}}function Y(n,t){for(var e=0,r=n.length;r>e;e++)for(var i,u=n[e],o=0,a=u.length;a>o;o++)(i=u[o])&&t(i,o,e);return n}function Z(n){return ko(n,qo),n}function V(n){var t,e;return function(r,i,u){var o,a=n[u].update,l=a.length;for(u!=e&&(e=u,t=0),i>=t&&(t=i+1);!(o=a[t])&&++t<l;);return o}}function X(n,t,e){function r(){var t=this[o];t&&(this.removeEventListener(n,t,t.$),delete this[o])}function i(){var i=l(t,co(arguments));r.call(this),this.addEventListener(n,this[o]=i,i.$=e),i._=t}function u(){var t,e=new RegExp("^__on([^.]+)"+ao.requote(n)+"$");for(var r in this)if(t=r.match(e)){var i=this[r];this.removeEventListener(t[1],i,i.$),delete this[r]}}var o="__on"+n,a=n.indexOf("."),l=$;a>0&&(n=n.slice(0,a));var c=To.get(n);return c&&(n=c,l=B),a?t?i:r:t?b:u}function $(n,t){return function(e){var r=ao.event;ao.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ao.event=r}}}function B(n,t){var e=$(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function W(e){var r=".dragsuppress-"+ ++Do,i="click"+r,u=ao.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ro&&(Ro="onselectstart"in e?!1:x(e.style,"userSelect")),Ro){var o=n(e).style,a=o[Ro];o[Ro]="none"}return function(n){if(u.on(r,null),Ro&&(o[Ro]=a),n){var t=function(){u.on(i,null)};u.on(i,function(){S(),t()},!0),setTimeout(t,0)}}}function J(n,e){e.changedTouches&&(e=e.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var i=r.createSVGPoint();if(0>Po){var u=t(n);if(u.scrollX||u.scrollY){r=ao.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Po=!(o.f||o.e),r.remove()}}return Po?(i.x=e.pageX,i.y=e.pageY):(i.x=e.clientX,i.y=e.clientY),i=i.matrixTransform(n.getScreenCTM().inverse()),[i.x,i.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ao.event.changedTouches[0].identifier}function K(n){return n>0?1:0>n?-1:0}function Q(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function nn(n){return n>1?0:-1>n?Fo:Math.acos(n)}function tn(n){return n>1?Io:-1>n?-Io:Math.asin(n)}function en(n){return((n=Math.exp(n))-1/n)/2}function rn(n){return((n=Math.exp(n))+1/n)/2}function un(n){return((n=Math.exp(2*n))-1)/(n+1)}function on(n){return(n=Math.sin(n/2))*n}function an(){}function ln(n,t,e){return this instanceof ln?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof ln?new ln(n.h,n.s,n.l):_n(""+n,wn,ln):new ln(n,t,e)}function cn(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?u+(o-u)*n/60:180>n?o:240>n?u+(o-u)*(240-n)/60:u}function i(n){return Math.round(255*r(n))}var u,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,u=2*e-o,new mn(i(n+120),i(n),i(n-120))}function fn(n,t,e){return this instanceof fn?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof fn?new fn(n.h,n.c,n.l):n instanceof hn?gn(n.l,n.a,n.b):gn((n=Sn((n=ao.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new fn(n,t,e)}function sn(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new hn(e,Math.cos(n*=Yo)*t,Math.sin(n)*t)}function hn(n,t,e){return this instanceof hn?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof hn?new hn(n.l,n.a,n.b):n instanceof fn?sn(n.h,n.c,n.l):Sn((n=mn(n)).r,n.g,n.b):new hn(n,t,e)}function pn(n,t,e){var r=(n+16)/116,i=r+t/500,u=r-e/200;return i=vn(i)*na,r=vn(r)*ta,u=vn(u)*ea,new mn(yn(3.2404542*i-1.5371385*r-.4985314*u),yn(-.969266*i+1.8760108*r+.041556*u),yn(.0556434*i-.2040259*r+1.0572252*u))}function gn(n,t,e){return n>0?new fn(Math.atan2(e,t)*Zo,Math.sqrt(t*t+e*e),n):new fn(NaN,NaN,n)}function vn(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function dn(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function yn(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mn(n,t,e){return this instanceof mn?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mn?new mn(n.r,n.g,n.b):_n(""+n,mn,cn):new mn(n,t,e)}function Mn(n){return new mn(n>>16,n>>8&255,255&n)}function xn(n){return Mn(n)+""}function bn(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function _n(n,t,e){var r,i,u,o=0,a=0,l=0;if(r=/([a-z]+)\((.*)\)/.exec(n=n.toLowerCase()))switch(i=r[2].split(","),r[1]){case"hsl":return e(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(Nn(i[0]),Nn(i[1]),Nn(i[2]))}return(u=ua.get(n))?t(u.r,u.g,u.b):(null==n||"#"!==n.charAt(0)||isNaN(u=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&u)>>4,o=o>>4|o,a=240&u,a=a>>4|a,l=15&u,l=l<<4|l):7===n.length&&(o=(16711680&u)>>16,a=(65280&u)>>8,l=255&u)),t(o,a,l))}function wn(n,t,e){var r,i,u=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-u,l=(o+u)/2;return a?(i=.5>l?a/(o+u):a/(2-o-u),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=NaN,i=l>0&&1>l?0:r),new ln(r,i,l)}function Sn(n,t,e){n=kn(n),t=kn(t),e=kn(e);var r=dn((.4124564*n+.3575761*t+.1804375*e)/na),i=dn((.2126729*n+.7151522*t+.072175*e)/ta),u=dn((.0193339*n+.119192*t+.9503041*e)/ea);return hn(116*i-16,500*(r-i),200*(i-u))}function kn(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function Nn(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function En(n){return"function"==typeof n?n:function(){return n}}function An(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Cn(t,e,n,r)}}function Cn(n,t,e,r){function i(){var n,t=l.status;if(!t&&Ln(l)||t>=200&&300>t||304===t){try{n=e.call(u,l)}catch(r){return void o.error.call(u,r)}o.load.call(u,n)}else o.error.call(u,l)}var u={},o=ao.dispatch("beforesend","progress","load","error"),a={},l=new XMLHttpRequest,c=null;return!this.XDomainRequest||"withCredentials"in l||!/^(http(s)?:)?\/\//.test(n)||(l=new XDomainRequest),"onload"in l?l.onload=l.onerror=i:l.onreadystatechange=function(){l.readyState>3&&i()},l.onprogress=function(n){var t=ao.event;ao.event=n;try{o.progress.call(u,l)}finally{ao.event=t}},u.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",u)},u.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",u):t},u.responseType=function(n){return arguments.length?(c=n,u):c},u.response=function(n){return e=n,u},["get","post"].forEach(function(n){u[n]=function(){return u.send.apply(u,[n].concat(co(arguments)))}}),u.send=function(e,r,i){if(2===arguments.length&&"function"==typeof r&&(i=r,r=null),l.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),l.setRequestHeader)for(var f in a)l.setRequestHeader(f,a[f]);return null!=t&&l.overrideMimeType&&l.overrideMimeType(t),null!=c&&(l.responseType=c),null!=i&&u.on("error",i).on("load",function(n){i(null,n)}),o.beforesend.call(u,l),l.send(null==r?null:r),u},u.abort=function(){return l.abort(),u},ao.rebind(u,o,"on"),null==r?u:u.get(zn(r))}function zn(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Ln(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qn(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var i=e+t,u={c:n,t:i,n:null};return aa?aa.n=u:oa=u,aa=u,la||(ca=clearTimeout(ca),la=1,fa(Tn)),u}function Tn(){var n=Rn(),t=Dn()-n;t>24?(isFinite(t)&&(clearTimeout(ca),ca=setTimeout(Tn,t)),la=0):(la=1,fa(Tn))}function Rn(){for(var n=Date.now(),t=oa;t;)n>=t.t&&t.c(n-t.t)&&(t.c=null),t=t.n;return n}function Dn(){for(var n,t=oa,e=1/0;t;)t.c?(t.t<e&&(e=t.t),t=(n=t).n):t=n?n.n=t.n:oa=t.n;return aa=n,e}function Pn(n,t){return t-(n?Math.ceil(Math.log(n)/Math.LN10):1)}function Un(n,t){var e=Math.pow(10,3*xo(8-t));return{scale:t>8?function(n){return n/e}:function(n){return n*e},symbol:n}}function jn(n){var t=n.decimal,e=n.thousands,r=n.grouping,i=n.currency,u=r&&e?function(n,t){for(var i=n.length,u=[],o=0,a=r[0],l=0;i>0&&a>0&&(l+a+1>t&&(a=Math.max(1,t-l)),u.push(n.substring(i-=a,i+a)),!((l+=a+1)>t));)a=r[o=(o+1)%r.length];return u.reverse().join(e)}:m;return function(n){var e=ha.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",l=e[4]||"",c=e[5],f=+e[6],s=e[7],h=e[8],p=e[9],g=1,v="",d="",y=!1,m=!0;switch(h&&(h=+h.substring(1)),(c||"0"===r&&"="===o)&&(c=r="0",o="="),p){case"n":s=!0,p="g";break;case"%":g=100,d="%",p="f";break;case"p":g=100,d="%",p="r";break;case"b":case"o":case"x":case"X":"#"===l&&(v="0"+p.toLowerCase());case"c":m=!1;case"d":y=!0,h=0;break;case"s":g=-1,p="r"}"$"===l&&(v=i[0],d=i[1]),"r"!=p||h||(p="g"),null!=h&&("g"==p?h=Math.max(1,Math.min(21,h)):"e"!=p&&"f"!=p||(h=Math.max(0,Math.min(20,h)))),p=pa.get(p)||Fn;var M=c&&s;return function(n){var e=d;if(y&&n%1)return"";var i=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>g){var l=ao.formatPrefix(n,h);n=l.scale(n),e=l.symbol+d}else n*=g;n=p(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=m?n.lastIndexOf("e"):-1;0>w?(x=n,b=""):(x=n.substring(0,w),b=n.substring(w))}else x=n.substring(0,_),b=t+n.substring(_+1);!c&&s&&(x=u(x,1/0));var S=v.length+x.length+b.length+(M?0:i.length),k=f>S?new Array(S=f-S+1).join(r):"";return M&&(x=u(k+x,k.length?f-b.length:1/0)),i+=v,n=x+b,("<"===o?i+n+k:">"===o?k+i+n:"^"===o?k.substring(0,S>>=1)+i+n+k.substring(S):i+(M?n:k+n))+e}}}function Fn(n){return n+""}function Hn(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function On(n,t,e){function r(t){var e=n(t),r=u(e,1);return r-t>t-e?e:r}function i(e){return t(e=n(new va(e-1)),1),e}function u(n,e){return t(n=new va(+n),e),n}function o(n,r,u){var o=i(n),a=[];if(u>1)for(;r>o;)e(o)%u||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{va=Hn;var r=new Hn;return r._=n,o(r,t,e)}finally{va=Date}}n.floor=n,n.round=r,n.ceil=i,n.offset=u,n.range=o;var l=n.utc=In(n);return l.floor=l,l.round=In(r),l.ceil=In(i),l.offset=In(u),l.range=a,n}function In(n){return function(t,e){try{va=Hn;var r=new Hn;return r._=t,n(r,e)._}finally{va=Date}}}function Yn(n){function t(n){function t(t){for(var e,i,u,o=[],a=-1,l=0;++a<r;)37===n.charCodeAt(a)&&(o.push(n.slice(l,a)),null!=(i=ya[e=n.charAt(++a)])&&(e=n.charAt(++a)),(u=A[e])&&(e=u(t,null==i?"e"===e?" ":"0":i)),o.push(e),l=a+1);return o.push(n.slice(l,a)),o.join("")}var r=n.length;return t.parse=function(t){var r={y:1900,m:0,d:1,H:0,M:0,S:0,L:0,Z:null},i=e(r,n,t,0);if(i!=t.length)return null;"p"in r&&(r.H=r.H%12+12*r.p);var u=null!=r.Z&&va!==Hn,o=new(u?Hn:va);return"j"in r?o.setFullYear(r.y,0,r.j):"W"in r||"U"in r?("w"in r||(r.w="W"in r?1:0),o.setFullYear(r.y,0,1),o.setFullYear(r.y,0,"W"in r?(r.w+6)%7+7*r.W-(o.getDay()+5)%7:r.w+7*r.U-(o.getDay()+6)%7)):o.setFullYear(r.y,r.m,r.d),o.setHours(r.H+(r.Z/100|0),r.M+r.Z%100,r.S,r.L),u?o._:o},t.toString=function(){return n},t}function e(n,t,e,r){for(var i,u,o,a=0,l=t.length,c=e.length;l>a;){if(r>=c)return-1;if(i=t.charCodeAt(a++),37===i){if(o=t.charAt(a++),u=C[o in ya?t.charAt(a++):o],!u||(r=u(n,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){_.lastIndex=0;var r=_.exec(t.slice(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){x.lastIndex=0;var r=x.exec(t.slice(e));return r?(n.w=b.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){N.lastIndex=0;var r=N.exec(t.slice(e));return r?(n.m=E.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.slice(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,A.c.toString(),t,r)}function l(n,t,r){return e(n,A.x.toString(),t,r)}function c(n,t,r){return e(n,A.X.toString(),t,r)}function f(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var s=n.dateTime,h=n.date,p=n.time,g=n.periods,v=n.days,d=n.shortDays,y=n.months,m=n.shortMonths;t.utc=function(n){function e(n){try{va=Hn;var t=new va;return t._=n,r(t)}finally{va=Date}}var r=t(n);return e.parse=function(n){try{va=Hn;var t=r.parse(n);return t&&t._}finally{va=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ct;var M=ao.map(),x=Vn(v),b=Xn(v),_=Vn(d),w=Xn(d),S=Vn(y),k=Xn(y),N=Vn(m),E=Xn(m);g.forEach(function(n,t){M.set(n.toLowerCase(),t)});var A={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return m[n.getMonth()]},B:function(n){return y[n.getMonth()]},c:t(s),d:function(n,t){return Zn(n.getDate(),t,2)},e:function(n,t){return Zn(n.getDate(),t,2)},H:function(n,t){return Zn(n.getHours(),t,2)},I:function(n,t){return Zn(n.getHours()%12||12,t,2)},j:function(n,t){return Zn(1+ga.dayOfYear(n),t,3)},L:function(n,t){return Zn(n.getMilliseconds(),t,3)},m:function(n,t){return Zn(n.getMonth()+1,t,2)},M:function(n,t){return Zn(n.getMinutes(),t,2)},p:function(n){return g[+(n.getHours()>=12)]},S:function(n,t){return Zn(n.getSeconds(),t,2)},U:function(n,t){return Zn(ga.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Zn(ga.mondayOfYear(n),t,2)},x:t(h),X:t(p),y:function(n,t){return Zn(n.getFullYear()%100,t,2)},Y:function(n,t){return Zn(n.getFullYear()%1e4,t,4)},Z:at,"%":function(){return"%"}},C={a:r,A:i,b:u,B:o,c:a,d:tt,e:tt,H:rt,I:rt,j:et,L:ot,m:nt,M:it,p:f,S:ut,U:Bn,w:$n,W:Wn,x:l,X:c,y:Gn,Y:Jn,Z:Kn,"%":lt};return t}function Zn(n,t,e){var r=0>n?"-":"",i=(r?-n:n)+"",u=i.length;return r+(e>u?new Array(e-u+1).join(t)+i:i)}function Vn(n){return new RegExp("^(?:"+n.map(ao.requote).join("|")+")","i")}function Xn(n){for(var t=new c,e=-1,r=n.length;++e<r;)t.set(n[e].toLowerCase(),e);return t}function $n(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+1));return r?(n.w=+r[0],e+r[0].length):-1}function Bn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e));return r?(n.U=+r[0],e+r[0].length):-1}function Wn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e));return r?(n.W=+r[0],e+r[0].length):-1}function Jn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+4));return r?(n.y=+r[0],e+r[0].length):-1}function Gn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.y=Qn(+r[0]),e+r[0].length):-1}function Kn(n,t,e){return/^[+-]\d{4}$/.test(t=t.slice(e,e+5))?(n.Z=-t,e+5):-1}function Qn(n){return n+(n>68?1900:2e3)}function nt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function tt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function et(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function rt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function it(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function ut(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ot(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function at(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=xo(t)/60|0,i=xo(t)%60;return e+Zn(r,"0",2)+Zn(i,"0",2)}function lt(n,t,e){Ma.lastIndex=0;var r=Ma.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ct(n){for(var t=n.length,e=-1;++e<t;)n[e][0]=this(n[e][0]);return function(t){for(var e=0,r=n[e];!r[1](t);)r=n[++e];return r[0](t)}}function ft(){}function st(n,t,e){var r=e.s=n+t,i=r-n,u=r-i;e.t=n-u+(t-i)}function ht(n,t){n&&wa.hasOwnProperty(n.type)&&wa[n.type](n,t)}function pt(n,t,e){var r,i=-1,u=n.length-e;for(t.lineStart();++i<u;)r=n[i],t.point(r[0],r[1],r[2]);t.lineEnd()}function gt(n,t){var e=-1,r=n.length;for(t.polygonStart();++e<r;)pt(n[e],t,1);t.polygonEnd()}function vt(){function n(n,t){n*=Yo,t=t*Yo/2+Fo/4;var e=n-r,o=e>=0?1:-1,a=o*e,l=Math.cos(t),c=Math.sin(t),f=u*c,s=i*l+f*Math.cos(a),h=f*o*Math.sin(a);ka.add(Math.atan2(h,s)),r=n,i=l,u=c}var t,e,r,i,u;Na.point=function(o,a){Na.point=n,r=(t=o)*Yo,i=Math.cos(a=(e=a)*Yo/2+Fo/4),u=Math.sin(a)},Na.lineEnd=function(){n(t,e)}}function dt(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function yt(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function mt(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function Mt(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function xt(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function bt(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function _t(n){return[Math.atan2(n[1],n[0]),tn(n[2])]}function wt(n,t){return xo(n[0]-t[0])<Uo&&xo(n[1]-t[1])<Uo}function St(n,t){n*=Yo;var e=Math.cos(t*=Yo);kt(e*Math.cos(n),e*Math.sin(n),Math.sin(t))}function kt(n,t,e){++Ea,Ca+=(n-Ca)/Ea,za+=(t-za)/Ea,La+=(e-La)/Ea}function Nt(){function n(n,i){n*=Yo;var u=Math.cos(i*=Yo),o=u*Math.cos(n),a=u*Math.sin(n),l=Math.sin(i),c=Math.atan2(Math.sqrt((c=e*l-r*a)*c+(c=r*o-t*l)*c+(c=t*a-e*o)*c),t*o+e*a+r*l);Aa+=c,qa+=c*(t+(t=o)),Ta+=c*(e+(e=a)),Ra+=c*(r+(r=l)),kt(t,e,r)}var t,e,r;ja.point=function(i,u){i*=Yo;var o=Math.cos(u*=Yo);t=o*Math.cos(i),e=o*Math.sin(i),r=Math.sin(u),ja.point=n,kt(t,e,r)}}function Et(){ja.point=St}function At(){function n(n,t){n*=Yo;var e=Math.cos(t*=Yo),o=e*Math.cos(n),a=e*Math.sin(n),l=Math.sin(t),c=i*l-u*a,f=u*o-r*l,s=r*a-i*o,h=Math.sqrt(c*c+f*f+s*s),p=r*o+i*a+u*l,g=h&&-nn(p)/h,v=Math.atan2(h,p);Da+=g*c,Pa+=g*f,Ua+=g*s,Aa+=v,qa+=v*(r+(r=o)),Ta+=v*(i+(i=a)),Ra+=v*(u+(u=l)),kt(r,i,u)}var t,e,r,i,u;ja.point=function(o,a){t=o,e=a,ja.point=n,o*=Yo;var l=Math.cos(a*=Yo);r=l*Math.cos(o),i=l*Math.sin(o),u=Math.sin(a),kt(r,i,u)},ja.lineEnd=function(){n(t,e),ja.lineEnd=Et,ja.point=St}}function Ct(n,t){function e(e,r){return e=n(e,r),t(e[0],e[1])}return n.invert&&t.invert&&(e.invert=function(e,r){return e=t.invert(e,r),e&&n.invert(e[0],e[1])}),e}function zt(){return!0}function Lt(n,t,e,r,i){var u=[],o=[];if(n.forEach(function(n){if(!((t=n.length-1)<=0)){var t,e=n[0],r=n[t];if(wt(e,r)){i.lineStart();for(var a=0;t>a;++a)i.point((e=n[a])[0],e[1]);return void i.lineEnd()}var l=new Tt(e,n,null,!0),c=new Tt(e,null,l,!1);l.o=c,u.push(l),o.push(c),l=new Tt(r,n,null,!1),c=new Tt(r,null,l,!0),l.o=c,u.push(l),o.push(c)}}),o.sort(t),qt(u),qt(o),u.length){for(var a=0,l=e,c=o.length;c>a;++a)o[a].e=l=!l;for(var f,s,h=u[0];;){for(var p=h,g=!0;p.v;)if((p=p.n)===h)return;f=p.z,i.lineStart();do{if(p.v=p.o.v=!0,p.e){if(g)for(var a=0,c=f.length;c>a;++a)i.point((s=f[a])[0],s[1]);else r(p.x,p.n.x,1,i);p=p.n}else{if(g){f=p.p.z;for(var a=f.length-1;a>=0;--a)i.point((s=f[a])[0],s[1])}else r(p.x,p.p.x,-1,i);p=p.p}p=p.o,f=p.z,g=!g}while(!p.v);i.lineEnd()}}}function qt(n){if(t=n.length){for(var t,e,r=0,i=n[0];++r<t;)i.n=e=n[r],e.p=i,i=e;i.n=e=n[0],e.p=i}}function Tt(n,t,e,r){this.x=n,this.z=t,this.o=e,this.e=r,this.v=!1,this.n=this.p=null}function Rt(n,t,e,r){return function(i,u){function o(t,e){var r=i(t,e);n(t=r[0],e=r[1])&&u.point(t,e)}function a(n,t){var e=i(n,t);d.point(e[0],e[1])}function l(){m.point=a,d.lineStart()}function c(){m.point=o,d.lineEnd()}function f(n,t){v.push([n,t]);var e=i(n,t);x.point(e[0],e[1])}function s(){x.lineStart(),v=[]}function h(){f(v[0][0],v[0][1]),x.lineEnd();var n,t=x.clean(),e=M.buffer(),r=e.length;if(v.pop(),g.push(v),v=null,r)if(1&t){n=e[0];var i,r=n.length-1,o=-1;if(r>0){for(b||(u.polygonStart(),b=!0),u.lineStart();++o<r;)u.point((i=n[o])[0],i[1]);u.lineEnd()}}else r>1&&2&t&&e.push(e.pop().concat(e.shift())),p.push(e.filter(Dt))}var p,g,v,d=t(u),y=i.invert(r[0],r[1]),m={point:o,lineStart:l,lineEnd:c,polygonStart:function(){m.point=f,m.lineStart=s,m.lineEnd=h,p=[],g=[]},polygonEnd:function(){m.point=o,m.lineStart=l,m.lineEnd=c,p=ao.merge(p);var n=Ot(y,g);p.length?(b||(u.polygonStart(),b=!0),Lt(p,Ut,n,e,u)):n&&(b||(u.polygonStart(),b=!0),u.lineStart(),e(null,null,1,u),u.lineEnd()),b&&(u.polygonEnd(),b=!1),p=g=null},sphere:function(){u.polygonStart(),u.lineStart(),e(null,null,1,u),u.lineEnd(),u.polygonEnd()}},M=Pt(),x=t(M),b=!1;return m}}function Dt(n){return n.length>1}function Pt(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:b,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Ut(n,t){return((n=n.x)[0]<0?n[1]-Io-Uo:Io-n[1])-((t=t.x)[0]<0?t[1]-Io-Uo:Io-t[1])}function jt(n){var t,e=NaN,r=NaN,i=NaN;return{lineStart:function(){n.lineStart(),t=1},point:function(u,o){var a=u>0?Fo:-Fo,l=xo(u-e);xo(l-Fo)<Uo?(n.point(e,r=(r+o)/2>0?Io:-Io),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(u,r),t=0):i!==a&&l>=Fo&&(xo(e-i)<Uo&&(e-=i*Uo),xo(u-a)<Uo&&(u-=a*Uo),r=Ft(e,r,u,o),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),t=0),n.point(e=u,r=o),i=a},lineEnd:function(){n.lineEnd(),e=r=NaN},clean:function(){return 2-t}}}function Ft(n,t,e,r){var i,u,o=Math.sin(n-e);return xo(o)>Uo?Math.atan((Math.sin(t)*(u=Math.cos(r))*Math.sin(e)-Math.sin(r)*(i=Math.cos(t))*Math.sin(n))/(i*u*o)):(t+r)/2}function Ht(n,t,e,r){var i;if(null==n)i=e*Io,r.point(-Fo,i),r.point(0,i),r.point(Fo,i),r.point(Fo,0),r.point(Fo,-i),r.point(0,-i),r.point(-Fo,-i),r.point(-Fo,0),r.point(-Fo,i);else if(xo(n[0]-t[0])>Uo){var u=n[0]<t[0]?Fo:-Fo;i=e*u/2,r.point(-u,i),r.point(0,i),r.point(u,i)}else r.point(t[0],t[1])}function Ot(n,t){var e=n[0],r=n[1],i=[Math.sin(e),-Math.cos(e),0],u=0,o=0;ka.reset();for(var a=0,l=t.length;l>a;++a){var c=t[a],f=c.length;if(f)for(var s=c[0],h=s[0],p=s[1]/2+Fo/4,g=Math.sin(p),v=Math.cos(p),d=1;;){d===f&&(d=0),n=c[d];var y=n[0],m=n[1]/2+Fo/4,M=Math.sin(m),x=Math.cos(m),b=y-h,_=b>=0?1:-1,w=_*b,S=w>Fo,k=g*M;if(ka.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),u+=S?b+_*Ho:b,S^h>=e^y>=e){var N=mt(dt(s),dt(n));bt(N);var E=mt(i,N);bt(E);var A=(S^b>=0?-1:1)*tn(E[2]);(r>A||r===A&&(N[0]||N[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=y,g=M,v=x,s=n}}return(-Uo>u||Uo>u&&-Uo>ka)^1&o}function It(n){function t(n,t){return Math.cos(n)*Math.cos(t)>u}function e(n){var e,u,l,c,f;return{lineStart:function(){c=l=!1,f=1},point:function(s,h){var p,g=[s,h],v=t(s,h),d=o?v?0:i(s,h):v?i(s+(0>s?Fo:-Fo),h):0;if(!e&&(c=l=v)&&n.lineStart(),v!==l&&(p=r(e,g),(wt(e,p)||wt(g,p))&&(g[0]+=Uo,g[1]+=Uo,v=t(g[0],g[1]))),v!==l)f=0,v?(n.lineStart(),p=r(g,e),n.point(p[0],p[1])):(p=r(e,g),n.point(p[0],p[1]),n.lineEnd()),e=p;else if(a&&e&&o^v){var y;d&u||!(y=r(g,e,!0))||(f=0,o?(n.lineStart(),n.point(y[0][0],y[0][1]),n.point(y[1][0],y[1][1]),n.lineEnd()):(n.point(y[1][0],y[1][1]),n.lineEnd(),n.lineStart(),n.point(y[0][0],y[0][1])))}!v||e&&wt(e,g)||n.point(g[0],g[1]),e=g,l=v,u=d},lineEnd:function(){l&&n.lineEnd(),e=null},clean:function(){return f|(c&&l)<<1}}}function r(n,t,e){var r=dt(n),i=dt(t),o=[1,0,0],a=mt(r,i),l=yt(a,a),c=a[0],f=l-c*c;if(!f)return!e&&n;var s=u*l/f,h=-u*c/f,p=mt(o,a),g=xt(o,s),v=xt(a,h);Mt(g,v);var d=p,y=yt(g,d),m=yt(d,d),M=y*y-m*(yt(g,g)-1);if(!(0>M)){var x=Math.sqrt(M),b=xt(d,(-y-x)/m);if(Mt(b,g),b=_t(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],N=t[1];w>S&&(_=w,w=S,S=_);var E=S-w,A=xo(E-Fo)<Uo,C=A||Uo>E;if(!A&&k>N&&(_=k,k=N,N=_),C?A?k+N>0^b[1]<(xo(b[0]-w)<Uo?k:N):k<=b[1]&&b[1]<=N:E>Fo^(w<=b[0]&&b[0]<=S)){var z=xt(d,(-y+x)/m);return Mt(z,g),[b,_t(z)]}}}function i(t,e){var r=o?n:Fo-n,i=0;return-r>t?i|=1:t>r&&(i|=2),-r>e?i|=4:e>r&&(i|=8),i}var u=Math.cos(n),o=u>0,a=xo(u)>Uo,l=ve(n,6*Yo);return Rt(t,e,l,o?[0,-n]:[-Fo,n-Fo])}function Yt(n,t,e,r){return function(i){var u,o=i.a,a=i.b,l=o.x,c=o.y,f=a.x,s=a.y,h=0,p=1,g=f-l,v=s-c;if(u=n-l,g||!(u>0)){if(u/=g,0>g){if(h>u)return;p>u&&(p=u)}else if(g>0){if(u>p)return;u>h&&(h=u)}if(u=e-l,g||!(0>u)){if(u/=g,0>g){if(u>p)return;u>h&&(h=u)}else if(g>0){if(h>u)return;p>u&&(p=u)}if(u=t-c,v||!(u>0)){if(u/=v,0>v){if(h>u)return;p>u&&(p=u)}else if(v>0){if(u>p)return;u>h&&(h=u)}if(u=r-c,v||!(0>u)){if(u/=v,0>v){if(u>p)return;u>h&&(h=u)}else if(v>0){if(h>u)return;p>u&&(p=u)}return h>0&&(i.a={x:l+h*g,y:c+h*v}),1>p&&(i.b={x:l+p*g,y:c+p*v}),i}}}}}}function Zt(n,t,e,r){function i(r,i){return xo(r[0]-n)<Uo?i>0?0:3:xo(r[0]-e)<Uo?i>0?2:1:xo(r[1]-t)<Uo?i>0?1:0:i>0?3:2}function u(n,t){return o(n.x,t.x)}function o(n,t){var e=i(n,1),r=i(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function l(n){for(var t=0,e=d.length,r=n[1],i=0;e>i;++i)for(var u,o=1,a=d[i],l=a.length,c=a[0];l>o;++o)u=a[o],c[1]<=r?u[1]>r&&Q(c,u,n)>0&&++t:u[1]<=r&&Q(c,u,n)<0&&--t,c=u;return 0!==t}function c(u,a,l,c){var f=0,s=0;if(null==u||(f=i(u,l))!==(s=i(a,l))||o(u,a)<0^l>0){do c.point(0===f||3===f?n:e,f>1?r:t);while((f=(f+l+4)%4)!==s)}else c.point(a[0],a[1])}function f(i,u){return i>=n&&e>=i&&u>=t&&r>=u}function s(n,t){f(n,t)&&a.point(n,t)}function h(){C.point=g,d&&d.push(y=[]),S=!0,w=!1,b=_=NaN}function p(){v&&(g(m,M),x&&w&&E.rejoin(),v.push(E.buffer())),C.point=s,w&&a.lineEnd()}function g(n,t){n=Math.max(-Ha,Math.min(Ha,n)),t=Math.max(-Ha,Math.min(Ha,t));var e=f(n,t);if(d&&y.push([n,t]),S)m=n,M=t,x=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:b,y:_},b:{x:n,y:t}};A(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}b=n,_=t,w=e}var v,d,y,m,M,x,b,_,w,S,k,N=a,E=Pt(),A=Yt(n,t,e,r),C={point:s,lineStart:h,lineEnd:p,polygonStart:function(){a=E,v=[],d=[],k=!0},polygonEnd:function(){a=N,v=ao.merge(v);var t=l([n,r]),e=k&&t,i=v.length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),c(null,null,1,a),a.lineEnd()),i&&Lt(v,u,t,c,a),a.polygonEnd()),v=d=y=null}};return C}}function Vt(n){var t=0,e=Fo/3,r=ae(n),i=r(t,e);return i.parallels=function(n){return arguments.length?r(t=n[0]*Fo/180,e=n[1]*Fo/180):[t/Fo*180,e/Fo*180]},i}function Xt(n,t){function e(n,t){var e=Math.sqrt(u-2*i*Math.sin(t))/i;return[e*Math.sin(n*=i),o-e*Math.cos(n)]}var r=Math.sin(n),i=(r+Math.sin(t))/2,u=1+r*(2*i-r),o=Math.sqrt(u)/i;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/i,tn((u-(n*n+e*e)*i*i)/(2*i))]},e}function $t(){function n(n,t){Ia+=i*n-r*t,r=n,i=t}var t,e,r,i;$a.point=function(u,o){$a.point=n,t=r=u,e=i=o},$a.lineEnd=function(){n(t,e)}}function Bt(n,t){Ya>n&&(Ya=n),n>Va&&(Va=n),Za>t&&(Za=t),t>Xa&&(Xa=t)}function Wt(){function n(n,t){o.push("M",n,",",t,u)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function i(){o.push("Z")}var u=Jt(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return u=Jt(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Jt(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Gt(n,t){Ca+=n,za+=t,++La}function Kt(){function n(n,r){var i=n-t,u=r-e,o=Math.sqrt(i*i+u*u);qa+=o*(t+n)/2,Ta+=o*(e+r)/2,Ra+=o,Gt(t=n,e=r)}var t,e;Wa.point=function(r,i){Wa.point=n,Gt(t=r,e=i)}}function Qt(){Wa.point=Gt}function ne(){function n(n,t){var e=n-r,u=t-i,o=Math.sqrt(e*e+u*u);qa+=o*(r+n)/2,Ta+=o*(i+t)/2,Ra+=o,o=i*n-r*t,Da+=o*(r+n),Pa+=o*(i+t),Ua+=3*o,Gt(r=n,i=t)}var t,e,r,i;Wa.point=function(u,o){Wa.point=n,Gt(t=r=u,e=i=o)},Wa.lineEnd=function(){n(t,e)}}function te(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,Ho)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function i(){a.point=t}function u(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:i,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=i,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function ee(n){function t(n){return(a?r:e)(n)}function e(t){return ue(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){M=NaN,S.point=u,t.lineStart()}function u(e,r){var u=dt([e,r]),o=n(e,r);i(M,x,m,b,_,w,M=o[0],x=o[1],m=e,b=u[0],_=u[1],w=u[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function l(){ r(),S.point=c,S.lineEnd=f}function c(n,t){u(s=n,h=t),p=M,g=x,v=b,d=_,y=w,S.point=u}function f(){i(M,x,m,b,_,w,p,g,s,v,d,y,a,t),S.lineEnd=o,o()}var s,h,p,g,v,d,y,m,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=l},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function i(t,e,r,a,l,c,f,s,h,p,g,v,d,y){var m=f-t,M=s-e,x=m*m+M*M;if(x>4*u&&d--){var b=a+p,_=l+g,w=c+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),N=xo(xo(w)-1)<Uo||xo(r-h)<Uo?(r+h)/2:Math.atan2(_,b),E=n(N,k),A=E[0],C=E[1],z=A-t,L=C-e,q=M*z-m*L;(q*q/x>u||xo((m*z+M*L)/x-.5)>.3||o>a*p+l*g+c*v)&&(i(t,e,r,a,l,c,A,C,N,b/=S,_/=S,w,d,y),y.point(A,C),i(A,C,N,b,_,w,f,s,h,p,g,v,d,y))}}var u=.5,o=Math.cos(30*Yo),a=16;return t.precision=function(n){return arguments.length?(a=(u=n*n)>0&&16,t):Math.sqrt(u)},t}function re(n){var t=ee(function(t,e){return n([t*Zo,e*Zo])});return function(n){return le(t(n))}}function ie(n){this.stream=n}function ue(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function oe(n){return ae(function(){return n})()}function ae(n){function t(n){return n=a(n[0]*Yo,n[1]*Yo),[n[0]*h+l,c-n[1]*h]}function e(n){return n=a.invert((n[0]-l)/h,(c-n[1])/h),n&&[n[0]*Zo,n[1]*Zo]}function r(){a=Ct(o=se(y,M,x),u);var n=u(v,d);return l=p-n[0]*h,c=g+n[1]*h,i()}function i(){return f&&(f.valid=!1,f=null),t}var u,o,a,l,c,f,s=ee(function(n,t){return n=u(n,t),[n[0]*h+l,c-n[1]*h]}),h=150,p=480,g=250,v=0,d=0,y=0,M=0,x=0,b=Fa,_=m,w=null,S=null;return t.stream=function(n){return f&&(f.valid=!1),f=le(b(o,s(_(n)))),f.valid=!0,f},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Fa):It((w=+n)*Yo),i()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Zt(n[0][0],n[0][1],n[1][0],n[1][1]):m,i()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(p=+n[0],g=+n[1],r()):[p,g]},t.center=function(n){return arguments.length?(v=n[0]%360*Yo,d=n[1]%360*Yo,r()):[v*Zo,d*Zo]},t.rotate=function(n){return arguments.length?(y=n[0]%360*Yo,M=n[1]%360*Yo,x=n.length>2?n[2]%360*Yo:0,r()):[y*Zo,M*Zo,x*Zo]},ao.rebind(t,s,"precision"),function(){return u=n.apply(this,arguments),t.invert=u.invert&&e,r()}}function le(n){return ue(n,function(t,e){n.point(t*Yo,e*Yo)})}function ce(n,t){return[n,t]}function fe(n,t){return[n>Fo?n-Ho:-Fo>n?n+Ho:n,t]}function se(n,t,e){return n?t||e?Ct(pe(n),ge(t,e)):pe(n):t||e?ge(t,e):fe}function he(n){return function(t,e){return t+=n,[t>Fo?t-Ho:-Fo>t?t+Ho:t,e]}}function pe(n){var t=he(n);return t.invert=he(-n),t}function ge(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*r+a*i;return[Math.atan2(l*u-f*o,a*r-c*i),tn(f*u+l*o)]}var r=Math.cos(n),i=Math.sin(n),u=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*u-l*o;return[Math.atan2(l*u+c*o,a*r+f*i),tn(f*r-a*i)]},e}function ve(n,t){var e=Math.cos(n),r=Math.sin(n);return function(i,u,o,a){var l=o*t;null!=i?(i=de(e,i),u=de(e,u),(o>0?u>i:i>u)&&(i+=o*Ho)):(i=n+o*Ho,u=n-.5*l);for(var c,f=i;o>0?f>u:u>f;f-=l)a.point((c=_t([e,-r*Math.cos(f),-r*Math.sin(f)]))[0],c[1])}}function de(n,t){var e=dt(t);e[0]-=n,bt(e);var r=nn(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Uo)%(2*Math.PI)}function ye(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function me(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function Me(n){return n.source}function xe(n){return n.target}function be(n,t,e,r){var i=Math.cos(t),u=Math.sin(t),o=Math.cos(r),a=Math.sin(r),l=i*Math.cos(n),c=i*Math.sin(n),f=o*Math.cos(e),s=o*Math.sin(e),h=2*Math.asin(Math.sqrt(on(r-t)+i*o*on(e-n))),p=1/Math.sin(h),g=h?function(n){var t=Math.sin(n*=h)*p,e=Math.sin(h-n)*p,r=e*l+t*f,i=e*c+t*s,o=e*u+t*a;return[Math.atan2(i,r)*Zo,Math.atan2(o,Math.sqrt(r*r+i*i))*Zo]}:function(){return[n*Zo,t*Zo]};return g.distance=h,g}function _e(){function n(n,i){var u=Math.sin(i*=Yo),o=Math.cos(i),a=xo((n*=Yo)-t),l=Math.cos(a);Ja+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*u-e*o*l)*a),e*u+r*o*l),t=n,e=u,r=o}var t,e,r;Ga.point=function(i,u){t=i*Yo,e=Math.sin(u*=Yo),r=Math.cos(u),Ga.point=n},Ga.lineEnd=function(){Ga.point=Ga.lineEnd=b}}function we(n,t){function e(t,e){var r=Math.cos(t),i=Math.cos(e),u=n(r*i);return[u*i*Math.sin(t),u*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),i=t(r),u=Math.sin(i),o=Math.cos(i);return[Math.atan2(n*u,r*o),Math.asin(r&&e*u/r)]},e}function Se(n,t){function e(n,t){o>0?-Io+Uo>t&&(t=-Io+Uo):t>Io-Uo&&(t=Io-Uo);var e=o/Math.pow(i(t),u);return[e*Math.sin(u*n),o-e*Math.cos(u*n)]}var r=Math.cos(n),i=function(n){return Math.tan(Fo/4+n/2)},u=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(i(t)/i(n)),o=r*Math.pow(i(n),u)/u;return u?(e.invert=function(n,t){var e=o-t,r=K(u)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/u,2*Math.atan(Math.pow(o/r,1/u))-Io]},e):Ne}function ke(n,t){function e(n,t){var e=u-t;return[e*Math.sin(i*n),u-e*Math.cos(i*n)]}var r=Math.cos(n),i=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),u=r/i+n;return xo(i)<Uo?ce:(e.invert=function(n,t){var e=u-t;return[Math.atan2(n,e)/i,u-K(i)*Math.sqrt(n*n+e*e)]},e)}function Ne(n,t){return[n,Math.log(Math.tan(Fo/4+t/2))]}function Ee(n){var t,e=oe(n),r=e.scale,i=e.translate,u=e.clipExtent;return e.scale=function(){var n=r.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.translate=function(){var n=i.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.clipExtent=function(n){var o=u.apply(e,arguments);if(o===e){if(t=null==n){var a=Fo*r(),l=i();u([[l[0]-a,l[1]-a],[l[0]+a,l[1]+a]])}}else t&&(o=null);return o},e.clipExtent(null)}function Ae(n,t){return[Math.log(Math.tan(Fo/4+t/2)),-n]}function Ce(n){return n[0]}function ze(n){return n[1]}function Le(n){for(var t=n.length,e=[0,1],r=2,i=2;t>i;i++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[i])<=0;)--r;e[r++]=i}return e.slice(0,r)}function qe(n,t){return n[0]-t[0]||n[1]-t[1]}function Te(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Re(n,t,e,r){var i=n[0],u=e[0],o=t[0]-i,a=r[0]-u,l=n[1],c=e[1],f=t[1]-l,s=r[1]-c,h=(a*(l-c)-s*(i-u))/(s*o-a*f);return[i+h*o,l+h*f]}function De(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Pe(){rr(this),this.edge=this.site=this.circle=null}function Ue(n){var t=cl.pop()||new Pe;return t.site=n,t}function je(n){Be(n),ol.remove(n),cl.push(n),rr(n)}function Fe(n){var t=n.circle,e=t.x,r=t.cy,i={x:e,y:r},u=n.P,o=n.N,a=[n];je(n);for(var l=u;l.circle&&xo(e-l.circle.x)<Uo&&xo(r-l.circle.cy)<Uo;)u=l.P,a.unshift(l),je(l),l=u;a.unshift(l),Be(l);for(var c=o;c.circle&&xo(e-c.circle.x)<Uo&&xo(r-c.circle.cy)<Uo;)o=c.N,a.push(c),je(c),c=o;a.push(c),Be(c);var f,s=a.length;for(f=1;s>f;++f)c=a[f],l=a[f-1],nr(c.edge,l.site,c.site,i);l=a[0],c=a[s-1],c.edge=Ke(l.site,c.site,null,i),$e(l),$e(c)}function He(n){for(var t,e,r,i,u=n.x,o=n.y,a=ol._;a;)if(r=Oe(a,o)-u,r>Uo)a=a.L;else{if(i=u-Ie(a,o),!(i>Uo)){r>-Uo?(t=a.P,e=a):i>-Uo?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var l=Ue(n);if(ol.insert(t,l),t||e){if(t===e)return Be(t),e=Ue(t.site),ol.insert(l,e),l.edge=e.edge=Ke(t.site,l.site),$e(t),void $e(e);if(!e)return void(l.edge=Ke(t.site,l.site));Be(t),Be(e);var c=t.site,f=c.x,s=c.y,h=n.x-f,p=n.y-s,g=e.site,v=g.x-f,d=g.y-s,y=2*(h*d-p*v),m=h*h+p*p,M=v*v+d*d,x={x:(d*m-p*M)/y+f,y:(h*M-v*m)/y+s};nr(e.edge,c,g,x),l.edge=Ke(c,n,null,x),e.edge=Ke(n,g,null,x),$e(t),$e(e)}}function Oe(n,t){var e=n.site,r=e.x,i=e.y,u=i-t;if(!u)return r;var o=n.P;if(!o)return-(1/0);e=o.site;var a=e.x,l=e.y,c=l-t;if(!c)return a;var f=a-r,s=1/u-1/c,h=f/c;return s?(-h+Math.sqrt(h*h-2*s*(f*f/(-2*c)-l+c/2+i-u/2)))/s+r:(r+a)/2}function Ie(n,t){var e=n.N;if(e)return Oe(e,t);var r=n.site;return r.y===t?r.x:1/0}function Ye(n){this.site=n,this.edges=[]}function Ze(n){for(var t,e,r,i,u,o,a,l,c,f,s=n[0][0],h=n[1][0],p=n[0][1],g=n[1][1],v=ul,d=v.length;d--;)if(u=v[d],u&&u.prepare())for(a=u.edges,l=a.length,o=0;l>o;)f=a[o].end(),r=f.x,i=f.y,c=a[++o%l].start(),t=c.x,e=c.y,(xo(r-t)>Uo||xo(i-e)>Uo)&&(a.splice(o,0,new tr(Qe(u.site,f,xo(r-s)<Uo&&g-i>Uo?{x:s,y:xo(t-s)<Uo?e:g}:xo(i-g)<Uo&&h-r>Uo?{x:xo(e-g)<Uo?t:h,y:g}:xo(r-h)<Uo&&i-p>Uo?{x:h,y:xo(t-h)<Uo?e:p}:xo(i-p)<Uo&&r-s>Uo?{x:xo(e-p)<Uo?t:s,y:p}:null),u.site,null)),++l)}function Ve(n,t){return t.angle-n.angle}function Xe(){rr(this),this.x=this.y=this.arc=this.site=this.cy=null}function $e(n){var t=n.P,e=n.N;if(t&&e){var r=t.site,i=n.site,u=e.site;if(r!==u){var o=i.x,a=i.y,l=r.x-o,c=r.y-a,f=u.x-o,s=u.y-a,h=2*(l*s-c*f);if(!(h>=-jo)){var p=l*l+c*c,g=f*f+s*s,v=(s*p-c*g)/h,d=(l*g-f*p)/h,s=d+a,y=fl.pop()||new Xe;y.arc=n,y.site=i,y.x=v+o,y.y=s+Math.sqrt(v*v+d*d),y.cy=s,n.circle=y;for(var m=null,M=ll._;M;)if(y.y<M.y||y.y===M.y&&y.x<=M.x){if(!M.L){m=M.P;break}M=M.L}else{if(!M.R){m=M;break}M=M.R}ll.insert(m,y),m||(al=y)}}}}function Be(n){var t=n.circle;t&&(t.P||(al=t.N),ll.remove(t),fl.push(t),rr(t),n.circle=null)}function We(n){for(var t,e=il,r=Yt(n[0][0],n[0][1],n[1][0],n[1][1]),i=e.length;i--;)t=e[i],(!Je(t,n)||!r(t)||xo(t.a.x-t.b.x)<Uo&&xo(t.a.y-t.b.y)<Uo)&&(t.a=t.b=null,e.splice(i,1))}function Je(n,t){var e=n.b;if(e)return!0;var r,i,u=n.a,o=t[0][0],a=t[1][0],l=t[0][1],c=t[1][1],f=n.l,s=n.r,h=f.x,p=f.y,g=s.x,v=s.y,d=(h+g)/2,y=(p+v)/2;if(v===p){if(o>d||d>=a)return;if(h>g){if(u){if(u.y>=c)return}else u={x:d,y:l};e={x:d,y:c}}else{if(u){if(u.y<l)return}else u={x:d,y:c};e={x:d,y:l}}}else if(r=(h-g)/(v-p),i=y-r*d,-1>r||r>1)if(h>g){if(u){if(u.y>=c)return}else u={x:(l-i)/r,y:l};e={x:(c-i)/r,y:c}}else{if(u){if(u.y<l)return}else u={x:(c-i)/r,y:c};e={x:(l-i)/r,y:l}}else if(v>p){if(u){if(u.x>=a)return}else u={x:o,y:r*o+i};e={x:a,y:r*a+i}}else{if(u){if(u.x<o)return}else u={x:a,y:r*a+i};e={x:o,y:r*o+i}}return n.a=u,n.b=e,!0}function Ge(n,t){this.l=n,this.r=t,this.a=this.b=null}function Ke(n,t,e,r){var i=new Ge(n,t);return il.push(i),e&&nr(i,n,t,e),r&&nr(i,t,n,r),ul[n.i].edges.push(new tr(i,n,t)),ul[t.i].edges.push(new tr(i,t,n)),i}function Qe(n,t,e){var r=new Ge(n,null);return r.a=t,r.b=e,il.push(r),r}function nr(n,t,e,r){n.a||n.b?n.l===e?n.b=r:n.a=r:(n.a=r,n.l=t,n.r=e)}function tr(n,t,e){var r=n.a,i=n.b;this.edge=n,this.site=t,this.angle=e?Math.atan2(e.y-t.y,e.x-t.x):n.l===t?Math.atan2(i.x-r.x,r.y-i.y):Math.atan2(r.x-i.x,i.y-r.y)}function er(){this._=null}function rr(n){n.U=n.C=n.L=n.R=n.P=n.N=null}function ir(n,t){var e=t,r=t.R,i=e.U;i?i.L===e?i.L=r:i.R=r:n._=r,r.U=i,e.U=r,e.R=r.L,e.R&&(e.R.U=e),r.L=e}function ur(n,t){var e=t,r=t.L,i=e.U;i?i.L===e?i.L=r:i.R=r:n._=r,r.U=i,e.U=r,e.L=r.R,e.L&&(e.L.U=e),r.R=e}function or(n){for(;n.L;)n=n.L;return n}function ar(n,t){var e,r,i,u=n.sort(lr).pop();for(il=[],ul=new Array(n.length),ol=new er,ll=new er;;)if(i=al,u&&(!i||u.y<i.y||u.y===i.y&&u.x<i.x))u.x===e&&u.y===r||(ul[u.i]=new Ye(u),He(u),e=u.x,r=u.y),u=n.pop();else{if(!i)break;Fe(i.arc)}t&&(We(t),Ze(t));var o={cells:ul,edges:il};return ol=ll=il=ul=null,o}function lr(n,t){return t.y-n.y||t.x-n.x}function cr(n,t,e){return(n.x-e.x)*(t.y-n.y)-(n.x-t.x)*(e.y-n.y)}function fr(n){return n.x}function sr(n){return n.y}function hr(){return{leaf:!0,nodes:[],point:null,x:null,y:null}}function pr(n,t,e,r,i,u){if(!n(t,e,r,i,u)){var o=.5*(e+i),a=.5*(r+u),l=t.nodes;l[0]&&pr(n,l[0],e,r,o,a),l[1]&&pr(n,l[1],o,r,i,a),l[2]&&pr(n,l[2],e,a,o,u),l[3]&&pr(n,l[3],o,a,i,u)}}function gr(n,t,e,r,i,u,o){var a,l=1/0;return function c(n,f,s,h,p){if(!(f>u||s>o||r>h||i>p)){if(g=n.point){var g,v=t-n.x,d=e-n.y,y=v*v+d*d;if(l>y){var m=Math.sqrt(l=y);r=t-m,i=e-m,u=t+m,o=e+m,a=g}}for(var M=n.nodes,x=.5*(f+h),b=.5*(s+p),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:c(n,f,s,x,b);break;case 1:c(n,x,s,h,b);break;case 2:c(n,f,b,x,p);break;case 3:c(n,x,b,h,p)}}}(n,r,i,u,o),a}function vr(n,t){n=ao.rgb(n),t=ao.rgb(t);var e=n.r,r=n.g,i=n.b,u=t.r-e,o=t.g-r,a=t.b-i;return function(n){return"#"+bn(Math.round(e+u*n))+bn(Math.round(r+o*n))+bn(Math.round(i+a*n))}}function dr(n,t){var e,r={},i={};for(e in n)e in t?r[e]=Mr(n[e],t[e]):i[e]=n[e];for(e in t)e in n||(i[e]=t[e]);return function(n){for(e in r)i[e]=r[e](n);return i}}function yr(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function mr(n,t){var e,r,i,u=hl.lastIndex=pl.lastIndex=0,o=-1,a=[],l=[];for(n+="",t+="";(e=hl.exec(n))&&(r=pl.exec(t));)(i=r.index)>u&&(i=t.slice(u,i),a[o]?a[o]+=i:a[++o]=i),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,l.push({i:o,x:yr(e,r)})),u=pl.lastIndex;return u<t.length&&(i=t.slice(u),a[o]?a[o]+=i:a[++o]=i),a.length<2?l[0]?(t=l[0].x,function(n){return t(n)+""}):function(){return t}:(t=l.length,function(n){for(var e,r=0;t>r;++r)a[(e=l[r]).i]=e.x(n);return a.join("")})}function Mr(n,t){for(var e,r=ao.interpolators.length;--r>=0&&!(e=ao.interpolators[r](n,t)););return e}function xr(n,t){var e,r=[],i=[],u=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(Mr(n[e],t[e]));for(;u>e;++e)i[e]=n[e];for(;o>e;++e)i[e]=t[e];return function(n){for(e=0;a>e;++e)i[e]=r[e](n);return i}}function br(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function _r(n){return function(t){return 1-n(1-t)}}function wr(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function Sr(n){return n*n}function kr(n){return n*n*n}function Nr(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function Er(n){return function(t){return Math.pow(t,n)}}function Ar(n){return 1-Math.cos(n*Io)}function Cr(n){return Math.pow(2,10*(n-1))}function zr(n){return 1-Math.sqrt(1-n*n)}function Lr(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/Ho*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*Ho/t)}}function qr(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function Tr(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Rr(n,t){n=ao.hcl(n),t=ao.hcl(t);var e=n.h,r=n.c,i=n.l,u=t.h-e,o=t.c-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return sn(e+u*n,r+o*n,i+a*n)+""}}function Dr(n,t){n=ao.hsl(n),t=ao.hsl(t);var e=n.h,r=n.s,i=n.l,u=t.h-e,o=t.s-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return cn(e+u*n,r+o*n,i+a*n)+""}}function Pr(n,t){n=ao.lab(n),t=ao.lab(t);var e=n.l,r=n.a,i=n.b,u=t.l-e,o=t.a-r,a=t.b-i;return function(n){return pn(e+u*n,r+o*n,i+a*n)+""}}function Ur(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function jr(n){var t=[n.a,n.b],e=[n.c,n.d],r=Hr(t),i=Fr(t,e),u=Hr(Or(e,t,-i))||0;t[0]*e[1]<e[0]*t[1]&&(t[0]*=-1,t[1]*=-1,r*=-1,i*=-1),this.rotate=(r?Math.atan2(t[1],t[0]):Math.atan2(-e[0],e[1]))*Zo,this.translate=[n.e,n.f],this.scale=[r,u],this.skew=u?Math.atan2(i,u)*Zo:0}function Fr(n,t){return n[0]*t[0]+n[1]*t[1]}function Hr(n){var t=Math.sqrt(Fr(n,n));return t&&(n[0]/=t,n[1]/=t),t}function Or(n,t,e){return n[0]+=e*t[0],n[1]+=e*t[1],n}function Ir(n){return n.length?n.pop()+",":""}function Yr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push("translate(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else(t[0]||t[1])&&e.push("translate("+t+")")}function Zr(n,t,e,r){n!==t?(n-t>180?t+=360:t-n>180&&(n+=360),r.push({i:e.push(Ir(e)+"rotate(",null,")")-2,x:yr(n,t)})):t&&e.push(Ir(e)+"rotate("+t+")")}function Vr(n,t,e,r){n!==t?r.push({i:e.push(Ir(e)+"skewX(",null,")")-2,x:yr(n,t)}):t&&e.push(Ir(e)+"skewX("+t+")")}function Xr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push(Ir(e)+"scale(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else 1===t[0]&&1===t[1]||e.push(Ir(e)+"scale("+t+")")}function $r(n,t){var e=[],r=[];return n=ao.transform(n),t=ao.transform(t),Yr(n.translate,t.translate,e,r),Zr(n.rotate,t.rotate,e,r),Vr(n.skew,t.skew,e,r),Xr(n.scale,t.scale,e,r),n=t=null,function(n){for(var t,i=-1,u=r.length;++i<u;)e[(t=r[i]).i]=t.x(n);return e.join("")}}function Br(n,t){return t=(t-=n=+n)||1/t,function(e){return(e-n)/t}}function Wr(n,t){return t=(t-=n=+n)||1/t,function(e){return Math.max(0,Math.min(1,(e-n)/t))}}function Jr(n){for(var t=n.source,e=n.target,r=Kr(t,e),i=[t];t!==r;)t=t.parent,i.push(t);for(var u=i.length;e!==r;)i.splice(u,0,e),e=e.parent;return i}function Gr(n){for(var t=[],e=n.parent;null!=e;)t.push(n),n=e,e=e.parent;return t.push(n),t}function Kr(n,t){if(n===t)return n;for(var e=Gr(n),r=Gr(t),i=e.pop(),u=r.pop(),o=null;i===u;)o=i,i=e.pop(),u=r.pop();return o}function Qr(n){n.fixed|=2}function ni(n){n.fixed&=-7}function ti(n){n.fixed|=4,n.px=n.x,n.py=n.y}function ei(n){n.fixed&=-5}function ri(n,t,e){var r=0,i=0;if(n.charge=0,!n.leaf)for(var u,o=n.nodes,a=o.length,l=-1;++l<a;)u=o[l],null!=u&&(ri(u,t,e),n.charge+=u.charge,r+=u.charge*u.cx,i+=u.charge*u.cy);if(n.point){n.leaf||(n.point.x+=Math.random()-.5,n.point.y+=Math.random()-.5);var c=t*e[n.point.index];n.charge+=n.pointCharge=c,r+=c*n.point.x,i+=c*n.point.y}n.cx=r/n.charge,n.cy=i/n.charge}function ii(n,t){return ao.rebind(n,t,"sort","children","value"),n.nodes=n,n.links=fi,n}function ui(n,t){for(var e=[n];null!=(n=e.pop());)if(t(n),(i=n.children)&&(r=i.length))for(var r,i;--r>=0;)e.push(i[r])}function oi(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(u=n.children)&&(i=u.length))for(var i,u,o=-1;++o<i;)e.push(u[o]);for(;null!=(n=r.pop());)t(n)}function ai(n){return n.children}function li(n){return n.value}function ci(n,t){return t.value-n.value}function fi(n){return ao.merge(n.map(function(n){return(n.children||[]).map(function(t){return{source:n,target:t}})}))}function si(n){return n.x}function hi(n){return n.y}function pi(n,t,e){n.y0=t,n.y=e}function gi(n){return ao.range(n.length)}function vi(n){for(var t=-1,e=n[0].length,r=[];++t<e;)r[t]=0;return r}function di(n){for(var t,e=1,r=0,i=n[0][1],u=n.length;u>e;++e)(t=n[e][1])>i&&(r=e,i=t);return r}function yi(n){return n.reduce(mi,0)}function mi(n,t){return n+t[1]}function Mi(n,t){return xi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function xi(n,t){for(var e=-1,r=+n[0],i=(n[1]-r)/t,u=[];++e<=t;)u[e]=i*e+r;return u}function bi(n){return[ao.min(n),ao.max(n)]}function _i(n,t){return n.value-t.value}function wi(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Si(n,t){n._pack_next=t,t._pack_prev=n}function ki(n,t){var e=t.x-n.x,r=t.y-n.y,i=n.r+t.r;return.999*i*i>e*e+r*r}function Ni(n){function t(n){f=Math.min(n.x-n.r,f),s=Math.max(n.x+n.r,s),h=Math.min(n.y-n.r,h),p=Math.max(n.y+n.r,p)}if((e=n.children)&&(c=e.length)){var e,r,i,u,o,a,l,c,f=1/0,s=-(1/0),h=1/0,p=-(1/0);if(e.forEach(Ei),r=e[0],r.x=-r.r,r.y=0,t(r),c>1&&(i=e[1],i.x=i.r,i.y=0,t(i),c>2))for(u=e[2],zi(r,i,u),t(u),wi(r,u),r._pack_prev=u,wi(u,i),i=r._pack_next,o=3;c>o;o++){zi(r,i,u=e[o]);var g=0,v=1,d=1;for(a=i._pack_next;a!==i;a=a._pack_next,v++)if(ki(a,u)){g=1;break}if(1==g)for(l=r._pack_prev;l!==a._pack_prev&&!ki(l,u);l=l._pack_prev,d++);g?(d>v||v==d&&i.r<r.r?Si(r,i=a):Si(r=l,i),o--):(wi(r,u),i=u,t(u))}var y=(f+s)/2,m=(h+p)/2,M=0;for(o=0;c>o;o++)u=e[o],u.x-=y,u.y-=m,M=Math.max(M,u.r+Math.sqrt(u.x*u.x+u.y*u.y));n.r=M,e.forEach(Ai)}}function Ei(n){n._pack_next=n._pack_prev=n}function Ai(n){delete n._pack_next,delete n._pack_prev}function Ci(n,t,e,r){var i=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,i)for(var u=-1,o=i.length;++u<o;)Ci(i[u],t,e,r)}function zi(n,t,e){var r=n.r+e.r,i=t.x-n.x,u=t.y-n.y;if(r&&(i||u)){var o=t.r+e.r,a=i*i+u*u;o*=o,r*=r;var l=.5+(r-o)/(2*a),c=Math.sqrt(Math.max(0,2*o*(r+a)-(r-=a)*r-o*o))/(2*a);e.x=n.x+l*i+c*u,e.y=n.y+l*u-c*i}else e.x=n.x+r,e.y=n.y}function Li(n,t){return n.parent==t.parent?1:2}function qi(n){var t=n.children;return t.length?t[0]:n.t}function Ti(n){var t,e=n.children;return(t=e.length)?e[t-1]:n.t}function Ri(n,t,e){var r=e/(t.i-n.i);t.c-=r,t.s+=e,n.c+=r,t.z+=e,t.m+=e}function Di(n){for(var t,e=0,r=0,i=n.children,u=i.length;--u>=0;)t=i[u],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Pi(n,t,e){return n.a.parent===t.parent?n.a:e}function Ui(n){return 1+ao.max(n,function(n){return n.y})}function ji(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Fi(n){var t=n.children;return t&&t.length?Fi(t[0]):n}function Hi(n){var t,e=n.children;return e&&(t=e.length)?Hi(e[t-1]):n}function Oi(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Ii(n,t){var e=n.x+t[3],r=n.y+t[0],i=n.dx-t[1]-t[3],u=n.dy-t[0]-t[2];return 0>i&&(e+=i/2,i=0),0>u&&(r+=u/2,u=0),{x:e,y:r,dx:i,dy:u}}function Yi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Zi(n){return n.rangeExtent?n.rangeExtent():Yi(n.range())}function Vi(n,t,e,r){var i=e(n[0],n[1]),u=r(t[0],t[1]);return function(n){return u(i(n))}}function Xi(n,t){var e,r=0,i=n.length-1,u=n[r],o=n[i];return u>o&&(e=r,r=i,i=e,e=u,u=o,o=e),n[r]=t.floor(u),n[i]=t.ceil(o),n}function $i(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:Sl}function Bi(n,t,e,r){var i=[],u=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]<n[0]&&(n=n.slice().reverse(),t=t.slice().reverse());++o<=a;)i.push(e(n[o-1],n[o])),u.push(r(t[o-1],t[o]));return function(t){var e=ao.bisect(n,t,1,a)-1;return u[e](i[e](t))}}function Wi(n,t,e,r){function i(){var i=Math.min(n.length,t.length)>2?Bi:Vi,l=r?Wr:Br;return o=i(n,t,l,e),a=i(t,n,l,Mr),u}function u(n){return o(n)}var o,a;return u.invert=function(n){return a(n)},u.domain=function(t){return arguments.length?(n=t.map(Number),i()):n},u.range=function(n){return arguments.length?(t=n,i()):t},u.rangeRound=function(n){return u.range(n).interpolate(Ur)},u.clamp=function(n){return arguments.length?(r=n,i()):r},u.interpolate=function(n){return arguments.length?(e=n,i()):e},u.ticks=function(t){return Qi(n,t)},u.tickFormat=function(t,e){return nu(n,t,e)},u.nice=function(t){return Gi(n,t),i()},u.copy=function(){return Wi(n,t,e,r)},i()}function Ji(n,t){return ao.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Gi(n,t){return Xi(n,$i(Ki(n,t)[2])),Xi(n,$i(Ki(n,t)[2])),n}function Ki(n,t){null==t&&(t=10);var e=Yi(n),r=e[1]-e[0],i=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),u=t/r*i;return.15>=u?i*=10:.35>=u?i*=5:.75>=u&&(i*=2),e[0]=Math.ceil(e[0]/i)*i,e[1]=Math.floor(e[1]/i)*i+.5*i,e[2]=i,e}function Qi(n,t){return ao.range.apply(ao,Ki(n,t))}function nu(n,t,e){var r=Ki(n,t);if(e){var i=ha.exec(e);if(i.shift(),"s"===i[8]){var u=ao.formatPrefix(Math.max(xo(r[0]),xo(r[1])));return i[7]||(i[7]="."+tu(u.scale(r[2]))),i[8]="f",e=ao.format(i.join("")),function(n){return e(u.scale(n))+u.symbol}}i[7]||(i[7]="."+eu(i[8],r)),e=i.join("")}else e=",."+tu(r[2])+"f";return ao.format(e)}function tu(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function eu(n,t){var e=tu(t[2]);return n in kl?Math.abs(e-tu(Math.max(xo(t[0]),xo(t[1]))))+ +("e"!==n):e-2*("%"===n)}function ru(n,t,e,r){function i(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function u(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(i(t))}return o.invert=function(t){return u(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(i)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(i)),o):t},o.nice=function(){var t=Xi(r.map(i),e?Math:El);return n.domain(t),r=t.map(u),o},o.ticks=function(){var n=Yi(r),o=[],a=n[0],l=n[1],c=Math.floor(i(a)),f=Math.ceil(i(l)),s=t%1?2:t;if(isFinite(f-c)){if(e){for(;f>c;c++)for(var h=1;s>h;h++)o.push(u(c)*h);o.push(u(c))}else for(o.push(u(c));c++<f;)for(var h=s-1;h>0;h--)o.push(u(c)*h);for(c=0;o[c]<a;c++);for(f=o.length;o[f-1]>l;f--);o=o.slice(c,f)}return o},o.tickFormat=function(n,e){if(!arguments.length)return Nl;arguments.length<2?e=Nl:"function"!=typeof e&&(e=ao.format(e));var r=Math.max(1,t*n/o.ticks().length);return function(n){var o=n/u(Math.round(i(n)));return t-.5>o*t&&(o*=t),r>=o?e(n):""}},o.copy=function(){return ru(n.copy(),t,e,r)},Ji(o,n)}function iu(n,t,e){function r(t){return n(i(t))}var i=uu(t),u=uu(1/t);return r.invert=function(t){return u(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(i)),r):e},r.ticks=function(n){return Qi(e,n)},r.tickFormat=function(n,t){return nu(e,n,t)},r.nice=function(n){return r.domain(Gi(e,n))},r.exponent=function(o){return arguments.length?(i=uu(t=o),u=uu(1/t),n.domain(e.map(i)),r):t},r.copy=function(){return iu(n.copy(),t,e)},Ji(r,n)}function uu(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function ou(n,t){function e(e){return u[((i.get(e)||("range"===t.t?i.set(e,n.push(e)):NaN))-1)%u.length]}function r(t,e){return ao.range(n.length).map(function(n){return t+e*n})}var i,u,o;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new c;for(var u,o=-1,a=r.length;++o<a;)i.has(u=r[o])||i.set(u,n.push(u));return e[t.t].apply(e,t.a)},e.range=function(n){return arguments.length?(u=n,o=0,t={t:"range",a:arguments},e):u},e.rangePoints=function(i,a){arguments.length<2&&(a=0);var l=i[0],c=i[1],f=n.length<2?(l=(l+c)/2,0):(c-l)/(n.length-1+a);return u=r(l+f*a/2,f),o=0,t={t:"rangePoints",a:arguments},e},e.rangeRoundPoints=function(i,a){arguments.length<2&&(a=0);var l=i[0],c=i[1],f=n.length<2?(l=c=Math.round((l+c)/2),0):(c-l)/(n.length-1+a)|0;return u=r(l+Math.round(f*a/2+(c-l-(n.length-1+a)*f)/2),f),o=0,t={t:"rangeRoundPoints",a:arguments},e},e.rangeBands=function(i,a,l){arguments.length<2&&(a=0),arguments.length<3&&(l=a);var c=i[1]<i[0],f=i[c-0],s=i[1-c],h=(s-f)/(n.length-a+2*l);return u=r(f+h*l,h),c&&u.reverse(),o=h*(1-a),t={t:"rangeBands",a:arguments},e},e.rangeRoundBands=function(i,a,l){arguments.length<2&&(a=0),arguments.length<3&&(l=a);var c=i[1]<i[0],f=i[c-0],s=i[1-c],h=Math.floor((s-f)/(n.length-a+2*l));return u=r(f+Math.round((s-f-(n.length-a)*h)/2),h),c&&u.reverse(),o=Math.round(h*(1-a)),t={t:"rangeRoundBands",a:arguments},e},e.rangeBand=function(){return o},e.rangeExtent=function(){return Yi(t.a[0])},e.copy=function(){return ou(n,t)},e.domain(n)}function au(n,t){function u(){var e=0,r=t.length;for(a=[];++e<r;)a[e-1]=ao.quantile(n,e/r);return o}function o(n){return isNaN(n=+n)?void 0:t[ao.bisect(a,n)]}var a;return o.domain=function(t){return arguments.length?(n=t.map(r).filter(i).sort(e),u()):n},o.range=function(n){return arguments.length?(t=n,u()):t},o.quantiles=function(){return a},o.invertExtent=function(e){return e=t.indexOf(e),0>e?[NaN,NaN]:[e>0?a[e-1]:n[0],e<a.length?a[e]:n[n.length-1]]},o.copy=function(){return au(n,t)},u()}function lu(n,t,e){function r(t){return e[Math.max(0,Math.min(o,Math.floor(u*(t-n))))]}function i(){return u=e.length/(t-n),o=e.length-1,r}var u,o;return r.domain=function(e){return arguments.length?(n=+e[0],t=+e[e.length-1],i()):[n,t]},r.range=function(n){return arguments.length?(e=n,i()):e},r.invertExtent=function(t){return t=e.indexOf(t),t=0>t?NaN:t/u+n,[t,t+1/u]},r.copy=function(){return lu(n,t,e)},i()}function cu(n,t){function e(e){return e>=e?t[ao.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return cu(n,t)},e}function fu(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Qi(n,t)},t.tickFormat=function(t,e){return nu(n,t,e)},t.copy=function(){return fu(n)},t}function su(){return 0}function hu(n){return n.innerRadius}function pu(n){return n.outerRadius}function gu(n){return n.startAngle}function vu(n){return n.endAngle}function du(n){return n&&n.padAngle}function yu(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function mu(n,t,e,r,i){var u=n[0]-t[0],o=n[1]-t[1],a=(i?r:-r)/Math.sqrt(u*u+o*o),l=a*o,c=-a*u,f=n[0]+l,s=n[1]+c,h=t[0]+l,p=t[1]+c,g=(f+h)/2,v=(s+p)/2,d=h-f,y=p-s,m=d*d+y*y,M=e-r,x=f*p-h*s,b=(0>y?-1:1)*Math.sqrt(Math.max(0,M*M*m-x*x)),_=(x*y-d*b)/m,w=(-x*d-y*b)/m,S=(x*y+d*b)/m,k=(-x*d+y*b)/m,N=_-g,E=w-v,A=S-g,C=k-v;return N*N+E*E>A*A+C*C&&(_=S,w=k),[[_-l,w-c],[_*e/M,w*e/M]]}function Mu(n){function t(t){function o(){c.push("M",u(n(f),a))}for(var l,c=[],f=[],s=-1,h=t.length,p=En(e),g=En(r);++s<h;)i.call(this,l=t[s],s)?f.push([+p.call(this,l,s),+g.call(this,l,s)]):f.length&&(o(),f=[]);return f.length&&o(),c.length?c.join(""):null}var e=Ce,r=ze,i=zt,u=xu,o=u.key,a=.7;return t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t.defined=function(n){return arguments.length?(i=n,t):i},t.interpolate=function(n){return arguments.length?(o="function"==typeof n?u=n:(u=Tl.get(n)||xu).key,t):o},t.tension=function(n){return arguments.length?(a=n,t):a},t}function xu(n){return n.length>1?n.join("L"):n+"Z"}function bu(n){return n.join("L")+"Z"}function _u(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("H",(r[0]+(r=n[t])[0])/2,"V",r[1]);return e>1&&i.push("H",r[0]),i.join("")}function wu(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("V",(r=n[t])[1],"H",r[0]);return i.join("")}function Su(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("H",(r=n[t])[0],"V",r[1]);return i.join("")}function ku(n,t){return n.length<4?xu(n):n[1]+Au(n.slice(1,-1),Cu(n,t))}function Nu(n,t){return n.length<3?bu(n):n[0]+Au((n.push(n[0]),n),Cu([n[n.length-2]].concat(n,[n[1]]),t))}function Eu(n,t){return n.length<3?xu(n):n[0]+Au(n,Cu(n,t))}function Au(n,t){if(t.length<1||n.length!=t.length&&n.length!=t.length+2)return xu(n);var e=n.length!=t.length,r="",i=n[0],u=n[1],o=t[0],a=o,l=1;if(e&&(r+="Q"+(u[0]-2*o[0]/3)+","+(u[1]-2*o[1]/3)+","+u[0]+","+u[1],i=n[1],l=2),t.length>1){a=t[1],u=n[l],l++,r+="C"+(i[0]+o[0])+","+(i[1]+o[1])+","+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1];for(var c=2;c<t.length;c++,l++)u=n[l],a=t[c],r+="S"+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1]}if(e){var f=n[l];r+="Q"+(u[0]+2*a[0]/3)+","+(u[1]+2*a[1]/3)+","+f[0]+","+f[1]}return r}function Cu(n,t){for(var e,r=[],i=(1-t)/2,u=n[0],o=n[1],a=1,l=n.length;++a<l;)e=u,u=o,o=n[a],r.push([i*(o[0]-e[0]),i*(o[1]-e[1])]);return r}function zu(n){if(n.length<3)return xu(n);var t=1,e=n.length,r=n[0],i=r[0],u=r[1],o=[i,i,i,(r=n[1])[0]],a=[u,u,u,r[1]],l=[i,",",u,"L",Ru(Pl,o),",",Ru(Pl,a)];for(n.push(n[e-1]);++t<=e;)r=n[t],o.shift(),o.push(r[0]),a.shift(),a.push(r[1]),Du(l,o,a);return n.pop(),l.push("L",r),l.join("")}function Lu(n){if(n.length<4)return xu(n);for(var t,e=[],r=-1,i=n.length,u=[0],o=[0];++r<3;)t=n[r],u.push(t[0]),o.push(t[1]);for(e.push(Ru(Pl,u)+","+Ru(Pl,o)),--r;++r<i;)t=n[r],u.shift(),u.push(t[0]),o.shift(),o.push(t[1]),Du(e,u,o);return e.join("")}function qu(n){for(var t,e,r=-1,i=n.length,u=i+4,o=[],a=[];++r<4;)e=n[r%i],o.push(e[0]),a.push(e[1]);for(t=[Ru(Pl,o),",",Ru(Pl,a)],--r;++r<u;)e=n[r%i],o.shift(),o.push(e[0]),a.shift(),a.push(e[1]),Du(t,o,a);return t.join("")}function Tu(n,t){var e=n.length-1;if(e)for(var r,i,u=n[0][0],o=n[0][1],a=n[e][0]-u,l=n[e][1]-o,c=-1;++c<=e;)r=n[c],i=c/e,r[0]=t*r[0]+(1-t)*(u+i*a),r[1]=t*r[1]+(1-t)*(o+i*l);return zu(n)}function Ru(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]+n[3]*t[3]}function Du(n,t,e){n.push("C",Ru(Rl,t),",",Ru(Rl,e),",",Ru(Dl,t),",",Ru(Dl,e),",",Ru(Pl,t),",",Ru(Pl,e))}function Pu(n,t){return(t[1]-n[1])/(t[0]-n[0])}function Uu(n){for(var t=0,e=n.length-1,r=[],i=n[0],u=n[1],o=r[0]=Pu(i,u);++t<e;)r[t]=(o+(o=Pu(i=u,u=n[t+1])))/2;return r[t]=o,r}function ju(n){for(var t,e,r,i,u=[],o=Uu(n),a=-1,l=n.length-1;++a<l;)t=Pu(n[a],n[a+1]),xo(t)<Uo?o[a]=o[a+1]=0:(e=o[a]/t,r=o[a+1]/t,i=e*e+r*r,i>9&&(i=3*t/Math.sqrt(i),o[a]=i*e,o[a+1]=i*r));for(a=-1;++a<=l;)i=(n[Math.min(l,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),u.push([i||0,o[a]*i||0]);return u}function Fu(n){return n.length<3?xu(n):n[0]+Au(n,ju(n))}function Hu(n){for(var t,e,r,i=-1,u=n.length;++i<u;)t=n[i],e=t[0],r=t[1]-Io,t[0]=e*Math.cos(r),t[1]=e*Math.sin(r);return n}function Ou(n){function t(t){function l(){v.push("M",a(n(y),s),f,c(n(d.reverse()),s),"Z")}for(var h,p,g,v=[],d=[],y=[],m=-1,M=t.length,x=En(e),b=En(i),_=e===r?function(){ return p}:En(r),w=i===u?function(){return g}:En(u);++m<M;)o.call(this,h=t[m],m)?(d.push([p=+x.call(this,h,m),g=+b.call(this,h,m)]),y.push([+_.call(this,h,m),+w.call(this,h,m)])):d.length&&(l(),d=[],y=[]);return d.length&&l(),v.length?v.join(""):null}var e=Ce,r=Ce,i=0,u=ze,o=zt,a=xu,l=a.key,c=a,f="L",s=.7;return t.x=function(n){return arguments.length?(e=r=n,t):r},t.x0=function(n){return arguments.length?(e=n,t):e},t.x1=function(n){return arguments.length?(r=n,t):r},t.y=function(n){return arguments.length?(i=u=n,t):u},t.y0=function(n){return arguments.length?(i=n,t):i},t.y1=function(n){return arguments.length?(u=n,t):u},t.defined=function(n){return arguments.length?(o=n,t):o},t.interpolate=function(n){return arguments.length?(l="function"==typeof n?a=n:(a=Tl.get(n)||xu).key,c=a.reverse||a,f=a.closed?"M":"L",t):l},t.tension=function(n){return arguments.length?(s=n,t):s},t}function Iu(n){return n.radius}function Yu(n){return[n.x,n.y]}function Zu(n){return function(){var t=n.apply(this,arguments),e=t[0],r=t[1]-Io;return[e*Math.cos(r),e*Math.sin(r)]}}function Vu(){return 64}function Xu(){return"circle"}function $u(n){var t=Math.sqrt(n/Fo);return"M0,"+t+"A"+t+","+t+" 0 1,1 0,"+-t+"A"+t+","+t+" 0 1,1 0,"+t+"Z"}function Bu(n){return function(){var t,e,r;(t=this[n])&&(r=t[e=t.active])&&(r.timer.c=null,r.timer.t=NaN,--t.count?delete t[e]:delete this[n],t.active+=.5,r.event&&r.event.interrupt.call(this,this.__data__,r.index))}}function Wu(n,t,e){return ko(n,Yl),n.namespace=t,n.id=e,n}function Ju(n,t,e,r){var i=n.id,u=n.namespace;return Y(n,"function"==typeof e?function(n,o,a){n[u][i].tween.set(t,r(e.call(n,n.__data__,o,a)))}:(e=r(e),function(n){n[u][i].tween.set(t,e)}))}function Gu(n){return null==n&&(n=""),function(){this.textContent=n}}function Ku(n){return null==n?"__transition__":"__transition_"+n+"__"}function Qu(n,t,e,r,i){function u(n){var t=v.delay;return f.t=t+l,n>=t?o(n-t):void(f.c=o)}function o(e){var i=g.active,u=g[i];u&&(u.timer.c=null,u.timer.t=NaN,--g.count,delete g[i],u.event&&u.event.interrupt.call(n,n.__data__,u.index));for(var o in g)if(r>+o){var c=g[o];c.timer.c=null,c.timer.t=NaN,--g.count,delete g[o]}f.c=a,qn(function(){return f.c&&a(e||1)&&(f.c=null,f.t=NaN),1},0,l),g.active=r,v.event&&v.event.start.call(n,n.__data__,t),p=[],v.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&p.push(r)}),h=v.ease,s=v.duration}function a(i){for(var u=i/s,o=h(u),a=p.length;a>0;)p[--a].call(n,o);return u>=1?(v.event&&v.event.end.call(n,n.__data__,t),--g.count?delete g[r]:delete n[e],1):void 0}var l,f,s,h,p,g=n[e]||(n[e]={active:0,count:0}),v=g[r];v||(l=i.time,f=qn(u,0,l),v=g[r]={tween:new c,time:l,timer:f,delay:i.delay,duration:i.duration,ease:i.ease,index:t},i=null,++g.count)}function no(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function to(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function eo(n){return n.toISOString()}function ro(n,t,e){function r(t){return n(t)}function i(n,e){var r=n[1]-n[0],i=r/e,u=ao.bisect(Kl,i);return u==Kl.length?[t.year,Ki(n.map(function(n){return n/31536e6}),e)[2]]:u?t[i/Kl[u-1]<Kl[u]/i?u-1:u]:[tc,Ki(n,e)[2]]}return r.invert=function(t){return io(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain(t),r):n.domain().map(io)},r.nice=function(n,t){function e(e){return!isNaN(e)&&!n.range(e,io(+e+1),t).length}var u=r.domain(),o=Yi(u),a=null==n?i(o,10):"number"==typeof n&&i(o,n);return a&&(n=a[0],t=a[1]),r.domain(Xi(u,t>1?{floor:function(t){for(;e(t=n.floor(t));)t=io(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=io(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Yi(r.domain()),u=null==n?i(e,10):"number"==typeof n?i(e,n):!n.range&&[{range:n},t];return u&&(n=u[0],t=u[1]),n.range(e[0],io(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return ro(n.copy(),t,e)},Ji(r,n)}function io(n){return new Date(n)}function uo(n){return JSON.parse(n.responseText)}function oo(n){var t=fo.createRange();return t.selectNode(fo.body),t.createContextualFragment(n.responseText)}var ao={version:"3.5.17"},lo=[].slice,co=function(n){return lo.call(n)},fo=this.document;if(fo)try{co(fo.documentElement.childNodes)[0].nodeType}catch(so){co=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}if(Date.now||(Date.now=function(){return+new Date}),fo)try{fo.createElement("DIV").style.setProperty("opacity",0,"")}catch(ho){var po=this.Element.prototype,go=po.setAttribute,vo=po.setAttributeNS,yo=this.CSSStyleDeclaration.prototype,mo=yo.setProperty;po.setAttribute=function(n,t){go.call(this,n,t+"")},po.setAttributeNS=function(n,t,e){vo.call(this,n,t,e+"")},yo.setProperty=function(n,t,e){mo.call(this,n,t+"",e)}}ao.ascending=e,ao.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:NaN},ao.min=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i<u;)if(null!=(r=n[i])&&r>=r){e=r;break}for(;++i<u;)null!=(r=n[i])&&e>r&&(e=r)}else{for(;++i<u;)if(null!=(r=t.call(n,n[i],i))&&r>=r){e=r;break}for(;++i<u;)null!=(r=t.call(n,n[i],i))&&e>r&&(e=r)}return e},ao.max=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i<u;)if(null!=(r=n[i])&&r>=r){e=r;break}for(;++i<u;)null!=(r=n[i])&&r>e&&(e=r)}else{for(;++i<u;)if(null!=(r=t.call(n,n[i],i))&&r>=r){e=r;break}for(;++i<u;)null!=(r=t.call(n,n[i],i))&&r>e&&(e=r)}return e},ao.extent=function(n,t){var e,r,i,u=-1,o=n.length;if(1===arguments.length){for(;++u<o;)if(null!=(r=n[u])&&r>=r){e=i=r;break}for(;++u<o;)null!=(r=n[u])&&(e>r&&(e=r),r>i&&(i=r))}else{for(;++u<o;)if(null!=(r=t.call(n,n[u],u))&&r>=r){e=i=r;break}for(;++u<o;)null!=(r=t.call(n,n[u],u))&&(e>r&&(e=r),r>i&&(i=r))}return[e,i]},ao.sum=function(n,t){var e,r=0,u=n.length,o=-1;if(1===arguments.length)for(;++o<u;)i(e=+n[o])&&(r+=e);else for(;++o<u;)i(e=+t.call(n,n[o],o))&&(r+=e);return r},ao.mean=function(n,t){var e,u=0,o=n.length,a=-1,l=o;if(1===arguments.length)for(;++a<o;)i(e=r(n[a]))?u+=e:--l;else for(;++a<o;)i(e=r(t.call(n,n[a],a)))?u+=e:--l;return l?u/l:void 0},ao.quantile=function(n,t){var e=(n.length-1)*t+1,r=Math.floor(e),i=+n[r-1],u=e-r;return u?i+u*(n[r]-i):i},ao.median=function(n,t){var u,o=[],a=n.length,l=-1;if(1===arguments.length)for(;++l<a;)i(u=r(n[l]))&&o.push(u);else for(;++l<a;)i(u=r(t.call(n,n[l],l)))&&o.push(u);return o.length?ao.quantile(o.sort(e),.5):void 0},ao.variance=function(n,t){var e,u,o=n.length,a=0,l=0,c=-1,f=0;if(1===arguments.length)for(;++c<o;)i(e=r(n[c]))&&(u=e-a,a+=u/++f,l+=u*(e-a));else for(;++c<o;)i(e=r(t.call(n,n[c],c)))&&(u=e-a,a+=u/++f,l+=u*(e-a));return f>1?l/(f-1):void 0},ao.deviation=function(){var n=ao.variance.apply(this,arguments);return n?Math.sqrt(n):n};var Mo=u(e);ao.bisectLeft=Mo.left,ao.bisect=ao.bisectRight=Mo.right,ao.bisector=function(n){return u(1===n.length?function(t,r){return e(n(t),r)}:n)},ao.shuffle=function(n,t,e){(u=arguments.length)<3&&(e=n.length,2>u&&(t=0));for(var r,i,u=e-t;u;)i=Math.random()*u--|0,r=n[u+t],n[u+t]=n[i+t],n[i+t]=r;return n},ao.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ao.pairs=function(n){for(var t,e=0,r=n.length-1,i=n[0],u=new Array(0>r?0:r);r>e;)u[e]=[t=i,i=n[++e]];return u},ao.transpose=function(n){if(!(i=n.length))return[];for(var t=-1,e=ao.min(n,o),r=new Array(e);++t<e;)for(var i,u=-1,a=r[t]=new Array(i);++u<i;)a[u]=n[u][t];return r},ao.zip=function(){return ao.transpose(arguments)},ao.keys=function(n){var t=[];for(var e in n)t.push(e);return t},ao.values=function(n){var t=[];for(var e in n)t.push(n[e]);return t},ao.entries=function(n){var t=[];for(var e in n)t.push({key:e,value:n[e]});return t},ao.merge=function(n){for(var t,e,r,i=n.length,u=-1,o=0;++u<i;)o+=n[u].length;for(e=new Array(o);--i>=0;)for(r=n[i],t=r.length;--t>=0;)e[--o]=r[t];return e};var xo=Math.abs;ao.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),(t-n)/e===1/0)throw new Error("infinite range");var r,i=[],u=a(xo(e)),o=-1;if(n*=u,t*=u,e*=u,0>e)for(;(r=n+e*++o)>t;)i.push(r/u);else for(;(r=n+e*++o)<t;)i.push(r/u);return i},ao.map=function(n,t){var e=new c;if(n instanceof c)n.forEach(function(n,t){e.set(n,t)});else if(Array.isArray(n)){var r,i=-1,u=n.length;if(1===arguments.length)for(;++i<u;)e.set(i,n[i]);else for(;++i<u;)e.set(t.call(n,r=n[i],i),r)}else for(var o in n)e.set(o,n[o]);return e};var bo="__proto__",_o="\x00";l(c,{has:h,get:function(n){return this._[f(n)]},set:function(n,t){return this._[f(n)]=t},remove:p,keys:g,values:function(){var n=[];for(var t in this._)n.push(this._[t]);return n},entries:function(){var n=[];for(var t in this._)n.push({key:s(t),value:this._[t]});return n},size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t),this._[t])}}),ao.nest=function(){function n(t,o,a){if(a>=u.length)return r?r.call(i,o):e?o.sort(e):o;for(var l,f,s,h,p=-1,g=o.length,v=u[a++],d=new c;++p<g;)(h=d.get(l=v(f=o[p])))?h.push(f):d.set(l,[f]);return t?(f=t(),s=function(e,r){f.set(e,n(t,r,a))}):(f={},s=function(e,r){f[e]=n(t,r,a)}),d.forEach(s),f}function t(n,e){if(e>=u.length)return n;var r=[],i=o[e++];return n.forEach(function(n,i){r.push({key:n,values:t(i,e)})}),i?r.sort(function(n,t){return i(n.key,t.key)}):r}var e,r,i={},u=[],o=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n(ao.map,e,0),0)},i.key=function(n){return u.push(n),i},i.sortKeys=function(n){return o[u.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},ao.set=function(n){var t=new y;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},l(y,{has:h,add:function(n){return this._[f(n+="")]=!0,n},remove:p,values:g,size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t))}}),ao.behavior={},ao.rebind=function(n,t){for(var e,r=1,i=arguments.length;++r<i;)n[e=arguments[r]]=M(n,t,t[e]);return n};var wo=["webkit","ms","moz","Moz","o","O"];ao.dispatch=function(){for(var n=new _,t=-1,e=arguments.length;++t<e;)n[arguments[t]]=w(n);return n},_.prototype.on=function(n,t){var e=n.indexOf("."),r="";if(e>=0&&(r=n.slice(e+1),n=n.slice(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},ao.event=null,ao.requote=function(n){return n.replace(So,"\\$&")};var So=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ko={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},No=function(n,t){return t.querySelector(n)},Eo=function(n,t){return t.querySelectorAll(n)},Ao=function(n,t){var e=n.matches||n[x(n,"matchesSelector")];return(Ao=function(n,t){return e.call(n,t)})(n,t)};"function"==typeof Sizzle&&(No=function(n,t){return Sizzle(n,t)[0]||null},Eo=Sizzle,Ao=Sizzle.matchesSelector),ao.selection=function(){return ao.select(fo.documentElement)};var Co=ao.selection.prototype=[];Co.select=function(n){var t,e,r,i,u=[];n=A(n);for(var o=-1,a=this.length;++o<a;){u.push(t=[]),t.parentNode=(r=this[o]).parentNode;for(var l=-1,c=r.length;++l<c;)(i=r[l])?(t.push(e=n.call(i,i.__data__,l,o)),e&&"__data__"in i&&(e.__data__=i.__data__)):t.push(null)}return E(u)},Co.selectAll=function(n){var t,e,r=[];n=C(n);for(var i=-1,u=this.length;++i<u;)for(var o=this[i],a=-1,l=o.length;++a<l;)(e=o[a])&&(r.push(t=co(n.call(e,e.__data__,a,i))),t.parentNode=e);return E(r)};var zo="http://www.w3.org/1999/xhtml",Lo={svg:"http://www.w3.org/2000/svg",xhtml:zo,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};ao.ns={prefix:Lo,qualify:function(n){var t=n.indexOf(":"),e=n;return t>=0&&"xmlns"!==(e=n.slice(0,t))&&(n=n.slice(t+1)),Lo.hasOwnProperty(e)?{space:Lo[e],local:n}:n}},Co.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ao.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(z(t,n[t]));return this}return this.each(z(n,t))},Co.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=T(n)).length,i=-1;if(t=e.classList){for(;++i<r;)if(!t.contains(n[i]))return!1}else for(t=e.getAttribute("class");++i<r;)if(!q(n[i]).test(t))return!1;return!0}for(t in n)this.each(R(t,n[t]));return this}return this.each(R(n,t))},Co.style=function(n,e,r){var i=arguments.length;if(3>i){if("string"!=typeof n){2>i&&(e="");for(r in n)this.each(P(r,n[r],e));return this}if(2>i){var u=this.node();return t(u).getComputedStyle(u,null).getPropertyValue(n)}r=""}return this.each(P(n,e,r))},Co.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(U(t,n[t]));return this}return this.each(U(n,t))},Co.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},Co.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},Co.append=function(n){return n=j(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},Co.insert=function(n,t){return n=j(n),t=A(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},Co.remove=function(){return this.each(F)},Co.data=function(n,t){function e(n,e){var r,i,u,o=n.length,s=e.length,h=Math.min(o,s),p=new Array(s),g=new Array(s),v=new Array(o);if(t){var d,y=new c,m=new Array(o);for(r=-1;++r<o;)(i=n[r])&&(y.has(d=t.call(i,i.__data__,r))?v[r]=i:y.set(d,i),m[r]=d);for(r=-1;++r<s;)(i=y.get(d=t.call(e,u=e[r],r)))?i!==!0&&(p[r]=i,i.__data__=u):g[r]=H(u),y.set(d,!0);for(r=-1;++r<o;)r in m&&y.get(m[r])!==!0&&(v[r]=n[r])}else{for(r=-1;++r<h;)i=n[r],u=e[r],i?(i.__data__=u,p[r]=i):g[r]=H(u);for(;s>r;++r)g[r]=H(e[r]);for(;o>r;++r)v[r]=n[r]}g.update=p,g.parentNode=p.parentNode=v.parentNode=n.parentNode,a.push(g),l.push(p),f.push(v)}var r,i,u=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++u<o;)(i=r[u])&&(n[u]=i.__data__);return n}var a=Z([]),l=E([]),f=E([]);if("function"==typeof n)for(;++u<o;)e(r=this[u],n.call(r,r.parentNode.__data__,u));else for(;++u<o;)e(r=this[u],n);return l.enter=function(){return a},l.exit=function(){return f},l},Co.datum=function(n){return arguments.length?this.property("__data__",n):this.property("__data__")},Co.filter=function(n){var t,e,r,i=[];"function"!=typeof n&&(n=O(n));for(var u=0,o=this.length;o>u;u++){i.push(t=[]),t.parentNode=(e=this[u]).parentNode;for(var a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return E(i)},Co.order=function(){for(var n=-1,t=this.length;++n<t;)for(var e,r=this[n],i=r.length-1,u=r[i];--i>=0;)(e=r[i])&&(u&&u!==e.nextSibling&&u.parentNode.insertBefore(e,u),u=e);return this},Co.sort=function(n){n=I.apply(this,arguments);for(var t=-1,e=this.length;++t<e;)this[t].sort(n);return this.order()},Co.each=function(n){return Y(this,function(t,e,r){n.call(t,t.__data__,e,r)})},Co.call=function(n){var t=co(arguments);return n.apply(t[0]=this,t),this},Co.empty=function(){return!this.node()},Co.node=function(){for(var n=0,t=this.length;t>n;n++)for(var e=this[n],r=0,i=e.length;i>r;r++){var u=e[r];if(u)return u}return null},Co.size=function(){var n=0;return Y(this,function(){++n}),n};var qo=[];ao.selection.enter=Z,ao.selection.enter.prototype=qo,qo.append=Co.append,qo.empty=Co.empty,qo.node=Co.node,qo.call=Co.call,qo.size=Co.size,qo.select=function(n){for(var t,e,r,i,u,o=[],a=-1,l=this.length;++a<l;){r=(i=this[a]).update,o.push(t=[]),t.parentNode=i.parentNode;for(var c=-1,f=i.length;++c<f;)(u=i[c])?(t.push(r[c]=e=n.call(i.parentNode,u.__data__,c,a)),e.__data__=u.__data__):t.push(null)}return E(o)},qo.insert=function(n,t){return arguments.length<2&&(t=V(this)),Co.insert.call(this,n,t)},ao.select=function(t){var e;return"string"==typeof t?(e=[No(t,fo)],e.parentNode=fo.documentElement):(e=[t],e.parentNode=n(t)),E([e])},ao.selectAll=function(n){var t;return"string"==typeof n?(t=co(Eo(n,fo)),t.parentNode=fo.documentElement):(t=co(n),t.parentNode=null),E([t])},Co.on=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(X(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(X(n,t,e))};var To=ao.map({mouseenter:"mouseover",mouseleave:"mouseout"});fo&&To.forEach(function(n){"on"+n in fo&&To.remove(n)});var Ro,Do=0;ao.mouse=function(n){return J(n,k())};var Po=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ao.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=k().changedTouches),t)for(var r,i=0,u=t.length;u>i;++i)if((r=t[i]).identifier===e)return J(n,r)},ao.behavior.drag=function(){function n(){this.on("mousedown.drag",u).on("touchstart.drag",o)}function e(n,t,e,u,o){return function(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-M[0],e=r[1]-M[1],g|=n|e,M=r,p({type:"drag",x:r[0]+c[0],y:r[1]+c[1],dx:n,dy:e}))}function l(){t(h,v)&&(y.on(u+d,null).on(o+d,null),m(g),p({type:"dragend"}))}var c,f=this,s=ao.event.target.correspondingElement||ao.event.target,h=f.parentNode,p=r.of(f,arguments),g=0,v=n(),d=".drag"+(null==v?"":"-"+v),y=ao.select(e(s)).on(u+d,a).on(o+d,l),m=W(s),M=t(h,v);i?(c=i.apply(f,arguments),c=[c.x-M[0],c.y-M[1]]):c=[0,0],p({type:"dragstart"})}}var r=N(n,"drag","dragstart","dragend"),i=null,u=e(b,ao.mouse,t,"mousemove","mouseup"),o=e(G,ao.touch,m,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},ao.rebind(n,r,"on")},ao.touches=function(n,t){return arguments.length<2&&(t=k().touches),t?co(t).map(function(t){var e=J(n,t);return e.identifier=t.identifier,e}):[]};var Uo=1e-6,jo=Uo*Uo,Fo=Math.PI,Ho=2*Fo,Oo=Ho-Uo,Io=Fo/2,Yo=Fo/180,Zo=180/Fo,Vo=Math.SQRT2,Xo=2,$o=4;ao.interpolateZoom=function(n,t){var e,r,i=n[0],u=n[1],o=n[2],a=t[0],l=t[1],c=t[2],f=a-i,s=l-u,h=f*f+s*s;if(jo>h)r=Math.log(c/o)/Vo,e=function(n){return[i+n*f,u+n*s,o*Math.exp(Vo*n*r)]};else{var p=Math.sqrt(h),g=(c*c-o*o+$o*h)/(2*o*Xo*p),v=(c*c-o*o-$o*h)/(2*c*Xo*p),d=Math.log(Math.sqrt(g*g+1)-g),y=Math.log(Math.sqrt(v*v+1)-v);r=(y-d)/Vo,e=function(n){var t=n*r,e=rn(d),a=o/(Xo*p)*(e*un(Vo*t+d)-en(d));return[i+a*f,u+a*s,o*e/rn(Vo*t+d)]}}return e.duration=1e3*r,e},ao.behavior.zoom=function(){function n(n){n.on(L,s).on(Wo+".zoom",p).on("dblclick.zoom",g).on(R,h)}function e(n){return[(n[0]-k.x)/k.k,(n[1]-k.y)/k.k]}function r(n){return[n[0]*k.k+k.x,n[1]*k.k+k.y]}function i(n){k.k=Math.max(A[0],Math.min(A[1],n))}function u(n,t){t=r(t),k.x+=n[0]-t[0],k.y+=n[1]-t[1]}function o(t,e,r,o){t.__chart__={x:k.x,y:k.y,k:k.k},i(Math.pow(2,o)),u(d=e,r),t=ao.select(t),C>0&&(t=t.transition().duration(C)),t.call(n.event)}function a(){b&&b.domain(x.range().map(function(n){return(n-k.x)/k.k}).map(x.invert)),w&&w.domain(_.range().map(function(n){return(n-k.y)/k.k}).map(_.invert))}function l(n){z++||n({type:"zoomstart"})}function c(n){a(),n({type:"zoom",scale:k.k,translate:[k.x,k.y]})}function f(n){--z||(n({type:"zoomend"}),d=null)}function s(){function n(){a=1,u(ao.mouse(i),h),c(o)}function r(){s.on(q,null).on(T,null),p(a),f(o)}var i=this,o=D.of(i,arguments),a=0,s=ao.select(t(i)).on(q,n).on(T,r),h=e(ao.mouse(i)),p=W(i);Il.call(i),l(o)}function h(){function n(){var n=ao.touches(g);return p=k.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=e(n))}),n}function t(){var t=ao.event.target;ao.select(t).on(x,r).on(b,a),_.push(t);for(var e=ao.event.changedTouches,i=0,u=e.length;u>i;++i)d[e[i].identifier]=null;var l=n(),c=Date.now();if(1===l.length){if(500>c-M){var f=l[0];o(g,f,d[f.identifier],Math.floor(Math.log(k.k)/Math.LN2)+1),S()}M=c}else if(l.length>1){var f=l[0],s=l[1],h=f[0]-s[0],p=f[1]-s[1];y=h*h+p*p}}function r(){var n,t,e,r,o=ao.touches(g);Il.call(g);for(var a=0,l=o.length;l>a;++a,r=null)if(e=o[a],r=d[e.identifier]){if(t)break;n=e,t=r}if(r){var f=(f=e[0]-n[0])*f+(f=e[1]-n[1])*f,s=y&&Math.sqrt(f/y);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+r[0])/2,(t[1]+r[1])/2],i(s*p)}M=null,u(n,t),c(v)}function a(){if(ao.event.touches.length){for(var t=ao.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var i in d)return void n()}ao.selectAll(_).on(m,null),w.on(L,s).on(R,h),N(),f(v)}var p,g=this,v=D.of(g,arguments),d={},y=0,m=".zoom-"+ao.event.changedTouches[0].identifier,x="touchmove"+m,b="touchend"+m,_=[],w=ao.select(g),N=W(g);t(),l(v),w.on(L,null).on(R,t)}function p(){var n=D.of(this,arguments);m?clearTimeout(m):(Il.call(this),v=e(d=y||ao.mouse(this)),l(n)),m=setTimeout(function(){m=null,f(n)},50),S(),i(Math.pow(2,.002*Bo())*k.k),u(d,v),c(n)}function g(){var n=ao.mouse(this),t=Math.log(k.k)/Math.LN2;o(this,n,e(n),ao.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var v,d,y,m,M,x,b,_,w,k={x:0,y:0,k:1},E=[960,500],A=Jo,C=250,z=0,L="mousedown.zoom",q="mousemove.zoom",T="mouseup.zoom",R="touchstart.zoom",D=N(n,"zoomstart","zoom","zoomend");return Wo||(Wo="onwheel"in fo?(Bo=function(){return-ao.event.deltaY*(ao.event.deltaMode?120:1)},"wheel"):"onmousewheel"in fo?(Bo=function(){return ao.event.wheelDelta},"mousewheel"):(Bo=function(){return-ao.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=D.of(this,arguments),t=k;Hl?ao.select(this).transition().each("start.zoom",function(){k=this.__chart__||{x:0,y:0,k:1},l(n)}).tween("zoom:zoom",function(){var e=E[0],r=E[1],i=d?d[0]:e/2,u=d?d[1]:r/2,o=ao.interpolateZoom([(i-k.x)/k.k,(u-k.y)/k.k,e/k.k],[(i-t.x)/t.k,(u-t.y)/t.k,e/t.k]);return function(t){var r=o(t),a=e/r[2];this.__chart__=k={x:i-r[0]*a,y:u-r[1]*a,k:a},c(n)}}).each("interrupt.zoom",function(){f(n)}).each("end.zoom",function(){f(n)}):(this.__chart__=k,l(n),c(n),f(n))})},n.translate=function(t){return arguments.length?(k={x:+t[0],y:+t[1],k:k.k},a(),n):[k.x,k.y]},n.scale=function(t){return arguments.length?(k={x:k.x,y:k.y,k:null},i(+t),a(),n):k.k},n.scaleExtent=function(t){return arguments.length?(A=null==t?Jo:[+t[0],+t[1]],n):A},n.center=function(t){return arguments.length?(y=t&&[+t[0],+t[1]],n):y},n.size=function(t){return arguments.length?(E=t&&[+t[0],+t[1]],n):E},n.duration=function(t){return arguments.length?(C=+t,n):C},n.x=function(t){return arguments.length?(b=t,x=t.copy(),k={x:0,y:0,k:1},n):b},n.y=function(t){return arguments.length?(w=t,_=t.copy(),k={x:0,y:0,k:1},n):w},ao.rebind(n,D,"on")};var Bo,Wo,Jo=[0,1/0];ao.color=an,an.prototype.toString=function(){return this.rgb()+""},ao.hsl=ln;var Go=ln.prototype=new an;Go.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,this.l/n)},Go.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,n*this.l)},Go.rgb=function(){return cn(this.h,this.s,this.l)},ao.hcl=fn;var Ko=fn.prototype=new an;Ko.brighter=function(n){return new fn(this.h,this.c,Math.min(100,this.l+Qo*(arguments.length?n:1)))},Ko.darker=function(n){return new fn(this.h,this.c,Math.max(0,this.l-Qo*(arguments.length?n:1)))},Ko.rgb=function(){return sn(this.h,this.c,this.l).rgb()},ao.lab=hn;var Qo=18,na=.95047,ta=1,ea=1.08883,ra=hn.prototype=new an;ra.brighter=function(n){return new hn(Math.min(100,this.l+Qo*(arguments.length?n:1)),this.a,this.b)},ra.darker=function(n){return new hn(Math.max(0,this.l-Qo*(arguments.length?n:1)),this.a,this.b)},ra.rgb=function(){return pn(this.l,this.a,this.b)},ao.rgb=mn;var ia=mn.prototype=new an;ia.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,i=30;return t||e||r?(t&&i>t&&(t=i),e&&i>e&&(e=i),r&&i>r&&(r=i),new mn(Math.min(255,t/n),Math.min(255,e/n),Math.min(255,r/n))):new mn(i,i,i)},ia.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mn(n*this.r,n*this.g,n*this.b)},ia.hsl=function(){return wn(this.r,this.g,this.b)},ia.toString=function(){return"#"+bn(this.r)+bn(this.g)+bn(this.b)};var ua=ao.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});ua.forEach(function(n,t){ua.set(n,Mn(t))}),ao.functor=En,ao.xhr=An(m),ao.dsv=function(n,t){function e(n,e,u){arguments.length<3&&(u=e,e=null);var o=Cn(n,t,null==e?r:i(e),u);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:i(n)):e},o}function r(n){return e.parse(n.responseText)}function i(n){return function(t){return e.parse(t.responseText,n)}}function u(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),l=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var i=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(i(n),e)}:i})},e.parseRows=function(n,t){function e(){if(f>=c)return o;if(i)return i=!1,u;var t=f;if(34===n.charCodeAt(t)){for(var e=t;e++<c;)if(34===n.charCodeAt(e)){if(34!==n.charCodeAt(e+1))break;++e}f=e+2;var r=n.charCodeAt(e+1);return 13===r?(i=!0,10===n.charCodeAt(e+2)&&++f):10===r&&(i=!0),n.slice(t+1,e).replace(/""/g,'"')}for(;c>f;){var r=n.charCodeAt(f++),a=1;if(10===r)i=!0;else if(13===r)i=!0,10===n.charCodeAt(f)&&(++f,++a);else if(r!==l)continue;return n.slice(t,f-a)}return n.slice(t)}for(var r,i,u={},o={},a=[],c=n.length,f=0,s=0;(r=e())!==o;){for(var h=[];r!==u&&r!==o;)h.push(r),r=e();t&&null==(h=t(h,s++))||a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new y,i=[];return t.forEach(function(n){for(var t in n)r.has(t)||i.push(r.add(t))}),[i.map(o).join(n)].concat(t.map(function(t){return i.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(u).join("\n")},e},ao.csv=ao.dsv(",","text/csv"),ao.tsv=ao.dsv(" ","text/tab-separated-values");var oa,aa,la,ca,fa=this[x(this,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ao.timer=function(){qn.apply(this,arguments)},ao.timer.flush=function(){Rn(),Dn()},ao.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var sa=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Un);ao.formatPrefix=function(n,t){var e=0;return(n=+n)&&(0>n&&(n*=-1),t&&(n=ao.round(n,Pn(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((e-1)/3)))),sa[8+e/3]};var ha=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,pa=ao.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=ao.round(n,Pn(n,t))).toFixed(Math.max(0,Math.min(20,Pn(n*(1+1e-15),t))))}}),ga=ao.time={},va=Date;Hn.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){da.setUTCDate.apply(this._,arguments)},setDay:function(){da.setUTCDay.apply(this._,arguments)},setFullYear:function(){da.setUTCFullYear.apply(this._,arguments)},setHours:function(){da.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){da.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){da.setUTCMinutes.apply(this._,arguments)},setMonth:function(){da.setUTCMonth.apply(this._,arguments)},setSeconds:function(){da.setUTCSeconds.apply(this._,arguments)},setTime:function(){da.setTime.apply(this._,arguments)}};var da=Date.prototype;ga.year=On(function(n){return n=ga.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ga.years=ga.year.range,ga.years.utc=ga.year.utc.range,ga.day=On(function(n){var t=new va(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),ga.days=ga.day.range,ga.days.utc=ga.day.utc.range,ga.dayOfYear=function(n){var t=ga.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=ga[n]=On(function(n){return(n=ga.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ga[n+"s"]=e.range,ga[n+"s"].utc=e.utc.range,ga[n+"OfYear"]=function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)}}),ga.week=ga.sunday,ga.weeks=ga.sunday.range,ga.weeks.utc=ga.sunday.utc.range,ga.weekOfYear=ga.sundayOfYear;var ya={"-":"",_:" ",0:"0"},ma=/^\s*\d+/,Ma=/^%/;ao.locale=function(n){return{numberFormat:jn(n),timeFormat:Yn(n)}};var xa=ao.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"], shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});ao.format=xa.numberFormat,ao.geo={},ft.prototype={s:0,t:0,add:function(n){st(n,this.t,ba),st(ba.s,this.s,this),this.s?this.t+=ba.t:this.s=ba.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var ba=new ft;ao.geo.stream=function(n,t){n&&_a.hasOwnProperty(n.type)?_a[n.type](n,t):ht(n,t)};var _a={Feature:function(n,t){ht(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,i=e.length;++r<i;)ht(e[r].geometry,t)}},wa={Sphere:function(n,t){t.sphere()},Point:function(n,t){n=n.coordinates,t.point(n[0],n[1],n[2])},MultiPoint:function(n,t){for(var e=n.coordinates,r=-1,i=e.length;++r<i;)n=e[r],t.point(n[0],n[1],n[2])},LineString:function(n,t){pt(n.coordinates,t,0)},MultiLineString:function(n,t){for(var e=n.coordinates,r=-1,i=e.length;++r<i;)pt(e[r],t,0)},Polygon:function(n,t){gt(n.coordinates,t)},MultiPolygon:function(n,t){for(var e=n.coordinates,r=-1,i=e.length;++r<i;)gt(e[r],t)},GeometryCollection:function(n,t){for(var e=n.geometries,r=-1,i=e.length;++r<i;)ht(e[r],t)}};ao.geo.area=function(n){return Sa=0,ao.geo.stream(n,Na),Sa};var Sa,ka=new ft,Na={sphere:function(){Sa+=4*Fo},point:b,lineStart:b,lineEnd:b,polygonStart:function(){ka.reset(),Na.lineStart=vt},polygonEnd:function(){var n=2*ka;Sa+=0>n?4*Fo+n:n,Na.lineStart=Na.lineEnd=Na.point=b}};ao.geo.bounds=function(){function n(n,t){M.push(x=[f=n,h=n]),s>t&&(s=t),t>p&&(p=t)}function t(t,e){var r=dt([t*Yo,e*Yo]);if(y){var i=mt(y,r),u=[i[1],-i[0],0],o=mt(u,i);bt(o),o=_t(o);var l=t-g,c=l>0?1:-1,v=o[0]*Zo*c,d=xo(l)>180;if(d^(v>c*g&&c*t>v)){var m=o[1]*Zo;m>p&&(p=m)}else if(v=(v+360)%360-180,d^(v>c*g&&c*t>v)){var m=-o[1]*Zo;s>m&&(s=m)}else s>e&&(s=e),e>p&&(p=e);d?g>t?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t):h>=f?(f>t&&(f=t),t>h&&(h=t)):t>g?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t)}else n(t,e);y=r,g=t}function e(){b.point=t}function r(){x[0]=f,x[1]=h,b.point=n,y=null}function i(n,e){if(y){var r=n-g;m+=xo(r)>180?r+(r>0?360:-360):r}else v=n,d=e;Na.point(n,e),t(n,e)}function u(){Na.lineStart()}function o(){i(v,d),Na.lineEnd(),xo(m)>Uo&&(f=-(h=180)),x[0]=f,x[1]=h,y=null}function a(n,t){return(t-=n)<0?t+360:t}function l(n,t){return n[0]-t[0]}function c(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:n<t[0]||t[1]<n}var f,s,h,p,g,v,d,y,m,M,x,b={point:n,lineStart:e,lineEnd:r,polygonStart:function(){b.point=i,b.lineStart=u,b.lineEnd=o,m=0,Na.polygonStart()},polygonEnd:function(){Na.polygonEnd(),b.point=n,b.lineStart=e,b.lineEnd=r,0>ka?(f=-(h=180),s=-(p=90)):m>Uo?p=90:-Uo>m&&(s=-90),x[0]=f,x[1]=h}};return function(n){p=h=-(f=s=1/0),M=[],ao.geo.stream(n,b);var t=M.length;if(t){M.sort(l);for(var e,r=1,i=M[0],u=[i];t>r;++r)e=M[r],c(e[0],i)||c(e[1],i)?(a(i[0],e[1])>a(i[0],i[1])&&(i[1]=e[1]),a(e[0],i[1])>a(i[0],i[1])&&(i[0]=e[0])):u.push(i=e);for(var o,e,g=-(1/0),t=u.length-1,r=0,i=u[t];t>=r;i=e,++r)e=u[r],(o=a(i[1],e[0]))>g&&(g=o,f=e[0],h=i[1])}return M=x=null,f===1/0||s===1/0?[[NaN,NaN],[NaN,NaN]]:[[f,s],[h,p]]}}(),ao.geo.centroid=function(n){Ea=Aa=Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,ja);var t=Da,e=Pa,r=Ua,i=t*t+e*e+r*r;return jo>i&&(t=qa,e=Ta,r=Ra,Uo>Aa&&(t=Ca,e=za,r=La),i=t*t+e*e+r*r,jo>i)?[NaN,NaN]:[Math.atan2(e,t)*Zo,tn(r/Math.sqrt(i))*Zo]};var Ea,Aa,Ca,za,La,qa,Ta,Ra,Da,Pa,Ua,ja={sphere:b,point:St,lineStart:Nt,lineEnd:Et,polygonStart:function(){ja.lineStart=At},polygonEnd:function(){ja.lineStart=Nt}},Fa=Rt(zt,jt,Ht,[-Fo,-Fo/2]),Ha=1e9;ao.geo.clipExtent=function(){var n,t,e,r,i,u,o={stream:function(n){return i&&(i.valid=!1),i=u(n),i.valid=!0,i},extent:function(a){return arguments.length?(u=Zt(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),i&&(i.valid=!1,i=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(ao.geo.conicEqualArea=function(){return Vt(Xt)}).raw=Xt,ao.geo.albers=function(){return ao.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ao.geo.albersUsa=function(){function n(n){var u=n[0],o=n[1];return t=null,e(u,o),t||(r(u,o),t)||i(u,o),t}var t,e,r,i,u=ao.geo.albers(),o=ao.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=ao.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),l={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=u.scale(),e=u.translate(),r=(n[0]-e[0])/t,i=(n[1]-e[1])/t;return(i>=.12&&.234>i&&r>=-.425&&-.214>r?o:i>=.166&&.234>i&&r>=-.214&&-.115>r?a:u).invert(n)},n.stream=function(n){var t=u.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,i){t.point(n,i),e.point(n,i),r.point(n,i)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(u.precision(t),o.precision(t),a.precision(t),n):u.precision()},n.scale=function(t){return arguments.length?(u.scale(t),o.scale(.35*t),a.scale(t),n.translate(u.translate())):u.scale()},n.translate=function(t){if(!arguments.length)return u.translate();var c=u.scale(),f=+t[0],s=+t[1];return e=u.translate(t).clipExtent([[f-.455*c,s-.238*c],[f+.455*c,s+.238*c]]).stream(l).point,r=o.translate([f-.307*c,s+.201*c]).clipExtent([[f-.425*c+Uo,s+.12*c+Uo],[f-.214*c-Uo,s+.234*c-Uo]]).stream(l).point,i=a.translate([f-.205*c,s+.212*c]).clipExtent([[f-.214*c+Uo,s+.166*c+Uo],[f-.115*c-Uo,s+.234*c-Uo]]).stream(l).point,n},n.scale(1070)};var Oa,Ia,Ya,Za,Va,Xa,$a={point:b,lineStart:b,lineEnd:b,polygonStart:function(){Ia=0,$a.lineStart=$t},polygonEnd:function(){$a.lineStart=$a.lineEnd=$a.point=b,Oa+=xo(Ia/2)}},Ba={point:Bt,lineStart:b,lineEnd:b,polygonStart:b,polygonEnd:b},Wa={point:Gt,lineStart:Kt,lineEnd:Qt,polygonStart:function(){Wa.lineStart=ne},polygonEnd:function(){Wa.point=Gt,Wa.lineStart=Kt,Wa.lineEnd=Qt}};ao.geo.path=function(){function n(n){return n&&("function"==typeof a&&u.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=i(u)),ao.geo.stream(n,o)),u.result()}function t(){return o=null,n}var e,r,i,u,o,a=4.5;return n.area=function(n){return Oa=0,ao.geo.stream(n,i($a)),Oa},n.centroid=function(n){return Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,i(Wa)),Ua?[Da/Ua,Pa/Ua]:Ra?[qa/Ra,Ta/Ra]:La?[Ca/La,za/La]:[NaN,NaN]},n.bounds=function(n){return Va=Xa=-(Ya=Za=1/0),ao.geo.stream(n,i(Ba)),[[Ya,Za],[Va,Xa]]},n.projection=function(n){return arguments.length?(i=(e=n)?n.stream||re(n):m,t()):e},n.context=function(n){return arguments.length?(u=null==(r=n)?new Wt:new te(n),"function"!=typeof a&&u.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(u.pointRadius(+t),+t),n):a},n.projection(ao.geo.albersUsa()).context(null)},ao.geo.transform=function(n){return{stream:function(t){var e=new ie(t);for(var r in n)e[r]=n[r];return e}}},ie.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},ao.geo.projection=oe,ao.geo.projectionMutator=ae,(ao.geo.equirectangular=function(){return oe(ce)}).raw=ce.invert=ce,ao.geo.rotation=function(n){function t(t){return t=n(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t}return n=se(n[0]%360*Yo,n[1]*Yo,n.length>2?n[2]*Yo:0),t.invert=function(t){return t=n.invert(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t},t},fe.invert=ce,ao.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=se(-n[0]*Yo,-n[1]*Yo,0).invert,i=[];return e(null,null,1,{point:function(n,e){i.push(n=t(n,e)),n[0]*=Zo,n[1]*=Zo}}),{type:"Polygon",coordinates:[i]}}var t,e,r=[0,0],i=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=ve((t=+r)*Yo,i*Yo),n):t},n.precision=function(r){return arguments.length?(e=ve(t*Yo,(i=+r)*Yo),n):i},n.angle(90)},ao.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Yo,i=n[1]*Yo,u=t[1]*Yo,o=Math.sin(r),a=Math.cos(r),l=Math.sin(i),c=Math.cos(i),f=Math.sin(u),s=Math.cos(u);return Math.atan2(Math.sqrt((e=s*o)*e+(e=c*f-l*s*a)*e),l*f+c*s*a)},ao.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ao.range(Math.ceil(u/d)*d,i,d).map(h).concat(ao.range(Math.ceil(c/y)*y,l,y).map(p)).concat(ao.range(Math.ceil(r/g)*g,e,g).filter(function(n){return xo(n%d)>Uo}).map(f)).concat(ao.range(Math.ceil(a/v)*v,o,v).filter(function(n){return xo(n%y)>Uo}).map(s))}var e,r,i,u,o,a,l,c,f,s,h,p,g=10,v=g,d=90,y=360,m=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(u).concat(p(l).slice(1),h(i).reverse().slice(1),p(c).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(u=+t[0][0],i=+t[1][0],c=+t[0][1],l=+t[1][1],u>i&&(t=u,u=i,i=t),c>l&&(t=c,c=l,l=t),n.precision(m)):[[u,c],[i,l]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(m)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],y=+t[1],n):[d,y]},n.minorStep=function(t){return arguments.length?(g=+t[0],v=+t[1],n):[g,v]},n.precision=function(t){return arguments.length?(m=+t,f=ye(a,o,90),s=me(r,e,m),h=ye(c,l,90),p=me(u,i,m),n):m},n.majorExtent([[-180,-90+Uo],[180,90-Uo]]).minorExtent([[-180,-80-Uo],[180,80+Uo]])},ao.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||i.apply(this,arguments)]}}var t,e,r=Me,i=xe;return n.distance=function(){return ao.geo.distance(t||r.apply(this,arguments),e||i.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(i=t,e="function"==typeof t?null:t,n):i},n.precision=function(){return arguments.length?n:0},n},ao.geo.interpolate=function(n,t){return be(n[0]*Yo,n[1]*Yo,t[0]*Yo,t[1]*Yo)},ao.geo.length=function(n){return Ja=0,ao.geo.stream(n,Ga),Ja};var Ja,Ga={sphere:b,point:b,lineStart:_e,lineEnd:b,polygonStart:b,polygonEnd:b},Ka=we(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ao.geo.azimuthalEqualArea=function(){return oe(Ka)}).raw=Ka;var Qa=we(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},m);(ao.geo.azimuthalEquidistant=function(){return oe(Qa)}).raw=Qa,(ao.geo.conicConformal=function(){return Vt(Se)}).raw=Se,(ao.geo.conicEquidistant=function(){return Vt(ke)}).raw=ke;var nl=we(function(n){return 1/n},Math.atan);(ao.geo.gnomonic=function(){return oe(nl)}).raw=nl,Ne.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Io]},(ao.geo.mercator=function(){return Ee(Ne)}).raw=Ne;var tl=we(function(){return 1},Math.asin);(ao.geo.orthographic=function(){return oe(tl)}).raw=tl;var el=we(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ao.geo.stereographic=function(){return oe(el)}).raw=el,Ae.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Io]},(ao.geo.transverseMercator=function(){var n=Ee(Ae),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[n[1],-n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},e([0,0,90])}).raw=Ae,ao.geom={},ao.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,i=En(e),u=En(r),o=n.length,a=[],l=[];for(t=0;o>t;t++)a.push([+i.call(this,n[t],t),+u.call(this,n[t],t),t]);for(a.sort(qe),t=0;o>t;t++)l.push([a[t][0],-a[t][1]]);var c=Le(a),f=Le(l),s=f[0]===c[0],h=f[f.length-1]===c[c.length-1],p=[];for(t=c.length-1;t>=0;--t)p.push(n[a[c[t]][2]]);for(t=+s;t<f.length-h;++t)p.push(n[a[f[t]][2]]);return p}var e=Ce,r=ze;return arguments.length?t(n):(t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t)},ao.geom.polygon=function(n){return ko(n,rl),n};var rl=ao.geom.polygon.prototype=[];rl.area=function(){for(var n,t=-1,e=this.length,r=this[e-1],i=0;++t<e;)n=r,r=this[t],i+=n[1]*r[0]-n[0]*r[1];return.5*i},rl.centroid=function(n){var t,e,r=-1,i=this.length,u=0,o=0,a=this[i-1];for(arguments.length||(n=-1/(6*this.area()));++r<i;)t=a,a=this[r],e=t[0]*a[1]-a[0]*t[1],u+=(t[0]+a[0])*e,o+=(t[1]+a[1])*e;return[u*n,o*n]},rl.clip=function(n){for(var t,e,r,i,u,o,a=De(n),l=-1,c=this.length-De(this),f=this[c-1];++l<c;){for(t=n.slice(),n.length=0,i=this[l],u=t[(r=t.length-a)-1],e=-1;++e<r;)o=t[e],Te(o,f,i)?(Te(u,f,i)||n.push(Re(u,o,f,i)),n.push(o)):Te(u,f,i)&&n.push(Re(u,o,f,i)),u=o;a&&n.push(n[0]),f=i}return n};var il,ul,ol,al,ll,cl=[],fl=[];Ye.prototype.prepare=function(){for(var n,t=this.edges,e=t.length;e--;)n=t[e].edge,n.b&&n.a||t.splice(e,1);return t.sort(Ve),t.length},tr.prototype={start:function(){return this.edge.l===this.site?this.edge.a:this.edge.b},end:function(){return this.edge.l===this.site?this.edge.b:this.edge.a}},er.prototype={insert:function(n,t){var e,r,i;if(n){if(t.P=n,t.N=n.N,n.N&&(n.N.P=t),n.N=t,n.R){for(n=n.R;n.L;)n=n.L;n.L=t}else n.R=t;e=n}else this._?(n=or(this._),t.P=null,t.N=n,n.P=n.L=t,e=n):(t.P=t.N=null,this._=t,e=null);for(t.L=t.R=null,t.U=e,t.C=!0,n=t;e&&e.C;)r=e.U,e===r.L?(i=r.R,i&&i.C?(e.C=i.C=!1,r.C=!0,n=r):(n===e.R&&(ir(this,e),n=e,e=n.U),e.C=!1,r.C=!0,ur(this,r))):(i=r.L,i&&i.C?(e.C=i.C=!1,r.C=!0,n=r):(n===e.L&&(ur(this,e),n=e,e=n.U),e.C=!1,r.C=!0,ir(this,r))),e=n.U;this._.C=!1},remove:function(n){n.N&&(n.N.P=n.P),n.P&&(n.P.N=n.N),n.N=n.P=null;var t,e,r,i=n.U,u=n.L,o=n.R;if(e=u?o?or(o):u:o,i?i.L===n?i.L=e:i.R=e:this._=e,u&&o?(r=e.C,e.C=n.C,e.L=u,u.U=e,e!==o?(i=e.U,e.U=n.U,n=e.R,i.L=n,e.R=o,o.U=e):(e.U=i,i=e,n=e.R)):(r=n.C,n=e),n&&(n.U=i),!r){if(n&&n.C)return void(n.C=!1);do{if(n===this._)break;if(n===i.L){if(t=i.R,t.C&&(t.C=!1,i.C=!0,ir(this,i),t=i.R),t.L&&t.L.C||t.R&&t.R.C){t.R&&t.R.C||(t.L.C=!1,t.C=!0,ur(this,t),t=i.R),t.C=i.C,i.C=t.R.C=!1,ir(this,i),n=this._;break}}else if(t=i.L,t.C&&(t.C=!1,i.C=!0,ur(this,i),t=i.L),t.L&&t.L.C||t.R&&t.R.C){t.L&&t.L.C||(t.R.C=!1,t.C=!0,ir(this,t),t=i.L),t.C=i.C,i.C=t.L.C=!1,ur(this,i),n=this._;break}t.C=!0,n=i,i=i.U}while(!n.C);n&&(n.C=!1)}}},ao.geom.voronoi=function(n){function t(n){var t=new Array(n.length),r=a[0][0],i=a[0][1],u=a[1][0],o=a[1][1];return ar(e(n),a).cells.forEach(function(e,a){var l=e.edges,c=e.site,f=t[a]=l.length?l.map(function(n){var t=n.start();return[t.x,t.y]}):c.x>=r&&c.x<=u&&c.y>=i&&c.y<=o?[[r,o],[u,o],[u,i],[r,i]]:[];f.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(u(n,t)/Uo)*Uo,y:Math.round(o(n,t)/Uo)*Uo,i:t}})}var r=Ce,i=ze,u=r,o=i,a=sl;return n?t(n):(t.links=function(n){return ar(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return ar(e(n)).cells.forEach(function(e,r){for(var i,u,o=e.site,a=e.edges.sort(Ve),l=-1,c=a.length,f=a[c-1].edge,s=f.l===o?f.r:f.l;++l<c;)i=f,u=s,f=a[l].edge,s=f.l===o?f.r:f.l,r<u.i&&r<s.i&&cr(o,u,s)<0&&t.push([n[r],n[u.i],n[s.i]])}),t},t.x=function(n){return arguments.length?(u=En(r=n),t):r},t.y=function(n){return arguments.length?(o=En(i=n),t):i},t.clipExtent=function(n){return arguments.length?(a=null==n?sl:n,t):a===sl?null:a},t.size=function(n){return arguments.length?t.clipExtent(n&&[[0,0],n]):a===sl?null:a&&a[1]},t)};var sl=[[-1e6,-1e6],[1e6,1e6]];ao.geom.delaunay=function(n){return ao.geom.voronoi().triangles(n)},ao.geom.quadtree=function(n,t,e,r,i){function u(n){function u(n,t,e,r,i,u,o,a){if(!isNaN(e)&&!isNaN(r))if(n.leaf){var l=n.x,f=n.y;if(null!=l)if(xo(l-e)+xo(f-r)<.01)c(n,t,e,r,i,u,o,a);else{var s=n.point;n.x=n.y=n.point=null,c(n,s,l,f,i,u,o,a),c(n,t,e,r,i,u,o,a)}else n.x=e,n.y=r,n.point=t}else c(n,t,e,r,i,u,o,a)}function c(n,t,e,r,i,o,a,l){var c=.5*(i+a),f=.5*(o+l),s=e>=c,h=r>=f,p=h<<1|s;n.leaf=!1,n=n.nodes[p]||(n.nodes[p]=hr()),s?i=c:a=c,h?o=f:l=f,u(n,t,e,r,i,o,a,l)}var f,s,h,p,g,v,d,y,m,M=En(a),x=En(l);if(null!=t)v=t,d=e,y=r,m=i;else if(y=m=-(v=d=1/0),s=[],h=[],g=n.length,o)for(p=0;g>p;++p)f=n[p],f.x<v&&(v=f.x),f.y<d&&(d=f.y),f.x>y&&(y=f.x),f.y>m&&(m=f.y),s.push(f.x),h.push(f.y);else for(p=0;g>p;++p){var b=+M(f=n[p],p),_=+x(f,p);v>b&&(v=b),d>_&&(d=_),b>y&&(y=b),_>m&&(m=_),s.push(b),h.push(_)}var w=y-v,S=m-d;w>S?m=d+w:y=v+S;var k=hr();if(k.add=function(n){u(k,n,+M(n,++p),+x(n,p),v,d,y,m)},k.visit=function(n){pr(n,k,v,d,y,m)},k.find=function(n){return gr(k,n[0],n[1],v,d,y,m)},p=-1,null==t){for(;++p<g;)u(k,n[p],s[p],h[p],v,d,y,m);--p}else n.forEach(k.add);return s=h=n=f=null,k}var o,a=Ce,l=ze;return(o=arguments.length)?(a=fr,l=sr,3===o&&(i=e,r=t,e=t=0),u(n)):(u.x=function(n){return arguments.length?(a=n,u):a},u.y=function(n){return arguments.length?(l=n,u):l},u.extent=function(n){return arguments.length?(null==n?t=e=r=i=null:(t=+n[0][0],e=+n[0][1],r=+n[1][0],i=+n[1][1]),u):null==t?null:[[t,e],[r,i]]},u.size=function(n){return arguments.length?(null==n?t=e=r=i=null:(t=e=0,r=+n[0],i=+n[1]),u):null==t?null:[r-t,i-e]},u)},ao.interpolateRgb=vr,ao.interpolateObject=dr,ao.interpolateNumber=yr,ao.interpolateString=mr;var hl=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,pl=new RegExp(hl.source,"g");ao.interpolate=Mr,ao.interpolators=[function(n,t){var e=typeof t;return("string"===e?ua.has(t.toLowerCase())||/^(#|rgb\(|hsl\()/i.test(t)?vr:mr:t instanceof an?vr:Array.isArray(t)?xr:"object"===e&&isNaN(t)?dr:yr)(n,t)}],ao.interpolateArray=xr;var gl=function(){return m},vl=ao.map({linear:gl,poly:Er,quad:function(){return Sr},cubic:function(){return kr},sin:function(){return Ar},exp:function(){return Cr},circle:function(){return zr},elastic:Lr,back:qr,bounce:function(){return Tr}}),dl=ao.map({"in":m,out:_r,"in-out":wr,"out-in":function(n){return wr(_r(n))}});ao.ease=function(n){var t=n.indexOf("-"),e=t>=0?n.slice(0,t):n,r=t>=0?n.slice(t+1):"in";return e=vl.get(e)||gl,r=dl.get(r)||m,br(r(e.apply(null,lo.call(arguments,1))))},ao.interpolateHcl=Rr,ao.interpolateHsl=Dr,ao.interpolateLab=Pr,ao.interpolateRound=Ur,ao.transform=function(n){var t=fo.createElementNS(ao.ns.prefix.svg,"g");return(ao.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new jr(e?e.matrix:yl)})(n)},jr.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var yl={a:1,b:0,c:0,d:1,e:0,f:0};ao.interpolateTransform=$r,ao.layout={},ao.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++e<r;)t.push(Jr(n[e]));return t}},ao.layout.chord=function(){function n(){var n,c,s,h,p,g={},v=[],d=ao.range(u),y=[];for(e=[],r=[],n=0,h=-1;++h<u;){for(c=0,p=-1;++p<u;)c+=i[h][p];v.push(c),y.push(ao.range(u)),n+=c}for(o&&d.sort(function(n,t){return o(v[n],v[t])}),a&&y.forEach(function(n,t){n.sort(function(n,e){return a(i[t][n],i[t][e])})}),n=(Ho-f*u)/n,c=0,h=-1;++h<u;){for(s=c,p=-1;++p<u;){var m=d[h],M=y[m][p],x=i[m][M],b=c,_=c+=x*n;g[m+"-"+M]={index:m,subindex:M,startAngle:b,endAngle:_,value:x}}r[m]={index:m,startAngle:s,endAngle:c,value:v[m]},c+=f}for(h=-1;++h<u;)for(p=h-1;++p<u;){var w=g[h+"-"+p],S=g[p+"-"+h];(w.value||S.value)&&e.push(w.value<S.value?{source:S,target:w}:{source:w,target:S})}l&&t()}function t(){e.sort(function(n,t){return l((n.source.value+n.target.value)/2,(t.source.value+t.target.value)/2)})}var e,r,i,u,o,a,l,c={},f=0;return c.matrix=function(n){return arguments.length?(u=(i=n)&&i.length,e=r=null,c):i},c.padding=function(n){return arguments.length?(f=n,e=r=null,c):f},c.sortGroups=function(n){return arguments.length?(o=n,e=r=null,c):o},c.sortSubgroups=function(n){return arguments.length?(a=n,e=null,c):a},c.sortChords=function(n){return arguments.length?(l=n,e&&t(),c):l},c.chords=function(){return e||n(),e},c.groups=function(){return r||n(),r},c},ao.layout.force=function(){function n(n){return function(t,e,r,i){if(t.point!==n){var u=t.cx-n.x,o=t.cy-n.y,a=i-e,l=u*u+o*o;if(l>a*a/y){if(v>l){var c=t.charge/l;n.px-=u*c,n.py-=o*c}return!0}if(t.point&&l&&v>l){var c=t.pointCharge/l;n.px-=u*c,n.py-=o*c}}return!t.charge}}function t(n){n.px=ao.event.x,n.py=ao.event.y,l.resume()}var e,r,i,u,o,a,l={},c=ao.dispatch("start","tick","end"),f=[1,1],s=.9,h=ml,p=Ml,g=-30,v=xl,d=.1,y=.64,M=[],x=[];return l.tick=function(){if((i*=.99)<.005)return e=null,c.end({type:"end",alpha:i=0}),!0;var t,r,l,h,p,v,y,m,b,_=M.length,w=x.length;for(r=0;w>r;++r)l=x[r],h=l.source,p=l.target,m=p.x-h.x,b=p.y-h.y,(v=m*m+b*b)&&(v=i*o[r]*((v=Math.sqrt(v))-u[r])/v,m*=v,b*=v,p.x-=m*(y=h.weight+p.weight?h.weight/(h.weight+p.weight):.5),p.y-=b*y,h.x+=m*(y=1-y),h.y+=b*y);if((y=i*d)&&(m=f[0]/2,b=f[1]/2,r=-1,y))for(;++r<_;)l=M[r],l.x+=(m-l.x)*y,l.y+=(b-l.y)*y;if(g)for(ri(t=ao.geom.quadtree(M),i,a),r=-1;++r<_;)(l=M[r]).fixed||t.visit(n(l));for(r=-1;++r<_;)l=M[r],l.fixed?(l.x=l.px,l.y=l.py):(l.x-=(l.px-(l.px=l.x))*s,l.y-=(l.py-(l.py=l.y))*s);c.tick({type:"tick",alpha:i})},l.nodes=function(n){return arguments.length?(M=n,l):M},l.links=function(n){return arguments.length?(x=n,l):x},l.size=function(n){return arguments.length?(f=n,l):f},l.linkDistance=function(n){return arguments.length?(h="function"==typeof n?n:+n,l):h},l.distance=l.linkDistance,l.linkStrength=function(n){return arguments.length?(p="function"==typeof n?n:+n,l):p},l.friction=function(n){return arguments.length?(s=+n,l):s},l.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,l):g},l.chargeDistance=function(n){return arguments.length?(v=n*n,l):Math.sqrt(v)},l.gravity=function(n){return arguments.length?(d=+n,l):d},l.theta=function(n){return arguments.length?(y=n*n,l):Math.sqrt(y)},l.alpha=function(n){return arguments.length?(n=+n,i?n>0?i=n:(e.c=null,e.t=NaN,e=null,c.end({type:"end",alpha:i=0})):n>0&&(c.start({type:"start",alpha:i=n}),e=qn(l.tick)),l):i},l.start=function(){function n(n,r){if(!e){for(e=new Array(i),l=0;i>l;++l)e[l]=[];for(l=0;c>l;++l){var u=x[l];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var o,a=e[t],l=-1,f=a.length;++l<f;)if(!isNaN(o=a[l][n]))return o;return Math.random()*r}var t,e,r,i=M.length,c=x.length,s=f[0],v=f[1];for(t=0;i>t;++t)(r=M[t]).index=t,r.weight=0;for(t=0;c>t;++t)r=x[t],"number"==typeof r.source&&(r.source=M[r.source]),"number"==typeof r.target&&(r.target=M[r.target]),++r.source.weight,++r.target.weight;for(t=0;i>t;++t)r=M[t],isNaN(r.x)&&(r.x=n("x",s)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof h)for(t=0;c>t;++t)u[t]=+h.call(this,x[t],t);else for(t=0;c>t;++t)u[t]=h;if(o=[],"function"==typeof p)for(t=0;c>t;++t)o[t]=+p.call(this,x[t],t);else for(t=0;c>t;++t)o[t]=p;if(a=[],"function"==typeof g)for(t=0;i>t;++t)a[t]=+g.call(this,M[t],t);else for(t=0;i>t;++t)a[t]=g;return l.resume()},l.resume=function(){return l.alpha(.1)},l.stop=function(){return l.alpha(0)},l.drag=function(){return r||(r=ao.behavior.drag().origin(m).on("dragstart.force",Qr).on("drag.force",t).on("dragend.force",ni)),arguments.length?void this.on("mouseover.force",ti).on("mouseout.force",ei).call(r):r},ao.rebind(l,c,"on")};var ml=20,Ml=1,xl=1/0;ao.layout.hierarchy=function(){function n(i){var u,o=[i],a=[];for(i.depth=0;null!=(u=o.pop());)if(a.push(u),(c=e.call(n,u,u.depth))&&(l=c.length)){for(var l,c,f;--l>=0;)o.push(f=c[l]),f.parent=u,f.depth=u.depth+1;r&&(u.value=0),u.children=c}else r&&(u.value=+r.call(n,u,u.depth)||0),delete u.children;return oi(i,function(n){var e,i;t&&(e=n.children)&&e.sort(t),r&&(i=n.parent)&&(i.value+=n.value)}),a}var t=ci,e=ai,r=li;return n.sort=function(e){return arguments.length?(t=e,n):t},n.children=function(t){return arguments.length?(e=t,n):e},n.value=function(t){return arguments.length?(r=t,n):r},n.revalue=function(t){return r&&(ui(t,function(n){n.children&&(n.value=0)}),oi(t,function(t){var e;t.children||(t.value=+r.call(n,t,t.depth)||0),(e=t.parent)&&(e.value+=t.value)})),t},n},ao.layout.partition=function(){function n(t,e,r,i){var u=t.children;if(t.x=e,t.y=t.depth*i,t.dx=r,t.dy=i,u&&(o=u.length)){var o,a,l,c=-1;for(r=t.value?r/t.value:0;++c<o;)n(a=u[c],e,l=a.value*r,i),e+=l}}function t(n){var e=n.children,r=0;if(e&&(i=e.length))for(var i,u=-1;++u<i;)r=Math.max(r,t(e[u]));return 1+r}function e(e,u){var o=r.call(this,e,u);return n(o[0],0,i[0],i[1]/t(o[0])),o}var r=ao.layout.hierarchy(),i=[1,1];return e.size=function(n){return arguments.length?(i=n,e):i},ii(e,r)},ao.layout.pie=function(){function n(o){var a,l=o.length,c=o.map(function(e,r){return+t.call(n,e,r)}),f=+("function"==typeof r?r.apply(this,arguments):r),s=("function"==typeof i?i.apply(this,arguments):i)-f,h=Math.min(Math.abs(s)/l,+("function"==typeof u?u.apply(this,arguments):u)),p=h*(0>s?-1:1),g=ao.sum(c),v=g?(s-l*p)/g:0,d=ao.range(l),y=[];return null!=e&&d.sort(e===bl?function(n,t){return c[t]-c[n]}:function(n,t){return e(o[n],o[t])}),d.forEach(function(n){y[n]={data:o[n],value:a=c[n],startAngle:f,endAngle:f+=a*v+p,padAngle:h}}),y}var t=Number,e=bl,r=0,i=Ho,u=0;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(i=t,n):i},n.padAngle=function(t){return arguments.length?(u=t,n):u},n};var bl={};ao.layout.stack=function(){function n(a,l){if(!(h=a.length))return a;var c=a.map(function(e,r){return t.call(n,e,r)}),f=c.map(function(t){return t.map(function(t,e){return[u.call(n,t,e),o.call(n,t,e)]})}),s=e.call(n,f,l);c=ao.permute(c,s),f=ao.permute(f,s);var h,p,g,v,d=r.call(n,f,l),y=c[0].length;for(g=0;y>g;++g)for(i.call(n,c[0][g],v=d[g],f[0][g][1]),p=1;h>p;++p)i.call(n,c[p][g],v+=f[p-1][g][1],f[p][g][1]);return a}var t=m,e=gi,r=vi,i=pi,u=si,o=hi;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:_l.get(t)||gi,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:wl.get(t)||vi,n):r},n.x=function(t){return arguments.length?(u=t,n):u},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(i=t,n):i},n};var _l=ao.map({"inside-out":function(n){var t,e,r=n.length,i=n.map(di),u=n.map(yi),o=ao.range(r).sort(function(n,t){return i[n]-i[t]}),a=0,l=0,c=[],f=[];for(t=0;r>t;++t)e=o[t],l>a?(a+=u[e],c.push(e)):(l+=u[e],f.push(e));return f.reverse().concat(c)},reverse:function(n){return ao.range(n.length).reverse()},"default":gi}),wl=ao.map({silhouette:function(n){var t,e,r,i=n.length,u=n[0].length,o=[],a=0,l=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;u>e;++e)l[e]=(a-o[e])/2;return l},wiggle:function(n){var t,e,r,i,u,o,a,l,c,f=n.length,s=n[0],h=s.length,p=[];for(p[0]=l=c=0,e=1;h>e;++e){for(t=0,i=0;f>t;++t)i+=n[t][e][1];for(t=0,u=0,a=s[e][0]-s[e-1][0];f>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;u+=o*n[t][e][1]}p[e]=l-=i?u/i*a:0,c>l&&(c=l)}for(e=0;h>e;++e)p[e]-=c;return p},expand:function(n){var t,e,r,i=n.length,u=n[0].length,o=1/i,a=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];if(r)for(t=0;i>t;t++)n[t][e][1]/=r;else for(t=0;i>t;t++)n[t][e][1]=o}for(e=0;u>e;++e)a[e]=0;return a},zero:vi});ao.layout.histogram=function(){function n(n,u){for(var o,a,l=[],c=n.map(e,this),f=r.call(this,c,u),s=i.call(this,f,c,u),u=-1,h=c.length,p=s.length-1,g=t?1:1/h;++u<p;)o=l[u]=[],o.dx=s[u+1]-(o.x=s[u]),o.y=0;if(p>0)for(u=-1;++u<h;)a=c[u],a>=f[0]&&a<=f[1]&&(o=l[ao.bisect(s,a,1,p)-1],o.y+=g,o.push(n[u]));return l}var t=!0,e=Number,r=bi,i=Mi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=En(t),n):r},n.bins=function(t){return arguments.length?(i="number"==typeof t?function(n){return xi(n,t)}:En(t),n):i},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ao.layout.pack=function(){function n(n,u){var o=e.call(this,n,u),a=o[0],l=i[0],c=i[1],f=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,oi(a,function(n){n.r=+f(n.value)}),oi(a,Ni),r){var s=r*(t?1:Math.max(2*a.r/l,2*a.r/c))/2;oi(a,function(n){n.r+=s}),oi(a,Ni),oi(a,function(n){n.r-=s})}return Ci(a,l/2,c/2,t?1:1/Math.max(2*a.r/l,2*a.r/c)),o}var t,e=ao.layout.hierarchy().sort(_i),r=0,i=[1,1];return n.size=function(t){return arguments.length?(i=t,n):i},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},ii(n,e)},ao.layout.tree=function(){function n(n,i){var f=o.call(this,n,i),s=f[0],h=t(s);if(oi(h,e),h.parent.m=-h.z,ui(h,r),c)ui(s,u);else{var p=s,g=s,v=s;ui(s,function(n){n.x<p.x&&(p=n),n.x>g.x&&(g=n),n.depth>v.depth&&(v=n)});var d=a(p,g)/2-p.x,y=l[0]/(g.x+a(g,p)/2+d),m=l[1]/(v.depth||1);ui(s,function(n){n.x=(n.x+d)*y,n.y=n.depth*m})}return f}function t(n){for(var t,e={A:null,children:[n]},r=[e];null!=(t=r.pop());)for(var i,u=t.children,o=0,a=u.length;a>o;++o)r.push((u[o]=i={_:u[o],parent:t,children:(i=u[o].children)&&i.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=i);return e.children[0]}function e(n){var t=n.children,e=n.parent.children,r=n.i?e[n.i-1]:null;if(t.length){Di(n);var u=(t[0].z+t[t.length-1].z)/2;r?(n.z=r.z+a(n._,r._),n.m=n.z-u):n.z=u}else r&&(n.z=r.z+a(n._,r._));n.parent.A=i(n,r,n.parent.A||e[0])}function r(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function i(n,t,e){if(t){for(var r,i=n,u=n,o=t,l=i.parent.children[0],c=i.m,f=u.m,s=o.m,h=l.m;o=Ti(o),i=qi(i),o&&i;)l=qi(l),u=Ti(u),u.a=n,r=o.z+s-i.z-c+a(o._,i._),r>0&&(Ri(Pi(o,n,e),n,r),c+=r,f+=r),s+=o.m,c+=i.m,h+=l.m,f+=u.m;o&&!Ti(u)&&(u.t=o,u.m+=s-f),i&&!qi(l)&&(l.t=i,l.m+=c-h,e=n)}return e}function u(n){n.x*=l[0],n.y=n.depth*l[1]}var o=ao.layout.hierarchy().sort(null).value(null),a=Li,l=[1,1],c=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(c=null==(l=t)?u:null,n):c?null:l},n.nodeSize=function(t){return arguments.length?(c=null==(l=t)?null:u,n):c?l:null},ii(n,o)},ao.layout.cluster=function(){function n(n,u){var o,a=t.call(this,n,u),l=a[0],c=0;oi(l,function(n){var t=n.children;t&&t.length?(n.x=ji(t),n.y=Ui(t)):(n.x=o?c+=e(n,o):0,n.y=0,o=n)});var f=Fi(l),s=Hi(l),h=f.x-e(f,s)/2,p=s.x+e(s,f)/2;return oi(l,i?function(n){n.x=(n.x-l.x)*r[0],n.y=(l.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(p-h)*r[0],n.y=(1-(l.y?n.y/l.y:1))*r[1]}),a}var t=ao.layout.hierarchy().sort(null).value(null),e=Li,r=[1,1],i=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(i=null==(r=t),n):i?null:r},n.nodeSize=function(t){return arguments.length?(i=null!=(r=t),n):i?r:null},ii(n,t)},ao.layout.treemap=function(){function n(n,t){for(var e,r,i=-1,u=n.length;++i<u;)r=(e=n[i]).value*(0>t?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var u=e.children;if(u&&u.length){var o,a,l,c=s(e),f=[],h=u.slice(),g=1/0,v="slice"===p?c.dx:"dice"===p?c.dy:"slice-dice"===p?1&e.depth?c.dy:c.dx:Math.min(c.dx,c.dy);for(n(h,c.dx*c.dy/e.value),f.area=0;(l=h.length)>0;)f.push(o=h[l-1]),f.area+=o.area,"squarify"!==p||(a=r(f,v))<=g?(h.pop(),g=a):(f.area-=f.pop().area,i(f,v,c,!1),v=Math.min(c.dx,c.dy),f.length=f.area=0,g=1/0);f.length&&(i(f,v,c,!0),f.length=f.area=0),u.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var u,o=s(t),a=r.slice(),l=[];for(n(a,o.dx*o.dy/t.value),l.area=0;u=a.pop();)l.push(u),l.area+=u.area,null!=u.z&&(i(l,u.z?o.dx:o.dy,o,!a.length),l.length=l.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,i=0,u=1/0,o=-1,a=n.length;++o<a;)(e=n[o].area)&&(u>e&&(u=e),e>i&&(i=e));return r*=r,t*=t,r?Math.max(t*i*g/r,r/(t*u*g)):1/0}function i(n,t,e,r){var i,u=-1,o=n.length,a=e.x,c=e.y,f=t?l(n.area/t):0; if(t==e.dx){for((r||f>e.dy)&&(f=e.dy);++u<o;)i=n[u],i.x=a,i.y=c,i.dy=f,a+=i.dx=Math.min(e.x+e.dx-a,f?l(i.area/f):0);i.z=!0,i.dx+=e.x+e.dx-a,e.y+=f,e.dy-=f}else{for((r||f>e.dx)&&(f=e.dx);++u<o;)i=n[u],i.x=a,i.y=c,i.dx=f,c+=i.dy=Math.min(e.y+e.dy-c,f?l(i.area/f):0);i.z=!1,i.dy+=e.y+e.dy-c,e.x+=f,e.dx-=f}}function u(r){var i=o||a(r),u=i[0];return u.x=u.y=0,u.value?(u.dx=c[0],u.dy=c[1]):u.dx=u.dy=0,o&&a.revalue(u),n([u],u.dx*u.dy/u.value),(o?e:t)(u),h&&(o=i),i}var o,a=ao.layout.hierarchy(),l=Math.round,c=[1,1],f=null,s=Oi,h=!1,p="squarify",g=.5*(1+Math.sqrt(5));return u.size=function(n){return arguments.length?(c=n,u):c},u.padding=function(n){function t(t){var e=n.call(u,t,t.depth);return null==e?Oi(t):Ii(t,"number"==typeof e?[e,e,e,e]:e)}function e(t){return Ii(t,n)}if(!arguments.length)return f;var r;return s=null==(f=n)?Oi:"function"==(r=typeof n)?t:"number"===r?(n=[n,n,n,n],e):e,u},u.round=function(n){return arguments.length?(l=n?Math.round:Number,u):l!=Number},u.sticky=function(n){return arguments.length?(h=n,o=null,u):h},u.ratio=function(n){return arguments.length?(g=n,u):g},u.mode=function(n){return arguments.length?(p=n+"",u):p},ii(u,a)},ao.random={normal:function(n,t){var e=arguments.length;return 2>e&&(t=1),1>e&&(n=0),function(){var e,r,i;do e=2*Math.random()-1,r=2*Math.random()-1,i=e*e+r*r;while(!i||i>1);return n+t*e*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(){var n=ao.random.normal.apply(ao,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ao.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},ao.scale={};var Sl={floor:m,ceil:m};ao.scale.linear=function(){return Wi([0,1],[0,1],Mr,!1)};var kl={s:1,g:1,p:1,r:1,e:1};ao.scale.log=function(){return ru(ao.scale.linear().domain([0,1]),10,!0,[1,10])};var Nl=ao.format(".0e"),El={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ao.scale.pow=function(){return iu(ao.scale.linear(),1,[0,1])},ao.scale.sqrt=function(){return ao.scale.pow().exponent(.5)},ao.scale.ordinal=function(){return ou([],{t:"range",a:[[]]})},ao.scale.category10=function(){return ao.scale.ordinal().range(Al)},ao.scale.category20=function(){return ao.scale.ordinal().range(Cl)},ao.scale.category20b=function(){return ao.scale.ordinal().range(zl)},ao.scale.category20c=function(){return ao.scale.ordinal().range(Ll)};var Al=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(xn),Cl=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(xn),zl=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(xn),Ll=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(xn);ao.scale.quantile=function(){return au([],[])},ao.scale.quantize=function(){return lu(0,1,[0,1])},ao.scale.threshold=function(){return cu([.5],[0,1])},ao.scale.identity=function(){return fu([0,1])},ao.svg={},ao.svg.arc=function(){function n(){var n=Math.max(0,+e.apply(this,arguments)),c=Math.max(0,+r.apply(this,arguments)),f=o.apply(this,arguments)-Io,s=a.apply(this,arguments)-Io,h=Math.abs(s-f),p=f>s?0:1;if(n>c&&(g=c,c=n,n=g),h>=Oo)return t(c,p)+(n?t(n,1-p):"")+"Z";var g,v,d,y,m,M,x,b,_,w,S,k,N=0,E=0,A=[];if((y=(+l.apply(this,arguments)||0)/2)&&(d=u===ql?Math.sqrt(n*n+c*c):+u.apply(this,arguments),p||(E*=-1),c&&(E=tn(d/c*Math.sin(y))),n&&(N=tn(d/n*Math.sin(y)))),c){m=c*Math.cos(f+E),M=c*Math.sin(f+E),x=c*Math.cos(s-E),b=c*Math.sin(s-E);var C=Math.abs(s-f-2*E)<=Fo?0:1;if(E&&yu(m,M,x,b)===p^C){var z=(f+s)/2;m=c*Math.cos(z),M=c*Math.sin(z),x=b=null}}else m=M=0;if(n){_=n*Math.cos(s-N),w=n*Math.sin(s-N),S=n*Math.cos(f+N),k=n*Math.sin(f+N);var L=Math.abs(f-s+2*N)<=Fo?0:1;if(N&&yu(_,w,S,k)===1-p^L){var q=(f+s)/2;_=n*Math.cos(q),w=n*Math.sin(q),S=k=null}}else _=w=0;if(h>Uo&&(g=Math.min(Math.abs(c-n)/2,+i.apply(this,arguments)))>.001){v=c>n^p?0:1;var T=g,R=g;if(Fo>h){var D=null==S?[_,w]:null==x?[m,M]:Re([m,M],[S,k],[x,b],[_,w]),P=m-D[0],U=M-D[1],j=x-D[0],F=b-D[1],H=1/Math.sin(Math.acos((P*j+U*F)/(Math.sqrt(P*P+U*U)*Math.sqrt(j*j+F*F)))/2),O=Math.sqrt(D[0]*D[0]+D[1]*D[1]);R=Math.min(g,(n-O)/(H-1)),T=Math.min(g,(c-O)/(H+1))}if(null!=x){var I=mu(null==S?[_,w]:[S,k],[m,M],c,T,p),Y=mu([x,b],[_,w],c,T,p);g===T?A.push("M",I[0],"A",T,",",T," 0 0,",v," ",I[1],"A",c,",",c," 0 ",1-p^yu(I[1][0],I[1][1],Y[1][0],Y[1][1]),",",p," ",Y[1],"A",T,",",T," 0 0,",v," ",Y[0]):A.push("M",I[0],"A",T,",",T," 0 1,",v," ",Y[0])}else A.push("M",m,",",M);if(null!=S){var Z=mu([m,M],[S,k],n,-R,p),V=mu([_,w],null==x?[m,M]:[x,b],n,-R,p);g===R?A.push("L",V[0],"A",R,",",R," 0 0,",v," ",V[1],"A",n,",",n," 0 ",p^yu(V[1][0],V[1][1],Z[1][0],Z[1][1]),",",1-p," ",Z[1],"A",R,",",R," 0 0,",v," ",Z[0]):A.push("L",V[0],"A",R,",",R," 0 0,",v," ",Z[0])}else A.push("L",_,",",w)}else A.push("M",m,",",M),null!=x&&A.push("A",c,",",c," 0 ",C,",",p," ",x,",",b),A.push("L",_,",",w),null!=S&&A.push("A",n,",",n," 0 ",L,",",1-p," ",S,",",k);return A.push("Z"),A.join("")}function t(n,t){return"M0,"+n+"A"+n+","+n+" 0 1,"+t+" 0,"+-n+"A"+n+","+n+" 0 1,"+t+" 0,"+n}var e=hu,r=pu,i=su,u=ql,o=gu,a=vu,l=du;return n.innerRadius=function(t){return arguments.length?(e=En(t),n):e},n.outerRadius=function(t){return arguments.length?(r=En(t),n):r},n.cornerRadius=function(t){return arguments.length?(i=En(t),n):i},n.padRadius=function(t){return arguments.length?(u=t==ql?ql:En(t),n):u},n.startAngle=function(t){return arguments.length?(o=En(t),n):o},n.endAngle=function(t){return arguments.length?(a=En(t),n):a},n.padAngle=function(t){return arguments.length?(l=En(t),n):l},n.centroid=function(){var n=(+e.apply(this,arguments)+ +r.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Io;return[Math.cos(t)*n,Math.sin(t)*n]},n};var ql="auto";ao.svg.line=function(){return Mu(m)};var Tl=ao.map({linear:xu,"linear-closed":bu,step:_u,"step-before":wu,"step-after":Su,basis:zu,"basis-open":Lu,"basis-closed":qu,bundle:Tu,cardinal:Eu,"cardinal-open":ku,"cardinal-closed":Nu,monotone:Fu});Tl.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Rl=[0,2/3,1/3,0],Dl=[0,1/3,2/3,0],Pl=[0,1/6,2/3,1/6];ao.svg.line.radial=function(){var n=Mu(Hu);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},wu.reverse=Su,Su.reverse=wu,ao.svg.area=function(){return Ou(m)},ao.svg.area.radial=function(){var n=Ou(Hu);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ao.svg.chord=function(){function n(n,a){var l=t(this,u,n,a),c=t(this,o,n,a);return"M"+l.p0+r(l.r,l.p1,l.a1-l.a0)+(e(l,c)?i(l.r,l.p1,l.r,l.p0):i(l.r,l.p1,c.r,c.p0)+r(c.r,c.p1,c.a1-c.a0)+i(c.r,c.p1,l.r,l.p0))+"Z"}function t(n,t,e,r){var i=t.call(n,e,r),u=a.call(n,i,r),o=l.call(n,i,r)-Io,f=c.call(n,i,r)-Io;return{r:u,a0:o,a1:f,p0:[u*Math.cos(o),u*Math.sin(o)],p1:[u*Math.cos(f),u*Math.sin(f)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Fo)+",1 "+t}function i(n,t,e,r){return"Q 0,0 "+r}var u=Me,o=xe,a=Iu,l=gu,c=vu;return n.radius=function(t){return arguments.length?(a=En(t),n):a},n.source=function(t){return arguments.length?(u=En(t),n):u},n.target=function(t){return arguments.length?(o=En(t),n):o},n.startAngle=function(t){return arguments.length?(l=En(t),n):l},n.endAngle=function(t){return arguments.length?(c=En(t),n):c},n},ao.svg.diagonal=function(){function n(n,i){var u=t.call(this,n,i),o=e.call(this,n,i),a=(u.y+o.y)/2,l=[u,{x:u.x,y:a},{x:o.x,y:a},o];return l=l.map(r),"M"+l[0]+"C"+l[1]+" "+l[2]+" "+l[3]}var t=Me,e=xe,r=Yu;return n.source=function(e){return arguments.length?(t=En(e),n):t},n.target=function(t){return arguments.length?(e=En(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ao.svg.diagonal.radial=function(){var n=ao.svg.diagonal(),t=Yu,e=n.projection;return n.projection=function(n){return arguments.length?e(Zu(t=n)):t},n},ao.svg.symbol=function(){function n(n,r){return(Ul.get(t.call(this,n,r))||$u)(e.call(this,n,r))}var t=Xu,e=Vu;return n.type=function(e){return arguments.length?(t=En(e),n):t},n.size=function(t){return arguments.length?(e=En(t),n):e},n};var Ul=ao.map({circle:$u,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Fl)),e=t*Fl;return"M0,"+-t+"L"+e+",0 0,"+t+" "+-e+",0Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ao.svg.symbolTypes=Ul.keys();var jl=Math.sqrt(3),Fl=Math.tan(30*Yo);Co.transition=function(n){for(var t,e,r=Hl||++Zl,i=Ku(n),u=[],o=Ol||{time:Date.now(),ease:Nr,delay:0,duration:250},a=-1,l=this.length;++a<l;){u.push(t=[]);for(var c=this[a],f=-1,s=c.length;++f<s;)(e=c[f])&&Qu(e,f,i,r,o),t.push(e)}return Wu(u,i,r)},Co.interrupt=function(n){return this.each(null==n?Il:Bu(Ku(n)))};var Hl,Ol,Il=Bu(Ku()),Yl=[],Zl=0;Yl.call=Co.call,Yl.empty=Co.empty,Yl.node=Co.node,Yl.size=Co.size,ao.transition=function(n,t){return n&&n.transition?Hl?n.transition(t):n:ao.selection().transition(n)},ao.transition.prototype=Yl,Yl.select=function(n){var t,e,r,i=this.id,u=this.namespace,o=[];n=A(n);for(var a=-1,l=this.length;++a<l;){o.push(t=[]);for(var c=this[a],f=-1,s=c.length;++f<s;)(r=c[f])&&(e=n.call(r,r.__data__,f,a))?("__data__"in r&&(e.__data__=r.__data__),Qu(e,f,u,i,r[u][i]),t.push(e)):t.push(null)}return Wu(o,u,i)},Yl.selectAll=function(n){var t,e,r,i,u,o=this.id,a=this.namespace,l=[];n=C(n);for(var c=-1,f=this.length;++c<f;)for(var s=this[c],h=-1,p=s.length;++h<p;)if(r=s[h]){u=r[a][o],e=n.call(r,r.__data__,h,c),l.push(t=[]);for(var g=-1,v=e.length;++g<v;)(i=e[g])&&Qu(i,g,a,o,u),t.push(i)}return Wu(l,a,o)},Yl.filter=function(n){var t,e,r,i=[];"function"!=typeof n&&(n=O(n));for(var u=0,o=this.length;o>u;u++){i.push(t=[]);for(var e=this[u],a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return Wu(i,this.namespace,this.id)},Yl.tween=function(n,t){var e=this.id,r=this.namespace;return arguments.length<2?this.node()[r][e].tween.get(n):Y(this,null==t?function(t){t[r][e].tween.remove(n)}:function(i){i[r][e].tween.set(n,t)})},Yl.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function i(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function u(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?$r:Mr,a=ao.ns.qualify(n);return Ju(this,"attr."+n,t,a.local?u:i)},Yl.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(i));return r&&function(n){this.setAttribute(i,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(i.space,i.local));return r&&function(n){this.setAttributeNS(i.space,i.local,r(n))}}var i=ao.ns.qualify(n);return this.tween("attr."+n,i.local?r:e)},Yl.style=function(n,e,r){function i(){this.style.removeProperty(n)}function u(e){return null==e?i:(e+="",function(){var i,u=t(this).getComputedStyle(this,null).getPropertyValue(n);return u!==e&&(i=Mr(u,e),function(t){this.style.setProperty(n,i(t),r)})})}var o=arguments.length;if(3>o){if("string"!=typeof n){2>o&&(e="");for(r in n)this.style(r,n[r],e);return this}r=""}return Ju(this,"style."+n,e,u)},Yl.styleTween=function(n,e,r){function i(i,u){var o=e.call(this,i,u,t(this).getComputedStyle(this,null).getPropertyValue(n));return o&&function(t){this.style.setProperty(n,o(t),r)}}return arguments.length<3&&(r=""),this.tween("style."+n,i)},Yl.text=function(n){return Ju(this,"text",n,Gu)},Yl.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Yl.ease=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].ease:("function"!=typeof n&&(n=ao.ease.apply(ao,arguments)),Y(this,function(r){r[e][t].ease=n}))},Yl.delay=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].delay:Y(this,"function"==typeof n?function(r,i,u){r[e][t].delay=+n.call(r,r.__data__,i,u)}:(n=+n,function(r){r[e][t].delay=n}))},Yl.duration=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].duration:Y(this,"function"==typeof n?function(r,i,u){r[e][t].duration=Math.max(1,n.call(r,r.__data__,i,u))}:(n=Math.max(1,n),function(r){r[e][t].duration=n}))},Yl.each=function(n,t){var e=this.id,r=this.namespace;if(arguments.length<2){var i=Ol,u=Hl;try{Hl=e,Y(this,function(t,i,u){Ol=t[r][e],n.call(t,t.__data__,i,u)})}finally{Ol=i,Hl=u}}else Y(this,function(i){var u=i[r][e];(u.event||(u.event=ao.dispatch("start","end","interrupt"))).on(n,t)});return this},Yl.transition=function(){for(var n,t,e,r,i=this.id,u=++Zl,o=this.namespace,a=[],l=0,c=this.length;c>l;l++){a.push(n=[]);for(var t=this[l],f=0,s=t.length;s>f;f++)(e=t[f])&&(r=e[o][i],Qu(e,f,o,u,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),n.push(e)}return Wu(a,o,u)},ao.svg.axis=function(){function n(n){n.each(function(){var n,c=ao.select(this),f=this.__chart__||e,s=this.__chart__=e.copy(),h=null==l?s.ticks?s.ticks.apply(s,a):s.domain():l,p=null==t?s.tickFormat?s.tickFormat.apply(s,a):m:t,g=c.selectAll(".tick").data(h,s),v=g.enter().insert("g",".domain").attr("class","tick").style("opacity",Uo),d=ao.transition(g.exit()).style("opacity",Uo).remove(),y=ao.transition(g.order()).style("opacity",1),M=Math.max(i,0)+o,x=Zi(s),b=c.selectAll(".domain").data([0]),_=(b.enter().append("path").attr("class","domain"),ao.transition(b));v.append("line"),v.append("text");var w,S,k,N,E=v.select("line"),A=y.select("line"),C=g.select("text").text(p),z=v.select("text"),L=y.select("text"),q="top"===r||"left"===r?-1:1;if("bottom"===r||"top"===r?(n=no,w="x",k="y",S="x2",N="y2",C.attr("dy",0>q?"0em":".71em").style("text-anchor","middle"),_.attr("d","M"+x[0]+","+q*u+"V0H"+x[1]+"V"+q*u)):(n=to,w="y",k="x",S="y2",N="x2",C.attr("dy",".32em").style("text-anchor",0>q?"end":"start"),_.attr("d","M"+q*u+","+x[0]+"H0V"+x[1]+"H"+q*u)),E.attr(N,q*i),z.attr(k,q*M),A.attr(S,0).attr(N,q*i),L.attr(w,0).attr(k,q*M),s.rangeBand){var T=s,R=T.rangeBand()/2;f=s=function(n){return T(n)+R}}else f.rangeBand?f=s:d.call(n,s,f);v.call(n,f,s),y.call(n,s,s)})}var t,e=ao.scale.linear(),r=Vl,i=6,u=6,o=3,a=[10],l=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Xl?t+"":Vl,n):r},n.ticks=function(){return arguments.length?(a=co(arguments),n):a},n.tickValues=function(t){return arguments.length?(l=t,n):l},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(i=+t,u=+arguments[e-1],n):i},n.innerTickSize=function(t){return arguments.length?(i=+t,n):i},n.outerTickSize=function(t){return arguments.length?(u=+t,n):u},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Vl="bottom",Xl={top:1,right:1,bottom:1,left:1};ao.svg.brush=function(){function n(t){t.each(function(){var t=ao.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=t.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),t.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=t.selectAll(".resize").data(v,m);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return $l[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,s=ao.transition(t),h=ao.transition(o);c&&(l=Zi(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),r(s)),f&&(l=Zi(f),h.attr("y",l[0]).attr("height",l[1]-l[0]),i(s)),e(s)})}function e(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+s[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",s[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",s[1]-s[0])}function i(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function u(){function u(){32==ao.event.keyCode&&(C||(M=null,L[0]-=s[1],L[1]-=h[1],C=2),S())}function v(){32==ao.event.keyCode&&2==C&&(L[0]+=s[1],L[1]+=h[1],C=0,S())}function d(){var n=ao.mouse(b),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),C||(ao.event.altKey?(M||(M=[(s[0]+s[1])/2,(h[0]+h[1])/2]),L[0]=s[+(n[0]<M[0])],L[1]=h[+(n[1]<M[1])]):M=null),E&&y(n,c,0)&&(r(k),t=!0),A&&y(n,f,1)&&(i(k),t=!0),t&&(e(k),w({type:"brush",mode:C?"move":"resize"}))}function y(n,t,e){var r,i,u=Zi(t),l=u[0],c=u[1],f=L[e],v=e?h:s,d=v[1]-v[0];return C&&(l-=f,c-=d+f),r=(e?g:p)?Math.max(l,Math.min(c,n[e])):n[e],C?i=(r+=f)+d:(M&&(f=Math.max(l,Math.min(c,2*M[e]-r))),r>f?(i=r,r=f):i=f),v[0]!=r||v[1]!=i?(e?a=null:o=null,v[0]=r,v[1]=i,!0):void 0}function m(){d(),k.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ao.select("body").style("cursor",null),q.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),z(),w({type:"brushend"})}var M,x,b=this,_=ao.select(ao.event.target),w=l.of(b,arguments),k=ao.select(b),N=_.datum(),E=!/^(n|s)$/.test(N)&&c,A=!/^(e|w)$/.test(N)&&f,C=_.classed("extent"),z=W(b),L=ao.mouse(b),q=ao.select(t(b)).on("keydown.brush",u).on("keyup.brush",v);if(ao.event.changedTouches?q.on("touchmove.brush",d).on("touchend.brush",m):q.on("mousemove.brush",d).on("mouseup.brush",m),k.interrupt().selectAll("*").interrupt(),C)L[0]=s[0]-L[0],L[1]=h[0]-L[1];else if(N){var T=+/w$/.test(N),R=+/^n/.test(N);x=[s[1-T]-L[0],h[1-R]-L[1]],L[0]=s[T],L[1]=h[R]}else ao.event.altKey&&(M=L.slice());k.style("pointer-events","none").selectAll(".resize").style("display",null),ao.select("body").style("cursor",_.style("cursor")),w({type:"brushstart"}),d()}var o,a,l=N(n,"brushstart","brush","brushend"),c=null,f=null,s=[0,0],h=[0,0],p=!0,g=!0,v=Bl[0];return n.event=function(n){n.each(function(){var n=l.of(this,arguments),t={x:s,y:h,i:o,j:a},e=this.__chart__||t;this.__chart__=t,Hl?ao.select(this).transition().each("start.brush",function(){o=e.i,a=e.j,s=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=xr(s,t.x),r=xr(h,t.y);return o=a=null,function(i){s=t.x=e(i),h=t.y=r(i),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){o=t.i,a=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,v=Bl[!c<<1|!f],n):c},n.y=function(t){return arguments.length?(f=t,v=Bl[!c<<1|!f],n):f},n.clamp=function(t){return arguments.length?(c&&f?(p=!!t[0],g=!!t[1]):c?p=!!t:f&&(g=!!t),n):c&&f?[p,g]:c?p:f?g:null},n.extent=function(t){var e,r,i,u,l;return arguments.length?(c&&(e=t[0],r=t[1],f&&(e=e[0],r=r[0]),o=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(l=e,e=r,r=l),e==s[0]&&r==s[1]||(s=[e,r])),f&&(i=t[0],u=t[1],c&&(i=i[1],u=u[1]),a=[i,u],f.invert&&(i=f(i),u=f(u)),i>u&&(l=i,i=u,u=l),i==h[0]&&u==h[1]||(h=[i,u])),n):(c&&(o?(e=o[0],r=o[1]):(e=s[0],r=s[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(l=e,e=r,r=l))),f&&(a?(i=a[0],u=a[1]):(i=h[0],u=h[1],f.invert&&(i=f.invert(i),u=f.invert(u)),i>u&&(l=i,i=u,u=l))),c&&f?[[e,i],[r,u]]:c?[e,r]:f&&[i,u])},n.clear=function(){return n.empty()||(s=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!c&&s[0]==s[1]||!!f&&h[0]==h[1]},ao.rebind(n,l,"on")};var $l={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Bl=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Wl=ga.format=xa.timeFormat,Jl=Wl.utc,Gl=Jl("%Y-%m-%dT%H:%M:%S.%LZ");Wl.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?eo:Gl,eo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},eo.toString=Gl.toString,ga.second=On(function(n){return new va(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ga.seconds=ga.second.range,ga.seconds.utc=ga.second.utc.range,ga.minute=On(function(n){return new va(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ga.minutes=ga.minute.range,ga.minutes.utc=ga.minute.utc.range,ga.hour=On(function(n){var t=n.getTimezoneOffset()/60;return new va(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ga.hours=ga.hour.range,ga.hours.utc=ga.hour.utc.range,ga.month=On(function(n){return n=ga.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ga.months=ga.month.range,ga.months.utc=ga.month.utc.range;var Kl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Ql=[[ga.second,1],[ga.second,5],[ga.second,15],[ga.second,30],[ga.minute,1],[ga.minute,5],[ga.minute,15],[ga.minute,30],[ga.hour,1],[ga.hour,3],[ga.hour,6],[ga.hour,12],[ga.day,1],[ga.day,2],[ga.week,1],[ga.month,1],[ga.month,3],[ga.year,1]],nc=Wl.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",zt]]),tc={range:function(n,t,e){return ao.range(Math.ceil(n/e)*e,+t,e).map(io)},floor:m,ceil:m};Ql.year=ga.year,ga.scale=function(){return ro(ao.scale.linear(),Ql,nc)};var ec=Ql.map(function(n){return[n[0].utc,n[1]]}),rc=Jl.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",zt]]);ec.year=ga.year.utc,ga.scale.utc=function(){return ro(ao.scale.linear(),ec,rc)},ao.text=An(function(n){return n.responseText}),ao.json=function(n,t){return Cn(n,"application/json",uo,t)},ao.html=function(n,t){return Cn(n,"text/html",oo,t)},ao.xml=An(function(n){return n.responseXML}),"function"==typeof define&&define.amd?(this.d3=ao,define(ao)):"object"==typeof module&&module.exports?module.exports=ao:this.d3=ao}(); -!function(a){"use strict";function b(a){this.owner=a}function c(a,b){if(Object.create)b.prototype=Object.create(a.prototype);else{var c=function(){};c.prototype=a.prototype,b.prototype=new c}return b.prototype.constructor=b,b}function d(a){var b=this.internal=new e(this);b.loadConfig(a),b.beforeInit(a),b.init(),b.afterInit(a),function c(a,b,d){Object.keys(a).forEach(function(e){b[e]=a[e].bind(d),Object.keys(a[e]).length>0&&c(a[e],b[e],d)})}(h,this,this)}function e(b){var c=this;c.d3=a.d3?a.d3:"undefined"!=typeof require?require("d3"):void 0,c.api=b,c.config=c.getDefaultConfig(),c.data={},c.cache={},c.axes={}}function f(a){b.call(this,a)}function g(a,b){function c(a,b){a.attr("transform",function(a){return"translate("+Math.ceil(b(a)+u)+", 0)"})}function d(a,b){a.attr("transform",function(a){return"translate(0,"+Math.ceil(b(a))+")"})}function e(a){var b=a[0],c=a[a.length-1];return c>b?[b,c]:[c,b]}function f(a){var b,c,d=[];if(a.ticks)return a.ticks.apply(a,n);for(c=a.domain(),b=Math.ceil(c[0]);b<c[1];b++)d.push(b);return d.length>0&&d[0]>0&&d.unshift(d[0]-(d[1]-d[0])),d}function g(){var a,c=p.copy();return b.isCategory&&(a=p.domain(),c.domain([a[0],a[1]-1])),c}function h(a){var b=m?m(a):a;return"undefined"!=typeof b?b:""}function i(a){if(A)return A;var b={h:11.5,w:5.5};return a.select("text").text(h).each(function(a){var c=this.getBoundingClientRect(),d=h(a),e=c.height,f=d?c.width/d.length:void 0;e&&f&&(b.h=e,b.w=f)}).text(""),A=b,b}function j(c){return b.withoutTransition?c:a.transition(c)}function k(m){m.each(function(){function m(a,c){function d(a,b){f=void 0;for(var h=1;h<b.length;h++)if(" "===b.charAt(h)&&(f=h),e=b.substr(0,h+1),g=U.w*e.length,g>c)return d(a.concat(b.substr(0,f?f:h)),b.slice(f?f+1:h));return a.concat(b)}var e,f,g,i=h(a),j=[];return"[object Array]"===Object.prototype.toString.call(i)?i:((!c||0>=c)&&(c=X?95:b.isCategory?Math.ceil(F(G[1])-F(G[0]))-12:110),d(j,i+""))}function n(a,b){var c=U.h;return 0===b&&(c="left"===q||"right"===q?-((V[a.index]-1)*(U.h/2)-3):".71em"),c}function v(a){var b=p(a)+(o?0:u);return L[0]<b&&b<L[1]?r:0}function w(a){return a?a>0?"start":"end":"middle"}function x(a){return a?"rotate("+a+")":""}function y(a){return a?8*Math.sin(Math.PI*(a/180)):0}function z(a){return a?11.5-2.5*(a/15)*(a>0?1:-1):W}var A,B,C,D=k.g=a.select(this),E=this.__chart__||p,F=this.__chart__=g(),G=t?t:f(F),H=D.selectAll(".tick").data(G,F),I=H.enter().insert("g",".domain").attr("class","tick").style("opacity",1e-6),J=H.exit().remove(),K=j(H).style("opacity",1),L=p.rangeExtent?p.rangeExtent():e(p.range()),M=D.selectAll(".domain").data([0]),N=(M.enter().append("path").attr("class","domain"),j(M));I.append("line"),I.append("text");var O=I.select("line"),P=K.select("line"),Q=I.select("text"),R=K.select("text");b.isCategory?(u=Math.ceil((F(1)-F(0))/2),B=o?0:u,C=o?u:0):u=B=0;var S,T,U=i(D.select(".tick")),V=[],W=Math.max(r,0)+s,X="left"===q||"right"===q;S=H.select("text"),T=S.selectAll("tspan").data(function(a,c){var d=b.tickMultiline?m(a,b.tickWidth):[].concat(h(a));return V[c]=d.length,d.map(function(a){return{index:c,splitted:a}})}),T.enter().append("tspan"),T.exit().remove(),T.text(function(a){return a.splitted});var Y=b.tickTextRotate;switch(q){case"bottom":A=c,O.attr("y2",r),Q.attr("y",W),P.attr("x1",B).attr("x2",B).attr("y2",v),R.attr("x",0).attr("y",z(Y)).style("text-anchor",w(Y)).attr("transform",x(Y)),T.attr("x",0).attr("dy",n).attr("dx",y(Y)),N.attr("d","M"+L[0]+","+l+"V0H"+L[1]+"V"+l);break;case"top":A=c,O.attr("y2",-r),Q.attr("y",-W),P.attr("x2",0).attr("y2",-r),R.attr("x",0).attr("y",-W),S.style("text-anchor","middle"),T.attr("x",0).attr("dy","0em"),N.attr("d","M"+L[0]+","+-l+"V0H"+L[1]+"V"+-l);break;case"left":A=d,O.attr("x2",-r),Q.attr("x",-W),P.attr("x2",-r).attr("y1",C).attr("y2",C),R.attr("x",-W).attr("y",u),S.style("text-anchor","end"),T.attr("x",-W).attr("dy",n),N.attr("d","M"+-l+","+L[0]+"H0V"+L[1]+"H"+-l);break;case"right":A=d,O.attr("x2",r),Q.attr("x",W),P.attr("x2",r).attr("y2",0),R.attr("x",W).attr("y",0),S.style("text-anchor","start"),T.attr("x",W).attr("dy",n),N.attr("d","M"+l+","+L[0]+"H0V"+L[1]+"H"+l)}if(F.rangeBand){var Z=F,$=Z.rangeBand()/2;E=F=function(a){return Z(a)+$}}else E.rangeBand?E=F:J.call(A,F);I.call(A,E),K.call(A,F)})}var l,m,n,o,p=a.scale.linear(),q="bottom",r=6,s=3,t=null,u=0,v=!0;return b=b||{},l=b.withOuterTick?6:0,k.scale=function(a){return arguments.length?(p=a,k):p},k.orient=function(a){return arguments.length?(q=a in{top:1,right:1,bottom:1,left:1}?a+"":"bottom",k):q},k.tickFormat=function(a){return arguments.length?(m=a,k):m},k.tickCentered=function(a){return arguments.length?(o=a,k):o},k.tickOffset=function(){return u},k.tickInterval=function(){var a,c;return b.isCategory?a=2*u:(c=k.g.select("path.domain").node().getTotalLength()-2*l,a=c/k.g.selectAll("line").size()),a===1/0?0:a},k.ticks=function(){return arguments.length?(n=arguments,k):n},k.tickCulling=function(a){return arguments.length?(v=a,k):v},k.tickValues=function(a){if("function"==typeof a)t=function(){return a(p.domain())};else{if(!arguments.length)return t;t=a}return k},k}var h,i,j,k={version:"0.4.11"};k.generate=function(a){return new d(a)},k.chart={fn:d.prototype,internal:{fn:e.prototype,axis:{fn:f.prototype}}},h=k.chart.fn,i=k.chart.internal.fn,j=k.chart.internal.axis.fn,i.beforeInit=function(){},i.afterInit=function(){},i.init=function(){var a=this,b=a.config;if(a.initParams(),b.data_url)a.convertUrlToData(b.data_url,b.data_mimeType,b.data_headers,b.data_keys,a.initWithData);else if(b.data_json)a.initWithData(a.convertJsonToData(b.data_json,b.data_keys));else if(b.data_rows)a.initWithData(a.convertRowsToData(b.data_rows));else{if(!b.data_columns)throw Error("url or json or rows or columns is required.");a.initWithData(a.convertColumnsToData(b.data_columns))}},i.initParams=function(){var a=this,b=a.d3,c=a.config;a.clipId="c3-"+ +new Date+"-clip",a.clipIdForXAxis=a.clipId+"-xaxis",a.clipIdForYAxis=a.clipId+"-yaxis",a.clipIdForGrid=a.clipId+"-grid",a.clipIdForSubchart=a.clipId+"-subchart",a.clipPath=a.getClipPath(a.clipId),a.clipPathForXAxis=a.getClipPath(a.clipIdForXAxis),a.clipPathForYAxis=a.getClipPath(a.clipIdForYAxis),a.clipPathForGrid=a.getClipPath(a.clipIdForGrid),a.clipPathForSubchart=a.getClipPath(a.clipIdForSubchart),a.dragStart=null,a.dragging=!1,a.flowing=!1,a.cancelClick=!1,a.mouseover=!1,a.transiting=!1,a.color=a.generateColor(),a.levelColor=a.generateLevelColor(),a.dataTimeFormat=c.data_xLocaltime?b.time.format:b.time.format.utc,a.axisTimeFormat=c.axis_x_localtime?b.time.format:b.time.format.utc,a.defaultAxisTimeFormat=a.axisTimeFormat.multi([[".%L",function(a){return a.getMilliseconds()}],[":%S",function(a){return a.getSeconds()}],["%I:%M",function(a){return a.getMinutes()}],["%I %p",function(a){return a.getHours()}],["%-m/%-d",function(a){return a.getDay()&&1!==a.getDate()}],["%-m/%-d",function(a){return 1!==a.getDate()}],["%-m/%-d",function(a){return a.getMonth()}],["%Y/%-m/%-d",function(){return!0}]]),a.hiddenTargetIds=[],a.hiddenLegendIds=[],a.focusedTargetIds=[],a.defocusedTargetIds=[],a.xOrient=c.axis_rotated?"left":"bottom",a.yOrient=c.axis_rotated?c.axis_y_inner?"top":"bottom":c.axis_y_inner?"right":"left",a.y2Orient=c.axis_rotated?c.axis_y2_inner?"bottom":"top":c.axis_y2_inner?"left":"right",a.subXOrient=c.axis_rotated?"left":"bottom",a.isLegendRight="right"===c.legend_position,a.isLegendInset="inset"===c.legend_position,a.isLegendTop="top-left"===c.legend_inset_anchor||"top-right"===c.legend_inset_anchor,a.isLegendLeft="top-left"===c.legend_inset_anchor||"bottom-left"===c.legend_inset_anchor,a.legendStep=0,a.legendItemWidth=0,a.legendItemHeight=0,a.currentMaxTickWidths={x:0,y:0,y2:0},a.rotated_padding_left=30,a.rotated_padding_right=c.axis_rotated&&!c.axis_x_show?0:30,a.rotated_padding_top=5,a.withoutFadeIn={},a.intervalForObserveInserted=void 0,a.axes.subx=b.selectAll([])},i.initChartElements=function(){this.initBar&&this.initBar(),this.initLine&&this.initLine(),this.initArc&&this.initArc(),this.initGauge&&this.initGauge(),this.initText&&this.initText()},i.initWithData=function(a){var b,c,d=this,e=d.d3,g=d.config,h=!0;d.axis=new f(d),d.initPie&&d.initPie(),d.initBrush&&d.initBrush(),d.initZoom&&d.initZoom(),g.bindto?"function"==typeof g.bindto.node?d.selectChart=g.bindto:d.selectChart=e.select(g.bindto):d.selectChart=e.selectAll([]),d.selectChart.empty()&&(d.selectChart=e.select(document.createElement("div")).style("opacity",0),d.observeInserted(d.selectChart),h=!1),d.selectChart.html("").classed("c3",!0),d.data.xs={},d.data.targets=d.convertDataToTargets(a),g.data_filter&&(d.data.targets=d.data.targets.filter(g.data_filter)),g.data_hide&&d.addHiddenTargetIds(g.data_hide===!0?d.mapToIds(d.data.targets):g.data_hide),g.legend_hide&&d.addHiddenLegendIds(g.legend_hide===!0?d.mapToIds(d.data.targets):g.legend_hide),d.hasType("gauge")&&(g.legend_show=!1),d.updateSizes(),d.updateScales(),d.x.domain(e.extent(d.getXDomain(d.data.targets))),d.y.domain(d.getYDomain(d.data.targets,"y")),d.y2.domain(d.getYDomain(d.data.targets,"y2")),d.subX.domain(d.x.domain()),d.subY.domain(d.y.domain()),d.subY2.domain(d.y2.domain()),d.orgXDomain=d.x.domain(),d.brush&&d.brush.scale(d.subX),g.zoom_enabled&&d.zoom.scale(d.x),d.svg=d.selectChart.append("svg").style("overflow","hidden").on("mouseenter",function(){return g.onmouseover.call(d)}).on("mouseleave",function(){return g.onmouseout.call(d)}),d.config.svg_classname&&d.svg.attr("class",d.config.svg_classname),b=d.svg.append("defs"),d.clipChart=d.appendClip(b,d.clipId),d.clipXAxis=d.appendClip(b,d.clipIdForXAxis),d.clipYAxis=d.appendClip(b,d.clipIdForYAxis),d.clipGrid=d.appendClip(b,d.clipIdForGrid),d.clipSubchart=d.appendClip(b,d.clipIdForSubchart),d.updateSvgSize(),c=d.main=d.svg.append("g").attr("transform",d.getTranslate("main")),d.initSubchart&&d.initSubchart(),d.initTooltip&&d.initTooltip(),d.initLegend&&d.initLegend(),d.initTitle&&d.initTitle(),c.append("text").attr("class",l.text+" "+l.empty).attr("text-anchor","middle").attr("dominant-baseline","middle"),d.initRegion(),d.initGrid(),c.append("g").attr("clip-path",d.clipPath).attr("class",l.chart),g.grid_lines_front&&d.initGridLines(),d.initEventRect(),d.initChartElements(),c.insert("rect",g.zoom_privileged?null:"g."+l.regions).attr("class",l.zoomRect).attr("width",d.width).attr("height",d.height).style("opacity",0).on("dblclick.zoom",null),g.axis_x_extent&&d.brush.extent(d.getDefaultExtent()),d.axis.init(),d.updateTargets(d.data.targets),h&&(d.updateDimension(),d.config.oninit.call(d),d.redraw({withTransition:!1,withTransform:!0,withUpdateXDomain:!0,withUpdateOrgXDomain:!0,withTransitionForAxis:!1})),d.bindResize(),d.api.element=d.selectChart.node()},i.smoothLines=function(a,b){var c=this;"grid"===b&&a.each(function(){var a=c.d3.select(this),b=a.attr("x1"),d=a.attr("x2"),e=a.attr("y1"),f=a.attr("y2");a.attr({x1:Math.ceil(b),x2:Math.ceil(d),y1:Math.ceil(e),y2:Math.ceil(f)})})},i.updateSizes=function(){var a=this,b=a.config,c=a.legend?a.getLegendHeight():0,d=a.legend?a.getLegendWidth():0,e=a.isLegendRight||a.isLegendInset?0:c,f=a.hasArcType(),g=b.axis_rotated||f?0:a.getHorizontalAxisHeight("x"),h=b.subchart_show&&!f?b.subchart_size_height+g:0;a.currentWidth=a.getCurrentWidth(),a.currentHeight=a.getCurrentHeight(),a.margin=b.axis_rotated?{top:a.getHorizontalAxisHeight("y2")+a.getCurrentPaddingTop(),right:f?0:a.getCurrentPaddingRight(),bottom:a.getHorizontalAxisHeight("y")+e+a.getCurrentPaddingBottom(),left:h+(f?0:a.getCurrentPaddingLeft())}:{top:4+a.getCurrentPaddingTop(),right:f?0:a.getCurrentPaddingRight(),bottom:g+h+e+a.getCurrentPaddingBottom(),left:f?0:a.getCurrentPaddingLeft()},a.margin2=b.axis_rotated?{top:a.margin.top,right:NaN,bottom:20+e,left:a.rotated_padding_left}:{top:a.currentHeight-h-e,right:NaN,bottom:g+e,left:a.margin.left},a.margin3={top:0,right:NaN,bottom:0,left:0},a.updateSizeForLegend&&a.updateSizeForLegend(c,d),a.width=a.currentWidth-a.margin.left-a.margin.right,a.height=a.currentHeight-a.margin.top-a.margin.bottom,a.width<0&&(a.width=0),a.height<0&&(a.height=0),a.width2=b.axis_rotated?a.margin.left-a.rotated_padding_left-a.rotated_padding_right:a.width,a.height2=b.axis_rotated?a.height:a.currentHeight-a.margin2.top-a.margin2.bottom,a.width2<0&&(a.width2=0),a.height2<0&&(a.height2=0),a.arcWidth=a.width-(a.isLegendRight?d+10:0),a.arcHeight=a.height-(a.isLegendRight?0:10),a.hasType("gauge")&&!b.gauge_fullCircle&&(a.arcHeight+=a.height-a.getGaugeLabelHeight()),a.updateRadius&&a.updateRadius(),a.isLegendRight&&f&&(a.margin3.left=a.arcWidth/2+1.1*a.radiusExpanded)},i.updateTargets=function(a){var b=this;b.updateTargetsForText(a),b.updateTargetsForBar(a),b.updateTargetsForLine(a),b.hasArcType()&&b.updateTargetsForArc&&b.updateTargetsForArc(a),b.updateTargetsForSubchart&&b.updateTargetsForSubchart(a),b.showTargets()},i.showTargets=function(){var a=this;a.svg.selectAll("."+l.target).filter(function(b){return a.isTargetToShow(b.id)}).transition().duration(a.config.transition_duration).style("opacity",1)},i.redraw=function(a,b){var c,d,e,f,g,h,i,j,k,m,n,o,p,q,r,s,t,u,v,x,y,z,A,B,C,D,E,F,G,H=this,I=H.main,J=H.d3,K=H.config,L=H.getShapeIndices(H.isAreaType),M=H.getShapeIndices(H.isBarType),N=H.getShapeIndices(H.isLineType),O=H.hasArcType(),P=H.filterTargetsToShow(H.data.targets),Q=H.xv.bind(H);if(a=a||{},c=w(a,"withY",!0),d=w(a,"withSubchart",!0),e=w(a,"withTransition",!0),h=w(a,"withTransform",!1),i=w(a,"withUpdateXDomain",!1),j=w(a,"withUpdateOrgXDomain",!1),k=w(a,"withTrimXDomain",!0),p=w(a,"withUpdateXAxis",i),m=w(a,"withLegend",!1),n=w(a,"withEventRect",!0),o=w(a,"withDimension",!0),f=w(a,"withTransitionForExit",e),g=w(a,"withTransitionForAxis",e),v=e?K.transition_duration:0,x=f?v:0,y=g?v:0,b=b||H.axis.generateTransitions(y),m&&K.legend_show?H.updateLegend(H.mapToIds(H.data.targets),a,b):o&&H.updateDimension(!0),H.isCategorized()&&0===P.length&&H.x.domain([0,H.axes.x.selectAll(".tick").size()]),P.length?(H.updateXDomain(P,i,j,k),K.axis_x_tick_values||(B=H.axis.updateXAxisTickValues(P))):(H.xAxis.tickValues([]),H.subXAxis.tickValues([])),K.zoom_rescale&&!a.flow&&(E=H.x.orgDomain()),H.y.domain(H.getYDomain(P,"y",E)),H.y2.domain(H.getYDomain(P,"y2",E)),!K.axis_y_tick_values&&K.axis_y_tick_count&&H.yAxis.tickValues(H.axis.generateTickValues(H.y.domain(),K.axis_y_tick_count)),!K.axis_y2_tick_values&&K.axis_y2_tick_count&&H.y2Axis.tickValues(H.axis.generateTickValues(H.y2.domain(),K.axis_y2_tick_count)),H.axis.redraw(b,O),H.axis.updateLabels(e),(i||p)&&P.length)if(K.axis_x_tick_culling&&B){for(C=1;C<B.length;C++)if(B.length/C<K.axis_x_tick_culling_max){D=C;break}H.svg.selectAll("."+l.axisX+" .tick text").each(function(a){var b=B.indexOf(a);b>=0&&J.select(this).style("display",b%D?"none":"block")})}else H.svg.selectAll("."+l.axisX+" .tick text").style("display","block");q=H.generateDrawArea?H.generateDrawArea(L,!1):void 0,r=H.generateDrawBar?H.generateDrawBar(M):void 0,s=H.generateDrawLine?H.generateDrawLine(N,!1):void 0,t=H.generateXYForText(L,M,N,!0),u=H.generateXYForText(L,M,N,!1),c&&(H.subY.domain(H.getYDomain(P,"y")),H.subY2.domain(H.getYDomain(P,"y2"))),H.updateXgridFocus(),I.select("text."+l.text+"."+l.empty).attr("x",H.width/2).attr("y",H.height/2).text(K.data_empty_label_text).transition().style("opacity",P.length?0:1),H.updateGrid(v),H.updateRegion(v),H.updateBar(x),H.updateLine(x),H.updateArea(x),H.updateCircle(),H.hasDataLabel()&&H.updateText(x),H.redrawTitle&&H.redrawTitle(),H.redrawArc&&H.redrawArc(v,x,h),H.redrawSubchart&&H.redrawSubchart(d,b,v,x,L,M,N),I.selectAll("."+l.selectedCircles).filter(H.isBarType.bind(H)).selectAll("circle").remove(),K.interaction_enabled&&!a.flow&&n&&(H.redrawEventRect(),H.updateZoom&&H.updateZoom()),H.updateCircleY(),F=(H.config.axis_rotated?H.circleY:H.circleX).bind(H),G=(H.config.axis_rotated?H.circleX:H.circleY).bind(H),a.flow&&(A=H.generateFlow({targets:P,flow:a.flow,duration:a.flow.duration,drawBar:r,drawLine:s,drawArea:q,cx:F,cy:G,xv:Q,xForText:t,yForText:u})),(v||A)&&H.isTabVisible()?J.transition().duration(v).each(function(){var b=[];[H.redrawBar(r,!0),H.redrawLine(s,!0),H.redrawArea(q,!0),H.redrawCircle(F,G,!0),H.redrawText(t,u,a.flow,!0),H.redrawRegion(!0),H.redrawGrid(!0)].forEach(function(a){a.forEach(function(a){b.push(a)})}),z=H.generateWait(),b.forEach(function(a){z.add(a)})}).call(z,function(){A&&A(),K.onrendered&&K.onrendered.call(H)}):(H.redrawBar(r),H.redrawLine(s),H.redrawArea(q),H.redrawCircle(F,G),H.redrawText(t,u,a.flow),H.redrawRegion(),H.redrawGrid(),K.onrendered&&K.onrendered.call(H)),H.mapToIds(H.data.targets).forEach(function(a){H.withoutFadeIn[a]=!0})},i.updateAndRedraw=function(a){var b,c=this,d=c.config;a=a||{},a.withTransition=w(a,"withTransition",!0),a.withTransform=w(a,"withTransform",!1),a.withLegend=w(a,"withLegend",!1),a.withUpdateXDomain=!0,a.withUpdateOrgXDomain=!0,a.withTransitionForExit=!1,a.withTransitionForTransform=w(a,"withTransitionForTransform",a.withTransition),c.updateSizes(),a.withLegend&&d.legend_show||(b=c.axis.generateTransitions(a.withTransitionForAxis?d.transition_duration:0),c.updateScales(),c.updateSvgSize(),c.transformAll(a.withTransitionForTransform,b)),c.redraw(a,b)},i.redrawWithoutRescale=function(){this.redraw({withY:!1,withSubchart:!1,withEventRect:!1,withTransitionForAxis:!1})},i.isTimeSeries=function(){return"timeseries"===this.config.axis_x_type},i.isCategorized=function(){return this.config.axis_x_type.indexOf("categor")>=0},i.isCustomX=function(){var a=this,b=a.config;return!a.isTimeSeries()&&(b.data_x||v(b.data_xs))},i.isTimeSeriesY=function(){return"timeseries"===this.config.axis_y_type},i.getTranslate=function(a){var b,c,d=this,e=d.config;return"main"===a?(b=s(d.margin.left),c=s(d.margin.top)):"context"===a?(b=s(d.margin2.left),c=s(d.margin2.top)):"legend"===a?(b=d.margin3.left,c=d.margin3.top):"x"===a?(b=0,c=e.axis_rotated?0:d.height):"y"===a?(b=0,c=e.axis_rotated?d.height:0):"y2"===a?(b=e.axis_rotated?0:d.width,c=e.axis_rotated?1:0):"subx"===a?(b=0,c=e.axis_rotated?0:d.height2):"arc"===a&&(b=d.arcWidth/2,c=d.arcHeight/2),"translate("+b+","+c+")"},i.initialOpacity=function(a){return null!==a.value&&this.withoutFadeIn[a.id]?1:0},i.initialOpacityForCircle=function(a){return null!==a.value&&this.withoutFadeIn[a.id]?this.opacityForCircle(a):0},i.opacityForCircle=function(a){var b=this.config.point_show?1:0;return m(a.value)?this.isScatterType(a)?.5:b:0},i.opacityForText=function(){return this.hasDataLabel()?1:0},i.xx=function(a){return a?this.x(a.x):null},i.xv=function(a){var b=this,c=a.value;return b.isTimeSeries()?c=b.parseDate(a.value):b.isCategorized()&&"string"==typeof a.value&&(c=b.config.axis_x_categories.indexOf(a.value)),Math.ceil(b.x(c))},i.yv=function(a){var b=this,c=a.axis&&"y2"===a.axis?b.y2:b.y;return Math.ceil(c(a.value))},i.subxx=function(a){return a?this.subX(a.x):null},i.transformMain=function(a,b){var c,d,e,f=this;b&&b.axisX?c=b.axisX:(c=f.main.select("."+l.axisX),a&&(c=c.transition())),b&&b.axisY?d=b.axisY:(d=f.main.select("."+l.axisY),a&&(d=d.transition())),b&&b.axisY2?e=b.axisY2:(e=f.main.select("."+l.axisY2),a&&(e=e.transition())),(a?f.main.transition():f.main).attr("transform",f.getTranslate("main")),c.attr("transform",f.getTranslate("x")),d.attr("transform",f.getTranslate("y")),e.attr("transform",f.getTranslate("y2")),f.main.select("."+l.chartArcs).attr("transform",f.getTranslate("arc"))},i.transformAll=function(a,b){var c=this;c.transformMain(a,b),c.config.subchart_show&&c.transformContext(a,b),c.legend&&c.transformLegend(a)},i.updateSvgSize=function(){var a=this,b=a.svg.select(".c3-brush .background");a.svg.attr("width",a.currentWidth).attr("height",a.currentHeight),a.svg.selectAll(["#"+a.clipId,"#"+a.clipIdForGrid]).select("rect").attr("width",a.width).attr("height",a.height),a.svg.select("#"+a.clipIdForXAxis).select("rect").attr("x",a.getXAxisClipX.bind(a)).attr("y",a.getXAxisClipY.bind(a)).attr("width",a.getXAxisClipWidth.bind(a)).attr("height",a.getXAxisClipHeight.bind(a)),a.svg.select("#"+a.clipIdForYAxis).select("rect").attr("x",a.getYAxisClipX.bind(a)).attr("y",a.getYAxisClipY.bind(a)).attr("width",a.getYAxisClipWidth.bind(a)).attr("height",a.getYAxisClipHeight.bind(a)),a.svg.select("#"+a.clipIdForSubchart).select("rect").attr("width",a.width).attr("height",b.size()?b.attr("height"):0),a.svg.select("."+l.zoomRect).attr("width",a.width).attr("height",a.height),a.selectChart.style("max-height",a.currentHeight+"px")},i.updateDimension=function(a){var b=this;a||(b.config.axis_rotated?(b.axes.x.call(b.xAxis),b.axes.subx.call(b.subXAxis)):(b.axes.y.call(b.yAxis),b.axes.y2.call(b.y2Axis))),b.updateSizes(),b.updateScales(),b.updateSvgSize(),b.transformAll(!1)},i.observeInserted=function(b){var c,d=this;return"undefined"==typeof MutationObserver?void a.console.error("MutationObserver not defined."):(c=new MutationObserver(function(e){e.forEach(function(e){"childList"===e.type&&e.previousSibling&&(c.disconnect(),d.intervalForObserveInserted=a.setInterval(function(){b.node().parentNode&&(a.clearInterval(d.intervalForObserveInserted),d.updateDimension(),d.brush&&d.brush.update(),d.config.oninit.call(d),d.redraw({withTransform:!0,withUpdateXDomain:!0,withUpdateOrgXDomain:!0,withTransition:!1,withTransitionForTransform:!1,withLegend:!0}),b.transition().style("opacity",1))},10))})}),void c.observe(b.node(),{attributes:!0,childList:!0,characterData:!0}))},i.bindResize=function(){var b=this,c=b.config;if(b.resizeFunction=b.generateResize(),b.resizeFunction.add(function(){c.onresize.call(b)}),c.resize_auto&&b.resizeFunction.add(function(){void 0!==b.resizeTimeout&&a.clearTimeout(b.resizeTimeout),b.resizeTimeout=a.setTimeout(function(){delete b.resizeTimeout,b.api.flush()},100)}),b.resizeFunction.add(function(){c.onresized.call(b)}),a.attachEvent)a.attachEvent("onresize",b.resizeFunction);else if(a.addEventListener)a.addEventListener("resize",b.resizeFunction,!1);else{var d=a.onresize;d?d.add&&d.remove||(d=b.generateResize(),d.add(a.onresize)):d=b.generateResize(),d.add(b.resizeFunction),a.onresize=d}},i.generateResize=function(){function a(){b.forEach(function(a){a()})}var b=[];return a.add=function(a){b.push(a)},a.remove=function(a){for(var c=0;c<b.length;c++)if(b[c]===a){b.splice(c,1);break}},a},i.endall=function(a,b){var c=0;a.each(function(){++c}).each("end",function(){--c||b.apply(this,arguments)})},i.generateWait=function(){var a=[],b=function(b,c){var d=setInterval(function(){var b=0;a.forEach(function(a){if(a.empty())return void(b+=1);try{a.transition()}catch(c){b+=1}}),b===a.length&&(clearInterval(d),c&&c())},10)};return b.add=function(b){a.push(b)},b},i.parseDate=function(b){var c,d=this;return b instanceof Date?c=b:"string"==typeof b?c=d.dataTimeFormat(d.config.data_xFormat).parse(b):"number"!=typeof b||isNaN(b)||(c=new Date(+b)),c&&!isNaN(+c)||a.console.error("Failed to parse x '"+b+"' to Date object"),c},i.isTabVisible=function(){var a;return"undefined"!=typeof document.hidden?a="hidden":"undefined"!=typeof document.mozHidden?a="mozHidden":"undefined"!=typeof document.msHidden?a="msHidden":"undefined"!=typeof document.webkitHidden&&(a="webkitHidden"),!document[a]},i.getDefaultConfig=function(){var a={bindto:"#chart",svg_classname:void 0,size_width:void 0,size_height:void 0,padding_left:void 0,padding_right:void 0,padding_top:void 0,padding_bottom:void 0,resize_auto:!0,zoom_enabled:!1,zoom_extent:void 0,zoom_privileged:!1,zoom_rescale:!1,zoom_onzoom:function(){},zoom_onzoomstart:function(){},zoom_onzoomend:function(){},zoom_x_min:void 0,zoom_x_max:void 0,interaction_brighten:!0,interaction_enabled:!0,onmouseover:function(){},onmouseout:function(){},onresize:function(){},onresized:function(){},oninit:function(){},onrendered:function(){},transition_duration:350,data_x:void 0,data_xs:{},data_xFormat:"%Y-%m-%d",data_xLocaltime:!0,data_xSort:!0,data_idConverter:function(a){return a},data_names:{},data_classes:{},data_groups:[],data_axes:{},data_type:void 0,data_types:{},data_labels:{},data_order:"desc",data_regions:{},data_color:void 0,data_colors:{},data_hide:!1,data_filter:void 0,data_selection_enabled:!1,data_selection_grouped:!1,data_selection_isselectable:function(){return!0},data_selection_multiple:!0,data_selection_draggable:!1,data_onclick:function(){},data_onmouseover:function(){},data_onmouseout:function(){},data_onselected:function(){},data_onunselected:function(){},data_url:void 0,data_headers:void 0,data_json:void 0,data_rows:void 0,data_columns:void 0,data_mimeType:void 0,data_keys:void 0,data_empty_label_text:"",subchart_show:!1,subchart_size_height:60,subchart_axis_x_show:!0,subchart_onbrush:function(){},color_pattern:[],color_threshold:{},legend_show:!0,legend_hide:!1,legend_position:"bottom",legend_inset_anchor:"top-left",legend_inset_x:10,legend_inset_y:0,legend_inset_step:void 0,legend_item_onclick:void 0,legend_item_onmouseover:void 0,legend_item_onmouseout:void 0,legend_equally:!1,legend_padding:0,legend_item_tile_width:10,legend_item_tile_height:10,axis_rotated:!1,axis_x_show:!0,axis_x_type:"indexed",axis_x_localtime:!0,axis_x_categories:[],axis_x_tick_centered:!1,axis_x_tick_format:void 0,axis_x_tick_culling:{},axis_x_tick_culling_max:10,axis_x_tick_count:void 0,axis_x_tick_fit:!0,axis_x_tick_values:null,axis_x_tick_rotate:0,axis_x_tick_outer:!0,axis_x_tick_multiline:!0,axis_x_tick_width:null,axis_x_max:void 0,axis_x_min:void 0,axis_x_padding:{},axis_x_height:void 0,axis_x_extent:void 0,axis_x_label:{},axis_y_show:!0,axis_y_type:void 0,axis_y_max:void 0,axis_y_min:void 0,axis_y_inverted:!1,axis_y_center:void 0,axis_y_inner:void 0,axis_y_label:{},axis_y_tick_format:void 0,axis_y_tick_outer:!0,axis_y_tick_values:null,axis_y_tick_rotate:0,axis_y_tick_count:void 0,axis_y_tick_time_value:void 0,axis_y_tick_time_interval:void 0,axis_y_padding:{},axis_y_default:void 0,axis_y2_show:!1,axis_y2_max:void 0,axis_y2_min:void 0,axis_y2_inverted:!1,axis_y2_center:void 0,axis_y2_inner:void 0,axis_y2_label:{},axis_y2_tick_format:void 0,axis_y2_tick_outer:!0,axis_y2_tick_values:null,axis_y2_tick_count:void 0,axis_y2_padding:{},axis_y2_default:void 0,grid_x_show:!1,grid_x_type:"tick",grid_x_lines:[],grid_y_show:!1,grid_y_lines:[],grid_y_ticks:10,grid_focus_show:!0,grid_lines_front:!0,point_show:!0,point_r:2.5,point_sensitivity:10,point_focus_expand_enabled:!0,point_focus_expand_r:void 0,point_select_r:void 0,line_connectNull:!1,line_step_type:"step",bar_width:void 0,bar_width_ratio:.6,bar_width_max:void 0,bar_zerobased:!0,area_zerobased:!0,area_above:!1,pie_label_show:!0,pie_label_format:void 0,pie_label_threshold:.05,pie_label_ratio:void 0,pie_expand:{},pie_expand_duration:50,gauge_fullCircle:!1,gauge_label_show:!0,gauge_label_format:void 0,gauge_min:0,gauge_max:100,gauge_startingAngle:-1*Math.PI/2,gauge_units:void 0,gauge_width:void 0,gauge_expand:{},gauge_expand_duration:50,donut_label_show:!0,donut_label_format:void 0,donut_label_threshold:.05,donut_label_ratio:void 0,donut_width:void 0,donut_title:"",donut_expand:{},donut_expand_duration:50,spline_interpolation_type:"cardinal",regions:[],tooltip_show:!0,tooltip_grouped:!0,tooltip_format_title:void 0,tooltip_format_name:void 0,tooltip_format_value:void 0,tooltip_position:void 0,tooltip_contents:function(a,b,c,d){return this.getTooltipContent?this.getTooltipContent(a,b,c,d):""},tooltip_init_show:!1,tooltip_init_x:0,tooltip_init_position:{top:"0px",left:"50px"},tooltip_onshow:function(){},tooltip_onhide:function(){},title_text:void 0,title_padding:{top:0,right:0,bottom:0,left:0},title_position:"top-center"};return Object.keys(this.additionalConfig).forEach(function(b){a[b]=this.additionalConfig[b]},this),a},i.additionalConfig={},i.loadConfig=function(a){function b(){var a=d.shift();return a&&c&&"object"==typeof c&&a in c?(c=c[a],b()):a?void 0:c}var c,d,e,f=this.config;Object.keys(f).forEach(function(g){c=a,d=g.split("_"),e=b(),q(e)&&(f[g]=e)})},i.getScale=function(a,b,c){return(c?this.d3.time.scale():this.d3.scale.linear()).range([a,b])},i.getX=function(a,b,c,d){var e,f=this,g=f.getScale(a,b,f.isTimeSeries()),h=c?g.domain(c):g;f.isCategorized()?(d=d||function(){return 0},g=function(a,b){var c=h(a)+d(a);return b?c:Math.ceil(c)}):g=function(a,b){var c=h(a);return b?c:Math.ceil(c)};for(e in h)g[e]=h[e];return g.orgDomain=function(){return h.domain()},f.isCategorized()&&(g.domain=function(a){return arguments.length?(h.domain(a),g):(a=this.orgDomain(),[a[0],a[1]+1])}),g},i.getY=function(a,b,c){var d=this.getScale(a,b,this.isTimeSeriesY());return c&&d.domain(c),d},i.getYScale=function(a){return"y2"===this.axis.getId(a)?this.y2:this.y},i.getSubYScale=function(a){return"y2"===this.axis.getId(a)?this.subY2:this.subY},i.updateScales=function(){var a=this,b=a.config,c=!a.x;a.xMin=b.axis_rotated?1:0,a.xMax=b.axis_rotated?a.height:a.width,a.yMin=b.axis_rotated?0:a.height,a.yMax=b.axis_rotated?a.width:1,a.subXMin=a.xMin,a.subXMax=a.xMax,a.subYMin=b.axis_rotated?0:a.height2,a.subYMax=b.axis_rotated?a.width2:1,a.x=a.getX(a.xMin,a.xMax,c?void 0:a.x.orgDomain(),function(){return a.xAxis.tickOffset()}),a.y=a.getY(a.yMin,a.yMax,c?b.axis_y_default:a.y.domain()),a.y2=a.getY(a.yMin,a.yMax,c?b.axis_y2_default:a.y2.domain()),a.subX=a.getX(a.xMin,a.xMax,a.orgXDomain,function(b){return b%1?0:a.subXAxis.tickOffset()}),a.subY=a.getY(a.subYMin,a.subYMax,c?b.axis_y_default:a.subY.domain()),a.subY2=a.getY(a.subYMin,a.subYMax,c?b.axis_y2_default:a.subY2.domain()),a.xAxisTickFormat=a.axis.getXAxisTickFormat(),a.xAxisTickValues=a.axis.getXAxisTickValues(),a.yAxisTickValues=a.axis.getYAxisTickValues(),a.y2AxisTickValues=a.axis.getY2AxisTickValues(),a.xAxis=a.axis.getXAxis(a.x,a.xOrient,a.xAxisTickFormat,a.xAxisTickValues,b.axis_x_tick_outer),a.subXAxis=a.axis.getXAxis(a.subX,a.subXOrient,a.xAxisTickFormat,a.xAxisTickValues,b.axis_x_tick_outer),a.yAxis=a.axis.getYAxis(a.y,a.yOrient,b.axis_y_tick_format,a.yAxisTickValues,b.axis_y_tick_outer),a.y2Axis=a.axis.getYAxis(a.y2,a.y2Orient,b.axis_y2_tick_format,a.y2AxisTickValues,b.axis_y2_tick_outer),c||(a.brush&&a.brush.scale(a.subX),b.zoom_enabled&&a.zoom.scale(a.x)),a.updateArc&&a.updateArc()},i.getYDomainMin=function(a){var b,c,d,e,f,g,h=this,i=h.config,j=h.mapToIds(a),k=h.getValuesAsIdKeyed(a);if(i.data_groups.length>0)for(g=h.hasNegativeValueInTargets(a),b=0;b<i.data_groups.length;b++)if(e=i.data_groups[b].filter(function(a){return j.indexOf(a)>=0}),0!==e.length)for(d=e[0],g&&k[d]&&k[d].forEach(function(a,b){k[d][b]=0>a?a:0}),c=1;c<e.length;c++)f=e[c],k[f]&&k[f].forEach(function(a,b){h.axis.getId(f)!==h.axis.getId(d)||!k[d]||g&&+a>0||(k[d][b]+=+a)});return h.d3.min(Object.keys(k).map(function(a){return h.d3.min(k[a])}))},i.getYDomainMax=function(a){var b,c,d,e,f,g,h=this,i=h.config,j=h.mapToIds(a),k=h.getValuesAsIdKeyed(a);if(i.data_groups.length>0)for(g=h.hasPositiveValueInTargets(a),b=0;b<i.data_groups.length;b++)if(e=i.data_groups[b].filter(function(a){return j.indexOf(a)>=0}),0!==e.length)for(d=e[0],g&&k[d]&&k[d].forEach(function(a,b){k[d][b]=a>0?a:0}),c=1;c<e.length;c++)f=e[c],k[f]&&k[f].forEach(function(a,b){h.axis.getId(f)!==h.axis.getId(d)||!k[d]||g&&0>+a||(k[d][b]+=+a)});return h.d3.max(Object.keys(k).map(function(a){return h.d3.max(k[a])}))},i.getYDomain=function(a,b,c){var d,e,f,g,h,i,j,k,l,n,o,p=this,q=p.config,r=a.filter(function(a){return p.axis.getId(a.id)===b}),s=c?p.filterByXDomain(r,c):r,u="y2"===b?q.axis_y2_min:q.axis_y_min,w="y2"===b?q.axis_y2_max:q.axis_y_max,x=p.getYDomainMin(s),y=p.getYDomainMax(s),z="y2"===b?q.axis_y2_center:q.axis_y_center,A=p.hasType("bar",s)&&q.bar_zerobased||p.hasType("area",s)&&q.area_zerobased,B="y2"===b?q.axis_y2_inverted:q.axis_y_inverted,C=p.hasDataLabel()&&q.axis_rotated,D=p.hasDataLabel()&&!q.axis_rotated;return x=m(u)?u:m(w)?w>x?x:w-10:x,y=m(w)?w:m(u)?y>u?y:u+10:y,0===s.length?"y2"===b?p.y2.domain():p.y.domain():(isNaN(x)&&(x=0),isNaN(y)&&(y=x),x===y&&(0>x?y=0:x=0),n=x>=0&&y>=0,o=0>=x&&0>=y,(m(u)&&n||m(w)&&o)&&(A=!1),A&&(n&&(x=0),o&&(y=0)),e=Math.abs(y-x),f=g=h=.1*e,"undefined"!=typeof z&&(i=Math.max(Math.abs(x),Math.abs(y)),y=z+i,x=z-i),C?(j=p.getDataLabelLength(x,y,"width"),k=t(p.y.range()),l=[j[0]/k,j[1]/k], -g+=e*(l[1]/(1-l[0]-l[1])),h+=e*(l[0]/(1-l[0]-l[1]))):D&&(j=p.getDataLabelLength(x,y,"height"),g+=p.axis.convertPixelsToAxisPadding(j[1],e),h+=p.axis.convertPixelsToAxisPadding(j[0],e)),"y"===b&&v(q.axis_y_padding)&&(g=p.axis.getPadding(q.axis_y_padding,"top",g,e),h=p.axis.getPadding(q.axis_y_padding,"bottom",h,e)),"y2"===b&&v(q.axis_y2_padding)&&(g=p.axis.getPadding(q.axis_y2_padding,"top",g,e),h=p.axis.getPadding(q.axis_y2_padding,"bottom",h,e)),A&&(n&&(h=x),o&&(g=-y)),d=[x-h,y+g],B?d.reverse():d)},i.getXDomainMin=function(a){var b=this,c=b.config;return q(c.axis_x_min)?b.isTimeSeries()?this.parseDate(c.axis_x_min):c.axis_x_min:b.d3.min(a,function(a){return b.d3.min(a.values,function(a){return a.x})})},i.getXDomainMax=function(a){var b=this,c=b.config;return q(c.axis_x_max)?b.isTimeSeries()?this.parseDate(c.axis_x_max):c.axis_x_max:b.d3.max(a,function(a){return b.d3.max(a.values,function(a){return a.x})})},i.getXDomainPadding=function(a){var b,c,d,e,f=this,g=f.config,h=a[1]-a[0];return f.isCategorized()?c=0:f.hasType("bar")?(b=f.getMaxDataCount(),c=b>1?h/(b-1)/2:.5):c=.01*h,"object"==typeof g.axis_x_padding&&v(g.axis_x_padding)?(d=m(g.axis_x_padding.left)?g.axis_x_padding.left:c,e=m(g.axis_x_padding.right)?g.axis_x_padding.right:c):d=e="number"==typeof g.axis_x_padding?g.axis_x_padding:c,{left:d,right:e}},i.getXDomain=function(a){var b=this,c=[b.getXDomainMin(a),b.getXDomainMax(a)],d=c[0],e=c[1],f=b.getXDomainPadding(c),g=0,h=0;return d-e!==0||b.isCategorized()||(b.isTimeSeries()?(d=new Date(.5*d.getTime()),e=new Date(1.5*e.getTime())):(d=0===d?1:.5*d,e=0===e?-1:1.5*e)),(d||0===d)&&(g=b.isTimeSeries()?new Date(d.getTime()-f.left):d-f.left),(e||0===e)&&(h=b.isTimeSeries()?new Date(e.getTime()+f.right):e+f.right),[g,h]},i.updateXDomain=function(a,b,c,d,e){var f=this,g=f.config;return c&&(f.x.domain(e?e:f.d3.extent(f.getXDomain(a))),f.orgXDomain=f.x.domain(),g.zoom_enabled&&f.zoom.scale(f.x).updateScaleExtent(),f.subX.domain(f.x.domain()),f.brush&&f.brush.scale(f.subX)),b&&(f.x.domain(e?e:!f.brush||f.brush.empty()?f.orgXDomain:f.brush.extent()),g.zoom_enabled&&f.zoom.scale(f.x).updateScaleExtent()),d&&f.x.domain(f.trimXDomain(f.x.orgDomain())),f.x.domain()},i.trimXDomain=function(a){var b=this.getZoomDomain(),c=b[0],d=b[1];return a[0]<=c&&(a[1]=+a[1]+(c-a[0]),a[0]=c),d<=a[1]&&(a[0]=+a[0]-(a[1]-d),a[1]=d),a},i.isX=function(a){var b=this,c=b.config;return c.data_x&&a===c.data_x||v(c.data_xs)&&x(c.data_xs,a)},i.isNotX=function(a){return!this.isX(a)},i.getXKey=function(a){var b=this,c=b.config;return c.data_x?c.data_x:v(c.data_xs)?c.data_xs[a]:null},i.getXValuesOfXKey=function(a,b){var c,d=this,e=b&&v(b)?d.mapToIds(b):[];return e.forEach(function(b){d.getXKey(b)===a&&(c=d.data.xs[b])}),c},i.getIndexByX=function(a){var b=this,c=b.filterByX(b.data.targets,a);return c.length?c[0].index:null},i.getXValue=function(a,b){var c=this;return a in c.data.xs&&c.data.xs[a]&&m(c.data.xs[a][b])?c.data.xs[a][b]:b},i.getOtherTargetXs=function(){var a=this,b=Object.keys(a.data.xs);return b.length?a.data.xs[b[0]]:null},i.getOtherTargetX=function(a){var b=this.getOtherTargetXs();return b&&a<b.length?b[a]:null},i.addXs=function(a){var b=this;Object.keys(a).forEach(function(c){b.config.data_xs[c]=a[c]})},i.hasMultipleX=function(a){return this.d3.set(Object.keys(a).map(function(b){return a[b]})).size()>1},i.isMultipleX=function(){return v(this.config.data_xs)||!this.config.data_xSort||this.hasType("scatter")},i.addName=function(a){var b,c=this;return a&&(b=c.config.data_names[a.id],a.name=void 0!==b?b:a.id),a},i.getValueOnIndex=function(a,b){var c=a.filter(function(a){return a.index===b});return c.length?c[0]:null},i.updateTargetX=function(a,b){var c=this;a.forEach(function(a){a.values.forEach(function(d,e){d.x=c.generateTargetX(b[e],a.id,e)}),c.data.xs[a.id]=b})},i.updateTargetXs=function(a,b){var c=this;a.forEach(function(a){b[a.id]&&c.updateTargetX([a],b[a.id])})},i.generateTargetX=function(a,b,c){var d,e=this;return d=e.isTimeSeries()?a?e.parseDate(a):e.parseDate(e.getXValue(b,c)):e.isCustomX()&&!e.isCategorized()?m(a)?+a:e.getXValue(b,c):c},i.cloneTarget=function(a){return{id:a.id,id_org:a.id_org,values:a.values.map(function(a){return{x:a.x,value:a.value,id:a.id}})}},i.updateXs=function(){var a=this;a.data.targets.length&&(a.xs=[],a.data.targets[0].values.forEach(function(b){a.xs[b.index]=b.x}))},i.getPrevX=function(a){var b=this.xs[a-1];return"undefined"!=typeof b?b:null},i.getNextX=function(a){var b=this.xs[a+1];return"undefined"!=typeof b?b:null},i.getMaxDataCount=function(){var a=this;return a.d3.max(a.data.targets,function(a){return a.values.length})},i.getMaxDataCountTarget=function(a){var b,c=a.length,d=0;return c>1?a.forEach(function(a){a.values.length>d&&(b=a,d=a.values.length)}):b=c?a[0]:null,b},i.getEdgeX=function(a){var b=this;return a.length?[b.d3.min(a,function(a){return a.values[0].x}),b.d3.max(a,function(a){return a.values[a.values.length-1].x})]:[0,0]},i.mapToIds=function(a){return a.map(function(a){return a.id})},i.mapToTargetIds=function(a){var b=this;return a?[].concat(a):b.mapToIds(b.data.targets)},i.hasTarget=function(a,b){var c,d=this.mapToIds(a);for(c=0;c<d.length;c++)if(d[c]===b)return!0;return!1},i.isTargetToShow=function(a){return this.hiddenTargetIds.indexOf(a)<0},i.isLegendToShow=function(a){return this.hiddenLegendIds.indexOf(a)<0},i.filterTargetsToShow=function(a){var b=this;return a.filter(function(a){return b.isTargetToShow(a.id)})},i.mapTargetsToUniqueXs=function(a){var b=this,c=b.d3.set(b.d3.merge(a.map(function(a){return a.values.map(function(a){return+a.x})}))).values();return c=b.isTimeSeries()?c.map(function(a){return new Date(+a)}):c.map(function(a){return+a}),c.sort(function(a,b){return b>a?-1:a>b?1:a>=b?0:NaN})},i.addHiddenTargetIds=function(a){this.hiddenTargetIds=this.hiddenTargetIds.concat(a)},i.removeHiddenTargetIds=function(a){this.hiddenTargetIds=this.hiddenTargetIds.filter(function(b){return a.indexOf(b)<0})},i.addHiddenLegendIds=function(a){this.hiddenLegendIds=this.hiddenLegendIds.concat(a)},i.removeHiddenLegendIds=function(a){this.hiddenLegendIds=this.hiddenLegendIds.filter(function(b){return a.indexOf(b)<0})},i.getValuesAsIdKeyed=function(a){var b={};return a.forEach(function(a){b[a.id]=[],a.values.forEach(function(c){b[a.id].push(c.value)})}),b},i.checkValueInTargets=function(a,b){var c,d,e,f=Object.keys(a);for(c=0;c<f.length;c++)for(e=a[f[c]].values,d=0;d<e.length;d++)if(b(e[d].value))return!0;return!1},i.hasNegativeValueInTargets=function(a){return this.checkValueInTargets(a,function(a){return 0>a})},i.hasPositiveValueInTargets=function(a){return this.checkValueInTargets(a,function(a){return a>0})},i.isOrderDesc=function(){var a=this.config;return"string"==typeof a.data_order&&"desc"===a.data_order.toLowerCase()},i.isOrderAsc=function(){var a=this.config;return"string"==typeof a.data_order&&"asc"===a.data_order.toLowerCase()},i.orderTargets=function(a){var b=this,c=b.config,d=b.isOrderAsc(),e=b.isOrderDesc();return d||e?a.sort(function(a,b){var c=function(a,b){return a+Math.abs(b.value)},e=a.values.reduce(c,0),f=b.values.reduce(c,0);return d?f-e:e-f}):n(c.data_order)&&a.sort(c.data_order),a},i.filterByX=function(a,b){return this.d3.merge(a.map(function(a){return a.values})).filter(function(a){return a.x-b===0})},i.filterRemoveNull=function(a){return a.filter(function(a){return m(a.value)})},i.filterByXDomain=function(a,b){return a.map(function(a){return{id:a.id,id_org:a.id_org,values:a.values.filter(function(a){return b[0]<=a.x&&a.x<=b[1]})}})},i.hasDataLabel=function(){var a=this.config;return"boolean"==typeof a.data_labels&&a.data_labels?!0:!("object"!=typeof a.data_labels||!v(a.data_labels))},i.getDataLabelLength=function(a,b,c){var d=this,e=[0,0],f=1.3;return d.selectChart.select("svg").selectAll(".dummy").data([a,b]).enter().append("text").text(function(a){return d.dataLabelFormat(a.id)(a)}).each(function(a,b){e[b]=this.getBoundingClientRect()[c]*f}).remove(),e},i.isNoneArc=function(a){return this.hasTarget(this.data.targets,a.id)},i.isArc=function(a){return"data"in a&&this.hasTarget(this.data.targets,a.data.id)},i.findSameXOfValues=function(a,b){var c,d=a[b].x,e=[];for(c=b-1;c>=0&&d===a[c].x;c--)e.push(a[c]);for(c=b;c<a.length&&d===a[c].x;c++)e.push(a[c]);return e},i.findClosestFromTargets=function(a,b){var c,d=this;return c=a.map(function(a){return d.findClosest(a.values,b)}),d.findClosest(c,b)},i.findClosest=function(a,b){var c,d=this,e=d.config.point_sensitivity;return a.filter(function(a){return a&&d.isBarType(a.id)}).forEach(function(a){var b=d.main.select("."+l.bars+d.getTargetSelectorSuffix(a.id)+" ."+l.bar+"-"+a.index).node();!c&&d.isWithinBar(b)&&(c=a)}),a.filter(function(a){return a&&!d.isBarType(a.id)}).forEach(function(a){var f=d.dist(a,b);e>f&&(e=f,c=a)}),c},i.dist=function(a,b){var c=this,d=c.config,e=d.axis_rotated?1:0,f=d.axis_rotated?0:1,g=c.circleY(a,a.index),h=c.x(a.x);return Math.sqrt(Math.pow(h-b[e],2)+Math.pow(g-b[f],2))},i.convertValuesToStep=function(a){var b,c=[].concat(a);if(!this.isCategorized())return a;for(b=a.length+1;b>0;b--)c[b]=c[b-1];return c[0]={x:c[0].x-1,value:c[0].value,id:c[0].id},c[a.length+1]={x:c[a.length].x+1,value:c[a.length].value,id:c[a.length].id},c},i.updateDataAttributes=function(a,b){var c=this,d=c.config,e=d["data_"+a];return"undefined"==typeof b?e:(Object.keys(b).forEach(function(a){e[a]=b[a]}),c.redraw({withLegend:!0}),e)},i.convertUrlToData=function(a,b,c,d,e){var f=this,g=b?b:"csv",h=f.d3.xhr(a);c&&Object.keys(c).forEach(function(a){h.header(a,c[a])}),h.get(function(a,b){var c;if(!b)throw new Error(a.responseURL+" "+a.status+" ("+a.statusText+")");c="json"===g?f.convertJsonToData(JSON.parse(b.response),d):"tsv"===g?f.convertTsvToData(b.response):f.convertCsvToData(b.response),e.call(f,c)})},i.convertXsvToData=function(a,b){var c,d=b.parseRows(a);return 1===d.length?(c=[{}],d[0].forEach(function(a){c[0][a]=null})):c=b.parse(a),c},i.convertCsvToData=function(a){return this.convertXsvToData(a,this.d3.csv)},i.convertTsvToData=function(a){return this.convertXsvToData(a,this.d3.tsv)},i.convertJsonToData=function(a,b){var c,d,e=this,f=[];return b?(b.x?(c=b.value.concat(b.x),e.config.data_x=b.x):c=b.value,f.push(c),a.forEach(function(a){var b=[];c.forEach(function(c){var d=e.findValueInJson(a,c);p(d)&&(d=null),b.push(d)}),f.push(b)}),d=e.convertRowsToData(f)):(Object.keys(a).forEach(function(b){f.push([b].concat(a[b]))}),d=e.convertColumnsToData(f)),d},i.findValueInJson=function(a,b){b=b.replace(/\[(\w+)\]/g,".$1"),b=b.replace(/^\./,"");for(var c=b.split("."),d=0;d<c.length;++d){var e=c[d];if(!(e in a))return;a=a[e]}return a},i.convertRowsToData=function(a){var b,c,d=a[0],e={},f=[];for(b=1;b<a.length;b++){for(e={},c=0;c<a[b].length;c++){if(p(a[b][c]))throw new Error("Source data is missing a component at ("+b+","+c+")!");e[d[c]]=a[b][c]}f.push(e)}return f},i.convertColumnsToData=function(a){var b,c,d,e=[];for(b=0;b<a.length;b++)for(d=a[b][0],c=1;c<a[b].length;c++){if(p(e[c-1])&&(e[c-1]={}),p(a[b][c]))throw new Error("Source data is missing a component at ("+b+","+c+")!");e[c-1][d]=a[b][c]}return e},i.convertDataToTargets=function(a,b){var c,d=this,e=d.config,f=d.d3.keys(a[0]).filter(d.isNotX,d),g=d.d3.keys(a[0]).filter(d.isX,d);return f.forEach(function(c){var f=d.getXKey(c);d.isCustomX()||d.isTimeSeries()?g.indexOf(f)>=0?d.data.xs[c]=(b&&d.data.xs[c]?d.data.xs[c]:[]).concat(a.map(function(a){return a[f]}).filter(m).map(function(a,b){return d.generateTargetX(a,c,b)})):e.data_x?d.data.xs[c]=d.getOtherTargetXs():v(e.data_xs)&&(d.data.xs[c]=d.getXValuesOfXKey(f,d.data.targets)):d.data.xs[c]=a.map(function(a,b){return b})}),f.forEach(function(a){if(!d.data.xs[a])throw new Error('x is not defined for id = "'+a+'".')}),c=f.map(function(b,c){var f=e.data_idConverter(b);return{id:f,id_org:b,values:a.map(function(a,g){var h,i=d.getXKey(b),j=a[i],k=null===a[b]||isNaN(a[b])?null:+a[b];return d.isCustomX()&&d.isCategorized()&&0===c&&!p(j)?(0===c&&0===g&&(e.axis_x_categories=[]),h=e.axis_x_categories.indexOf(j),-1===h&&(h=e.axis_x_categories.length,e.axis_x_categories.push(j))):h=d.generateTargetX(j,b,g),(p(a[b])||d.data.xs[b].length<=g)&&(h=void 0),{x:h,value:k,id:f}}).filter(function(a){return q(a.x)})}}),c.forEach(function(a){var b;e.data_xSort&&(a.values=a.values.sort(function(a,b){var c=a.x||0===a.x?a.x:1/0,d=b.x||0===b.x?b.x:1/0;return c-d})),b=0,a.values.forEach(function(a){a.index=b++}),d.data.xs[a.id].sort(function(a,b){return a-b})}),d.hasNegativeValue=d.hasNegativeValueInTargets(c),d.hasPositiveValue=d.hasPositiveValueInTargets(c),e.data_type&&d.setTargetType(d.mapToIds(c).filter(function(a){return!(a in e.data_types)}),e.data_type),c.forEach(function(a){d.addCache(a.id_org,a)}),c},i.load=function(a,b){var c=this;a&&(b.filter&&(a=a.filter(b.filter)),(b.type||b.types)&&a.forEach(function(a){var d=b.types&&b.types[a.id]?b.types[a.id]:b.type;c.setTargetType(a.id,d)}),c.data.targets.forEach(function(b){for(var c=0;c<a.length;c++)if(b.id===a[c].id){b.values=a[c].values,a.splice(c,1);break}}),c.data.targets=c.data.targets.concat(a)),c.updateTargets(c.data.targets),c.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0,withLegend:!0}),b.done&&b.done()},i.loadFromArgs=function(a){var b=this;a.data?b.load(b.convertDataToTargets(a.data),a):a.url?b.convertUrlToData(a.url,a.mimeType,a.headers,a.keys,function(c){b.load(b.convertDataToTargets(c),a)}):a.json?b.load(b.convertDataToTargets(b.convertJsonToData(a.json,a.keys)),a):a.rows?b.load(b.convertDataToTargets(b.convertRowsToData(a.rows)),a):a.columns?b.load(b.convertDataToTargets(b.convertColumnsToData(a.columns)),a):b.load(null,a)},i.unload=function(a,b){var c=this;return b||(b=function(){}),a=a.filter(function(a){return c.hasTarget(c.data.targets,a)}),a&&0!==a.length?(c.svg.selectAll(a.map(function(a){return c.selectorTarget(a)})).transition().style("opacity",0).remove().call(c.endall,b),void a.forEach(function(a){c.withoutFadeIn[a]=!1,c.legend&&c.legend.selectAll("."+l.legendItem+c.getTargetSelectorSuffix(a)).remove(),c.data.targets=c.data.targets.filter(function(b){return b.id!==a})})):void b()},i.categoryName=function(a){var b=this.config;return a<b.axis_x_categories.length?b.axis_x_categories[a]:a},i.initEventRect=function(){var a=this;a.main.select("."+l.chart).append("g").attr("class",l.eventRects).style("fill-opacity",0)},i.redrawEventRect=function(){var a,b,c=this,d=c.config,e=c.isMultipleX(),f=c.main.select("."+l.eventRects).style("cursor",d.zoom_enabled?d.axis_rotated?"ns-resize":"ew-resize":null).classed(l.eventRectsMultiple,e).classed(l.eventRectsSingle,!e);f.selectAll("."+l.eventRect).remove(),c.eventRect=f.selectAll("."+l.eventRect),e?(a=c.eventRect.data([0]),c.generateEventRectsForMultipleXs(a.enter()),c.updateEventRect(a)):(b=c.getMaxDataCountTarget(c.data.targets),f.datum(b?b.values:[]),c.eventRect=f.selectAll("."+l.eventRect),a=c.eventRect.data(function(a){return a}),c.generateEventRectsForSingleX(a.enter()),c.updateEventRect(a),a.exit().remove())},i.updateEventRect=function(a){var b,c,d,e,f,g,h=this,i=h.config;a=a||h.eventRect.data(function(a){return a}),h.isMultipleX()?(b=0,c=0,d=h.width,e=h.height):(!h.isCustomX()&&!h.isTimeSeries()||h.isCategorized()?(f=h.getEventRectWidth(),g=function(a){return h.x(a.x)-f/2}):(h.updateXs(),f=function(a){var b=h.getPrevX(a.index),c=h.getNextX(a.index);return null===b&&null===c?i.axis_rotated?h.height:h.width:(null===b&&(b=h.x.domain()[0]),null===c&&(c=h.x.domain()[1]),Math.max(0,(h.x(c)-h.x(b))/2))},g=function(a){var b=h.getPrevX(a.index),c=h.getNextX(a.index),d=h.data.xs[a.id][a.index];return null===b&&null===c?0:(null===b&&(b=h.x.domain()[0]),(h.x(d)+h.x(b))/2)}),b=i.axis_rotated?0:g,c=i.axis_rotated?g:0,d=i.axis_rotated?h.width:f,e=i.axis_rotated?f:h.height),a.attr("class",h.classEvent.bind(h)).attr("x",b).attr("y",c).attr("width",d).attr("height",e)},i.generateEventRectsForSingleX=function(a){var b=this,c=b.d3,d=b.config;a.append("rect").attr("class",b.classEvent.bind(b)).style("cursor",d.data_selection_enabled&&d.data_selection_grouped?"pointer":null).on("mouseover",function(a){var c=a.index;b.dragging||b.flowing||b.hasArcType()||(d.point_focus_expand_enabled&&b.expandCircles(c,null,!0),b.expandBars(c,null,!0),b.main.selectAll("."+l.shape+"-"+c).each(function(a){d.data_onmouseover.call(b.api,a)}))}).on("mouseout",function(a){var c=a.index;b.config&&(b.hasArcType()||(b.hideXGridFocus(),b.hideTooltip(),b.unexpandCircles(),b.unexpandBars(),b.main.selectAll("."+l.shape+"-"+c).each(function(a){d.data_onmouseout.call(b.api,a)})))}).on("mousemove",function(a){var e,f=a.index,g=b.svg.select("."+l.eventRect+"-"+f);b.dragging||b.flowing||b.hasArcType()||(b.isStepType(a)&&"step-after"===b.config.line_step_type&&c.mouse(this)[0]<b.x(b.getXValue(a.id,f))&&(f-=1),e=b.filterTargetsToShow(b.data.targets).map(function(a){return b.addName(b.getValueOnIndex(a.values,f))}),d.tooltip_grouped&&(b.showTooltip(e,this),b.showXGridFocus(e)),(!d.tooltip_grouped||d.data_selection_enabled&&!d.data_selection_grouped)&&b.main.selectAll("."+l.shape+"-"+f).each(function(){c.select(this).classed(l.EXPANDED,!0),d.data_selection_enabled&&g.style("cursor",d.data_selection_grouped?"pointer":null),d.tooltip_grouped||(b.hideXGridFocus(),b.hideTooltip(),d.data_selection_grouped||(b.unexpandCircles(f),b.unexpandBars(f)))}).filter(function(a){return b.isWithinShape(this,a)}).each(function(a){d.data_selection_enabled&&(d.data_selection_grouped||d.data_selection_isselectable(a))&&g.style("cursor","pointer"),d.tooltip_grouped||(b.showTooltip([a],this),b.showXGridFocus([a]),d.point_focus_expand_enabled&&b.expandCircles(f,a.id,!0),b.expandBars(f,a.id,!0))}))}).on("click",function(a){var e=a.index;if(!b.hasArcType()&&b.toggleShape){if(b.cancelClick)return void(b.cancelClick=!1);b.isStepType(a)&&"step-after"===d.line_step_type&&c.mouse(this)[0]<b.x(b.getXValue(a.id,e))&&(e-=1),b.main.selectAll("."+l.shape+"-"+e).each(function(a){(d.data_selection_grouped||b.isWithinShape(this,a))&&(b.toggleShape(this,a,e),b.config.data_onclick.call(b.api,a,this))})}}).call(d.data_selection_draggable&&b.drag?c.behavior.drag().origin(Object).on("drag",function(){b.drag(c.mouse(this))}).on("dragstart",function(){b.dragstart(c.mouse(this))}).on("dragend",function(){b.dragend()}):function(){})},i.generateEventRectsForMultipleXs=function(a){function b(){c.svg.select("."+l.eventRect).style("cursor",null),c.hideXGridFocus(),c.hideTooltip(),c.unexpandCircles(),c.unexpandBars()}var c=this,d=c.d3,e=c.config;a.append("rect").attr("x",0).attr("y",0).attr("width",c.width).attr("height",c.height).attr("class",l.eventRect).on("mouseout",function(){c.config&&(c.hasArcType()||b())}).on("mousemove",function(){var a,f,g,h,i=c.filterTargetsToShow(c.data.targets);if(!c.dragging&&!c.hasArcType(i)){if(a=d.mouse(this),f=c.findClosestFromTargets(i,a),!c.mouseover||f&&f.id===c.mouseover.id||(e.data_onmouseout.call(c.api,c.mouseover),c.mouseover=void 0),!f)return void b();g=c.isScatterType(f)||!e.tooltip_grouped?[f]:c.filterByX(i,f.x),h=g.map(function(a){return c.addName(a)}),c.showTooltip(h,this),e.point_focus_expand_enabled&&c.expandCircles(f.index,f.id,!0),c.expandBars(f.index,f.id,!0),c.showXGridFocus(h),(c.isBarType(f.id)||c.dist(f,a)<e.point_sensitivity)&&(c.svg.select("."+l.eventRect).style("cursor","pointer"),c.mouseover||(e.data_onmouseover.call(c.api,f),c.mouseover=f))}}).on("click",function(){var a,b,f=c.filterTargetsToShow(c.data.targets);c.hasArcType(f)||(a=d.mouse(this),b=c.findClosestFromTargets(f,a),b&&(c.isBarType(b.id)||c.dist(b,a)<e.point_sensitivity)&&c.main.selectAll("."+l.shapes+c.getTargetSelectorSuffix(b.id)).selectAll("."+l.shape+"-"+b.index).each(function(){(e.data_selection_grouped||c.isWithinShape(this,b))&&(c.toggleShape(this,b,b.index),c.config.data_onclick.call(c.api,b,this))}))}).call(e.data_selection_draggable&&c.drag?d.behavior.drag().origin(Object).on("drag",function(){c.drag(d.mouse(this))}).on("dragstart",function(){c.dragstart(d.mouse(this))}).on("dragend",function(){c.dragend()}):function(){})},i.dispatchEvent=function(b,c,d){var e=this,f="."+l.eventRect+(e.isMultipleX()?"":"-"+c),g=e.main.select(f).node(),h=g.getBoundingClientRect(),i=h.left+(d?d[0]:0),j=h.top+(d?d[1]:0),k=document.createEvent("MouseEvents");k.initMouseEvent(b,!0,!0,a,0,i,j,i,j,!1,!1,!1,!1,0,null),g.dispatchEvent(k)},i.getCurrentWidth=function(){var a=this,b=a.config;return b.size_width?b.size_width:a.getParentWidth()},i.getCurrentHeight=function(){var a=this,b=a.config,c=b.size_height?b.size_height:a.getParentHeight();return c>0?c:320/(a.hasType("gauge")&&!b.gauge_fullCircle?2:1)},i.getCurrentPaddingTop=function(){var a=this,b=a.config,c=m(b.padding_top)?b.padding_top:0;return a.title&&a.title.node()&&(c+=a.getTitlePadding()),c},i.getCurrentPaddingBottom=function(){var a=this.config;return m(a.padding_bottom)?a.padding_bottom:0},i.getCurrentPaddingLeft=function(a){var b=this,c=b.config;return m(c.padding_left)?c.padding_left:c.axis_rotated?c.axis_x_show?Math.max(r(b.getAxisWidthByAxisId("x",a)),40):1:!c.axis_y_show||c.axis_y_inner?b.axis.getYAxisLabelPosition().isOuter?30:1:r(b.getAxisWidthByAxisId("y",a))},i.getCurrentPaddingRight=function(){var a=this,b=a.config,c=10,d=a.isLegendRight?a.getLegendWidth()+20:0;return m(b.padding_right)?b.padding_right+1:b.axis_rotated?c+d:!b.axis_y2_show||b.axis_y2_inner?2+d+(a.axis.getY2AxisLabelPosition().isOuter?20:0):r(a.getAxisWidthByAxisId("y2"))+d},i.getParentRectValue=function(a){for(var b,c=this.selectChart.node();c&&"BODY"!==c.tagName;){try{b=c.getBoundingClientRect()[a]}catch(d){"width"===a&&(b=c.offsetWidth)}if(b)break;c=c.parentNode}return b},i.getParentWidth=function(){return this.getParentRectValue("width")},i.getParentHeight=function(){var a=this.selectChart.style("height");return a.indexOf("px")>0?+a.replace("px",""):0},i.getSvgLeft=function(a){var b=this,c=b.config,d=c.axis_rotated||!c.axis_rotated&&!c.axis_y_inner,e=c.axis_rotated?l.axisX:l.axisY,f=b.main.select("."+e).node(),g=f&&d?f.getBoundingClientRect():{right:0},h=b.selectChart.node().getBoundingClientRect(),i=b.hasArcType(),j=g.right-h.left-(i?0:b.getCurrentPaddingLeft(a));return j>0?j:0},i.getAxisWidthByAxisId=function(a,b){var c=this,d=c.axis.getLabelPositionById(a);return c.axis.getMaxTickWidth(a,b)+(d.isInner?20:40)},i.getHorizontalAxisHeight=function(a){var b=this,c=b.config,d=30;return"x"!==a||c.axis_x_show?"x"===a&&c.axis_x_height?c.axis_x_height:"y"!==a||c.axis_y_show?"y2"!==a||c.axis_y2_show?("x"===a&&!c.axis_rotated&&c.axis_x_tick_rotate&&(d=30+b.axis.getMaxTickWidth(a)*Math.cos(Math.PI*(90-c.axis_x_tick_rotate)/180)),"y"===a&&c.axis_rotated&&c.axis_y_tick_rotate&&(d=30+b.axis.getMaxTickWidth(a)*Math.cos(Math.PI*(90-c.axis_y_tick_rotate)/180)),d+(b.axis.getLabelPositionById(a).isInner?0:10)+("y2"===a?-10:0)):b.rotated_padding_top:!c.legend_show||b.isLegendRight||b.isLegendInset?1:10:8},i.getEventRectWidth=function(){return Math.max(0,this.xAxis.tickInterval())},i.getShapeIndices=function(a){var b,c,d=this,e=d.config,f={},g=0;return d.filterTargetsToShow(d.data.targets.filter(a,d)).forEach(function(a){for(b=0;b<e.data_groups.length;b++)if(!(e.data_groups[b].indexOf(a.id)<0))for(c=0;c<e.data_groups[b].length;c++)if(e.data_groups[b][c]in f){f[a.id]=f[e.data_groups[b][c]];break}p(f[a.id])&&(f[a.id]=g++)}),f.__max__=g-1,f},i.getShapeX=function(a,b,c,d){var e=this,f=d?e.subX:e.x;return function(d){var e=d.id in c?c[d.id]:0;return d.x||0===d.x?f(d.x)-a*(b/2-e):0}},i.getShapeY=function(a){var b=this;return function(c){var d=a?b.getSubYScale(c.id):b.getYScale(c.id);return d(c.value)}},i.getShapeOffset=function(a,b,c){var d=this,e=d.orderTargets(d.filterTargetsToShow(d.data.targets.filter(a,d))),f=e.map(function(a){return a.id});return function(a,g){var h=c?d.getSubYScale(a.id):d.getYScale(a.id),i=h(0),j=i;return e.forEach(function(c){var e=d.isStepType(a)?d.convertValuesToStep(c.values):c.values;c.id!==a.id&&b[c.id]===b[a.id]&&f.indexOf(c.id)<f.indexOf(a.id)&&("undefined"!=typeof e[g]&&+e[g].x===+a.x||(g=-1,e.forEach(function(b,c){b.x===a.x&&(g=c)})),g in e&&e[g].value*a.value>=0&&(j+=h(e[g].value)-i))}),j}},i.isWithinShape=function(a,b){var c,d=this,e=d.d3.select(a);return d.isTargetToShow(b.id)?"circle"===a.nodeName?c=d.isStepType(b)?d.isWithinStep(a,d.getYScale(b.id)(b.value)):d.isWithinCircle(a,1.5*d.pointSelectR(b)):"path"===a.nodeName&&(c=e.classed(l.bar)?d.isWithinBar(a):!0):c=!1,c},i.getInterpolate=function(a){var b=this,c=b.isInterpolationType(b.config.spline_interpolation_type)?b.config.spline_interpolation_type:"cardinal";return b.isSplineType(a)?c:b.isStepType(a)?b.config.line_step_type:"linear"},i.initLine=function(){var a=this;a.main.select("."+l.chart).append("g").attr("class",l.chartLines)},i.updateTargetsForLine=function(a){var b,c,d=this,e=d.config,f=d.classChartLine.bind(d),g=d.classLines.bind(d),h=d.classAreas.bind(d),i=d.classCircles.bind(d),j=d.classFocus.bind(d);b=d.main.select("."+l.chartLines).selectAll("."+l.chartLine).data(a).attr("class",function(a){return f(a)+j(a)}),c=b.enter().append("g").attr("class",f).style("opacity",0).style("pointer-events","none"),c.append("g").attr("class",g),c.append("g").attr("class",h),c.append("g").attr("class",function(a){return d.generateClass(l.selectedCircles,a.id)}),c.append("g").attr("class",i).style("cursor",function(a){return e.data_selection_isselectable(a)?"pointer":null}),a.forEach(function(a){d.main.selectAll("."+l.selectedCircles+d.getTargetSelectorSuffix(a.id)).selectAll("."+l.selectedCircle).each(function(b){b.value=a.values[b.index].value})})},i.updateLine=function(a){var b=this;b.mainLine=b.main.selectAll("."+l.lines).selectAll("."+l.line).data(b.lineData.bind(b)),b.mainLine.enter().append("path").attr("class",b.classLine.bind(b)).style("stroke",b.color),b.mainLine.style("opacity",b.initialOpacity.bind(b)).style("shape-rendering",function(a){return b.isStepType(a)?"crispEdges":""}).attr("transform",null),b.mainLine.exit().transition().duration(a).style("opacity",0).remove()},i.redrawLine=function(a,b){return[(b?this.mainLine.transition(Math.random().toString()):this.mainLine).attr("d",a).style("stroke",this.color).style("opacity",1)]},i.generateDrawLine=function(a,b){var c=this,d=c.config,e=c.d3.svg.line(),f=c.generateGetLinePoints(a,b),g=b?c.getSubYScale:c.getYScale,h=function(a){return(b?c.subxx:c.xx).call(c,a)},i=function(a,b){return d.data_groups.length>0?f(a,b)[0][1]:g.call(c,a.id)(a.value)};return e=d.axis_rotated?e.x(i).y(h):e.x(h).y(i),d.line_connectNull||(e=e.defined(function(a){return null!=a.value})),function(a){var f,h=d.line_connectNull?c.filterRemoveNull(a.values):a.values,i=b?c.x:c.subX,j=g.call(c,a.id),k=0,l=0;return c.isLineType(a)?d.data_regions[a.id]?f=c.lineWithRegions(h,i,j,d.data_regions[a.id]):(c.isStepType(a)&&(h=c.convertValuesToStep(h)),f=e.interpolate(c.getInterpolate(a))(h)):(h[0]&&(k=i(h[0].x),l=j(h[0].value)),f=d.axis_rotated?"M "+l+" "+k:"M "+k+" "+l),f?f:"M 0 0"}},i.generateGetLinePoints=function(a,b){var c=this,d=c.config,e=a.__max__+1,f=c.getShapeX(0,e,a,!!b),g=c.getShapeY(!!b),h=c.getShapeOffset(c.isLineType,a,!!b),i=b?c.getSubYScale:c.getYScale;return function(a,b){var e=i.call(c,a.id)(0),j=h(a,b)||e,k=f(a),l=g(a);return d.axis_rotated&&(0<a.value&&e>l||a.value<0&&l>e)&&(l=e),[[k,l-(e-j)],[k,l-(e-j)],[k,l-(e-j)],[k,l-(e-j)]]}},i.lineWithRegions=function(a,b,c,d){function e(a,b){var c;for(c=0;c<b.length;c++)if(b[c].start<a&&a<=b[c].end)return!0;return!1}function f(a){return"M"+a[0][0]+" "+a[0][1]+" "+a[1][0]+" "+a[1][1]}var g,h,i,j,k,l,m,n,o,r,s,t,u=this,v=u.config,w=-1,x="M",y=u.isCategorized()?.5:0,z=[];if(q(d))for(g=0;g<d.length;g++)z[g]={},p(d[g].start)?z[g].start=a[0].x:z[g].start=u.isTimeSeries()?u.parseDate(d[g].start):d[g].start,p(d[g].end)?z[g].end=a[a.length-1].x:z[g].end=u.isTimeSeries()?u.parseDate(d[g].end):d[g].end;for(s=v.axis_rotated?function(a){return c(a.value)}:function(a){return b(a.x)},t=v.axis_rotated?function(a){return b(a.x)}:function(a){return c(a.value)},i=u.isTimeSeries()?function(a,d,e,g){var h,i=a.x.getTime(),j=d.x-a.x,l=new Date(i+j*e),m=new Date(i+j*(e+g));return h=v.axis_rotated?[[c(k(e)),b(l)],[c(k(e+g)),b(m)]]:[[b(l),c(k(e))],[b(m),c(k(e+g))]],f(h)}:function(a,d,e,g){var h;return h=v.axis_rotated?[[c(k(e),!0),b(j(e))],[c(k(e+g),!0),b(j(e+g))]]:[[b(j(e),!0),c(k(e))],[b(j(e+g),!0),c(k(e+g))]],f(h)},g=0;g<a.length;g++){if(p(z)||!e(a[g].x,z))x+=" "+s(a[g])+" "+t(a[g]);else for(j=u.getScale(a[g-1].x+y,a[g].x+y,u.isTimeSeries()),k=u.getScale(a[g-1].value,a[g].value),l=b(a[g].x)-b(a[g-1].x),m=c(a[g].value)-c(a[g-1].value),n=Math.sqrt(Math.pow(l,2)+Math.pow(m,2)),o=2/n,r=2*o,h=o;1>=h;h+=r)x+=i(a[g-1],a[g],h,o);w=a[g].x}return x},i.updateArea=function(a){var b=this,c=b.d3;b.mainArea=b.main.selectAll("."+l.areas).selectAll("."+l.area).data(b.lineData.bind(b)),b.mainArea.enter().append("path").attr("class",b.classArea.bind(b)).style("fill",b.color).style("opacity",function(){return b.orgAreaOpacity=+c.select(this).style("opacity"),0}),b.mainArea.style("opacity",b.orgAreaOpacity),b.mainArea.exit().transition().duration(a).style("opacity",0).remove()},i.redrawArea=function(a,b){return[(b?this.mainArea.transition(Math.random().toString()):this.mainArea).attr("d",a).style("fill",this.color).style("opacity",this.orgAreaOpacity)]},i.generateDrawArea=function(a,b){var c=this,d=c.config,e=c.d3.svg.area(),f=c.generateGetAreaPoints(a,b),g=b?c.getSubYScale:c.getYScale,h=function(a){return(b?c.subxx:c.xx).call(c,a)},i=function(a,b){return d.data_groups.length>0?f(a,b)[0][1]:g.call(c,a.id)(c.getAreaBaseValue(a.id))},j=function(a,b){return d.data_groups.length>0?f(a,b)[1][1]:g.call(c,a.id)(a.value)};return e=d.axis_rotated?e.x0(i).x1(j).y(h):e.x(h).y0(d.area_above?0:i).y1(j),d.line_connectNull||(e=e.defined(function(a){return null!==a.value})),function(a){var b,f=d.line_connectNull?c.filterRemoveNull(a.values):a.values,g=0,h=0;return c.isAreaType(a)?(c.isStepType(a)&&(f=c.convertValuesToStep(f)),b=e.interpolate(c.getInterpolate(a))(f)):(f[0]&&(g=c.x(f[0].x),h=c.getYScale(a.id)(f[0].value)),b=d.axis_rotated?"M "+h+" "+g:"M "+g+" "+h),b?b:"M 0 0"}},i.getAreaBaseValue=function(){return 0},i.generateGetAreaPoints=function(a,b){var c=this,d=c.config,e=a.__max__+1,f=c.getShapeX(0,e,a,!!b),g=c.getShapeY(!!b),h=c.getShapeOffset(c.isAreaType,a,!!b),i=b?c.getSubYScale:c.getYScale;return function(a,b){var e=i.call(c,a.id)(0),j=h(a,b)||e,k=f(a),l=g(a);return d.axis_rotated&&(0<a.value&&e>l||a.value<0&&l>e)&&(l=e),[[k,j],[k,l-(e-j)],[k,l-(e-j)],[k,j]]}},i.updateCircle=function(){var a=this;a.mainCircle=a.main.selectAll("."+l.circles).selectAll("."+l.circle).data(a.lineOrScatterData.bind(a)),a.mainCircle.enter().append("circle").attr("class",a.classCircle.bind(a)).attr("r",a.pointR.bind(a)).style("fill",a.color),a.mainCircle.style("opacity",a.initialOpacityForCircle.bind(a)),a.mainCircle.exit().remove()},i.redrawCircle=function(a,b,c){var d=this.main.selectAll("."+l.selectedCircle);return[(c?this.mainCircle.transition(Math.random().toString()):this.mainCircle).style("opacity",this.opacityForCircle.bind(this)).style("fill",this.color).attr("cx",a).attr("cy",b),(c?d.transition(Math.random().toString()):d).attr("cx",a).attr("cy",b)]},i.circleX=function(a){return a.x||0===a.x?this.x(a.x):null},i.updateCircleY=function(){var a,b,c=this;c.config.data_groups.length>0?(a=c.getShapeIndices(c.isLineType),b=c.generateGetLinePoints(a),c.circleY=function(a,c){return b(a,c)[0][1]}):c.circleY=function(a){return c.getYScale(a.id)(a.value)}},i.getCircles=function(a,b){var c=this;return(b?c.main.selectAll("."+l.circles+c.getTargetSelectorSuffix(b)):c.main).selectAll("."+l.circle+(m(a)?"-"+a:""))},i.expandCircles=function(a,b,c){var d=this,e=d.pointExpandedR.bind(d);c&&d.unexpandCircles(),d.getCircles(a,b).classed(l.EXPANDED,!0).attr("r",e)},i.unexpandCircles=function(a){var b=this,c=b.pointR.bind(b);b.getCircles(a).filter(function(){return b.d3.select(this).classed(l.EXPANDED)}).classed(l.EXPANDED,!1).attr("r",c)},i.pointR=function(a){var b=this,c=b.config;return b.isStepType(a)?0:n(c.point_r)?c.point_r(a):c.point_r; -},i.pointExpandedR=function(a){var b=this,c=b.config;return c.point_focus_expand_enabled?c.point_focus_expand_r?c.point_focus_expand_r:1.75*b.pointR(a):b.pointR(a)},i.pointSelectR=function(a){var b=this,c=b.config;return n(c.point_select_r)?c.point_select_r(a):c.point_select_r?c.point_select_r:4*b.pointR(a)},i.isWithinCircle=function(a,b){var c=this.d3,d=c.mouse(a),e=c.select(a),f=+e.attr("cx"),g=+e.attr("cy");return Math.sqrt(Math.pow(f-d[0],2)+Math.pow(g-d[1],2))<b},i.isWithinStep=function(a,b){return Math.abs(b-this.d3.mouse(a)[1])<30},i.initBar=function(){var a=this;a.main.select("."+l.chart).append("g").attr("class",l.chartBars)},i.updateTargetsForBar=function(a){var b,c,d=this,e=d.config,f=d.classChartBar.bind(d),g=d.classBars.bind(d),h=d.classFocus.bind(d);b=d.main.select("."+l.chartBars).selectAll("."+l.chartBar).data(a).attr("class",function(a){return f(a)+h(a)}),c=b.enter().append("g").attr("class",f).style("opacity",0).style("pointer-events","none"),c.append("g").attr("class",g).style("cursor",function(a){return e.data_selection_isselectable(a)?"pointer":null})},i.updateBar=function(a){var b=this,c=b.barData.bind(b),d=b.classBar.bind(b),e=b.initialOpacity.bind(b),f=function(a){return b.color(a.id)};b.mainBar=b.main.selectAll("."+l.bars).selectAll("."+l.bar).data(c),b.mainBar.enter().append("path").attr("class",d).style("stroke",f).style("fill",f),b.mainBar.style("opacity",e),b.mainBar.exit().transition().duration(a).style("opacity",0).remove()},i.redrawBar=function(a,b){return[(b?this.mainBar.transition(Math.random().toString()):this.mainBar).attr("d",a).style("fill",this.color).style("opacity",1)]},i.getBarW=function(a,b){var c=this,d=c.config,e="number"==typeof d.bar_width?d.bar_width:b?a.tickInterval()*d.bar_width_ratio/b:0;return d.bar_width_max&&e>d.bar_width_max?d.bar_width_max:e},i.getBars=function(a,b){var c=this;return(b?c.main.selectAll("."+l.bars+c.getTargetSelectorSuffix(b)):c.main).selectAll("."+l.bar+(m(a)?"-"+a:""))},i.expandBars=function(a,b,c){var d=this;c&&d.unexpandBars(),d.getBars(a,b).classed(l.EXPANDED,!0)},i.unexpandBars=function(a){var b=this;b.getBars(a).classed(l.EXPANDED,!1)},i.generateDrawBar=function(a,b){var c=this,d=c.config,e=c.generateGetBarPoints(a,b);return function(a,b){var c=e(a,b),f=d.axis_rotated?1:0,g=d.axis_rotated?0:1,h="M "+c[0][f]+","+c[0][g]+" L"+c[1][f]+","+c[1][g]+" L"+c[2][f]+","+c[2][g]+" L"+c[3][f]+","+c[3][g]+" z";return h}},i.generateGetBarPoints=function(a,b){var c=this,d=b?c.subXAxis:c.xAxis,e=a.__max__+1,f=c.getBarW(d,e),g=c.getShapeX(f,e,a,!!b),h=c.getShapeY(!!b),i=c.getShapeOffset(c.isBarType,a,!!b),j=b?c.getSubYScale:c.getYScale;return function(a,b){var d=j.call(c,a.id)(0),e=i(a,b)||d,k=g(a),l=h(a);return c.config.axis_rotated&&(0<a.value&&d>l||a.value<0&&l>d)&&(l=d),[[k,e],[k,l-(d-e)],[k+f,l-(d-e)],[k+f,e]]}},i.isWithinBar=function(a){var b=this.d3.mouse(a),c=a.getBoundingClientRect(),d=a.pathSegList.getItem(0),e=a.pathSegList.getItem(1),f=Math.min(d.x,e.x),g=Math.min(d.y,e.y),h=c.width,i=c.height,j=2,k=f-j,l=f+h+j,m=g+i+j,n=g-j;return k<b[0]&&b[0]<l&&n<b[1]&&b[1]<m},i.initText=function(){var a=this;a.main.select("."+l.chart).append("g").attr("class",l.chartTexts),a.mainText=a.d3.selectAll([])},i.updateTargetsForText=function(a){var b,c,d=this,e=d.classChartText.bind(d),f=d.classTexts.bind(d),g=d.classFocus.bind(d);b=d.main.select("."+l.chartTexts).selectAll("."+l.chartText).data(a).attr("class",function(a){return e(a)+g(a)}),c=b.enter().append("g").attr("class",e).style("opacity",0).style("pointer-events","none"),c.append("g").attr("class",f)},i.updateText=function(a){var b=this,c=b.config,d=b.barOrLineData.bind(b),e=b.classText.bind(b);b.mainText=b.main.selectAll("."+l.texts).selectAll("."+l.text).data(d),b.mainText.enter().append("text").attr("class",e).attr("text-anchor",function(a){return c.axis_rotated?a.value<0?"end":"start":"middle"}).style("stroke","none").style("fill",function(a){return b.color(a)}).style("fill-opacity",0),b.mainText.text(function(a,c,d){return b.dataLabelFormat(a.id)(a.value,a.id,c,d)}),b.mainText.exit().transition().duration(a).style("fill-opacity",0).remove()},i.redrawText=function(a,b,c,d){return[(d?this.mainText.transition():this.mainText).attr("x",a).attr("y",b).style("fill",this.color).style("fill-opacity",c?0:this.opacityForText.bind(this))]},i.getTextRect=function(a,b,c){var d,e=this.d3.select("body").append("div").classed("c3",!0),f=e.append("svg").style("visibility","hidden").style("position","fixed").style("top",0).style("left",0),g=this.d3.select(c).style("font");return f.selectAll(".dummy").data([a]).enter().append("text").classed(b?b:"",!0).style("font",g).text(a).each(function(){d=this.getBoundingClientRect()}),e.remove(),d},i.generateXYForText=function(a,b,c,d){var e=this,f=e.generateGetAreaPoints(a,!1),g=e.generateGetBarPoints(b,!1),h=e.generateGetLinePoints(c,!1),i=d?e.getXForText:e.getYForText;return function(a,b){var c=e.isAreaType(a)?f:e.isBarType(a)?g:h;return i.call(e,c(a,b),a,this)}},i.getXForText=function(a,b,c){var d,e,f=this,g=c.getBoundingClientRect();return f.config.axis_rotated?(e=f.isBarType(b)?4:6,d=a[2][1]+e*(b.value<0?-1:1)):d=f.hasType("bar")?(a[2][0]+a[0][0])/2:a[0][0],null===b.value&&(d>f.width?d=f.width-g.width:0>d&&(d=4)),d},i.getYForText=function(a,b,c){var d,e=this,f=c.getBoundingClientRect();return e.config.axis_rotated?d=(a[0][0]+a[2][0]+.6*f.height)/2:(d=a[2][1],b.value<0||0===b.value&&!e.hasPositiveValue?(d+=f.height,e.isBarType(b)&&e.isSafari()?d-=3:!e.isBarType(b)&&e.isChrome()&&(d+=3)):d+=e.isBarType(b)?-3:-6),null!==b.value||e.config.axis_rotated||(d<f.height?d=f.height:d>this.height&&(d=this.height-4)),d},i.setTargetType=function(a,b){var c=this,d=c.config;c.mapToTargetIds(a).forEach(function(a){c.withoutFadeIn[a]=b===d.data_types[a],d.data_types[a]=b}),a||(d.data_type=b)},i.hasType=function(a,b){var c=this,d=c.config.data_types,e=!1;return b=b||c.data.targets,b&&b.length?b.forEach(function(b){var c=d[b.id];(c&&c.indexOf(a)>=0||!c&&"line"===a)&&(e=!0)}):Object.keys(d).length?Object.keys(d).forEach(function(b){d[b]===a&&(e=!0)}):e=c.config.data_type===a,e},i.hasArcType=function(a){return this.hasType("pie",a)||this.hasType("donut",a)||this.hasType("gauge",a)},i.isLineType=function(a){var b=this.config,c=o(a)?a:a.id;return!b.data_types[c]||["line","spline","area","area-spline","step","area-step"].indexOf(b.data_types[c])>=0},i.isStepType=function(a){var b=o(a)?a:a.id;return["step","area-step"].indexOf(this.config.data_types[b])>=0},i.isSplineType=function(a){var b=o(a)?a:a.id;return["spline","area-spline"].indexOf(this.config.data_types[b])>=0},i.isAreaType=function(a){var b=o(a)?a:a.id;return["area","area-spline","area-step"].indexOf(this.config.data_types[b])>=0},i.isBarType=function(a){var b=o(a)?a:a.id;return"bar"===this.config.data_types[b]},i.isScatterType=function(a){var b=o(a)?a:a.id;return"scatter"===this.config.data_types[b]},i.isPieType=function(a){var b=o(a)?a:a.id;return"pie"===this.config.data_types[b]},i.isGaugeType=function(a){var b=o(a)?a:a.id;return"gauge"===this.config.data_types[b]},i.isDonutType=function(a){var b=o(a)?a:a.id;return"donut"===this.config.data_types[b]},i.isArcType=function(a){return this.isPieType(a)||this.isDonutType(a)||this.isGaugeType(a)},i.lineData=function(a){return this.isLineType(a)?[a]:[]},i.arcData=function(a){return this.isArcType(a.data)?[a]:[]},i.barData=function(a){return this.isBarType(a)?a.values:[]},i.lineOrScatterData=function(a){return this.isLineType(a)||this.isScatterType(a)?a.values:[]},i.barOrLineData=function(a){return this.isBarType(a)||this.isLineType(a)?a.values:[]},i.isInterpolationType=function(a){return["linear","linear-closed","basis","basis-open","basis-closed","bundle","cardinal","cardinal-open","cardinal-closed","monotone"].indexOf(a)>=0},i.initGrid=function(){var a=this,b=a.config,c=a.d3;a.grid=a.main.append("g").attr("clip-path",a.clipPathForGrid).attr("class",l.grid),b.grid_x_show&&a.grid.append("g").attr("class",l.xgrids),b.grid_y_show&&a.grid.append("g").attr("class",l.ygrids),b.grid_focus_show&&a.grid.append("g").attr("class",l.xgridFocus).append("line").attr("class",l.xgridFocus),a.xgrid=c.selectAll([]),b.grid_lines_front||a.initGridLines()},i.initGridLines=function(){var a=this,b=a.d3;a.gridLines=a.main.append("g").attr("clip-path",a.clipPathForGrid).attr("class",l.grid+" "+l.gridLines),a.gridLines.append("g").attr("class",l.xgridLines),a.gridLines.append("g").attr("class",l.ygridLines),a.xgridLines=b.selectAll([])},i.updateXGrid=function(a){var b=this,c=b.config,d=b.d3,e=b.generateGridData(c.grid_x_type,b.x),f=b.isCategorized()?b.xAxis.tickOffset():0;b.xgridAttr=c.axis_rotated?{x1:0,x2:b.width,y1:function(a){return b.x(a)-f},y2:function(a){return b.x(a)-f}}:{x1:function(a){return b.x(a)+f},x2:function(a){return b.x(a)+f},y1:0,y2:b.height},b.xgrid=b.main.select("."+l.xgrids).selectAll("."+l.xgrid).data(e),b.xgrid.enter().append("line").attr("class",l.xgrid),a||b.xgrid.attr(b.xgridAttr).style("opacity",function(){return+d.select(this).attr(c.axis_rotated?"y1":"x1")===(c.axis_rotated?b.height:0)?0:1}),b.xgrid.exit().remove()},i.updateYGrid=function(){var a=this,b=a.config,c=a.yAxis.tickValues()||a.y.ticks(b.grid_y_ticks);a.ygrid=a.main.select("."+l.ygrids).selectAll("."+l.ygrid).data(c),a.ygrid.enter().append("line").attr("class",l.ygrid),a.ygrid.attr("x1",b.axis_rotated?a.y:0).attr("x2",b.axis_rotated?a.y:a.width).attr("y1",b.axis_rotated?0:a.y).attr("y2",b.axis_rotated?a.height:a.y),a.ygrid.exit().remove(),a.smoothLines(a.ygrid,"grid")},i.gridTextAnchor=function(a){return a.position?a.position:"end"},i.gridTextDx=function(a){return"start"===a.position?4:"middle"===a.position?0:-4},i.xGridTextX=function(a){return"start"===a.position?-this.height:"middle"===a.position?-this.height/2:0},i.yGridTextX=function(a){return"start"===a.position?0:"middle"===a.position?this.width/2:this.width},i.updateGrid=function(a){var b,c,d,e=this,f=e.main,g=e.config;e.grid.style("visibility",e.hasArcType()?"hidden":"visible"),f.select("line."+l.xgridFocus).style("visibility","hidden"),g.grid_x_show&&e.updateXGrid(),e.xgridLines=f.select("."+l.xgridLines).selectAll("."+l.xgridLine).data(g.grid_x_lines),b=e.xgridLines.enter().append("g").attr("class",function(a){return l.xgridLine+(a["class"]?" "+a["class"]:"")}),b.append("line").style("opacity",0),b.append("text").attr("text-anchor",e.gridTextAnchor).attr("transform",g.axis_rotated?"":"rotate(-90)").attr("dx",e.gridTextDx).attr("dy",-5).style("opacity",0),e.xgridLines.exit().transition().duration(a).style("opacity",0).remove(),g.grid_y_show&&e.updateYGrid(),e.ygridLines=f.select("."+l.ygridLines).selectAll("."+l.ygridLine).data(g.grid_y_lines),c=e.ygridLines.enter().append("g").attr("class",function(a){return l.ygridLine+(a["class"]?" "+a["class"]:"")}),c.append("line").style("opacity",0),c.append("text").attr("text-anchor",e.gridTextAnchor).attr("transform",g.axis_rotated?"rotate(-90)":"").attr("dx",e.gridTextDx).attr("dy",-5).style("opacity",0),d=e.yv.bind(e),e.ygridLines.select("line").transition().duration(a).attr("x1",g.axis_rotated?d:0).attr("x2",g.axis_rotated?d:e.width).attr("y1",g.axis_rotated?0:d).attr("y2",g.axis_rotated?e.height:d).style("opacity",1),e.ygridLines.select("text").transition().duration(a).attr("x",g.axis_rotated?e.xGridTextX.bind(e):e.yGridTextX.bind(e)).attr("y",d).text(function(a){return a.text}).style("opacity",1),e.ygridLines.exit().transition().duration(a).style("opacity",0).remove()},i.redrawGrid=function(a){var b=this,c=b.config,d=b.xv.bind(b),e=b.xgridLines.select("line"),f=b.xgridLines.select("text");return[(a?e.transition():e).attr("x1",c.axis_rotated?0:d).attr("x2",c.axis_rotated?b.width:d).attr("y1",c.axis_rotated?d:0).attr("y2",c.axis_rotated?d:b.height).style("opacity",1),(a?f.transition():f).attr("x",c.axis_rotated?b.yGridTextX.bind(b):b.xGridTextX.bind(b)).attr("y",d).text(function(a){return a.text}).style("opacity",1)]},i.showXGridFocus=function(a){var b=this,c=b.config,d=a.filter(function(a){return a&&m(a.value)}),e=b.main.selectAll("line."+l.xgridFocus),f=b.xx.bind(b);c.tooltip_show&&(b.hasType("scatter")||b.hasArcType()||(e.style("visibility","visible").data([d[0]]).attr(c.axis_rotated?"y1":"x1",f).attr(c.axis_rotated?"y2":"x2",f),b.smoothLines(e,"grid")))},i.hideXGridFocus=function(){this.main.select("line."+l.xgridFocus).style("visibility","hidden")},i.updateXgridFocus=function(){var a=this,b=a.config;a.main.select("line."+l.xgridFocus).attr("x1",b.axis_rotated?0:-10).attr("x2",b.axis_rotated?a.width:-10).attr("y1",b.axis_rotated?-10:0).attr("y2",b.axis_rotated?-10:a.height)},i.generateGridData=function(a,b){var c,d,e,f,g=this,h=[],i=g.main.select("."+l.axisX).selectAll(".tick").size();if("year"===a)for(c=g.getXDomain(),d=c[0].getFullYear(),e=c[1].getFullYear(),f=d;e>=f;f++)h.push(new Date(f+"-01-01 00:00:00"));else h=b.ticks(10),h.length>i&&(h=h.filter(function(a){return(""+a).indexOf(".")<0}));return h},i.getGridFilterToRemove=function(a){return a?function(b){var c=!1;return[].concat(a).forEach(function(a){("value"in a&&b.value===a.value||"class"in a&&b["class"]===a["class"])&&(c=!0)}),c}:function(){return!0}},i.removeGridLines=function(a,b){var c=this,d=c.config,e=c.getGridFilterToRemove(a),f=function(a){return!e(a)},g=b?l.xgridLines:l.ygridLines,h=b?l.xgridLine:l.ygridLine;c.main.select("."+g).selectAll("."+h).filter(e).transition().duration(d.transition_duration).style("opacity",0).remove(),b?d.grid_x_lines=d.grid_x_lines.filter(f):d.grid_y_lines=d.grid_y_lines.filter(f)},i.initTooltip=function(){var a,b=this,c=b.config;if(b.tooltip=b.selectChart.style("position","relative").append("div").attr("class",l.tooltipContainer).style("position","absolute").style("pointer-events","none").style("display","none"),c.tooltip_init_show){if(b.isTimeSeries()&&o(c.tooltip_init_x)){for(c.tooltip_init_x=b.parseDate(c.tooltip_init_x),a=0;a<b.data.targets[0].values.length&&b.data.targets[0].values[a].x-c.tooltip_init_x!==0;a++);c.tooltip_init_x=a}b.tooltip.html(c.tooltip_contents.call(b,b.data.targets.map(function(a){return b.addName(a.values[c.tooltip_init_x])}),b.axis.getXAxisTickFormat(),b.getYFormat(b.hasArcType()),b.color)),b.tooltip.style("top",c.tooltip_init_position.top).style("left",c.tooltip_init_position.left).style("display","block")}},i.getTooltipContent=function(a,b,c,d){var e,f,g,h,i,j,k=this,l=k.config,m=l.tooltip_format_title||b,n=l.tooltip_format_name||function(a){return a},o=l.tooltip_format_value||c,p=k.isOrderAsc();if(0===l.data_groups.length)a.sort(function(a,b){var c=a?a.value:null,d=b?b.value:null;return p?c-d:d-c});else{var q=k.orderTargets(k.data.targets).map(function(a){return a.id});a.sort(function(a,b){var c=a?a.value:null,d=b?b.value:null;return c>0&&d>0&&(c=a?q.indexOf(a.id):null,d=b?q.indexOf(b.id):null),p?c-d:d-c})}for(f=0;f<a.length;f++)if(a[f]&&(a[f].value||0===a[f].value)&&(e||(g=y(m?m(a[f].x):a[f].x),e="<table class='"+k.CLASS.tooltip+"'>"+(g||0===g?"<tr><th colspan='2'>"+g+"</th></tr>":"")),h=y(o(a[f].value,a[f].ratio,a[f].id,a[f].index,a)),void 0!==h)){if(null===a[f].name)continue;i=y(n(a[f].name,a[f].ratio,a[f].id,a[f].index)),j=k.levelColor?k.levelColor(a[f].value):d(a[f].id),e+="<tr class='"+k.CLASS.tooltipName+"-"+k.getTargetSelectorSuffix(a[f].id)+"'>",e+="<td class='name'><span style='background-color:"+j+"'></span>"+i+"</td>",e+="<td class='value'>"+h+"</td>",e+="</tr>"}return e+"</table>"},i.tooltipPosition=function(a,b,c,d){var e,f,g,h,i,j=this,k=j.config,l=j.d3,m=j.hasArcType(),n=l.mouse(d);return m?(f=(j.width-(j.isLegendRight?j.getLegendWidth():0))/2+n[0],h=j.height/2+n[1]+20):(e=j.getSvgLeft(!0),k.axis_rotated?(f=e+n[0]+100,g=f+b,i=j.currentWidth-j.getCurrentPaddingRight(),h=j.x(a[0].x)+20):(f=e+j.getCurrentPaddingLeft(!0)+j.x(a[0].x)+20,g=f+b,i=e+j.currentWidth-j.getCurrentPaddingRight(),h=n[1]+15),g>i&&(f-=g-i+20),h+c>j.currentHeight&&(h-=c+30)),0>h&&(h=0),{top:h,left:f}},i.showTooltip=function(a,b){var c,d,e,f=this,g=f.config,h=f.hasArcType(),j=a.filter(function(a){return a&&m(a.value)}),k=g.tooltip_position||i.tooltipPosition;0!==j.length&&g.tooltip_show&&(f.tooltip.html(g.tooltip_contents.call(f,a,f.axis.getXAxisTickFormat(),f.getYFormat(h),f.color)).style("display","block"),c=f.tooltip.property("offsetWidth"),d=f.tooltip.property("offsetHeight"),e=k.call(this,j,c,d,b),f.tooltip.style("top",e.top+"px").style("left",e.left+"px"))},i.hideTooltip=function(){this.tooltip.style("display","none")},i.initLegend=function(){var a=this;return a.legendItemTextBox={},a.legendHasRendered=!1,a.legend=a.svg.append("g").attr("transform",a.getTranslate("legend")),a.config.legend_show?void a.updateLegendWithDefaults():(a.legend.style("visibility","hidden"),void(a.hiddenLegendIds=a.mapToIds(a.data.targets)))},i.updateLegendWithDefaults=function(){var a=this;a.updateLegend(a.mapToIds(a.data.targets),{withTransform:!1,withTransitionForTransform:!1,withTransition:!1})},i.updateSizeForLegend=function(a,b){var c=this,d=c.config,e={top:c.isLegendTop?c.getCurrentPaddingTop()+d.legend_inset_y+5.5:c.currentHeight-a-c.getCurrentPaddingBottom()-d.legend_inset_y,left:c.isLegendLeft?c.getCurrentPaddingLeft()+d.legend_inset_x+.5:c.currentWidth-b-c.getCurrentPaddingRight()-d.legend_inset_x+.5};c.margin3={top:c.isLegendRight?0:c.isLegendInset?e.top:c.currentHeight-a,right:NaN,bottom:0,left:c.isLegendRight?c.currentWidth-b:c.isLegendInset?e.left:0}},i.transformLegend=function(a){var b=this;(a?b.legend.transition():b.legend).attr("transform",b.getTranslate("legend"))},i.updateLegendStep=function(a){this.legendStep=a},i.updateLegendItemWidth=function(a){this.legendItemWidth=a},i.updateLegendItemHeight=function(a){this.legendItemHeight=a},i.getLegendWidth=function(){var a=this;return a.config.legend_show?a.isLegendRight||a.isLegendInset?a.legendItemWidth*(a.legendStep+1):a.currentWidth:0},i.getLegendHeight=function(){var a=this,b=0;return a.config.legend_show&&(b=a.isLegendRight?a.currentHeight:Math.max(20,a.legendItemHeight)*(a.legendStep+1)),b},i.opacityForLegend=function(a){return a.classed(l.legendItemHidden)?null:1},i.opacityForUnfocusedLegend=function(a){return a.classed(l.legendItemHidden)?null:.3},i.toggleFocusLegend=function(a,b){var c=this;a=c.mapToTargetIds(a),c.legend.selectAll("."+l.legendItem).filter(function(b){return a.indexOf(b)>=0}).classed(l.legendItemFocused,b).transition().duration(100).style("opacity",function(){var a=b?c.opacityForLegend:c.opacityForUnfocusedLegend;return a.call(c,c.d3.select(this))})},i.revertLegend=function(){var a=this,b=a.d3;a.legend.selectAll("."+l.legendItem).classed(l.legendItemFocused,!1).transition().duration(100).style("opacity",function(){return a.opacityForLegend(b.select(this))})},i.showLegend=function(a){var b=this,c=b.config;c.legend_show||(c.legend_show=!0,b.legend.style("visibility","visible"),b.legendHasRendered||b.updateLegendWithDefaults()),b.removeHiddenLegendIds(a),b.legend.selectAll(b.selectorLegends(a)).style("visibility","visible").transition().style("opacity",function(){return b.opacityForLegend(b.d3.select(this))})},i.hideLegend=function(a){var b=this,c=b.config;c.legend_show&&u(a)&&(c.legend_show=!1,b.legend.style("visibility","hidden")),b.addHiddenLegendIds(a),b.legend.selectAll(b.selectorLegends(a)).style("opacity",0).style("visibility","hidden")},i.clearLegendItemTextBoxCache=function(){this.legendItemTextBox={}},i.updateLegend=function(a,b,c){function d(a,b){return y.legendItemTextBox[b]||(y.legendItemTextBox[b]=y.getTextRect(a.textContent,l.legendItem,a)),y.legendItemTextBox[b]}function e(b,c,e){function f(a,b){b||(g=(o-G-n)/2,E>g&&(g=(o-n)/2,G=0,M++)),L[a]=M,K[M]=y.isLegendInset?10:g,H[a]=G,G+=n}var g,h,i=0===e,j=e===a.length-1,k=d(b,c),l=k.width+F+(!j||y.isLegendRight||y.isLegendInset?B:0)+z.legend_padding,m=k.height+A,n=y.isLegendRight||y.isLegendInset?m:l,o=y.isLegendRight||y.isLegendInset?y.getLegendHeight():y.getLegendWidth();return i&&(G=0,M=0,C=0,D=0),z.legend_show&&!y.isLegendToShow(c)?void(I[c]=J[c]=L[c]=H[c]=0):(I[c]=l,J[c]=m,(!C||l>=C)&&(C=l),(!D||m>=D)&&(D=m),h=y.isLegendRight||y.isLegendInset?D:C,void(z.legend_equally?(Object.keys(I).forEach(function(a){I[a]=C}),Object.keys(J).forEach(function(a){J[a]=D}),g=(o-h*a.length)/2,E>g?(G=0,M=0,a.forEach(function(a){f(a)})):f(c,!0)):f(c)))}var f,g,h,i,j,k,m,n,o,p,r,s,t,u,v,x,y=this,z=y.config,A=4,B=10,C=0,D=0,E=10,F=z.legend_item_tile_width+5,G=0,H={},I={},J={},K=[0],L={},M=0;a=a.filter(function(a){return!q(z.data_names[a])||null!==z.data_names[a]}),b=b||{},r=w(b,"withTransition",!0),s=w(b,"withTransitionForTransform",!0),y.isLegendInset&&(M=z.legend_inset_step?z.legend_inset_step:a.length,y.updateLegendStep(M)),y.isLegendRight?(f=function(a){return C*L[a]},i=function(a){return K[L[a]]+H[a]}):y.isLegendInset?(f=function(a){return C*L[a]+10},i=function(a){return K[L[a]]+H[a]}):(f=function(a){return K[L[a]]+H[a]},i=function(a){return D*L[a]}),g=function(a,b){return f(a,b)+4+z.legend_item_tile_width},j=function(a,b){return i(a,b)+9},h=function(a,b){return f(a,b)},k=function(a,b){return i(a,b)-5},m=function(a,b){return f(a,b)-2},n=function(a,b){return f(a,b)-2+z.legend_item_tile_width},o=function(a,b){return i(a,b)+4},p=y.legend.selectAll("."+l.legendItem).data(a).enter().append("g").attr("class",function(a){return y.generateClass(l.legendItem,a)}).style("visibility",function(a){return y.isLegendToShow(a)?"visible":"hidden"}).style("cursor","pointer").on("click",function(a){z.legend_item_onclick?z.legend_item_onclick.call(y,a):y.d3.event.altKey?(y.api.hide(),y.api.show(a)):(y.api.toggle(a),y.isTargetToShow(a)?y.api.focus(a):y.api.revert())}).on("mouseover",function(a){z.legend_item_onmouseover?z.legend_item_onmouseover.call(y,a):(y.d3.select(this).classed(l.legendItemFocused,!0),!y.transiting&&y.isTargetToShow(a)&&y.api.focus(a))}).on("mouseout",function(a){z.legend_item_onmouseout?z.legend_item_onmouseout.call(y,a):(y.d3.select(this).classed(l.legendItemFocused,!1),y.api.revert())}),p.append("text").text(function(a){return q(z.data_names[a])?z.data_names[a]:a}).each(function(a,b){e(this,a,b)}).style("pointer-events","none").attr("x",y.isLegendRight||y.isLegendInset?g:-200).attr("y",y.isLegendRight||y.isLegendInset?-200:j),p.append("rect").attr("class",l.legendItemEvent).style("fill-opacity",0).attr("x",y.isLegendRight||y.isLegendInset?h:-200).attr("y",y.isLegendRight||y.isLegendInset?-200:k),p.append("line").attr("class",l.legendItemTile).style("stroke",y.color).style("pointer-events","none").attr("x1",y.isLegendRight||y.isLegendInset?m:-200).attr("y1",y.isLegendRight||y.isLegendInset?-200:o).attr("x2",y.isLegendRight||y.isLegendInset?n:-200).attr("y2",y.isLegendRight||y.isLegendInset?-200:o).attr("stroke-width",z.legend_item_tile_height),x=y.legend.select("."+l.legendBackground+" rect"),y.isLegendInset&&C>0&&0===x.size()&&(x=y.legend.insert("g","."+l.legendItem).attr("class",l.legendBackground).append("rect")),t=y.legend.selectAll("text").data(a).text(function(a){return q(z.data_names[a])?z.data_names[a]:a}).each(function(a,b){e(this,a,b)}),(r?t.transition():t).attr("x",g).attr("y",j),u=y.legend.selectAll("rect."+l.legendItemEvent).data(a),(r?u.transition():u).attr("width",function(a){return I[a]}).attr("height",function(a){return J[a]}).attr("x",h).attr("y",k),v=y.legend.selectAll("line."+l.legendItemTile).data(a),(r?v.transition():v).style("stroke",y.color).attr("x1",m).attr("y1",o).attr("x2",n).attr("y2",o),x&&(r?x.transition():x).attr("height",y.getLegendHeight()-12).attr("width",C*(M+1)+10),y.legend.selectAll("."+l.legendItem).classed(l.legendItemHidden,function(a){return!y.isTargetToShow(a)}),y.updateLegendItemWidth(C),y.updateLegendItemHeight(D),y.updateLegendStep(M),y.updateSizes(),y.updateScales(),y.updateSvgSize(),y.transformAll(s,c),y.legendHasRendered=!0},i.initTitle=function(){var a=this;a.title=a.svg.append("text").text(a.config.title_text).attr("class",a.CLASS.title)},i.redrawTitle=function(){var a=this;a.title.attr("x",a.xForTitle.bind(a)).attr("y",a.yForTitle.bind(a))},i.xForTitle=function(){var a,b=this,c=b.config,d=c.title_position||"left";return a=d.indexOf("right")>=0?b.currentWidth-b.getTextRect(b.title.node().textContent,b.CLASS.title,b.title.node()).width-c.title_padding.right:d.indexOf("center")>=0?(b.currentWidth-b.getTextRect(b.title.node().textContent,b.CLASS.title,b.title.node()).width)/2:c.title_padding.left},i.yForTitle=function(){var a=this;return a.config.title_padding.top+a.getTextRect(a.title.node().textContent,a.CLASS.title,a.title.node()).height},i.getTitlePadding=function(){var a=this;return a.yForTitle()+a.config.title_padding.bottom},c(b,f),f.prototype.init=function(){var a=this.owner,b=a.config,c=a.main;a.axes.x=c.append("g").attr("class",l.axis+" "+l.axisX).attr("clip-path",a.clipPathForXAxis).attr("transform",a.getTranslate("x")).style("visibility",b.axis_x_show?"visible":"hidden"),a.axes.x.append("text").attr("class",l.axisXLabel).attr("transform",b.axis_rotated?"rotate(-90)":"").style("text-anchor",this.textAnchorForXAxisLabel.bind(this)),a.axes.y=c.append("g").attr("class",l.axis+" "+l.axisY).attr("clip-path",b.axis_y_inner?"":a.clipPathForYAxis).attr("transform",a.getTranslate("y")).style("visibility",b.axis_y_show?"visible":"hidden"),a.axes.y.append("text").attr("class",l.axisYLabel).attr("transform",b.axis_rotated?"":"rotate(-90)").style("text-anchor",this.textAnchorForYAxisLabel.bind(this)),a.axes.y2=c.append("g").attr("class",l.axis+" "+l.axisY2).attr("transform",a.getTranslate("y2")).style("visibility",b.axis_y2_show?"visible":"hidden"),a.axes.y2.append("text").attr("class",l.axisY2Label).attr("transform",b.axis_rotated?"":"rotate(-90)").style("text-anchor",this.textAnchorForY2AxisLabel.bind(this))},f.prototype.getXAxis=function(a,b,c,d,e,f,h){var i=this.owner,j=i.config,k={isCategory:i.isCategorized(),withOuterTick:e,tickMultiline:j.axis_x_tick_multiline,tickWidth:j.axis_x_tick_width,tickTextRotate:h?0:j.axis_x_tick_rotate,withoutTransition:f},l=g(i.d3,k).scale(a).orient(b);return i.isTimeSeries()&&d&&"function"!=typeof d&&(d=d.map(function(a){return i.parseDate(a)})),l.tickFormat(c).tickValues(d),i.isCategorized()&&(l.tickCentered(j.axis_x_tick_centered),u(j.axis_x_tick_culling)&&(j.axis_x_tick_culling=!1)),l},f.prototype.updateXAxisTickValues=function(a,b){var c,d=this.owner,e=d.config;return(e.axis_x_tick_fit||e.axis_x_tick_count)&&(c=this.generateTickValues(d.mapTargetsToUniqueXs(a),e.axis_x_tick_count,d.isTimeSeries())),b?b.tickValues(c):(d.xAxis.tickValues(c),d.subXAxis.tickValues(c)),c},f.prototype.getYAxis=function(a,b,c,d,e,f,h){var i=this.owner,j=i.config,k={withOuterTick:e,withoutTransition:f,tickTextRotate:h?0:j.axis_y_tick_rotate},l=g(i.d3,k).scale(a).orient(b).tickFormat(c);return i.isTimeSeriesY()?l.ticks(i.d3.time[j.axis_y_tick_time_value],j.axis_y_tick_time_interval):l.tickValues(d),l},f.prototype.getId=function(a){var b=this.owner.config;return a in b.data_axes?b.data_axes[a]:"y"},f.prototype.getXAxisTickFormat=function(){var a=this.owner,b=a.config,c=a.isTimeSeries()?a.defaultAxisTimeFormat:a.isCategorized()?a.categoryName:function(a){return 0>a?a.toFixed(0):a};return b.axis_x_tick_format&&(n(b.axis_x_tick_format)?c=b.axis_x_tick_format:a.isTimeSeries()&&(c=function(c){return c?a.axisTimeFormat(b.axis_x_tick_format)(c):""})),n(c)?function(b){return c.call(a,b)}:c},f.prototype.getTickValues=function(a,b){return a?a:b?b.tickValues():void 0},f.prototype.getXAxisTickValues=function(){return this.getTickValues(this.owner.config.axis_x_tick_values,this.owner.xAxis)},f.prototype.getYAxisTickValues=function(){return this.getTickValues(this.owner.config.axis_y_tick_values,this.owner.yAxis)},f.prototype.getY2AxisTickValues=function(){return this.getTickValues(this.owner.config.axis_y2_tick_values,this.owner.y2Axis)},f.prototype.getLabelOptionByAxisId=function(a){var b,c=this.owner,d=c.config;return"y"===a?b=d.axis_y_label:"y2"===a?b=d.axis_y2_label:"x"===a&&(b=d.axis_x_label),b},f.prototype.getLabelText=function(a){var b=this.getLabelOptionByAxisId(a);return o(b)?b:b?b.text:null},f.prototype.setLabelText=function(a,b){var c=this.owner,d=c.config,e=this.getLabelOptionByAxisId(a);o(e)?"y"===a?d.axis_y_label=b:"y2"===a?d.axis_y2_label=b:"x"===a&&(d.axis_x_label=b):e&&(e.text=b)},f.prototype.getLabelPosition=function(a,b){var c=this.getLabelOptionByAxisId(a),d=c&&"object"==typeof c&&c.position?c.position:b;return{isInner:d.indexOf("inner")>=0,isOuter:d.indexOf("outer")>=0,isLeft:d.indexOf("left")>=0,isCenter:d.indexOf("center")>=0,isRight:d.indexOf("right")>=0,isTop:d.indexOf("top")>=0,isMiddle:d.indexOf("middle")>=0,isBottom:d.indexOf("bottom")>=0}},f.prototype.getXAxisLabelPosition=function(){return this.getLabelPosition("x",this.owner.config.axis_rotated?"inner-top":"inner-right")},f.prototype.getYAxisLabelPosition=function(){return this.getLabelPosition("y",this.owner.config.axis_rotated?"inner-right":"inner-top")},f.prototype.getY2AxisLabelPosition=function(){return this.getLabelPosition("y2",this.owner.config.axis_rotated?"inner-right":"inner-top")},f.prototype.getLabelPositionById=function(a){return"y2"===a?this.getY2AxisLabelPosition():"y"===a?this.getYAxisLabelPosition():this.getXAxisLabelPosition()},f.prototype.textForXAxisLabel=function(){return this.getLabelText("x")},f.prototype.textForYAxisLabel=function(){return this.getLabelText("y")},f.prototype.textForY2AxisLabel=function(){return this.getLabelText("y2")},f.prototype.xForAxisLabel=function(a,b){var c=this.owner;return a?b.isLeft?0:b.isCenter?c.width/2:c.width:b.isBottom?-c.height:b.isMiddle?-c.height/2:0},f.prototype.dxForAxisLabel=function(a,b){return a?b.isLeft?"0.5em":b.isRight?"-0.5em":"0":b.isTop?"-0.5em":b.isBottom?"0.5em":"0"},f.prototype.textAnchorForAxisLabel=function(a,b){return a?b.isLeft?"start":b.isCenter?"middle":"end":b.isBottom?"start":b.isMiddle?"middle":"end"},f.prototype.xForXAxisLabel=function(){return this.xForAxisLabel(!this.owner.config.axis_rotated,this.getXAxisLabelPosition())},f.prototype.xForYAxisLabel=function(){return this.xForAxisLabel(this.owner.config.axis_rotated,this.getYAxisLabelPosition())},f.prototype.xForY2AxisLabel=function(){return this.xForAxisLabel(this.owner.config.axis_rotated,this.getY2AxisLabelPosition())},f.prototype.dxForXAxisLabel=function(){return this.dxForAxisLabel(!this.owner.config.axis_rotated,this.getXAxisLabelPosition())},f.prototype.dxForYAxisLabel=function(){return this.dxForAxisLabel(this.owner.config.axis_rotated,this.getYAxisLabelPosition())},f.prototype.dxForY2AxisLabel=function(){return this.dxForAxisLabel(this.owner.config.axis_rotated,this.getY2AxisLabelPosition())},f.prototype.dyForXAxisLabel=function(){var a=this.owner,b=a.config,c=this.getXAxisLabelPosition();return b.axis_rotated?c.isInner?"1.2em":-25-this.getMaxTickWidth("x"):c.isInner?"-0.5em":b.axis_x_height?b.axis_x_height-10:"3em"},f.prototype.dyForYAxisLabel=function(){var a=this.owner,b=this.getYAxisLabelPosition();return a.config.axis_rotated?b.isInner?"-0.5em":"3em":b.isInner?"1.2em":-10-(a.config.axis_y_inner?0:this.getMaxTickWidth("y")+10)},f.prototype.dyForY2AxisLabel=function(){var a=this.owner,b=this.getY2AxisLabelPosition();return a.config.axis_rotated?b.isInner?"1.2em":"-2.2em":b.isInner?"-0.5em":15+(a.config.axis_y2_inner?0:this.getMaxTickWidth("y2")+15)},f.prototype.textAnchorForXAxisLabel=function(){var a=this.owner;return this.textAnchorForAxisLabel(!a.config.axis_rotated,this.getXAxisLabelPosition())},f.prototype.textAnchorForYAxisLabel=function(){var a=this.owner;return this.textAnchorForAxisLabel(a.config.axis_rotated,this.getYAxisLabelPosition())},f.prototype.textAnchorForY2AxisLabel=function(){var a=this.owner;return this.textAnchorForAxisLabel(a.config.axis_rotated,this.getY2AxisLabelPosition())},f.prototype.getMaxTickWidth=function(a,b){var c,d,e,f,g,h=this.owner,i=h.config,j=0;return b&&h.currentMaxTickWidths[a]?h.currentMaxTickWidths[a]:(h.svg&&(c=h.filterTargetsToShow(h.data.targets),"y"===a?(d=h.y.copy().domain(h.getYDomain(c,"y")),e=this.getYAxis(d,h.yOrient,i.axis_y_tick_format,h.yAxisTickValues,!1,!0,!0)):"y2"===a?(d=h.y2.copy().domain(h.getYDomain(c,"y2")), -e=this.getYAxis(d,h.y2Orient,i.axis_y2_tick_format,h.y2AxisTickValues,!1,!0,!0)):(d=h.x.copy().domain(h.getXDomain(c)),e=this.getXAxis(d,h.xOrient,h.xAxisTickFormat,h.xAxisTickValues,!1,!0,!0),this.updateXAxisTickValues(c,e)),f=h.d3.select("body").append("div").classed("c3",!0),g=f.append("svg").style("visibility","hidden").style("position","fixed").style("top",0).style("left",0),g.append("g").call(e).each(function(){h.d3.select(this).selectAll("text").each(function(){var a=this.getBoundingClientRect();j<a.width&&(j=a.width)}),f.remove()})),h.currentMaxTickWidths[a]=0>=j?h.currentMaxTickWidths[a]:j,h.currentMaxTickWidths[a])},f.prototype.updateLabels=function(a){var b=this.owner,c=b.main.select("."+l.axisX+" ."+l.axisXLabel),d=b.main.select("."+l.axisY+" ."+l.axisYLabel),e=b.main.select("."+l.axisY2+" ."+l.axisY2Label);(a?c.transition():c).attr("x",this.xForXAxisLabel.bind(this)).attr("dx",this.dxForXAxisLabel.bind(this)).attr("dy",this.dyForXAxisLabel.bind(this)).text(this.textForXAxisLabel.bind(this)),(a?d.transition():d).attr("x",this.xForYAxisLabel.bind(this)).attr("dx",this.dxForYAxisLabel.bind(this)).attr("dy",this.dyForYAxisLabel.bind(this)).text(this.textForYAxisLabel.bind(this)),(a?e.transition():e).attr("x",this.xForY2AxisLabel.bind(this)).attr("dx",this.dxForY2AxisLabel.bind(this)).attr("dy",this.dyForY2AxisLabel.bind(this)).text(this.textForY2AxisLabel.bind(this))},f.prototype.getPadding=function(a,b,c,d){var e="number"==typeof a?a:a[b];return m(e)?"ratio"===a.unit?a[b]*d:this.convertPixelsToAxisPadding(e,d):c},f.prototype.convertPixelsToAxisPadding=function(a,b){var c=this.owner,d=c.config.axis_rotated?c.width:c.height;return b*(a/d)},f.prototype.generateTickValues=function(a,b,c){var d,e,f,g,h,i,j,k=a;if(b)if(d=n(b)?b():b,1===d)k=[a[0]];else if(2===d)k=[a[0],a[a.length-1]];else if(d>2){for(g=d-2,e=a[0],f=a[a.length-1],h=(f-e)/(g+1),k=[e],i=0;g>i;i++)j=+e+h*(i+1),k.push(c?new Date(j):j);k.push(f)}return c||(k=k.sort(function(a,b){return a-b})),k},f.prototype.generateTransitions=function(a){var b=this.owner,c=b.axes;return{axisX:a?c.x.transition().duration(a):c.x,axisY:a?c.y.transition().duration(a):c.y,axisY2:a?c.y2.transition().duration(a):c.y2,axisSubX:a?c.subx.transition().duration(a):c.subx}},f.prototype.redraw=function(a,b){var c=this.owner;c.axes.x.style("opacity",b?0:1),c.axes.y.style("opacity",b?0:1),c.axes.y2.style("opacity",b?0:1),c.axes.subx.style("opacity",b?0:1),a.axisX.call(c.xAxis),a.axisY.call(c.yAxis),a.axisY2.call(c.y2Axis),a.axisSubX.call(c.subXAxis)},i.getClipPath=function(b){var c=a.navigator.appVersion.toLowerCase().indexOf("msie 9.")>=0;return"url("+(c?"":document.URL.split("#")[0])+"#"+b+")"},i.appendClip=function(a,b){return a.append("clipPath").attr("id",b).append("rect")},i.getAxisClipX=function(a){var b=Math.max(30,this.margin.left);return a?-(1+b):-(b-1)},i.getAxisClipY=function(a){return a?-20:-this.margin.top},i.getXAxisClipX=function(){var a=this;return a.getAxisClipX(!a.config.axis_rotated)},i.getXAxisClipY=function(){var a=this;return a.getAxisClipY(!a.config.axis_rotated)},i.getYAxisClipX=function(){var a=this;return a.config.axis_y_inner?-1:a.getAxisClipX(a.config.axis_rotated)},i.getYAxisClipY=function(){var a=this;return a.getAxisClipY(a.config.axis_rotated)},i.getAxisClipWidth=function(a){var b=this,c=Math.max(30,b.margin.left),d=Math.max(30,b.margin.right);return a?b.width+2+c+d:b.margin.left+20},i.getAxisClipHeight=function(a){return(a?this.margin.bottom:this.margin.top+this.height)+20},i.getXAxisClipWidth=function(){var a=this;return a.getAxisClipWidth(!a.config.axis_rotated)},i.getXAxisClipHeight=function(){var a=this;return a.getAxisClipHeight(!a.config.axis_rotated)},i.getYAxisClipWidth=function(){var a=this;return a.getAxisClipWidth(a.config.axis_rotated)+(a.config.axis_y_inner?20:0)},i.getYAxisClipHeight=function(){var a=this;return a.getAxisClipHeight(a.config.axis_rotated)},i.initPie=function(){var a=this,b=a.d3,c=a.config;a.pie=b.layout.pie().value(function(a){return a.values.reduce(function(a,b){return a+b.value},0)}),c.data_order||a.pie.sort(null)},i.updateRadius=function(){var a=this,b=a.config,c=b.gauge_width||b.donut_width;a.radiusExpanded=Math.min(a.arcWidth,a.arcHeight)/2,a.radius=.95*a.radiusExpanded,a.innerRadiusRatio=c?(a.radius-c)/a.radius:.6,a.innerRadius=a.hasType("donut")||a.hasType("gauge")?a.radius*a.innerRadiusRatio:0},i.updateArc=function(){var a=this;a.svgArc=a.getSvgArc(),a.svgArcExpanded=a.getSvgArcExpanded(),a.svgArcExpandedSub=a.getSvgArcExpanded(.98)},i.updateAngle=function(a){var b,c,d,e,f=this,g=f.config,h=!1,i=0;return g?(f.pie(f.filterTargetsToShow(f.data.targets)).forEach(function(b){h||b.data.id!==a.data.id||(h=!0,a=b,a.index=i),i++}),isNaN(a.startAngle)&&(a.startAngle=0),isNaN(a.endAngle)&&(a.endAngle=a.startAngle),f.isGaugeType(a.data)&&(b=g.gauge_min,c=g.gauge_max,d=Math.PI*(g.gauge_fullCircle?2:1)/(c-b),e=a.value<b?0:a.value<c?a.value-b:c-b,a.startAngle=g.gauge_startingAngle,a.endAngle=a.startAngle+d*e),h?a:null):null},i.getSvgArc=function(){var a=this,b=a.d3.svg.arc().outerRadius(a.radius).innerRadius(a.innerRadius),c=function(c,d){var e;return d?b(c):(e=a.updateAngle(c),e?b(e):"M 0 0")};return c.centroid=b.centroid,c},i.getSvgArcExpanded=function(a){var b=this,c=b.d3.svg.arc().outerRadius(b.radiusExpanded*(a?a:1)).innerRadius(b.innerRadius);return function(a){var d=b.updateAngle(a);return d?c(d):"M 0 0"}},i.getArc=function(a,b,c){return c||this.isArcType(a.data)?this.svgArc(a,b):"M 0 0"},i.transformForArcLabel=function(a){var b,c,d,e,f,g=this,h=g.config,i=g.updateAngle(a),j="";return i&&!g.hasType("gauge")&&(b=this.svgArc.centroid(i),c=isNaN(b[0])?0:b[0],d=isNaN(b[1])?0:b[1],e=Math.sqrt(c*c+d*d),f=g.hasType("donut")&&h.donut_label_ratio?n(h.donut_label_ratio)?h.donut_label_ratio(a,g.radius,e):h.donut_label_ratio:g.hasType("pie")&&h.pie_label_ratio?n(h.pie_label_ratio)?h.pie_label_ratio(a,g.radius,e):h.pie_label_ratio:g.radius&&e?(36/g.radius>.375?1.175-36/g.radius:.8)*g.radius/e:0,j="translate("+c*f+","+d*f+")"),j},i.getArcRatio=function(a){var b=this,c=b.config,d=Math.PI*(b.hasType("gauge")&&!c.gauge_fullCircle?1:2);return a?(a.endAngle-a.startAngle)/d:null},i.convertToArcData=function(a){return this.addName({id:a.data.id,value:a.value,ratio:this.getArcRatio(a),index:a.index})},i.textForArcLabel=function(a){var b,c,d,e,f,g=this;return g.shouldShowArcLabel()?(b=g.updateAngle(a),c=b?b.value:null,d=g.getArcRatio(b),e=a.data.id,g.hasType("gauge")||g.meetsArcLabelThreshold(d)?(f=g.getArcLabelFormat(),f?f(c,d,e):g.defaultArcValueFormat(c,d)):""):""},i.expandArc=function(b){var c,d=this;return d.transiting?void(c=a.setInterval(function(){d.transiting||(a.clearInterval(c),d.legend.selectAll(".c3-legend-item-focused").size()>0&&d.expandArc(b))},10)):(b=d.mapToTargetIds(b),void d.svg.selectAll(d.selectorTargets(b,"."+l.chartArc)).each(function(a){d.shouldExpand(a.data.id)&&d.d3.select(this).selectAll("path").transition().duration(d.expandDuration(a.data.id)).attr("d",d.svgArcExpanded).transition().duration(2*d.expandDuration(a.data.id)).attr("d",d.svgArcExpandedSub).each(function(a){d.isDonutType(a.data)})}))},i.unexpandArc=function(a){var b=this;b.transiting||(a=b.mapToTargetIds(a),b.svg.selectAll(b.selectorTargets(a,"."+l.chartArc)).selectAll("path").transition().duration(function(a){return b.expandDuration(a.data.id)}).attr("d",b.svgArc),b.svg.selectAll("."+l.arc).style("opacity",1))},i.expandDuration=function(a){var b=this,c=b.config;return b.isDonutType(a)?c.donut_expand_duration:b.isGaugeType(a)?c.gauge_expand_duration:b.isPieType(a)?c.pie_expand_duration:50},i.shouldExpand=function(a){var b=this,c=b.config;return b.isDonutType(a)&&c.donut_expand||b.isGaugeType(a)&&c.gauge_expand||b.isPieType(a)&&c.pie_expand},i.shouldShowArcLabel=function(){var a=this,b=a.config,c=!0;return a.hasType("donut")?c=b.donut_label_show:a.hasType("pie")&&(c=b.pie_label_show),c},i.meetsArcLabelThreshold=function(a){var b=this,c=b.config,d=b.hasType("donut")?c.donut_label_threshold:c.pie_label_threshold;return a>=d},i.getArcLabelFormat=function(){var a=this,b=a.config,c=b.pie_label_format;return a.hasType("gauge")?c=b.gauge_label_format:a.hasType("donut")&&(c=b.donut_label_format),c},i.getArcTitle=function(){var a=this;return a.hasType("donut")?a.config.donut_title:""},i.updateTargetsForArc=function(a){var b,c,d=this,e=d.main,f=d.classChartArc.bind(d),g=d.classArcs.bind(d),h=d.classFocus.bind(d);b=e.select("."+l.chartArcs).selectAll("."+l.chartArc).data(d.pie(a)).attr("class",function(a){return f(a)+h(a.data)}),c=b.enter().append("g").attr("class",f),c.append("g").attr("class",g),c.append("text").attr("dy",d.hasType("gauge")?"-.1em":".35em").style("opacity",0).style("text-anchor","middle").style("pointer-events","none")},i.initArc=function(){var a=this;a.arcs=a.main.select("."+l.chart).append("g").attr("class",l.chartArcs).attr("transform",a.getTranslate("arc")),a.arcs.append("text").attr("class",l.chartArcsTitle).style("text-anchor","middle").text(a.getArcTitle())},i.redrawArc=function(a,b,c){var d,e=this,f=e.d3,g=e.config,h=e.main;d=h.selectAll("."+l.arcs).selectAll("."+l.arc).data(e.arcData.bind(e)),d.enter().append("path").attr("class",e.classArc.bind(e)).style("fill",function(a){return e.color(a.data)}).style("cursor",function(a){return g.interaction_enabled&&g.data_selection_isselectable(a)?"pointer":null}).style("opacity",0).each(function(a){e.isGaugeType(a.data)&&(a.startAngle=a.endAngle=g.gauge_startingAngle),this._current=a}),d.attr("transform",function(a){return!e.isGaugeType(a.data)&&c?"scale(0)":""}).style("opacity",function(a){return a===this._current?0:1}).on("mouseover",g.interaction_enabled?function(a){var b,c;e.transiting||(b=e.updateAngle(a),b&&(c=e.convertToArcData(b),e.expandArc(b.data.id),e.api.focus(b.data.id),e.toggleFocusLegend(b.data.id,!0),e.config.data_onmouseover(c,this)))}:null).on("mousemove",g.interaction_enabled?function(a){var b,c,d=e.updateAngle(a);d&&(b=e.convertToArcData(d),c=[b],e.showTooltip(c,this))}:null).on("mouseout",g.interaction_enabled?function(a){var b,c;e.transiting||(b=e.updateAngle(a),b&&(c=e.convertToArcData(b),e.unexpandArc(b.data.id),e.api.revert(),e.revertLegend(),e.hideTooltip(),e.config.data_onmouseout(c,this)))}:null).on("click",g.interaction_enabled?function(a,b){var c,d=e.updateAngle(a);d&&(c=e.convertToArcData(d),e.toggleShape&&e.toggleShape(this,c,b),e.config.data_onclick.call(e.api,c,this))}:null).each(function(){e.transiting=!0}).transition().duration(a).attrTween("d",function(a){var b,c=e.updateAngle(a);return c?(isNaN(this._current.startAngle)&&(this._current.startAngle=0),isNaN(this._current.endAngle)&&(this._current.endAngle=this._current.startAngle),b=f.interpolate(this._current,c),this._current=b(0),function(c){var d=b(c);return d.data=a.data,e.getArc(d,!0)}):function(){return"M 0 0"}}).attr("transform",c?"scale(1)":"").style("fill",function(a){return e.levelColor?e.levelColor(a.data.values[0].value):e.color(a.data.id)}).style("opacity",1).call(e.endall,function(){e.transiting=!1}),d.exit().transition().duration(b).style("opacity",0).remove(),h.selectAll("."+l.chartArc).select("text").style("opacity",0).attr("class",function(a){return e.isGaugeType(a.data)?l.gaugeValue:""}).text(e.textForArcLabel.bind(e)).attr("transform",e.transformForArcLabel.bind(e)).style("font-size",function(a){return e.isGaugeType(a.data)?Math.round(e.radius/5)+"px":""}).transition().duration(a).style("opacity",function(a){return e.isTargetToShow(a.data.id)&&e.isArcType(a.data)?1:0}),h.select("."+l.chartArcsTitle).style("opacity",e.hasType("donut")||e.hasType("gauge")?1:0),e.hasType("gauge")&&(e.arcs.select("."+l.chartArcsBackground).attr("d",function(){var a={data:[{value:g.gauge_max}],startAngle:g.gauge_startingAngle,endAngle:-1*g.gauge_startingAngle};return e.getArc(a,!0,!0)}),e.arcs.select("."+l.chartArcsGaugeUnit).attr("dy",".75em").text(g.gauge_label_show?g.gauge_units:""),e.arcs.select("."+l.chartArcsGaugeMin).attr("dx",-1*(e.innerRadius+(e.radius-e.innerRadius)/(g.gauge_fullCircle?1:2))+"px").attr("dy","1.2em").text(g.gauge_label_show?g.gauge_min:""),e.arcs.select("."+l.chartArcsGaugeMax).attr("dx",e.innerRadius+(e.radius-e.innerRadius)/(g.gauge_fullCircle?1:2)+"px").attr("dy","1.2em").text(g.gauge_label_show?g.gauge_max:""))},i.initGauge=function(){var a=this.arcs;this.hasType("gauge")&&(a.append("path").attr("class",l.chartArcsBackground),a.append("text").attr("class",l.chartArcsGaugeUnit).style("text-anchor","middle").style("pointer-events","none"),a.append("text").attr("class",l.chartArcsGaugeMin).style("text-anchor","middle").style("pointer-events","none"),a.append("text").attr("class",l.chartArcsGaugeMax).style("text-anchor","middle").style("pointer-events","none"))},i.getGaugeLabelHeight=function(){return this.config.gauge_label_show?20:0},i.initRegion=function(){var a=this;a.region=a.main.append("g").attr("clip-path",a.clipPath).attr("class",l.regions)},i.updateRegion=function(a){var b=this,c=b.config;b.region.style("visibility",b.hasArcType()?"hidden":"visible"),b.mainRegion=b.main.select("."+l.regions).selectAll("."+l.region).data(c.regions),b.mainRegion.enter().append("g").append("rect").style("fill-opacity",0),b.mainRegion.attr("class",b.classRegion.bind(b)),b.mainRegion.exit().transition().duration(a).style("opacity",0).remove()},i.redrawRegion=function(a){var b=this,c=b.mainRegion.selectAll("rect").each(function(){var a=b.d3.select(this.parentNode).datum();b.d3.select(this).datum(a)}),d=b.regionX.bind(b),e=b.regionY.bind(b),f=b.regionWidth.bind(b),g=b.regionHeight.bind(b);return[(a?c.transition():c).attr("x",d).attr("y",e).attr("width",f).attr("height",g).style("fill-opacity",function(a){return m(a.opacity)?a.opacity:.1})]},i.regionX=function(a){var b,c=this,d=c.config,e="y"===a.axis?c.y:c.y2;return b="y"===a.axis||"y2"===a.axis?d.axis_rotated&&"start"in a?e(a.start):0:d.axis_rotated?0:"start"in a?c.x(c.isTimeSeries()?c.parseDate(a.start):a.start):0},i.regionY=function(a){var b,c=this,d=c.config,e="y"===a.axis?c.y:c.y2;return b="y"===a.axis||"y2"===a.axis?d.axis_rotated?0:"end"in a?e(a.end):0:d.axis_rotated&&"start"in a?c.x(c.isTimeSeries()?c.parseDate(a.start):a.start):0},i.regionWidth=function(a){var b,c=this,d=c.config,e=c.regionX(a),f="y"===a.axis?c.y:c.y2;return b="y"===a.axis||"y2"===a.axis?d.axis_rotated&&"end"in a?f(a.end):c.width:d.axis_rotated?c.width:"end"in a?c.x(c.isTimeSeries()?c.parseDate(a.end):a.end):c.width,e>b?0:b-e},i.regionHeight=function(a){var b,c=this,d=c.config,e=this.regionY(a),f="y"===a.axis?c.y:c.y2;return b="y"===a.axis||"y2"===a.axis?d.axis_rotated?c.height:"start"in a?f(a.start):c.height:d.axis_rotated&&"end"in a?c.x(c.isTimeSeries()?c.parseDate(a.end):a.end):c.height,e>b?0:b-e},i.isRegionOnX=function(a){return!a.axis||"x"===a.axis},i.drag=function(a){var b,c,d,e,f,g,h,i,j=this,k=j.config,m=j.main,n=j.d3;j.hasArcType()||k.data_selection_enabled&&(k.zoom_enabled&&!j.zoom.altDomain||k.data_selection_multiple&&(b=j.dragStart[0],c=j.dragStart[1],d=a[0],e=a[1],f=Math.min(b,d),g=Math.max(b,d),h=k.data_selection_grouped?j.margin.top:Math.min(c,e),i=k.data_selection_grouped?j.height:Math.max(c,e),m.select("."+l.dragarea).attr("x",f).attr("y",h).attr("width",g-f).attr("height",i-h),m.selectAll("."+l.shapes).selectAll("."+l.shape).filter(function(a){return k.data_selection_isselectable(a)}).each(function(a,b){var c,d,e,k,m,o,p=n.select(this),q=p.classed(l.SELECTED),r=p.classed(l.INCLUDED),s=!1;if(p.classed(l.circle))c=1*p.attr("cx"),d=1*p.attr("cy"),m=j.togglePoint,s=c>f&&g>c&&d>h&&i>d;else{if(!p.classed(l.bar))return;o=z(this),c=o.x,d=o.y,e=o.width,k=o.height,m=j.togglePath,s=!(c>g||f>c+e||d>i||h>d+k)}s^r&&(p.classed(l.INCLUDED,!r),p.classed(l.SELECTED,!q),m.call(j,!q,p,a,b))})))},i.dragstart=function(a){var b=this,c=b.config;b.hasArcType()||c.data_selection_enabled&&(b.dragStart=a,b.main.select("."+l.chart).append("rect").attr("class",l.dragarea).style("opacity",.1),b.dragging=!0)},i.dragend=function(){var a=this,b=a.config;a.hasArcType()||b.data_selection_enabled&&(a.main.select("."+l.dragarea).transition().duration(100).style("opacity",0).remove(),a.main.selectAll("."+l.shape).classed(l.INCLUDED,!1),a.dragging=!1)},i.selectPoint=function(a,b,c){var d=this,e=d.config,f=(e.axis_rotated?d.circleY:d.circleX).bind(d),g=(e.axis_rotated?d.circleX:d.circleY).bind(d),h=d.pointSelectR.bind(d);e.data_onselected.call(d.api,b,a.node()),d.main.select("."+l.selectedCircles+d.getTargetSelectorSuffix(b.id)).selectAll("."+l.selectedCircle+"-"+c).data([b]).enter().append("circle").attr("class",function(){return d.generateClass(l.selectedCircle,c)}).attr("cx",f).attr("cy",g).attr("stroke",function(){return d.color(b)}).attr("r",function(a){return 1.4*d.pointSelectR(a)}).transition().duration(100).attr("r",h)},i.unselectPoint=function(a,b,c){var d=this;d.config.data_onunselected.call(d.api,b,a.node()),d.main.select("."+l.selectedCircles+d.getTargetSelectorSuffix(b.id)).selectAll("."+l.selectedCircle+"-"+c).transition().duration(100).attr("r",0).remove()},i.togglePoint=function(a,b,c,d){a?this.selectPoint(b,c,d):this.unselectPoint(b,c,d)},i.selectPath=function(a,b){var c=this;c.config.data_onselected.call(c,b,a.node()),c.config.interaction_brighten&&a.transition().duration(100).style("fill",function(){return c.d3.rgb(c.color(b)).brighter(.75)})},i.unselectPath=function(a,b){var c=this;c.config.data_onunselected.call(c,b,a.node()),c.config.interaction_brighten&&a.transition().duration(100).style("fill",function(){return c.color(b)})},i.togglePath=function(a,b,c,d){a?this.selectPath(b,c,d):this.unselectPath(b,c,d)},i.getToggle=function(a,b){var c,d=this;return"circle"===a.nodeName?c=d.isStepType(b)?function(){}:d.togglePoint:"path"===a.nodeName&&(c=d.togglePath),c},i.toggleShape=function(a,b,c){var d=this,e=d.d3,f=d.config,g=e.select(a),h=g.classed(l.SELECTED),i=d.getToggle(a,b).bind(d);f.data_selection_enabled&&f.data_selection_isselectable(b)&&(f.data_selection_multiple||d.main.selectAll("."+l.shapes+(f.data_selection_grouped?d.getTargetSelectorSuffix(b.id):"")).selectAll("."+l.shape).each(function(a,b){var c=e.select(this);c.classed(l.SELECTED)&&i(!1,c.classed(l.SELECTED,!1),a,b)}),g.classed(l.SELECTED,!h),i(!h,g,b,c))},i.initBrush=function(){var a=this,b=a.d3;a.brush=b.svg.brush().on("brush",function(){a.redrawForBrush()}),a.brush.update=function(){return a.context&&a.context.select("."+l.brush).call(this),this},a.brush.scale=function(b){return a.config.axis_rotated?this.y(b):this.x(b)}},i.initSubchart=function(){var a=this,b=a.config,c=a.context=a.svg.append("g").attr("transform",a.getTranslate("context")),d=b.subchart_show?"visible":"hidden";c.style("visibility",d),c.append("g").attr("clip-path",a.clipPathForSubchart).attr("class",l.chart),c.select("."+l.chart).append("g").attr("class",l.chartBars),c.select("."+l.chart).append("g").attr("class",l.chartLines),c.append("g").attr("clip-path",a.clipPath).attr("class",l.brush).call(a.brush),a.axes.subx=c.append("g").attr("class",l.axisX).attr("transform",a.getTranslate("subx")).attr("clip-path",b.axis_rotated?"":a.clipPathForXAxis).style("visibility",b.subchart_axis_x_show?d:"hidden")},i.updateTargetsForSubchart=function(a){var b,c,d,e,f=this,g=f.context,h=f.config,i=f.classChartBar.bind(f),j=f.classBars.bind(f),k=f.classChartLine.bind(f),m=f.classLines.bind(f),n=f.classAreas.bind(f);h.subchart_show&&(e=g.select("."+l.chartBars).selectAll("."+l.chartBar).data(a).attr("class",i),d=e.enter().append("g").style("opacity",0).attr("class",i),d.append("g").attr("class",j),c=g.select("."+l.chartLines).selectAll("."+l.chartLine).data(a).attr("class",k),b=c.enter().append("g").style("opacity",0).attr("class",k),b.append("g").attr("class",m),b.append("g").attr("class",n),g.selectAll("."+l.brush+" rect").attr(h.axis_rotated?"width":"height",h.axis_rotated?f.width2:f.height2))},i.updateBarForSubchart=function(a){var b=this;b.contextBar=b.context.selectAll("."+l.bars).selectAll("."+l.bar).data(b.barData.bind(b)),b.contextBar.enter().append("path").attr("class",b.classBar.bind(b)).style("stroke","none").style("fill",b.color),b.contextBar.style("opacity",b.initialOpacity.bind(b)),b.contextBar.exit().transition().duration(a).style("opacity",0).remove()},i.redrawBarForSubchart=function(a,b,c){(b?this.contextBar.transition(Math.random().toString()).duration(c):this.contextBar).attr("d",a).style("opacity",1)},i.updateLineForSubchart=function(a){var b=this;b.contextLine=b.context.selectAll("."+l.lines).selectAll("."+l.line).data(b.lineData.bind(b)),b.contextLine.enter().append("path").attr("class",b.classLine.bind(b)).style("stroke",b.color),b.contextLine.style("opacity",b.initialOpacity.bind(b)),b.contextLine.exit().transition().duration(a).style("opacity",0).remove()},i.redrawLineForSubchart=function(a,b,c){(b?this.contextLine.transition(Math.random().toString()).duration(c):this.contextLine).attr("d",a).style("opacity",1)},i.updateAreaForSubchart=function(a){var b=this,c=b.d3;b.contextArea=b.context.selectAll("."+l.areas).selectAll("."+l.area).data(b.lineData.bind(b)),b.contextArea.enter().append("path").attr("class",b.classArea.bind(b)).style("fill",b.color).style("opacity",function(){return b.orgAreaOpacity=+c.select(this).style("opacity"),0}),b.contextArea.style("opacity",0),b.contextArea.exit().transition().duration(a).style("opacity",0).remove()},i.redrawAreaForSubchart=function(a,b,c){(b?this.contextArea.transition(Math.random().toString()).duration(c):this.contextArea).attr("d",a).style("fill",this.color).style("opacity",this.orgAreaOpacity)},i.redrawSubchart=function(a,b,c,d,e,f,g){var h,i,j,k=this,l=k.d3,m=k.config;k.context.style("visibility",m.subchart_show?"visible":"hidden"),m.subchart_show&&(l.event&&"zoom"===l.event.type&&k.brush.extent(k.x.orgDomain()).update(),a&&(k.brush.empty()||k.brush.extent(k.x.orgDomain()).update(),h=k.generateDrawArea(e,!0),i=k.generateDrawBar(f,!0),j=k.generateDrawLine(g,!0),k.updateBarForSubchart(c),k.updateLineForSubchart(c),k.updateAreaForSubchart(c),k.redrawBarForSubchart(i,c,c),k.redrawLineForSubchart(j,c,c),k.redrawAreaForSubchart(h,c,c)))},i.redrawForBrush=function(){var a=this,b=a.x;a.redraw({withTransition:!1,withY:a.config.zoom_rescale,withSubchart:!1,withUpdateXDomain:!0,withDimension:!1}),a.config.subchart_onbrush.call(a.api,b.orgDomain())},i.transformContext=function(a,b){var c,d=this;b&&b.axisSubX?c=b.axisSubX:(c=d.context.select("."+l.axisX),a&&(c=c.transition())),d.context.attr("transform",d.getTranslate("context")),c.attr("transform",d.getTranslate("subx"))},i.getDefaultExtent=function(){var a=this,b=a.config,c=n(b.axis_x_extent)?b.axis_x_extent(a.getXDomain(a.data.targets)):b.axis_x_extent;return a.isTimeSeries()&&(c=[a.parseDate(c[0]),a.parseDate(c[1])]),c},i.initZoom=function(){var a,b=this,c=b.d3,d=b.config;b.zoom=c.behavior.zoom().on("zoomstart",function(){a=c.event.sourceEvent,b.zoom.altDomain=c.event.sourceEvent.altKey?b.x.orgDomain():null,d.zoom_onzoomstart.call(b.api,c.event.sourceEvent)}).on("zoom",function(){b.redrawForZoom.call(b)}).on("zoomend",function(){var e=c.event.sourceEvent;e&&a.clientX===e.clientX&&a.clientY===e.clientY||(b.redrawEventRect(),b.updateZoom(),d.zoom_onzoomend.call(b.api,b.x.orgDomain()))}),b.zoom.scale=function(a){return d.axis_rotated?this.y(a):this.x(a)},b.zoom.orgScaleExtent=function(){var a=d.zoom_extent?d.zoom_extent:[1,10];return[a[0],Math.max(b.getMaxDataCount()/a[1],a[1])]},b.zoom.updateScaleExtent=function(){var a=t(b.x.orgDomain())/t(b.getZoomDomain()),c=this.orgScaleExtent();return this.scaleExtent([c[0]*a,c[1]*a]),this}},i.getZoomDomain=function(){var a=this,b=a.config,c=a.d3,d=c.min([a.orgXDomain[0],b.zoom_x_min]),e=c.max([a.orgXDomain[1],b.zoom_x_max]);return[d,e]},i.updateZoom=function(){var a=this,b=a.config.zoom_enabled?a.zoom:function(){};a.main.select("."+l.zoomRect).call(b).on("dblclick.zoom",null),a.main.selectAll("."+l.eventRect).call(b).on("dblclick.zoom",null)},i.redrawForZoom=function(){var a=this,b=a.d3,c=a.config,d=a.zoom,e=a.x;if(c.zoom_enabled&&0!==a.filterTargetsToShow(a.data.targets).length){if("mousemove"===b.event.sourceEvent.type&&d.altDomain)return e.domain(d.altDomain),void d.scale(e).updateScaleExtent();a.isCategorized()&&e.orgDomain()[0]===a.orgXDomain[0]&&e.domain([a.orgXDomain[0]-1e-10,e.orgDomain()[1]]),a.redraw({withTransition:!1,withY:c.zoom_rescale,withSubchart:!1,withEventRect:!1,withDimension:!1}),"mousemove"===b.event.sourceEvent.type&&(a.cancelClick=!0),c.zoom_onzoom.call(a.api,e.orgDomain())}},i.generateColor=function(){var a=this,b=a.config,c=a.d3,d=b.data_colors,e=v(b.color_pattern)?b.color_pattern:c.scale.category10().range(),f=b.data_color,g=[];return function(a){var b,c=a.id||a.data&&a.data.id||a;return d[c]instanceof Function?b=d[c](a):d[c]?b=d[c]:(g.indexOf(c)<0&&g.push(c),b=e[g.indexOf(c)%e.length],d[c]=b),f instanceof Function?f(b,a):b}},i.generateLevelColor=function(){var a=this,b=a.config,c=b.color_pattern,d=b.color_threshold,e="value"===d.unit,f=d.values&&d.values.length?d.values:[],g=d.max||100;return v(b.color_threshold)?function(a){var b,d,h=c[c.length-1];for(b=0;b<f.length;b++)if(d=e?a:100*a/g,d<f[b]){h=c[b];break}return h}:null},i.getYFormat=function(a){var b=this,c=a&&!b.hasType("gauge")?b.defaultArcValueFormat:b.yFormat,d=a&&!b.hasType("gauge")?b.defaultArcValueFormat:b.y2Format;return function(a,e,f){var g="y2"===b.axis.getId(f)?d:c;return g.call(b,a,e)}},i.yFormat=function(a){var b=this,c=b.config,d=c.axis_y_tick_format?c.axis_y_tick_format:b.defaultValueFormat;return d(a)},i.y2Format=function(a){var b=this,c=b.config,d=c.axis_y2_tick_format?c.axis_y2_tick_format:b.defaultValueFormat;return d(a)},i.defaultValueFormat=function(a){return m(a)?+a:""},i.defaultArcValueFormat=function(a,b){return(100*b).toFixed(1)+"%"},i.dataLabelFormat=function(a){var b,c=this,d=c.config.data_labels,e=function(a){return m(a)?+a:""};return b="function"==typeof d.format?d.format:"object"==typeof d.format?d.format[a]?d.format[a]===!0?e:d.format[a]:function(){return""}:e},i.hasCaches=function(a){for(var b=0;b<a.length;b++)if(!(a[b]in this.cache))return!1;return!0},i.addCache=function(a,b){this.cache[a]=this.cloneTarget(b)},i.getCaches=function(a){var b,c=[];for(b=0;b<a.length;b++)a[b]in this.cache&&c.push(this.cloneTarget(this.cache[a[b]]));return c};var l=i.CLASS={target:"c3-target",chart:"c3-chart",chartLine:"c3-chart-line",chartLines:"c3-chart-lines",chartBar:"c3-chart-bar",chartBars:"c3-chart-bars",chartText:"c3-chart-text",chartTexts:"c3-chart-texts",chartArc:"c3-chart-arc",chartArcs:"c3-chart-arcs",chartArcsTitle:"c3-chart-arcs-title",chartArcsBackground:"c3-chart-arcs-background",chartArcsGaugeUnit:"c3-chart-arcs-gauge-unit",chartArcsGaugeMax:"c3-chart-arcs-gauge-max",chartArcsGaugeMin:"c3-chart-arcs-gauge-min",selectedCircle:"c3-selected-circle",selectedCircles:"c3-selected-circles",eventRect:"c3-event-rect",eventRects:"c3-event-rects",eventRectsSingle:"c3-event-rects-single",eventRectsMultiple:"c3-event-rects-multiple",zoomRect:"c3-zoom-rect",brush:"c3-brush",focused:"c3-focused",defocused:"c3-defocused",region:"c3-region",regions:"c3-regions",title:"c3-title",tooltipContainer:"c3-tooltip-container",tooltip:"c3-tooltip",tooltipName:"c3-tooltip-name",shape:"c3-shape",shapes:"c3-shapes",line:"c3-line",lines:"c3-lines",bar:"c3-bar",bars:"c3-bars",circle:"c3-circle",circles:"c3-circles",arc:"c3-arc",arcs:"c3-arcs",area:"c3-area",areas:"c3-areas",empty:"c3-empty",text:"c3-text",texts:"c3-texts",gaugeValue:"c3-gauge-value",grid:"c3-grid",gridLines:"c3-grid-lines",xgrid:"c3-xgrid",xgrids:"c3-xgrids",xgridLine:"c3-xgrid-line",xgridLines:"c3-xgrid-lines",xgridFocus:"c3-xgrid-focus",ygrid:"c3-ygrid",ygrids:"c3-ygrids",ygridLine:"c3-ygrid-line",ygridLines:"c3-ygrid-lines",axis:"c3-axis",axisX:"c3-axis-x",axisXLabel:"c3-axis-x-label",axisY:"c3-axis-y",axisYLabel:"c3-axis-y-label",axisY2:"c3-axis-y2",axisY2Label:"c3-axis-y2-label",legendBackground:"c3-legend-background",legendItem:"c3-legend-item",legendItemEvent:"c3-legend-item-event",legendItemTile:"c3-legend-item-tile",legendItemHidden:"c3-legend-item-hidden",legendItemFocused:"c3-legend-item-focused",dragarea:"c3-dragarea",EXPANDED:"_expanded_",SELECTED:"_selected_",INCLUDED:"_included_"};i.generateClass=function(a,b){return" "+a+" "+a+this.getTargetSelectorSuffix(b)},i.classText=function(a){return this.generateClass(l.text,a.index)},i.classTexts=function(a){return this.generateClass(l.texts,a.id)},i.classShape=function(a){return this.generateClass(l.shape,a.index)},i.classShapes=function(a){return this.generateClass(l.shapes,a.id)},i.classLine=function(a){return this.classShape(a)+this.generateClass(l.line,a.id)},i.classLines=function(a){return this.classShapes(a)+this.generateClass(l.lines,a.id)},i.classCircle=function(a){return this.classShape(a)+this.generateClass(l.circle,a.index)},i.classCircles=function(a){return this.classShapes(a)+this.generateClass(l.circles,a.id)},i.classBar=function(a){return this.classShape(a)+this.generateClass(l.bar,a.index)},i.classBars=function(a){return this.classShapes(a)+this.generateClass(l.bars,a.id)},i.classArc=function(a){return this.classShape(a.data)+this.generateClass(l.arc,a.data.id)},i.classArcs=function(a){return this.classShapes(a.data)+this.generateClass(l.arcs,a.data.id)},i.classArea=function(a){return this.classShape(a)+this.generateClass(l.area,a.id)},i.classAreas=function(a){return this.classShapes(a)+this.generateClass(l.areas,a.id)},i.classRegion=function(a,b){return this.generateClass(l.region,b)+" "+("class"in a?a["class"]:"")},i.classEvent=function(a){return this.generateClass(l.eventRect,a.index)},i.classTarget=function(a){var b=this,c=b.config.data_classes[a],d="";return c&&(d=" "+l.target+"-"+c),b.generateClass(l.target,a)+d},i.classFocus=function(a){return this.classFocused(a)+this.classDefocused(a)},i.classFocused=function(a){return" "+(this.focusedTargetIds.indexOf(a.id)>=0?l.focused:"")},i.classDefocused=function(a){return" "+(this.defocusedTargetIds.indexOf(a.id)>=0?l.defocused:"")},i.classChartText=function(a){return l.chartText+this.classTarget(a.id)},i.classChartLine=function(a){return l.chartLine+this.classTarget(a.id)},i.classChartBar=function(a){return l.chartBar+this.classTarget(a.id)},i.classChartArc=function(a){return l.chartArc+this.classTarget(a.data.id)},i.getTargetSelectorSuffix=function(a){return a||0===a?("-"+a).replace(/[\s?!@#$%^&*()_=+,.<>'":;\[\]\/|~`{}\\]/g,"-"):""},i.selectorTarget=function(a,b){return(b||"")+"."+l.target+this.getTargetSelectorSuffix(a)},i.selectorTargets=function(a,b){var c=this;return a=a||[],a.length?a.map(function(a){return c.selectorTarget(a,b)}):null},i.selectorLegend=function(a){return"."+l.legendItem+this.getTargetSelectorSuffix(a)},i.selectorLegends=function(a){var b=this;return a&&a.length?a.map(function(a){return b.selectorLegend(a)}):null};var m=i.isValue=function(a){return a||0===a},n=i.isFunction=function(a){return"function"==typeof a},o=i.isString=function(a){return"string"==typeof a},p=i.isUndefined=function(a){return"undefined"==typeof a},q=i.isDefined=function(a){return"undefined"!=typeof a},r=i.ceil10=function(a){return 10*Math.ceil(a/10)},s=i.asHalfPixel=function(a){return Math.ceil(a)+.5},t=i.diffDomain=function(a){return a[1]-a[0]},u=i.isEmpty=function(a){return"undefined"==typeof a||null===a||o(a)&&0===a.length||"object"==typeof a&&0===Object.keys(a).length},v=i.notEmpty=function(a){return!i.isEmpty(a)},w=i.getOption=function(a,b,c){return q(a[b])?a[b]:c},x=i.hasValue=function(a,b){var c=!1;return Object.keys(a).forEach(function(d){a[d]===b&&(c=!0)}),c},y=i.sanitise=function(a){return"string"==typeof a?a.replace(/</g,"<").replace(/>/g,">"):a},z=i.getPathBox=function(a){var b=a.getBoundingClientRect(),c=[a.pathSegList.getItem(0),a.pathSegList.getItem(1)],d=c[0].x,e=Math.min(c[0].y,c[1].y);return{x:d,y:e,width:b.width,height:b.height}};h.focus=function(a){var b,c=this.internal;a=c.mapToTargetIds(a),b=c.svg.selectAll(c.selectorTargets(a.filter(c.isTargetToShow,c))),this.revert(),this.defocus(),b.classed(l.focused,!0).classed(l.defocused,!1), -c.hasArcType()&&c.expandArc(a),c.toggleFocusLegend(a,!0),c.focusedTargetIds=a,c.defocusedTargetIds=c.defocusedTargetIds.filter(function(b){return a.indexOf(b)<0})},h.defocus=function(a){var b,c=this.internal;a=c.mapToTargetIds(a),b=c.svg.selectAll(c.selectorTargets(a.filter(c.isTargetToShow,c))),b.classed(l.focused,!1).classed(l.defocused,!0),c.hasArcType()&&c.unexpandArc(a),c.toggleFocusLegend(a,!1),c.focusedTargetIds=c.focusedTargetIds.filter(function(b){return a.indexOf(b)<0}),c.defocusedTargetIds=a},h.revert=function(a){var b,c=this.internal;a=c.mapToTargetIds(a),b=c.svg.selectAll(c.selectorTargets(a)),b.classed(l.focused,!1).classed(l.defocused,!1),c.hasArcType()&&c.unexpandArc(a),c.config.legend_show&&(c.showLegend(a.filter(c.isLegendToShow.bind(c))),c.legend.selectAll(c.selectorLegends(a)).filter(function(){return c.d3.select(this).classed(l.legendItemFocused)}).classed(l.legendItemFocused,!1)),c.focusedTargetIds=[],c.defocusedTargetIds=[]},h.show=function(a,b){var c,d=this.internal;a=d.mapToTargetIds(a),b=b||{},d.removeHiddenTargetIds(a),c=d.svg.selectAll(d.selectorTargets(a)),c.transition().style("opacity",1,"important").call(d.endall,function(){c.style("opacity",null).style("opacity",1)}),b.withLegend&&d.showLegend(a),d.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0,withLegend:!0})},h.hide=function(a,b){var c,d=this.internal;a=d.mapToTargetIds(a),b=b||{},d.addHiddenTargetIds(a),c=d.svg.selectAll(d.selectorTargets(a)),c.transition().style("opacity",0,"important").call(d.endall,function(){c.style("opacity",null).style("opacity",0)}),b.withLegend&&d.hideLegend(a),d.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0,withLegend:!0})},h.toggle=function(a,b){var c=this,d=this.internal;d.mapToTargetIds(a).forEach(function(a){d.isTargetToShow(a)?c.hide(a,b):c.show(a,b)})},h.zoom=function(a){var b=this.internal;return a&&(b.isTimeSeries()&&(a=a.map(function(a){return b.parseDate(a)})),b.brush.extent(a),b.redraw({withUpdateXDomain:!0,withY:b.config.zoom_rescale}),b.config.zoom_onzoom.call(this,b.x.orgDomain())),b.brush.extent()},h.zoom.enable=function(a){var b=this.internal;b.config.zoom_enabled=a,b.updateAndRedraw()},h.unzoom=function(){var a=this.internal;a.brush.clear().update(),a.redraw({withUpdateXDomain:!0})},h.zoom.max=function(a){var b=this.internal,c=b.config,d=b.d3;return 0===a||a?void(c.zoom_x_max=d.max([b.orgXDomain[1],a])):c.zoom_x_max},h.zoom.min=function(a){var b=this.internal,c=b.config,d=b.d3;return 0===a||a?void(c.zoom_x_min=d.min([b.orgXDomain[0],a])):c.zoom_x_min},h.zoom.range=function(a){return arguments.length?(q(a.max)&&this.domain.max(a.max),void(q(a.min)&&this.domain.min(a.min))):{max:this.domain.max(),min:this.domain.min()}},h.load=function(a){var b=this.internal,c=b.config;return a.xs&&b.addXs(a.xs),"names"in a&&h.data.names.bind(this)(a.names),"classes"in a&&Object.keys(a.classes).forEach(function(b){c.data_classes[b]=a.classes[b]}),"categories"in a&&b.isCategorized()&&(c.axis_x_categories=a.categories),"axes"in a&&Object.keys(a.axes).forEach(function(b){c.data_axes[b]=a.axes[b]}),"colors"in a&&Object.keys(a.colors).forEach(function(b){c.data_colors[b]=a.colors[b]}),"cacheIds"in a&&b.hasCaches(a.cacheIds)?void b.load(b.getCaches(a.cacheIds),a.done):void("unload"in a?b.unload(b.mapToTargetIds("boolean"==typeof a.unload&&a.unload?null:a.unload),function(){b.loadFromArgs(a)}):b.loadFromArgs(a))},h.unload=function(a){var b=this.internal;a=a||{},a instanceof Array?a={ids:a}:"string"==typeof a&&(a={ids:[a]}),b.unload(b.mapToTargetIds(a.ids),function(){b.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0,withLegend:!0}),a.done&&a.done()})},h.flow=function(a){var b,c,d,e,f,g,h,i,j=this.internal,k=[],l=j.getMaxDataCount(),n=0,o=0;if(a.json)c=j.convertJsonToData(a.json,a.keys);else if(a.rows)c=j.convertRowsToData(a.rows);else{if(!a.columns)return;c=j.convertColumnsToData(a.columns)}b=j.convertDataToTargets(c,!0),j.data.targets.forEach(function(a){var c,d,e=!1;for(c=0;c<b.length;c++)if(a.id===b[c].id){for(e=!0,a.values[a.values.length-1]&&(o=a.values[a.values.length-1].index+1),n=b[c].values.length,d=0;n>d;d++)b[c].values[d].index=o+d,j.isTimeSeries()||(b[c].values[d].x=o+d);a.values=a.values.concat(b[c].values),b.splice(c,1);break}e||k.push(a.id)}),j.data.targets.forEach(function(a){var b,c;for(b=0;b<k.length;b++)if(a.id===k[b])for(o=a.values[a.values.length-1].index+1,c=0;n>c;c++)a.values.push({id:a.id,index:o+c,x:j.isTimeSeries()?j.getOtherTargetX(o+c):o+c,value:null})}),j.data.targets.length&&b.forEach(function(a){var b,c=[];for(b=j.data.targets[0].values[0].index;o>b;b++)c.push({id:a.id,index:b,x:j.isTimeSeries()?j.getOtherTargetX(b):b,value:null});a.values.forEach(function(a){a.index+=o,j.isTimeSeries()||(a.x+=o)}),a.values=c.concat(a.values)}),j.data.targets=j.data.targets.concat(b),d=j.getMaxDataCount(),f=j.data.targets[0],g=f.values[0],q(a.to)?(n=0,i=j.isTimeSeries()?j.parseDate(a.to):a.to,f.values.forEach(function(a){a.x<i&&n++})):q(a.length)&&(n=a.length),l?1===l&&j.isTimeSeries()&&(h=(f.values[f.values.length-1].x-g.x)/2,e=[new Date(+g.x-h),new Date(+g.x+h)],j.updateXDomain(null,!0,!0,!1,e)):(h=j.isTimeSeries()?f.values.length>1?f.values[f.values.length-1].x-g.x:g.x-j.getXDomain(j.data.targets)[0]:1,e=[g.x-h,g.x],j.updateXDomain(null,!0,!0,!1,e)),j.updateTargets(j.data.targets),j.redraw({flow:{index:g.index,length:n,duration:m(a.duration)?a.duration:j.config.transition_duration,done:a.done,orgDataCount:l},withLegend:!0,withTransition:l>1,withTrimXDomain:!1,withUpdateXAxis:!0})},i.generateFlow=function(a){var b=this,c=b.config,d=b.d3;return function(){var e,f,g,h=a.targets,i=a.flow,j=a.drawBar,k=a.drawLine,m=a.drawArea,n=a.cx,o=a.cy,p=a.xv,q=a.xForText,r=a.yForText,s=a.duration,u=1,v=i.index,w=i.length,x=b.getValueOnIndex(b.data.targets[0].values,v),y=b.getValueOnIndex(b.data.targets[0].values,v+w),z=b.x.domain(),A=i.duration||s,B=i.done||function(){},C=b.generateWait(),D=b.xgrid||d.selectAll([]),E=b.xgridLines||d.selectAll([]),F=b.mainRegion||d.selectAll([]),G=b.mainText||d.selectAll([]),H=b.mainBar||d.selectAll([]),I=b.mainLine||d.selectAll([]),J=b.mainArea||d.selectAll([]),K=b.mainCircle||d.selectAll([]);b.flowing=!0,b.data.targets.forEach(function(a){a.values.splice(0,w)}),g=b.updateXDomain(h,!0,!0),b.updateXGrid&&b.updateXGrid(!0),i.orgDataCount?e=1===i.orgDataCount||(x&&x.x)===(y&&y.x)?b.x(z[0])-b.x(g[0]):b.isTimeSeries()?b.x(z[0])-b.x(g[0]):b.x(x.x)-b.x(y.x):1!==b.data.targets[0].values.length?e=b.x(z[0])-b.x(g[0]):b.isTimeSeries()?(x=b.getValueOnIndex(b.data.targets[0].values,0),y=b.getValueOnIndex(b.data.targets[0].values,b.data.targets[0].values.length-1),e=b.x(x.x)-b.x(y.x)):e=t(g)/2,u=t(z)/t(g),f="translate("+e+",0) scale("+u+",1)",b.hideXGridFocus(),d.transition().ease("linear").duration(A).each(function(){C.add(b.axes.x.transition().call(b.xAxis)),C.add(H.transition().attr("transform",f)),C.add(I.transition().attr("transform",f)),C.add(J.transition().attr("transform",f)),C.add(K.transition().attr("transform",f)),C.add(G.transition().attr("transform",f)),C.add(F.filter(b.isRegionOnX).transition().attr("transform",f)),C.add(D.transition().attr("transform",f)),C.add(E.transition().attr("transform",f))}).call(C,function(){var a,d=[],e=[],f=[];if(w){for(a=0;w>a;a++)d.push("."+l.shape+"-"+(v+a)),e.push("."+l.text+"-"+(v+a)),f.push("."+l.eventRect+"-"+(v+a));b.svg.selectAll("."+l.shapes).selectAll(d).remove(),b.svg.selectAll("."+l.texts).selectAll(e).remove(),b.svg.selectAll("."+l.eventRects).selectAll(f).remove(),b.svg.select("."+l.xgrid).remove()}D.attr("transform",null).attr(b.xgridAttr),E.attr("transform",null),E.select("line").attr("x1",c.axis_rotated?0:p).attr("x2",c.axis_rotated?b.width:p),E.select("text").attr("x",c.axis_rotated?b.width:0).attr("y",p),H.attr("transform",null).attr("d",j),I.attr("transform",null).attr("d",k),J.attr("transform",null).attr("d",m),K.attr("transform",null).attr("cx",n).attr("cy",o),G.attr("transform",null).attr("x",q).attr("y",r).style("fill-opacity",b.opacityForText.bind(b)),F.attr("transform",null),F.select("rect").filter(b.isRegionOnX).attr("x",b.regionX.bind(b)).attr("width",b.regionWidth.bind(b)),c.interaction_enabled&&b.redrawEventRect(),B(),b.flowing=!1})}},h.selected=function(a){var b=this.internal,c=b.d3;return c.merge(b.main.selectAll("."+l.shapes+b.getTargetSelectorSuffix(a)).selectAll("."+l.shape).filter(function(){return c.select(this).classed(l.SELECTED)}).map(function(a){return a.map(function(a){var b=a.__data__;return b.data?b.data:b})}))},h.select=function(a,b,c){var d=this.internal,e=d.d3,f=d.config;f.data_selection_enabled&&d.main.selectAll("."+l.shapes).selectAll("."+l.shape).each(function(g,h){var i=e.select(this),j=g.data?g.data.id:g.id,k=d.getToggle(this,g).bind(d),m=f.data_selection_grouped||!a||a.indexOf(j)>=0,n=!b||b.indexOf(h)>=0,o=i.classed(l.SELECTED);i.classed(l.line)||i.classed(l.area)||(m&&n?f.data_selection_isselectable(g)&&!o&&k(!0,i.classed(l.SELECTED,!0),g,h):q(c)&&c&&o&&k(!1,i.classed(l.SELECTED,!1),g,h))})},h.unselect=function(a,b){var c=this.internal,d=c.d3,e=c.config;e.data_selection_enabled&&c.main.selectAll("."+l.shapes).selectAll("."+l.shape).each(function(f,g){var h=d.select(this),i=f.data?f.data.id:f.id,j=c.getToggle(this,f).bind(c),k=e.data_selection_grouped||!a||a.indexOf(i)>=0,m=!b||b.indexOf(g)>=0,n=h.classed(l.SELECTED);h.classed(l.line)||h.classed(l.area)||k&&m&&e.data_selection_isselectable(f)&&n&&j(!1,h.classed(l.SELECTED,!1),f,g)})},h.transform=function(a,b){var c=this.internal,d=["pie","donut"].indexOf(a)>=0?{withTransform:!0}:null;c.transformTo(b,a,d)},i.transformTo=function(a,b,c){var d=this,e=!d.hasArcType(),f=c||{withTransitionForAxis:e};f.withTransitionForTransform=!1,d.transiting=!1,d.setTargetType(a,b),d.updateTargets(d.data.targets),d.updateAndRedraw(f)},h.groups=function(a){var b=this.internal,c=b.config;return p(a)?c.data_groups:(c.data_groups=a,b.redraw(),c.data_groups)},h.xgrids=function(a){var b=this.internal,c=b.config;return a?(c.grid_x_lines=a,b.redrawWithoutRescale(),c.grid_x_lines):c.grid_x_lines},h.xgrids.add=function(a){var b=this.internal;return this.xgrids(b.config.grid_x_lines.concat(a?a:[]))},h.xgrids.remove=function(a){var b=this.internal;b.removeGridLines(a,!0)},h.ygrids=function(a){var b=this.internal,c=b.config;return a?(c.grid_y_lines=a,b.redrawWithoutRescale(),c.grid_y_lines):c.grid_y_lines},h.ygrids.add=function(a){var b=this.internal;return this.ygrids(b.config.grid_y_lines.concat(a?a:[]))},h.ygrids.remove=function(a){var b=this.internal;b.removeGridLines(a,!1)},h.regions=function(a){var b=this.internal,c=b.config;return a?(c.regions=a,b.redrawWithoutRescale(),c.regions):c.regions},h.regions.add=function(a){var b=this.internal,c=b.config;return a?(c.regions=c.regions.concat(a),b.redrawWithoutRescale(),c.regions):c.regions},h.regions.remove=function(a){var b,c,d,e=this.internal,f=e.config;return a=a||{},b=e.getOption(a,"duration",f.transition_duration),c=e.getOption(a,"classes",[l.region]),d=e.main.select("."+l.regions).selectAll(c.map(function(a){return"."+a})),(b?d.transition().duration(b):d).style("opacity",0).remove(),f.regions=f.regions.filter(function(a){var b=!1;return a["class"]?(a["class"].split(" ").forEach(function(a){c.indexOf(a)>=0&&(b=!0)}),!b):!0}),f.regions},h.data=function(a){var b=this.internal.data.targets;return"undefined"==typeof a?b:b.filter(function(b){return[].concat(a).indexOf(b.id)>=0})},h.data.shown=function(a){return this.internal.filterTargetsToShow(this.data(a))},h.data.values=function(a){var b,c=null;return a&&(b=this.data(a),c=b[0]?b[0].values.map(function(a){return a.value}):null),c},h.data.names=function(a){return this.internal.clearLegendItemTextBoxCache(),this.internal.updateDataAttributes("names",a)},h.data.colors=function(a){return this.internal.updateDataAttributes("colors",a)},h.data.axes=function(a){return this.internal.updateDataAttributes("axes",a)},h.category=function(a,b){var c=this.internal,d=c.config;return arguments.length>1&&(d.axis_x_categories[a]=b,c.redraw()),d.axis_x_categories[a]},h.categories=function(a){var b=this.internal,c=b.config;return arguments.length?(c.axis_x_categories=a,b.redraw(),c.axis_x_categories):c.axis_x_categories},h.color=function(a){var b=this.internal;return b.color(a)},h.x=function(a){var b=this.internal;return arguments.length&&(b.updateTargetX(b.data.targets,a),b.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0})),b.data.xs},h.xs=function(a){var b=this.internal;return arguments.length&&(b.updateTargetXs(b.data.targets,a),b.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0})),b.data.xs},h.axis=function(){},h.axis.labels=function(a){var b=this.internal;arguments.length&&(Object.keys(a).forEach(function(c){b.axis.setLabelText(c,a[c])}),b.axis.updateLabels())},h.axis.max=function(a){var b=this.internal,c=b.config;return arguments.length?("object"==typeof a?(m(a.x)&&(c.axis_x_max=a.x),m(a.y)&&(c.axis_y_max=a.y),m(a.y2)&&(c.axis_y2_max=a.y2)):c.axis_y_max=c.axis_y2_max=a,void b.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0})):{x:c.axis_x_max,y:c.axis_y_max,y2:c.axis_y2_max}},h.axis.min=function(a){var b=this.internal,c=b.config;return arguments.length?("object"==typeof a?(m(a.x)&&(c.axis_x_min=a.x),m(a.y)&&(c.axis_y_min=a.y),m(a.y2)&&(c.axis_y2_min=a.y2)):c.axis_y_min=c.axis_y2_min=a,void b.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0})):{x:c.axis_x_min,y:c.axis_y_min,y2:c.axis_y2_min}},h.axis.range=function(a){return arguments.length?(q(a.max)&&this.axis.max(a.max),void(q(a.min)&&this.axis.min(a.min))):{max:this.axis.max(),min:this.axis.min()}},h.legend=function(){},h.legend.show=function(a){var b=this.internal;b.showLegend(b.mapToTargetIds(a)),b.updateAndRedraw({withLegend:!0})},h.legend.hide=function(a){var b=this.internal;b.hideLegend(b.mapToTargetIds(a)),b.updateAndRedraw({withLegend:!0})},h.resize=function(a){var b=this.internal,c=b.config;c.size_width=a?a.width:null,c.size_height=a?a.height:null,this.flush()},h.flush=function(){var a=this.internal;a.updateAndRedraw({withLegend:!0,withTransition:!1,withTransitionForTransform:!1})},h.destroy=function(){var b=this.internal;if(a.clearInterval(b.intervalForObserveInserted),void 0!==b.resizeTimeout&&a.clearTimeout(b.resizeTimeout),a.detachEvent)a.detachEvent("onresize",b.resizeFunction);else if(a.removeEventListener)a.removeEventListener("resize",b.resizeFunction);else{var c=a.onresize;c&&c.add&&c.remove&&c.remove(b.resizeFunction)}return b.selectChart.classed("c3",!1).html(""),Object.keys(b).forEach(function(a){b[a]=null}),null},h.tooltip=function(){},h.tooltip.show=function(a){var b,c,d=this.internal;a.mouse&&(c=a.mouse),a.data?d.isMultipleX()?(c=[d.x(a.data.x),d.getYScale(a.data.id)(a.data.value)],b=null):b=m(a.data.index)?a.data.index:d.getIndexByX(a.data.x):"undefined"!=typeof a.x?b=d.getIndexByX(a.x):"undefined"!=typeof a.index&&(b=a.index),d.dispatchEvent("mouseover",b,c),d.dispatchEvent("mousemove",b,c),d.config.tooltip_onshow.call(d,a.data)},h.tooltip.hide=function(){this.internal.dispatchEvent("mouseout",0),this.internal.config.tooltip_onhide.call(this)};var A;i.isSafari=function(){var b=a.navigator.userAgent;return b.indexOf("Safari")>=0&&b.indexOf("Chrome")<0},i.isChrome=function(){var b=a.navigator.userAgent;return b.indexOf("Chrome")>=0},Function.prototype.bind||(Function.prototype.bind=function(a){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var b=Array.prototype.slice.call(arguments,1),c=this,d=function(){},e=function(){return c.apply(this instanceof d?this:a,b.concat(Array.prototype.slice.call(arguments)))};return d.prototype=this.prototype,e.prototype=new d,e}),function(){"SVGPathSeg"in a||(a.SVGPathSeg=function(a,b,c){this.pathSegType=a,this.pathSegTypeAsLetter=b,this._owningPathSegList=c},SVGPathSeg.PATHSEG_UNKNOWN=0,SVGPathSeg.PATHSEG_CLOSEPATH=1,SVGPathSeg.PATHSEG_MOVETO_ABS=2,SVGPathSeg.PATHSEG_MOVETO_REL=3,SVGPathSeg.PATHSEG_LINETO_ABS=4,SVGPathSeg.PATHSEG_LINETO_REL=5,SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS=6,SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL=7,SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS=8,SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL=9,SVGPathSeg.PATHSEG_ARC_ABS=10,SVGPathSeg.PATHSEG_ARC_REL=11,SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS=12,SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL=13,SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS=14,SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL=15,SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS=16,SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL=17,SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS=18,SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL=19,SVGPathSeg.prototype._segmentChanged=function(){this._owningPathSegList&&this._owningPathSegList.segmentChanged(this)},a.SVGPathSegClosePath=function(a){SVGPathSeg.call(this,SVGPathSeg.PATHSEG_CLOSEPATH,"z",a)},SVGPathSegClosePath.prototype=Object.create(SVGPathSeg.prototype),SVGPathSegClosePath.prototype.toString=function(){return"[object SVGPathSegClosePath]"},SVGPathSegClosePath.prototype._asPathString=function(){return this.pathSegTypeAsLetter},SVGPathSegClosePath.prototype.clone=function(){return new SVGPathSegClosePath(void 0)},a.SVGPathSegMovetoAbs=function(a,b,c){SVGPathSeg.call(this,SVGPathSeg.PATHSEG_MOVETO_ABS,"M",a),this._x=b,this._y=c},SVGPathSegMovetoAbs.prototype=Object.create(SVGPathSeg.prototype),SVGPathSegMovetoAbs.prototype.toString=function(){return"[object SVGPathSegMovetoAbs]"},SVGPathSegMovetoAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x+" "+this._y},SVGPathSegMovetoAbs.prototype.clone=function(){return new SVGPathSegMovetoAbs(void 0,this._x,this._y)},Object.defineProperty(SVGPathSegMovetoAbs.prototype,"x",{get:function(){return this._x},set:function(a){this._x=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegMovetoAbs.prototype,"y",{get:function(){return this._y},set:function(a){this._y=a,this._segmentChanged()},enumerable:!0}),a.SVGPathSegMovetoRel=function(a,b,c){SVGPathSeg.call(this,SVGPathSeg.PATHSEG_MOVETO_REL,"m",a),this._x=b,this._y=c},SVGPathSegMovetoRel.prototype=Object.create(SVGPathSeg.prototype),SVGPathSegMovetoRel.prototype.toString=function(){return"[object SVGPathSegMovetoRel]"},SVGPathSegMovetoRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x+" "+this._y},SVGPathSegMovetoRel.prototype.clone=function(){return new SVGPathSegMovetoRel(void 0,this._x,this._y)},Object.defineProperty(SVGPathSegMovetoRel.prototype,"x",{get:function(){return this._x},set:function(a){this._x=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegMovetoRel.prototype,"y",{get:function(){return this._y},set:function(a){this._y=a,this._segmentChanged()},enumerable:!0}),a.SVGPathSegLinetoAbs=function(a,b,c){SVGPathSeg.call(this,SVGPathSeg.PATHSEG_LINETO_ABS,"L",a),this._x=b,this._y=c},SVGPathSegLinetoAbs.prototype=Object.create(SVGPathSeg.prototype),SVGPathSegLinetoAbs.prototype.toString=function(){return"[object SVGPathSegLinetoAbs]"},SVGPathSegLinetoAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x+" "+this._y},SVGPathSegLinetoAbs.prototype.clone=function(){return new SVGPathSegLinetoAbs(void 0,this._x,this._y)},Object.defineProperty(SVGPathSegLinetoAbs.prototype,"x",{get:function(){return this._x},set:function(a){this._x=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegLinetoAbs.prototype,"y",{get:function(){return this._y},set:function(a){this._y=a,this._segmentChanged()},enumerable:!0}),a.SVGPathSegLinetoRel=function(a,b,c){SVGPathSeg.call(this,SVGPathSeg.PATHSEG_LINETO_REL,"l",a),this._x=b,this._y=c},SVGPathSegLinetoRel.prototype=Object.create(SVGPathSeg.prototype),SVGPathSegLinetoRel.prototype.toString=function(){return"[object SVGPathSegLinetoRel]"},SVGPathSegLinetoRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x+" "+this._y},SVGPathSegLinetoRel.prototype.clone=function(){return new SVGPathSegLinetoRel(void 0,this._x,this._y)},Object.defineProperty(SVGPathSegLinetoRel.prototype,"x",{get:function(){return this._x},set:function(a){this._x=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegLinetoRel.prototype,"y",{get:function(){return this._y},set:function(a){this._y=a,this._segmentChanged()},enumerable:!0}),a.SVGPathSegCurvetoCubicAbs=function(a,b,c,d,e,f,g){SVGPathSeg.call(this,SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS,"C",a),this._x=b,this._y=c,this._x1=d,this._y1=e,this._x2=f,this._y2=g},SVGPathSegCurvetoCubicAbs.prototype=Object.create(SVGPathSeg.prototype),SVGPathSegCurvetoCubicAbs.prototype.toString=function(){return"[object SVGPathSegCurvetoCubicAbs]"},SVGPathSegCurvetoCubicAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x1+" "+this._y1+" "+this._x2+" "+this._y2+" "+this._x+" "+this._y},SVGPathSegCurvetoCubicAbs.prototype.clone=function(){return new SVGPathSegCurvetoCubicAbs(void 0,this._x,this._y,this._x1,this._y1,this._x2,this._y2)},Object.defineProperty(SVGPathSegCurvetoCubicAbs.prototype,"x",{get:function(){return this._x},set:function(a){this._x=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoCubicAbs.prototype,"y",{get:function(){return this._y},set:function(a){this._y=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoCubicAbs.prototype,"x1",{get:function(){return this._x1},set:function(a){this._x1=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoCubicAbs.prototype,"y1",{get:function(){return this._y1},set:function(a){this._y1=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoCubicAbs.prototype,"x2",{get:function(){return this._x2},set:function(a){this._x2=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoCubicAbs.prototype,"y2",{get:function(){return this._y2},set:function(a){this._y2=a,this._segmentChanged()},enumerable:!0}),a.SVGPathSegCurvetoCubicRel=function(a,b,c,d,e,f,g){SVGPathSeg.call(this,SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL,"c",a),this._x=b,this._y=c,this._x1=d,this._y1=e,this._x2=f,this._y2=g},SVGPathSegCurvetoCubicRel.prototype=Object.create(SVGPathSeg.prototype),SVGPathSegCurvetoCubicRel.prototype.toString=function(){return"[object SVGPathSegCurvetoCubicRel]"},SVGPathSegCurvetoCubicRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x1+" "+this._y1+" "+this._x2+" "+this._y2+" "+this._x+" "+this._y},SVGPathSegCurvetoCubicRel.prototype.clone=function(){return new SVGPathSegCurvetoCubicRel(void 0,this._x,this._y,this._x1,this._y1,this._x2,this._y2)},Object.defineProperty(SVGPathSegCurvetoCubicRel.prototype,"x",{get:function(){return this._x},set:function(a){this._x=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoCubicRel.prototype,"y",{get:function(){return this._y},set:function(a){this._y=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoCubicRel.prototype,"x1",{get:function(){return this._x1},set:function(a){this._x1=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoCubicRel.prototype,"y1",{get:function(){return this._y1},set:function(a){this._y1=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoCubicRel.prototype,"x2",{get:function(){return this._x2},set:function(a){this._x2=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoCubicRel.prototype,"y2",{get:function(){return this._y2},set:function(a){this._y2=a,this._segmentChanged()},enumerable:!0}),a.SVGPathSegCurvetoQuadraticAbs=function(a,b,c,d,e){SVGPathSeg.call(this,SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS,"Q",a),this._x=b,this._y=c,this._x1=d,this._y1=e},SVGPathSegCurvetoQuadraticAbs.prototype=Object.create(SVGPathSeg.prototype),SVGPathSegCurvetoQuadraticAbs.prototype.toString=function(){return"[object SVGPathSegCurvetoQuadraticAbs]"},SVGPathSegCurvetoQuadraticAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x1+" "+this._y1+" "+this._x+" "+this._y},SVGPathSegCurvetoQuadraticAbs.prototype.clone=function(){return new SVGPathSegCurvetoQuadraticAbs(void 0,this._x,this._y,this._x1,this._y1)},Object.defineProperty(SVGPathSegCurvetoQuadraticAbs.prototype,"x",{get:function(){return this._x},set:function(a){this._x=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoQuadraticAbs.prototype,"y",{get:function(){return this._y},set:function(a){this._y=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoQuadraticAbs.prototype,"x1",{get:function(){return this._x1},set:function(a){this._x1=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoQuadraticAbs.prototype,"y1",{get:function(){return this._y1},set:function(a){this._y1=a,this._segmentChanged()},enumerable:!0}),a.SVGPathSegCurvetoQuadraticRel=function(a,b,c,d,e){SVGPathSeg.call(this,SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL,"q",a),this._x=b,this._y=c,this._x1=d,this._y1=e},SVGPathSegCurvetoQuadraticRel.prototype=Object.create(SVGPathSeg.prototype),SVGPathSegCurvetoQuadraticRel.prototype.toString=function(){return"[object SVGPathSegCurvetoQuadraticRel]"},SVGPathSegCurvetoQuadraticRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x1+" "+this._y1+" "+this._x+" "+this._y},SVGPathSegCurvetoQuadraticRel.prototype.clone=function(){return new SVGPathSegCurvetoQuadraticRel(void 0,this._x,this._y,this._x1,this._y1)},Object.defineProperty(SVGPathSegCurvetoQuadraticRel.prototype,"x",{get:function(){return this._x},set:function(a){this._x=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoQuadraticRel.prototype,"y",{get:function(){return this._y},set:function(a){this._y=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoQuadraticRel.prototype,"x1",{get:function(){return this._x1},set:function(a){this._x1=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoQuadraticRel.prototype,"y1",{get:function(){return this._y1},set:function(a){this._y1=a,this._segmentChanged()},enumerable:!0}),a.SVGPathSegArcAbs=function(a,b,c,d,e,f,g,h){SVGPathSeg.call(this,SVGPathSeg.PATHSEG_ARC_ABS,"A",a),this._x=b,this._y=c,this._r1=d,this._r2=e,this._angle=f,this._largeArcFlag=g,this._sweepFlag=h},SVGPathSegArcAbs.prototype=Object.create(SVGPathSeg.prototype),SVGPathSegArcAbs.prototype.toString=function(){return"[object SVGPathSegArcAbs]"},SVGPathSegArcAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._r1+" "+this._r2+" "+this._angle+" "+(this._largeArcFlag?"1":"0")+" "+(this._sweepFlag?"1":"0")+" "+this._x+" "+this._y},SVGPathSegArcAbs.prototype.clone=function(){return new SVGPathSegArcAbs(void 0,this._x,this._y,this._r1,this._r2,this._angle,this._largeArcFlag,this._sweepFlag)},Object.defineProperty(SVGPathSegArcAbs.prototype,"x",{get:function(){return this._x},set:function(a){this._x=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegArcAbs.prototype,"y",{get:function(){return this._y},set:function(a){this._y=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegArcAbs.prototype,"r1",{get:function(){return this._r1},set:function(a){this._r1=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegArcAbs.prototype,"r2",{get:function(){return this._r2},set:function(a){this._r2=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegArcAbs.prototype,"angle",{get:function(){return this._angle},set:function(a){this._angle=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegArcAbs.prototype,"largeArcFlag",{get:function(){return this._largeArcFlag},set:function(a){this._largeArcFlag=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegArcAbs.prototype,"sweepFlag",{get:function(){return this._sweepFlag},set:function(a){this._sweepFlag=a,this._segmentChanged()},enumerable:!0}),a.SVGPathSegArcRel=function(a,b,c,d,e,f,g,h){SVGPathSeg.call(this,SVGPathSeg.PATHSEG_ARC_REL,"a",a),this._x=b,this._y=c,this._r1=d,this._r2=e,this._angle=f,this._largeArcFlag=g,this._sweepFlag=h},SVGPathSegArcRel.prototype=Object.create(SVGPathSeg.prototype),SVGPathSegArcRel.prototype.toString=function(){return"[object SVGPathSegArcRel]"},SVGPathSegArcRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._r1+" "+this._r2+" "+this._angle+" "+(this._largeArcFlag?"1":"0")+" "+(this._sweepFlag?"1":"0")+" "+this._x+" "+this._y},SVGPathSegArcRel.prototype.clone=function(){return new SVGPathSegArcRel(void 0,this._x,this._y,this._r1,this._r2,this._angle,this._largeArcFlag,this._sweepFlag)},Object.defineProperty(SVGPathSegArcRel.prototype,"x",{get:function(){return this._x},set:function(a){this._x=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegArcRel.prototype,"y",{get:function(){return this._y},set:function(a){this._y=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegArcRel.prototype,"r1",{get:function(){return this._r1},set:function(a){this._r1=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegArcRel.prototype,"r2",{get:function(){return this._r2},set:function(a){this._r2=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegArcRel.prototype,"angle",{get:function(){return this._angle},set:function(a){this._angle=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegArcRel.prototype,"largeArcFlag",{get:function(){return this._largeArcFlag},set:function(a){this._largeArcFlag=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegArcRel.prototype,"sweepFlag",{get:function(){return this._sweepFlag},set:function(a){this._sweepFlag=a,this._segmentChanged()},enumerable:!0}),a.SVGPathSegLinetoHorizontalAbs=function(a,b){SVGPathSeg.call(this,SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS,"H",a),this._x=b},SVGPathSegLinetoHorizontalAbs.prototype=Object.create(SVGPathSeg.prototype),SVGPathSegLinetoHorizontalAbs.prototype.toString=function(){return"[object SVGPathSegLinetoHorizontalAbs]"},SVGPathSegLinetoHorizontalAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x},SVGPathSegLinetoHorizontalAbs.prototype.clone=function(){return new SVGPathSegLinetoHorizontalAbs(void 0,this._x)},Object.defineProperty(SVGPathSegLinetoHorizontalAbs.prototype,"x",{get:function(){return this._x},set:function(a){this._x=a,this._segmentChanged()},enumerable:!0}),a.SVGPathSegLinetoHorizontalRel=function(a,b){SVGPathSeg.call(this,SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL,"h",a),this._x=b},SVGPathSegLinetoHorizontalRel.prototype=Object.create(SVGPathSeg.prototype),SVGPathSegLinetoHorizontalRel.prototype.toString=function(){return"[object SVGPathSegLinetoHorizontalRel]"},SVGPathSegLinetoHorizontalRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x},SVGPathSegLinetoHorizontalRel.prototype.clone=function(){return new SVGPathSegLinetoHorizontalRel(void 0,this._x)},Object.defineProperty(SVGPathSegLinetoHorizontalRel.prototype,"x",{get:function(){return this._x},set:function(a){this._x=a,this._segmentChanged()},enumerable:!0}),a.SVGPathSegLinetoVerticalAbs=function(a,b){SVGPathSeg.call(this,SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS,"V",a),this._y=b},SVGPathSegLinetoVerticalAbs.prototype=Object.create(SVGPathSeg.prototype),SVGPathSegLinetoVerticalAbs.prototype.toString=function(){return"[object SVGPathSegLinetoVerticalAbs]"},SVGPathSegLinetoVerticalAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._y},SVGPathSegLinetoVerticalAbs.prototype.clone=function(){return new SVGPathSegLinetoVerticalAbs(void 0,this._y)},Object.defineProperty(SVGPathSegLinetoVerticalAbs.prototype,"y",{get:function(){return this._y},set:function(a){this._y=a,this._segmentChanged()},enumerable:!0}),a.SVGPathSegLinetoVerticalRel=function(a,b){ -SVGPathSeg.call(this,SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL,"v",a),this._y=b},SVGPathSegLinetoVerticalRel.prototype=Object.create(SVGPathSeg.prototype),SVGPathSegLinetoVerticalRel.prototype.toString=function(){return"[object SVGPathSegLinetoVerticalRel]"},SVGPathSegLinetoVerticalRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._y},SVGPathSegLinetoVerticalRel.prototype.clone=function(){return new SVGPathSegLinetoVerticalRel(void 0,this._y)},Object.defineProperty(SVGPathSegLinetoVerticalRel.prototype,"y",{get:function(){return this._y},set:function(a){this._y=a,this._segmentChanged()},enumerable:!0}),a.SVGPathSegCurvetoCubicSmoothAbs=function(a,b,c,d,e){SVGPathSeg.call(this,SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS,"S",a),this._x=b,this._y=c,this._x2=d,this._y2=e},SVGPathSegCurvetoCubicSmoothAbs.prototype=Object.create(SVGPathSeg.prototype),SVGPathSegCurvetoCubicSmoothAbs.prototype.toString=function(){return"[object SVGPathSegCurvetoCubicSmoothAbs]"},SVGPathSegCurvetoCubicSmoothAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x2+" "+this._y2+" "+this._x+" "+this._y},SVGPathSegCurvetoCubicSmoothAbs.prototype.clone=function(){return new SVGPathSegCurvetoCubicSmoothAbs(void 0,this._x,this._y,this._x2,this._y2)},Object.defineProperty(SVGPathSegCurvetoCubicSmoothAbs.prototype,"x",{get:function(){return this._x},set:function(a){this._x=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoCubicSmoothAbs.prototype,"y",{get:function(){return this._y},set:function(a){this._y=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoCubicSmoothAbs.prototype,"x2",{get:function(){return this._x2},set:function(a){this._x2=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoCubicSmoothAbs.prototype,"y2",{get:function(){return this._y2},set:function(a){this._y2=a,this._segmentChanged()},enumerable:!0}),a.SVGPathSegCurvetoCubicSmoothRel=function(a,b,c,d,e){SVGPathSeg.call(this,SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL,"s",a),this._x=b,this._y=c,this._x2=d,this._y2=e},SVGPathSegCurvetoCubicSmoothRel.prototype=Object.create(SVGPathSeg.prototype),SVGPathSegCurvetoCubicSmoothRel.prototype.toString=function(){return"[object SVGPathSegCurvetoCubicSmoothRel]"},SVGPathSegCurvetoCubicSmoothRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x2+" "+this._y2+" "+this._x+" "+this._y},SVGPathSegCurvetoCubicSmoothRel.prototype.clone=function(){return new SVGPathSegCurvetoCubicSmoothRel(void 0,this._x,this._y,this._x2,this._y2)},Object.defineProperty(SVGPathSegCurvetoCubicSmoothRel.prototype,"x",{get:function(){return this._x},set:function(a){this._x=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoCubicSmoothRel.prototype,"y",{get:function(){return this._y},set:function(a){this._y=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoCubicSmoothRel.prototype,"x2",{get:function(){return this._x2},set:function(a){this._x2=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoCubicSmoothRel.prototype,"y2",{get:function(){return this._y2},set:function(a){this._y2=a,this._segmentChanged()},enumerable:!0}),a.SVGPathSegCurvetoQuadraticSmoothAbs=function(a,b,c){SVGPathSeg.call(this,SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS,"T",a),this._x=b,this._y=c},SVGPathSegCurvetoQuadraticSmoothAbs.prototype=Object.create(SVGPathSeg.prototype),SVGPathSegCurvetoQuadraticSmoothAbs.prototype.toString=function(){return"[object SVGPathSegCurvetoQuadraticSmoothAbs]"},SVGPathSegCurvetoQuadraticSmoothAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x+" "+this._y},SVGPathSegCurvetoQuadraticSmoothAbs.prototype.clone=function(){return new SVGPathSegCurvetoQuadraticSmoothAbs(void 0,this._x,this._y)},Object.defineProperty(SVGPathSegCurvetoQuadraticSmoothAbs.prototype,"x",{get:function(){return this._x},set:function(a){this._x=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoQuadraticSmoothAbs.prototype,"y",{get:function(){return this._y},set:function(a){this._y=a,this._segmentChanged()},enumerable:!0}),a.SVGPathSegCurvetoQuadraticSmoothRel=function(a,b,c){SVGPathSeg.call(this,SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL,"t",a),this._x=b,this._y=c},SVGPathSegCurvetoQuadraticSmoothRel.prototype=Object.create(SVGPathSeg.prototype),SVGPathSegCurvetoQuadraticSmoothRel.prototype.toString=function(){return"[object SVGPathSegCurvetoQuadraticSmoothRel]"},SVGPathSegCurvetoQuadraticSmoothRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x+" "+this._y},SVGPathSegCurvetoQuadraticSmoothRel.prototype.clone=function(){return new SVGPathSegCurvetoQuadraticSmoothRel(void 0,this._x,this._y)},Object.defineProperty(SVGPathSegCurvetoQuadraticSmoothRel.prototype,"x",{get:function(){return this._x},set:function(a){this._x=a,this._segmentChanged()},enumerable:!0}),Object.defineProperty(SVGPathSegCurvetoQuadraticSmoothRel.prototype,"y",{get:function(){return this._y},set:function(a){this._y=a,this._segmentChanged()},enumerable:!0}),SVGPathElement.prototype.createSVGPathSegClosePath=function(){return new SVGPathSegClosePath(void 0)},SVGPathElement.prototype.createSVGPathSegMovetoAbs=function(a,b){return new SVGPathSegMovetoAbs(void 0,a,b)},SVGPathElement.prototype.createSVGPathSegMovetoRel=function(a,b){return new SVGPathSegMovetoRel(void 0,a,b)},SVGPathElement.prototype.createSVGPathSegLinetoAbs=function(a,b){return new SVGPathSegLinetoAbs(void 0,a,b)},SVGPathElement.prototype.createSVGPathSegLinetoRel=function(a,b){return new SVGPathSegLinetoRel(void 0,a,b)},SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs=function(a,b,c,d,e,f){return new SVGPathSegCurvetoCubicAbs(void 0,a,b,c,d,e,f)},SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel=function(a,b,c,d,e,f){return new SVGPathSegCurvetoCubicRel(void 0,a,b,c,d,e,f)},SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs=function(a,b,c,d){return new SVGPathSegCurvetoQuadraticAbs(void 0,a,b,c,d)},SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel=function(a,b,c,d){return new SVGPathSegCurvetoQuadraticRel(void 0,a,b,c,d)},SVGPathElement.prototype.createSVGPathSegArcAbs=function(a,b,c,d,e,f,g){return new SVGPathSegArcAbs(void 0,a,b,c,d,e,f,g)},SVGPathElement.prototype.createSVGPathSegArcRel=function(a,b,c,d,e,f,g){return new SVGPathSegArcRel(void 0,a,b,c,d,e,f,g)},SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs=function(a){return new SVGPathSegLinetoHorizontalAbs(void 0,a)},SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel=function(a){return new SVGPathSegLinetoHorizontalRel(void 0,a)},SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs=function(a){return new SVGPathSegLinetoVerticalAbs(void 0,a)},SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel=function(a){return new SVGPathSegLinetoVerticalRel(void 0,a)},SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs=function(a,b,c,d){return new SVGPathSegCurvetoCubicSmoothAbs(void 0,a,b,c,d)},SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel=function(a,b,c,d){return new SVGPathSegCurvetoCubicSmoothRel(void 0,a,b,c,d)},SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs=function(a,b){return new SVGPathSegCurvetoQuadraticSmoothAbs(void 0,a,b)},SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel=function(a,b){return new SVGPathSegCurvetoQuadraticSmoothRel(void 0,a,b)}),"SVGPathSegList"in a||(a.SVGPathSegList=function(a){this._pathElement=a,this._list=this._parsePath(this._pathElement.getAttribute("d")),this._mutationObserverConfig={attributes:!0,attributeFilter:["d"]},this._pathElementMutationObserver=new MutationObserver(this._updateListFromPathMutations.bind(this)),this._pathElementMutationObserver.observe(this._pathElement,this._mutationObserverConfig)},Object.defineProperty(SVGPathSegList.prototype,"numberOfItems",{get:function(){return this._checkPathSynchronizedToList(),this._list.length},enumerable:!0}),Object.defineProperty(SVGPathElement.prototype,"pathSegList",{get:function(){return this._pathSegList||(this._pathSegList=new SVGPathSegList(this)),this._pathSegList},enumerable:!0}),Object.defineProperty(SVGPathElement.prototype,"normalizedPathSegList",{get:function(){return this.pathSegList},enumerable:!0}),Object.defineProperty(SVGPathElement.prototype,"animatedPathSegList",{get:function(){return this.pathSegList},enumerable:!0}),Object.defineProperty(SVGPathElement.prototype,"animatedNormalizedPathSegList",{get:function(){return this.pathSegList},enumerable:!0}),SVGPathSegList.prototype._checkPathSynchronizedToList=function(){this._updateListFromPathMutations(this._pathElementMutationObserver.takeRecords())},SVGPathSegList.prototype._updateListFromPathMutations=function(a){if(this._pathElement){var b=!1;a.forEach(function(a){"d"==a.attributeName&&(b=!0)}),b&&(this._list=this._parsePath(this._pathElement.getAttribute("d")))}},SVGPathSegList.prototype._writeListToPath=function(){this._pathElementMutationObserver.disconnect(),this._pathElement.setAttribute("d",SVGPathSegList._pathSegArrayAsString(this._list)),this._pathElementMutationObserver.observe(this._pathElement,this._mutationObserverConfig)},SVGPathSegList.prototype.segmentChanged=function(a){this._writeListToPath()},SVGPathSegList.prototype.clear=function(){this._checkPathSynchronizedToList(),this._list.forEach(function(a){a._owningPathSegList=null}),this._list=[],this._writeListToPath()},SVGPathSegList.prototype.initialize=function(a){return this._checkPathSynchronizedToList(),this._list=[a],a._owningPathSegList=this,this._writeListToPath(),a},SVGPathSegList.prototype._checkValidIndex=function(a){if(isNaN(a)||0>a||a>=this.numberOfItems)throw"INDEX_SIZE_ERR"},SVGPathSegList.prototype.getItem=function(a){return this._checkPathSynchronizedToList(),this._checkValidIndex(a),this._list[a]},SVGPathSegList.prototype.insertItemBefore=function(a,b){return this._checkPathSynchronizedToList(),b>this.numberOfItems&&(b=this.numberOfItems),a._owningPathSegList&&(a=a.clone()),this._list.splice(b,0,a),a._owningPathSegList=this,this._writeListToPath(),a},SVGPathSegList.prototype.replaceItem=function(a,b){return this._checkPathSynchronizedToList(),a._owningPathSegList&&(a=a.clone()),this._checkValidIndex(b),this._list[b]=a,a._owningPathSegList=this,this._writeListToPath(),a},SVGPathSegList.prototype.removeItem=function(a){this._checkPathSynchronizedToList(),this._checkValidIndex(a);var b=this._list[a];return this._list.splice(a,1),this._writeListToPath(),b},SVGPathSegList.prototype.appendItem=function(a){return this._checkPathSynchronizedToList(),a._owningPathSegList&&(a=a.clone()),this._list.push(a),a._owningPathSegList=this,this._writeListToPath(),a},SVGPathSegList._pathSegArrayAsString=function(a){var b="",c=!0;return a.forEach(function(a){c?(c=!1,b+=a._asPathString()):b+=" "+a._asPathString()}),b},SVGPathSegList.prototype._parsePath=function(a){if(!a||0==a.length)return[];var b=this,c=function(){this.pathSegList=[]};c.prototype.appendSegment=function(a){this.pathSegList.push(a)};var d=function(a){this._string=a,this._currentIndex=0,this._endIndex=this._string.length,this._previousCommand=SVGPathSeg.PATHSEG_UNKNOWN,this._skipOptionalSpaces()};d.prototype._isCurrentSpace=function(){var a=this._string[this._currentIndex];return" ">=a&&(" "==a||"\n"==a||" "==a||"\r"==a||"\f"==a)},d.prototype._skipOptionalSpaces=function(){for(;this._currentIndex<this._endIndex&&this._isCurrentSpace();)this._currentIndex++;return this._currentIndex<this._endIndex},d.prototype._skipOptionalSpacesOrDelimiter=function(){return this._currentIndex<this._endIndex&&!this._isCurrentSpace()&&","!=this._string.charAt(this._currentIndex)?!1:(this._skipOptionalSpaces()&&this._currentIndex<this._endIndex&&","==this._string.charAt(this._currentIndex)&&(this._currentIndex++,this._skipOptionalSpaces()),this._currentIndex<this._endIndex)},d.prototype.hasMoreData=function(){return this._currentIndex<this._endIndex},d.prototype.peekSegmentType=function(){var a=this._string[this._currentIndex];return this._pathSegTypeFromChar(a)},d.prototype._pathSegTypeFromChar=function(a){switch(a){case"Z":case"z":return SVGPathSeg.PATHSEG_CLOSEPATH;case"M":return SVGPathSeg.PATHSEG_MOVETO_ABS;case"m":return SVGPathSeg.PATHSEG_MOVETO_REL;case"L":return SVGPathSeg.PATHSEG_LINETO_ABS;case"l":return SVGPathSeg.PATHSEG_LINETO_REL;case"C":return SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS;case"c":return SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL;case"Q":return SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS;case"q":return SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL;case"A":return SVGPathSeg.PATHSEG_ARC_ABS;case"a":return SVGPathSeg.PATHSEG_ARC_REL;case"H":return SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS;case"h":return SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL;case"V":return SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS;case"v":return SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL;case"S":return SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS;case"s":return SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL;case"T":return SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS;case"t":return SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL;default:return SVGPathSeg.PATHSEG_UNKNOWN}},d.prototype._nextCommandHelper=function(a,b){return("+"==a||"-"==a||"."==a||a>="0"&&"9">=a)&&b!=SVGPathSeg.PATHSEG_CLOSEPATH?b==SVGPathSeg.PATHSEG_MOVETO_ABS?SVGPathSeg.PATHSEG_LINETO_ABS:b==SVGPathSeg.PATHSEG_MOVETO_REL?SVGPathSeg.PATHSEG_LINETO_REL:b:SVGPathSeg.PATHSEG_UNKNOWN},d.prototype.initialCommandIsMoveTo=function(){if(!this.hasMoreData())return!0;var a=this.peekSegmentType();return a==SVGPathSeg.PATHSEG_MOVETO_ABS||a==SVGPathSeg.PATHSEG_MOVETO_REL},d.prototype._parseNumber=function(){var a=0,b=0,c=1,d=0,e=1,f=1,g=this._currentIndex;if(this._skipOptionalSpaces(),this._currentIndex<this._endIndex&&"+"==this._string.charAt(this._currentIndex)?this._currentIndex++:this._currentIndex<this._endIndex&&"-"==this._string.charAt(this._currentIndex)&&(this._currentIndex++,e=-1),!(this._currentIndex==this._endIndex||(this._string.charAt(this._currentIndex)<"0"||this._string.charAt(this._currentIndex)>"9")&&"."!=this._string.charAt(this._currentIndex))){for(var h=this._currentIndex;this._currentIndex<this._endIndex&&this._string.charAt(this._currentIndex)>="0"&&this._string.charAt(this._currentIndex)<="9";)this._currentIndex++;if(this._currentIndex!=h)for(var i=this._currentIndex-1,j=1;i>=h;)b+=j*(this._string.charAt(i--)-"0"),j*=10;if(this._currentIndex<this._endIndex&&"."==this._string.charAt(this._currentIndex)){if(this._currentIndex++,this._currentIndex>=this._endIndex||this._string.charAt(this._currentIndex)<"0"||this._string.charAt(this._currentIndex)>"9")return;for(;this._currentIndex<this._endIndex&&this._string.charAt(this._currentIndex)>="0"&&this._string.charAt(this._currentIndex)<="9";)d+=(this._string.charAt(this._currentIndex++)-"0")*(c*=.1)}if(this._currentIndex!=g&&this._currentIndex+1<this._endIndex&&("e"==this._string.charAt(this._currentIndex)||"E"==this._string.charAt(this._currentIndex))&&"x"!=this._string.charAt(this._currentIndex+1)&&"m"!=this._string.charAt(this._currentIndex+1)){if(this._currentIndex++,"+"==this._string.charAt(this._currentIndex)?this._currentIndex++:"-"==this._string.charAt(this._currentIndex)&&(this._currentIndex++,f=-1),this._currentIndex>=this._endIndex||this._string.charAt(this._currentIndex)<"0"||this._string.charAt(this._currentIndex)>"9")return;for(;this._currentIndex<this._endIndex&&this._string.charAt(this._currentIndex)>="0"&&this._string.charAt(this._currentIndex)<="9";)a*=10,a+=this._string.charAt(this._currentIndex)-"0",this._currentIndex++}var k=b+d;if(k*=e,a&&(k*=Math.pow(10,f*a)),g!=this._currentIndex)return this._skipOptionalSpacesOrDelimiter(),k}},d.prototype._parseArcFlag=function(){if(!(this._currentIndex>=this._endIndex)){var a=!1,b=this._string.charAt(this._currentIndex++);if("0"==b)a=!1;else{if("1"!=b)return;a=!0}return this._skipOptionalSpacesOrDelimiter(),a}},d.prototype.parseSegment=function(){var a=this._string[this._currentIndex],c=this._pathSegTypeFromChar(a);if(c==SVGPathSeg.PATHSEG_UNKNOWN){if(this._previousCommand==SVGPathSeg.PATHSEG_UNKNOWN)return null;if(c=this._nextCommandHelper(a,this._previousCommand),c==SVGPathSeg.PATHSEG_UNKNOWN)return null}else this._currentIndex++;switch(this._previousCommand=c,c){case SVGPathSeg.PATHSEG_MOVETO_REL:return new SVGPathSegMovetoRel(b,this._parseNumber(),this._parseNumber());case SVGPathSeg.PATHSEG_MOVETO_ABS:return new SVGPathSegMovetoAbs(b,this._parseNumber(),this._parseNumber());case SVGPathSeg.PATHSEG_LINETO_REL:return new SVGPathSegLinetoRel(b,this._parseNumber(),this._parseNumber());case SVGPathSeg.PATHSEG_LINETO_ABS:return new SVGPathSegLinetoAbs(b,this._parseNumber(),this._parseNumber());case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:return new SVGPathSegLinetoHorizontalRel(b,this._parseNumber());case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:return new SVGPathSegLinetoHorizontalAbs(b,this._parseNumber());case SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:return new SVGPathSegLinetoVerticalRel(b,this._parseNumber());case SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:return new SVGPathSegLinetoVerticalAbs(b,this._parseNumber());case SVGPathSeg.PATHSEG_CLOSEPATH:return this._skipOptionalSpaces(),new SVGPathSegClosePath(b);case SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:var d={x1:this._parseNumber(),y1:this._parseNumber(),x2:this._parseNumber(),y2:this._parseNumber(),x:this._parseNumber(),y:this._parseNumber()};return new SVGPathSegCurvetoCubicRel(b,d.x,d.y,d.x1,d.y1,d.x2,d.y2);case SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:var d={x1:this._parseNumber(),y1:this._parseNumber(),x2:this._parseNumber(),y2:this._parseNumber(),x:this._parseNumber(),y:this._parseNumber()};return new SVGPathSegCurvetoCubicAbs(b,d.x,d.y,d.x1,d.y1,d.x2,d.y2);case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:var d={x2:this._parseNumber(),y2:this._parseNumber(),x:this._parseNumber(),y:this._parseNumber()};return new SVGPathSegCurvetoCubicSmoothRel(b,d.x,d.y,d.x2,d.y2);case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:var d={x2:this._parseNumber(),y2:this._parseNumber(),x:this._parseNumber(),y:this._parseNumber()};return new SVGPathSegCurvetoCubicSmoothAbs(b,d.x,d.y,d.x2,d.y2);case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:var d={x1:this._parseNumber(),y1:this._parseNumber(),x:this._parseNumber(),y:this._parseNumber()};return new SVGPathSegCurvetoQuadraticRel(b,d.x,d.y,d.x1,d.y1);case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:var d={x1:this._parseNumber(),y1:this._parseNumber(),x:this._parseNumber(),y:this._parseNumber()};return new SVGPathSegCurvetoQuadraticAbs(b,d.x,d.y,d.x1,d.y1);case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:return new SVGPathSegCurvetoQuadraticSmoothRel(b,this._parseNumber(),this._parseNumber());case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:return new SVGPathSegCurvetoQuadraticSmoothAbs(b,this._parseNumber(),this._parseNumber());case SVGPathSeg.PATHSEG_ARC_REL:var d={x1:this._parseNumber(),y1:this._parseNumber(),arcAngle:this._parseNumber(),arcLarge:this._parseArcFlag(),arcSweep:this._parseArcFlag(),x:this._parseNumber(),y:this._parseNumber()};return new SVGPathSegArcRel(b,d.x,d.y,d.x1,d.y1,d.arcAngle,d.arcLarge,d.arcSweep);case SVGPathSeg.PATHSEG_ARC_ABS:var d={x1:this._parseNumber(),y1:this._parseNumber(),arcAngle:this._parseNumber(),arcLarge:this._parseArcFlag(),arcSweep:this._parseArcFlag(),x:this._parseNumber(),y:this._parseNumber()};return new SVGPathSegArcAbs(b,d.x,d.y,d.x1,d.y1,d.arcAngle,d.arcLarge,d.arcSweep);default:throw"Unknown path seg type."}};var e=new c,f=new d(a);if(!f.initialCommandIsMoveTo())return[];for(;f.hasMoreData();){var g=f.parseSegment();if(!g)return[];e.appendSegment(g)}return e.pathSegList})}(),"function"==typeof define&&define.amd?define("c3",["d3"],function(){return k}):"undefined"!=typeof exports&&"undefined"!=typeof module?module.exports=k:a.c3=k}(window); +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.c3=e()}(this,function(){"use strict";function t(t,e){function i(t,e){t.attr("transform",function(t){return"translate("+Math.ceil(e(t)+w)+", 0)"})}function n(t,e){t.attr("transform",function(t){return"translate(0,"+Math.ceil(e(t))+")"})}function a(t){var e=t[0],i=t[t.length-1];return e<i?[e,i]:[i,e]}function o(t){var e,i,n=[];if(t.ticks)return t.ticks.apply(t,p);for(i=t.domain(),e=Math.ceil(i[0]);e<i[1];e++)n.push(e);return n.length>0&&n[0]>0&&n.unshift(n[0]-(n[1]-n[0])),n}function s(){var t,i=_.copy();return e.isCategory&&(t=_.domain(),i.domain([t[0],t[1]-1])),i}function c(t){var e=g?g(t):t;return void 0!==e?e:""}function d(t){if(r)return r;var e={h:11.5,w:5.5};return t.select("text").text(c).each(function(t){var i=this.getBoundingClientRect(),n=c(t),a=i.height,r=n?i.width/n.length:void 0;a&&r&&(e.h=a,e.w=r)}).text(""),r=e,e}function l(i){return e.withoutTransition?i:t.transition(i)}function u(r){r.each(function(){function r(t,i){function n(t,e){r=void 0;for(var s=1;s<e.length;s++)if(" "===e.charAt(s)&&(r=s),a=e.substr(0,s+1),o=z.w*a.length,i<o)return n(t.concat(e.substr(0,r||s)),e.slice(r?r+1:s));return t.concat(e)}var a,r,o,s=c(t),d=[];return"[object Array]"===Object.prototype.toString.call(s)?s:((!i||i<=0)&&(i=Y?95:e.isCategory?Math.ceil(P(L[1])-P(L[0]))-12:110),n(d,s+""))}function g(t,e){var i=z.h;return 0===e&&(i="left"===x||"right"===x?-((B[t.index]-1)*(z.h/2)-3):".71em"),i}var p,v,b,A=u.g=t.select(this),T=this.__chart__||_,P=this.__chart__=s(),L=S||o(P),C=A.selectAll(".tick").data(L,P),V=C.enter().insert("g",".domain").attr("class","tick").style("opacity",1e-6),G=C.exit().remove(),E=l(C).style("opacity",1),I=_.rangeExtent?_.rangeExtent():a(_.range()),O=A.selectAll(".domain").data([0]),R=(O.enter().append("path").attr("class","domain"),l(O));V.append("line"),V.append("text");var D=V.select("line"),F=E.select("line"),X=V.select("text"),k=E.select("text");e.isCategory?(w=Math.ceil((P(1)-P(0))/2),v=f?0:w,b=f?w:0):w=v=0;var M,H,z=d(A.select(".tick")),B=[],N=Math.max(y,0)+m,Y="left"===x||"right"===x;(H=(M=C.select("text")).selectAll("tspan").data(function(t,i){var n=e.tickMultiline?r(t,e.tickWidth):[].concat(c(t));return B[i]=n.length,n.map(function(t){return{index:i,splitted:t}})})).enter().append("tspan"),H.exit().remove(),H.text(function(t){return t.splitted});var j=e.tickTextRotate;switch(x){case"bottom":p=i,D.attr("y2",y),X.attr("y",N),F.attr("x1",v).attr("x2",v).attr("y2",function(t){var e=_(t)+(f?0:w);return I[0]<e&&e<I[1]?y:0}),k.attr("x",0).attr("y",function(t){return t?11.5-t/15*2.5*(t>0?1:-1):N}(j)).style("text-anchor",function(t){return t?t>0?"start":"end":"middle"}(j)).attr("transform",function(t){return t?"rotate("+t+")":""}(j)),H.attr("x",0).attr("dy",g).attr("dx",function(t){return t?8*Math.sin(Math.PI*(t/180)):0}(j)),R.attr("d","M"+I[0]+","+h+"V0H"+I[1]+"V"+h);break;case"top":p=i,D.attr("y2",-y),X.attr("y",-N),F.attr("x2",0).attr("y2",-y),k.attr("x",0).attr("y",-N),M.style("text-anchor","middle"),H.attr("x",0).attr("dy","0em"),R.attr("d","M"+I[0]+","+-h+"V0H"+I[1]+"V"+-h);break;case"left":p=n,D.attr("x2",-y),X.attr("x",-N),F.attr("x2",-y).attr("y1",b).attr("y2",b),k.attr("x",-N).attr("y",w),M.style("text-anchor","end"),H.attr("x",-N).attr("dy",g),R.attr("d","M"+-h+","+I[0]+"H0V"+I[1]+"H"+-h);break;case"right":p=n,D.attr("x2",y),X.attr("x",N),F.attr("x2",y).attr("y2",0),k.attr("x",N).attr("y",0),M.style("text-anchor","start"),H.attr("x",N).attr("dy",g),R.attr("d","M"+h+","+I[0]+"H0V"+I[1]+"H"+h)}if(P.rangeBand){var U=P,W=U.rangeBand()/2;T=P=function(t){return U(t)+W}}else T.rangeBand?T=P:G.call(p,P);V.call(p,T),E.call(p,P)})}var h,g,p,f,_=t.scale.linear(),x="bottom",y=6,m=3,S=null,w=0,v=!0;return e=e||{},h=e.withOuterTick?6:0,u.scale=function(t){return arguments.length?(_=t,u):_},u.orient=function(t){return arguments.length?(x=t in{top:1,right:1,bottom:1,left:1}?t+"":"bottom",u):x},u.tickFormat=function(t){return arguments.length?(g=t,u):g},u.tickCentered=function(t){return arguments.length?(f=t,u):f},u.tickOffset=function(){return w},u.tickInterval=function(){var t;return t=e.isCategory?2*w:(u.g.select("path.domain").node().getTotalLength()-2*h)/u.g.selectAll("line").size(),t===1/0?0:t},u.ticks=function(){return arguments.length?(p=arguments,u):p},u.tickCulling=function(t){return arguments.length?(v=t,u):v},u.tickValues=function(t){if("function"==typeof t)S=function(){return t(_.domain())};else{if(!arguments.length)return S;S=t}return u},u}function e(t){i.call(this,t)}function i(t){this.owner=t}function n(t){var e=this.internal=new a(this);e.loadConfig(t),e.beforeInit(t),e.init(),e.afterInit(t),function t(e,i,n){Object.keys(e).forEach(function(a){i[a]=e[a].bind(n),Object.keys(e[a]).length>0&&t(e[a],i[a],n)})}(v,this,this)}function a(t){var e=this;e.d3=window.d3?window.d3:"undefined"!=typeof require?require("d3"):void 0,e.api=t,e.config=e.getDefaultConfig(),e.data={},e.cache={},e.axes={}}var r,o={target:"c3-target",chart:"c3-chart",chartLine:"c3-chart-line",chartLines:"c3-chart-lines",chartBar:"c3-chart-bar",chartBars:"c3-chart-bars",chartText:"c3-chart-text",chartTexts:"c3-chart-texts",chartArc:"c3-chart-arc",chartArcs:"c3-chart-arcs",chartArcsTitle:"c3-chart-arcs-title",chartArcsBackground:"c3-chart-arcs-background",chartArcsGaugeUnit:"c3-chart-arcs-gauge-unit",chartArcsGaugeMax:"c3-chart-arcs-gauge-max",chartArcsGaugeMin:"c3-chart-arcs-gauge-min",selectedCircle:"c3-selected-circle",selectedCircles:"c3-selected-circles",eventRect:"c3-event-rect",eventRects:"c3-event-rects",eventRectsSingle:"c3-event-rects-single",eventRectsMultiple:"c3-event-rects-multiple",zoomRect:"c3-zoom-rect",brush:"c3-brush",focused:"c3-focused",defocused:"c3-defocused",region:"c3-region",regions:"c3-regions",title:"c3-title",tooltipContainer:"c3-tooltip-container",tooltip:"c3-tooltip",tooltipName:"c3-tooltip-name",shape:"c3-shape",shapes:"c3-shapes",line:"c3-line",lines:"c3-lines",bar:"c3-bar",bars:"c3-bars",circle:"c3-circle",circles:"c3-circles",arc:"c3-arc",arcs:"c3-arcs",area:"c3-area",areas:"c3-areas",empty:"c3-empty",text:"c3-text",texts:"c3-texts",gaugeValue:"c3-gauge-value",grid:"c3-grid",gridLines:"c3-grid-lines",xgrid:"c3-xgrid",xgrids:"c3-xgrids",xgridLine:"c3-xgrid-line",xgridLines:"c3-xgrid-lines",xgridFocus:"c3-xgrid-focus",ygrid:"c3-ygrid",ygrids:"c3-ygrids",ygridLine:"c3-ygrid-line",ygridLines:"c3-ygrid-lines",axis:"c3-axis",axisX:"c3-axis-x",axisXLabel:"c3-axis-x-label",axisY:"c3-axis-y",axisYLabel:"c3-axis-y-label",axisY2:"c3-axis-y2",axisY2Label:"c3-axis-y2-label",legendBackground:"c3-legend-background",legendItem:"c3-legend-item",legendItemEvent:"c3-legend-item-event",legendItemTile:"c3-legend-item-tile",legendItemHidden:"c3-legend-item-hidden",legendItemFocused:"c3-legend-item-focused",dragarea:"c3-dragarea",EXPANDED:"_expanded_",SELECTED:"_selected_",INCLUDED:"_included_"},s=function(t){return t||0===t},c=function(t){return"function"==typeof t},d=function(t){return Array.isArray(t)},l=function(t){return"string"==typeof t},u=function(t){return void 0===t},h=function(t){return void 0!==t},g=function(t){return 10*Math.ceil(t/10)},p=function(t){return Math.ceil(t)+.5},f=function(t){return t[1]-t[0]},_=function(t){return void 0===t||null===t||l(t)&&0===t.length||"object"==typeof t&&0===Object.keys(t).length},x=function(t){return!b.isEmpty(t)},y=function(t,e,i){return void 0!==t[e]?t[e]:i},m=function(t,e){var i=!1;return Object.keys(t).forEach(function(n){t[n]===e&&(i=!0)}),i},S=function(t){return"string"==typeof t?t.replace(/</g,"<").replace(/>/g,">"):t},w=function(t){var e=t.getBoundingClientRect(),i=[t.pathSegList.getItem(0),t.pathSegList.getItem(1)];return{x:i[0].x,y:Math.min(i[0].y,i[1].y),width:e.width,height:e.height}};!function(t,e){if(Object.create)e.prototype=Object.create(t.prototype);else{var i=function(){};i.prototype=t.prototype,e.prototype=new i}e.prototype.constructor=e}(i,e),e.prototype.init=function(){var t=this.owner,e=t.config,i=t.main;t.axes.x=i.append("g").attr("class",o.axis+" "+o.axisX).attr("clip-path",t.clipPathForXAxis).attr("transform",t.getTranslate("x")).style("visibility",e.axis_x_show?"visible":"hidden"),t.axes.x.append("text").attr("class",o.axisXLabel).attr("transform",e.axis_rotated?"rotate(-90)":"").style("text-anchor",this.textAnchorForXAxisLabel.bind(this)),t.axes.y=i.append("g").attr("class",o.axis+" "+o.axisY).attr("clip-path",e.axis_y_inner?"":t.clipPathForYAxis).attr("transform",t.getTranslate("y")).style("visibility",e.axis_y_show?"visible":"hidden"),t.axes.y.append("text").attr("class",o.axisYLabel).attr("transform",e.axis_rotated?"":"rotate(-90)").style("text-anchor",this.textAnchorForYAxisLabel.bind(this)),t.axes.y2=i.append("g").attr("class",o.axis+" "+o.axisY2).attr("transform",t.getTranslate("y2")).style("visibility",e.axis_y2_show?"visible":"hidden"),t.axes.y2.append("text").attr("class",o.axisY2Label).attr("transform",e.axis_rotated?"":"rotate(-90)").style("text-anchor",this.textAnchorForY2AxisLabel.bind(this))},e.prototype.getXAxis=function(e,i,n,a,r,o,s){var c=this.owner,d=c.config,l={isCategory:c.isCategorized(),withOuterTick:r,tickMultiline:d.axis_x_tick_multiline,tickWidth:d.axis_x_tick_width,tickTextRotate:s?0:d.axis_x_tick_rotate,withoutTransition:o},u=t(c.d3,l).scale(e).orient(i);return c.isTimeSeries()&&a&&"function"!=typeof a&&(a=a.map(function(t){return c.parseDate(t)})),u.tickFormat(n).tickValues(a),c.isCategorized()&&(u.tickCentered(d.axis_x_tick_centered),_(d.axis_x_tick_culling)&&(d.axis_x_tick_culling=!1)),u},e.prototype.updateXAxisTickValues=function(t,e){var i,n=this.owner,a=n.config;return(a.axis_x_tick_fit||a.axis_x_tick_count)&&(i=this.generateTickValues(n.mapTargetsToUniqueXs(t),a.axis_x_tick_count,n.isTimeSeries())),e?e.tickValues(i):(n.xAxis.tickValues(i),n.subXAxis.tickValues(i)),i},e.prototype.getYAxis=function(e,i,n,a,r,o,s){var c=this.owner,d=c.config,l={withOuterTick:r,withoutTransition:o,tickTextRotate:s?0:d.axis_y_tick_rotate},u=t(c.d3,l).scale(e).orient(i).tickFormat(n);return c.isTimeSeriesY()?u.ticks(c.d3.time[d.axis_y_tick_time_value],d.axis_y_tick_time_interval):u.tickValues(a),u},e.prototype.getId=function(t){var e=this.owner.config;return t in e.data_axes?e.data_axes[t]:"y"},e.prototype.getXAxisTickFormat=function(){var t=this.owner,e=t.config,i=t.isTimeSeries()?t.defaultAxisTimeFormat:t.isCategorized()?t.categoryName:function(t){return t<0?t.toFixed(0):t};return e.axis_x_tick_format&&(c(e.axis_x_tick_format)?i=e.axis_x_tick_format:t.isTimeSeries()&&(i=function(i){return i?t.axisTimeFormat(e.axis_x_tick_format)(i):""})),c(i)?function(e){return i.call(t,e)}:i},e.prototype.getTickValues=function(t,e){return t||(e?e.tickValues():void 0)},e.prototype.getXAxisTickValues=function(){return this.getTickValues(this.owner.config.axis_x_tick_values,this.owner.xAxis)},e.prototype.getYAxisTickValues=function(){return this.getTickValues(this.owner.config.axis_y_tick_values,this.owner.yAxis)},e.prototype.getY2AxisTickValues=function(){return this.getTickValues(this.owner.config.axis_y2_tick_values,this.owner.y2Axis)},e.prototype.getLabelOptionByAxisId=function(t){var e,i=this.owner.config;return"y"===t?e=i.axis_y_label:"y2"===t?e=i.axis_y2_label:"x"===t&&(e=i.axis_x_label),e},e.prototype.getLabelText=function(t){var e=this.getLabelOptionByAxisId(t);return l(e)?e:e?e.text:null},e.prototype.setLabelText=function(t,e){var i=this.owner.config,n=this.getLabelOptionByAxisId(t);l(n)?"y"===t?i.axis_y_label=e:"y2"===t?i.axis_y2_label=e:"x"===t&&(i.axis_x_label=e):n&&(n.text=e)},e.prototype.getLabelPosition=function(t,e){var i=this.getLabelOptionByAxisId(t),n=i&&"object"==typeof i&&i.position?i.position:e;return{isInner:n.indexOf("inner")>=0,isOuter:n.indexOf("outer")>=0,isLeft:n.indexOf("left")>=0,isCenter:n.indexOf("center")>=0,isRight:n.indexOf("right")>=0,isTop:n.indexOf("top")>=0,isMiddle:n.indexOf("middle")>=0,isBottom:n.indexOf("bottom")>=0}},e.prototype.getXAxisLabelPosition=function(){return this.getLabelPosition("x",this.owner.config.axis_rotated?"inner-top":"inner-right")},e.prototype.getYAxisLabelPosition=function(){return this.getLabelPosition("y",this.owner.config.axis_rotated?"inner-right":"inner-top")},e.prototype.getY2AxisLabelPosition=function(){return this.getLabelPosition("y2",this.owner.config.axis_rotated?"inner-right":"inner-top")},e.prototype.getLabelPositionById=function(t){return"y2"===t?this.getY2AxisLabelPosition():"y"===t?this.getYAxisLabelPosition():this.getXAxisLabelPosition()},e.prototype.textForXAxisLabel=function(){return this.getLabelText("x")},e.prototype.textForYAxisLabel=function(){return this.getLabelText("y")},e.prototype.textForY2AxisLabel=function(){return this.getLabelText("y2")},e.prototype.xForAxisLabel=function(t,e){var i=this.owner;return t?e.isLeft?0:e.isCenter?i.width/2:i.width:e.isBottom?-i.height:e.isMiddle?-i.height/2:0},e.prototype.dxForAxisLabel=function(t,e){return t?e.isLeft?"0.5em":e.isRight?"-0.5em":"0":e.isTop?"-0.5em":e.isBottom?"0.5em":"0"},e.prototype.textAnchorForAxisLabel=function(t,e){return t?e.isLeft?"start":e.isCenter?"middle":"end":e.isBottom?"start":e.isMiddle?"middle":"end"},e.prototype.xForXAxisLabel=function(){return this.xForAxisLabel(!this.owner.config.axis_rotated,this.getXAxisLabelPosition())},e.prototype.xForYAxisLabel=function(){return this.xForAxisLabel(this.owner.config.axis_rotated,this.getYAxisLabelPosition())},e.prototype.xForY2AxisLabel=function(){return this.xForAxisLabel(this.owner.config.axis_rotated,this.getY2AxisLabelPosition())},e.prototype.dxForXAxisLabel=function(){return this.dxForAxisLabel(!this.owner.config.axis_rotated,this.getXAxisLabelPosition())},e.prototype.dxForYAxisLabel=function(){return this.dxForAxisLabel(this.owner.config.axis_rotated,this.getYAxisLabelPosition())},e.prototype.dxForY2AxisLabel=function(){return this.dxForAxisLabel(this.owner.config.axis_rotated,this.getY2AxisLabelPosition())},e.prototype.dyForXAxisLabel=function(){var t=this.owner.config,e=this.getXAxisLabelPosition();return t.axis_rotated?e.isInner?"1.2em":-25-this.getMaxTickWidth("x"):e.isInner?"-0.5em":t.axis_x_height?t.axis_x_height-10:"3em"},e.prototype.dyForYAxisLabel=function(){var t=this.owner,e=this.getYAxisLabelPosition();return t.config.axis_rotated?e.isInner?"-0.5em":"3em":e.isInner?"1.2em":-10-(t.config.axis_y_inner?0:this.getMaxTickWidth("y")+10)},e.prototype.dyForY2AxisLabel=function(){var t=this.owner,e=this.getY2AxisLabelPosition();return t.config.axis_rotated?e.isInner?"1.2em":"-2.2em":e.isInner?"-0.5em":15+(t.config.axis_y2_inner?0:this.getMaxTickWidth("y2")+15)},e.prototype.textAnchorForXAxisLabel=function(){var t=this.owner;return this.textAnchorForAxisLabel(!t.config.axis_rotated,this.getXAxisLabelPosition())},e.prototype.textAnchorForYAxisLabel=function(){var t=this.owner;return this.textAnchorForAxisLabel(t.config.axis_rotated,this.getYAxisLabelPosition())},e.prototype.textAnchorForY2AxisLabel=function(){var t=this.owner;return this.textAnchorForAxisLabel(t.config.axis_rotated,this.getY2AxisLabelPosition())},e.prototype.getMaxTickWidth=function(t,e){var i,n,a,r,o=this.owner,s=o.config,c=0;return e&&o.currentMaxTickWidths[t]?o.currentMaxTickWidths[t]:(o.svg&&(i=o.filterTargetsToShow(o.data.targets),"y"===t?(n=o.y.copy().domain(o.getYDomain(i,"y")),a=this.getYAxis(n,o.yOrient,s.axis_y_tick_format,o.yAxisTickValues,!1,!0,!0)):"y2"===t?(n=o.y2.copy().domain(o.getYDomain(i,"y2")),a=this.getYAxis(n,o.y2Orient,s.axis_y2_tick_format,o.y2AxisTickValues,!1,!0,!0)):(n=o.x.copy().domain(o.getXDomain(i)),a=this.getXAxis(n,o.xOrient,o.xAxisTickFormat,o.xAxisTickValues,!1,!0,!0),this.updateXAxisTickValues(i,a)),(r=o.d3.select("body").append("div").classed("c3",!0)).append("svg").style("visibility","hidden").style("position","fixed").style("top",0).style("left",0).append("g").call(a).each(function(){o.d3.select(this).selectAll("text").each(function(){var t=this.getBoundingClientRect();c<t.width&&(c=t.width)}),r.remove()})),o.currentMaxTickWidths[t]=c<=0?o.currentMaxTickWidths[t]:c,o.currentMaxTickWidths[t])},e.prototype.updateLabels=function(t){var e=this.owner,i=e.main.select("."+o.axisX+" ."+o.axisXLabel),n=e.main.select("."+o.axisY+" ."+o.axisYLabel),a=e.main.select("."+o.axisY2+" ."+o.axisY2Label);(t?i.transition():i).attr("x",this.xForXAxisLabel.bind(this)).attr("dx",this.dxForXAxisLabel.bind(this)).attr("dy",this.dyForXAxisLabel.bind(this)).text(this.textForXAxisLabel.bind(this)),(t?n.transition():n).attr("x",this.xForYAxisLabel.bind(this)).attr("dx",this.dxForYAxisLabel.bind(this)).attr("dy",this.dyForYAxisLabel.bind(this)).text(this.textForYAxisLabel.bind(this)),(t?a.transition():a).attr("x",this.xForY2AxisLabel.bind(this)).attr("dx",this.dxForY2AxisLabel.bind(this)).attr("dy",this.dyForY2AxisLabel.bind(this)).text(this.textForY2AxisLabel.bind(this))},e.prototype.getPadding=function(t,e,i,n){var a="number"==typeof t?t:t[e];return s(a)?"ratio"===t.unit?t[e]*n:this.convertPixelsToAxisPadding(a,n):i},e.prototype.convertPixelsToAxisPadding=function(t,e){var i=this.owner;return e*(t/(i.config.axis_rotated?i.width:i.height))},e.prototype.generateTickValues=function(t,e,i){var n,a,r,o,s,d,l,u=t;if(e)if(1===(n=c(e)?e():e))u=[t[0]];else if(2===n)u=[t[0],t[t.length-1]];else if(n>2){for(o=n-2,a=t[0],s=((r=t[t.length-1])-a)/(o+1),u=[a],d=0;d<o;d++)l=+a+s*(d+1),u.push(i?new Date(l):l);u.push(r)}return i||(u=u.sort(function(t,e){return t-e})),u},e.prototype.generateTransitions=function(t){var e=this.owner.axes;return{axisX:t?e.x.transition().duration(t):e.x,axisY:t?e.y.transition().duration(t):e.y,axisY2:t?e.y2.transition().duration(t):e.y2,axisSubX:t?e.subx.transition().duration(t):e.subx}},e.prototype.redraw=function(t,e){var i=this.owner;i.axes.x.style("opacity",e?0:1),i.axes.y.style("opacity",e?0:1),i.axes.y2.style("opacity",e?0:1),i.axes.subx.style("opacity",e?0:1),t.axisX.call(i.xAxis),t.axisY.call(i.yAxis),t.axisY2.call(i.y2Axis),t.axisSubX.call(i.subXAxis)};var v,b,A={version:"0.4.14"};return A.generate=function(t){return new n(t)},A.chart={fn:n.prototype,internal:{fn:a.prototype,axis:{fn:e.prototype}}},v=A.chart.fn,b=A.chart.internal.fn,A.chart.internal.axis.fn,b.beforeInit=function(){},b.afterInit=function(){},b.init=function(){var t=this,e=t.config;if(t.initParams(),e.data_url)t.convertUrlToData(e.data_url,e.data_mimeType,e.data_headers,e.data_keys,t.initWithData);else if(e.data_json)t.initWithData(t.convertJsonToData(e.data_json,e.data_keys));else if(e.data_rows)t.initWithData(t.convertRowsToData(e.data_rows));else{if(!e.data_columns)throw Error("url or json or rows or columns is required.");t.initWithData(t.convertColumnsToData(e.data_columns))}},b.initParams=function(){var t=this,e=t.d3,i=t.config;t.clipId="c3-"+ +new Date+"-clip",t.clipIdForXAxis=t.clipId+"-xaxis",t.clipIdForYAxis=t.clipId+"-yaxis",t.clipIdForGrid=t.clipId+"-grid",t.clipIdForSubchart=t.clipId+"-subchart",t.clipPath=t.getClipPath(t.clipId),t.clipPathForXAxis=t.getClipPath(t.clipIdForXAxis),t.clipPathForYAxis=t.getClipPath(t.clipIdForYAxis),t.clipPathForGrid=t.getClipPath(t.clipIdForGrid),t.clipPathForSubchart=t.getClipPath(t.clipIdForSubchart),t.dragStart=null,t.dragging=!1,t.flowing=!1,t.cancelClick=!1,t.mouseover=!1,t.transiting=!1,t.color=t.generateColor(),t.levelColor=t.generateLevelColor(),t.dataTimeFormat=i.data_xLocaltime?e.time.format:e.time.format.utc,t.axisTimeFormat=i.axis_x_localtime?e.time.format:e.time.format.utc,t.defaultAxisTimeFormat=t.axisTimeFormat.multi([[".%L",function(t){return t.getMilliseconds()}],[":%S",function(t){return t.getSeconds()}],["%I:%M",function(t){return t.getMinutes()}],["%I %p",function(t){return t.getHours()}],["%-m/%-d",function(t){return t.getDay()&&1!==t.getDate()}],["%-m/%-d",function(t){return 1!==t.getDate()}],["%-m/%-d",function(t){return t.getMonth()}],["%Y/%-m/%-d",function(){return!0}]]),t.hiddenTargetIds=[],t.hiddenLegendIds=[],t.focusedTargetIds=[],t.defocusedTargetIds=[],t.xOrient=i.axis_rotated?"left":"bottom",t.yOrient=i.axis_rotated?i.axis_y_inner?"top":"bottom":i.axis_y_inner?"right":"left",t.y2Orient=i.axis_rotated?i.axis_y2_inner?"bottom":"top":i.axis_y2_inner?"left":"right",t.subXOrient=i.axis_rotated?"left":"bottom",t.isLegendRight="right"===i.legend_position,t.isLegendInset="inset"===i.legend_position,t.isLegendTop="top-left"===i.legend_inset_anchor||"top-right"===i.legend_inset_anchor,t.isLegendLeft="top-left"===i.legend_inset_anchor||"bottom-left"===i.legend_inset_anchor,t.legendStep=0,t.legendItemWidth=0,t.legendItemHeight=0,t.currentMaxTickWidths={x:0,y:0,y2:0},t.rotated_padding_left=30,t.rotated_padding_right=i.axis_rotated&&!i.axis_x_show?0:30,t.rotated_padding_top=5,t.withoutFadeIn={},t.intervalForObserveInserted=void 0,t.axes.subx=e.selectAll([])},b.initChartElements=function(){this.initBar&&this.initBar(),this.initLine&&this.initLine(),this.initArc&&this.initArc(),this.initGauge&&this.initGauge(),this.initText&&this.initText()},b.initWithData=function(t){var i,n,a=this,r=a.d3,s=a.config,c=!0;a.axis=new e(a),a.initPie&&a.initPie(),a.initBrush&&a.initBrush(),a.initZoom&&a.initZoom(),s.bindto?"function"==typeof s.bindto.node?a.selectChart=s.bindto:a.selectChart=r.select(s.bindto):a.selectChart=r.selectAll([]),a.selectChart.empty()&&(a.selectChart=r.select(document.createElement("div")).style("opacity",0),a.observeInserted(a.selectChart),c=!1),a.selectChart.html("").classed("c3",!0),a.data.xs={},a.data.targets=a.convertDataToTargets(t),s.data_filter&&(a.data.targets=a.data.targets.filter(s.data_filter)),s.data_hide&&a.addHiddenTargetIds(!0===s.data_hide?a.mapToIds(a.data.targets):s.data_hide),s.legend_hide&&a.addHiddenLegendIds(!0===s.legend_hide?a.mapToIds(a.data.targets):s.legend_hide),a.hasType("gauge")&&(s.legend_show=!1),a.updateSizes(),a.updateScales(),a.x.domain(r.extent(a.getXDomain(a.data.targets))),a.y.domain(a.getYDomain(a.data.targets,"y")),a.y2.domain(a.getYDomain(a.data.targets,"y2")),a.subX.domain(a.x.domain()),a.subY.domain(a.y.domain()),a.subY2.domain(a.y2.domain()),a.orgXDomain=a.x.domain(),a.brush&&a.brush.scale(a.subX),s.zoom_enabled&&a.zoom.scale(a.x),a.svg=a.selectChart.append("svg").style("overflow","hidden").on("mouseenter",function(){return s.onmouseover.call(a)}).on("mouseleave",function(){return s.onmouseout.call(a)}),a.config.svg_classname&&a.svg.attr("class",a.config.svg_classname),i=a.svg.append("defs"),a.clipChart=a.appendClip(i,a.clipId),a.clipXAxis=a.appendClip(i,a.clipIdForXAxis),a.clipYAxis=a.appendClip(i,a.clipIdForYAxis),a.clipGrid=a.appendClip(i,a.clipIdForGrid),a.clipSubchart=a.appendClip(i,a.clipIdForSubchart),a.updateSvgSize(),n=a.main=a.svg.append("g").attr("transform",a.getTranslate("main")),a.initSubchart&&a.initSubchart(),a.initTooltip&&a.initTooltip(),a.initLegend&&a.initLegend(),a.initTitle&&a.initTitle(),n.append("text").attr("class",o.text+" "+o.empty).attr("text-anchor","middle").attr("dominant-baseline","middle"),a.initRegion(),a.initGrid(),n.append("g").attr("clip-path",a.clipPath).attr("class",o.chart),s.grid_lines_front&&a.initGridLines(),a.initEventRect(),a.initChartElements(),n.insert("rect",s.zoom_privileged?null:"g."+o.regions).attr("class",o.zoomRect).attr("width",a.width).attr("height",a.height).style("opacity",0).on("dblclick.zoom",null),s.axis_x_extent&&a.brush.extent(a.getDefaultExtent()),a.axis.init(),a.updateTargets(a.data.targets),c&&(a.updateDimension(),a.config.oninit.call(a),a.redraw({withTransition:!1,withTransform:!0,withUpdateXDomain:!0,withUpdateOrgXDomain:!0,withTransitionForAxis:!1})),a.bindResize(),a.api.element=a.selectChart.node()},b.smoothLines=function(t,e){var i=this;"grid"===e&&t.each(function(){var t=i.d3.select(this),e=t.attr("x1"),n=t.attr("x2"),a=t.attr("y1"),r=t.attr("y2");t.attr({x1:Math.ceil(e),x2:Math.ceil(n),y1:Math.ceil(a),y2:Math.ceil(r)})})},b.updateSizes=function(){var t=this,e=t.config,i=t.legend?t.getLegendHeight():0,n=t.legend?t.getLegendWidth():0,a=t.isLegendRight||t.isLegendInset?0:i,r=t.hasArcType(),o=e.axis_rotated||r?0:t.getHorizontalAxisHeight("x"),s=e.subchart_show&&!r?e.subchart_size_height+o:0;t.currentWidth=t.getCurrentWidth(),t.currentHeight=t.getCurrentHeight(),t.margin=e.axis_rotated?{top:t.getHorizontalAxisHeight("y2")+t.getCurrentPaddingTop(),right:r?0:t.getCurrentPaddingRight(),bottom:t.getHorizontalAxisHeight("y")+a+t.getCurrentPaddingBottom(),left:s+(r?0:t.getCurrentPaddingLeft())}:{top:4+t.getCurrentPaddingTop(),right:r?0:t.getCurrentPaddingRight(),bottom:o+s+a+t.getCurrentPaddingBottom(),left:r?0:t.getCurrentPaddingLeft()},t.margin2=e.axis_rotated?{top:t.margin.top,right:NaN,bottom:20+a,left:t.rotated_padding_left}:{top:t.currentHeight-s-a,right:NaN,bottom:o+a,left:t.margin.left},t.margin3={top:0,right:NaN,bottom:0,left:0},t.updateSizeForLegend&&t.updateSizeForLegend(i,n),t.width=t.currentWidth-t.margin.left-t.margin.right,t.height=t.currentHeight-t.margin.top-t.margin.bottom,t.width<0&&(t.width=0),t.height<0&&(t.height=0),t.width2=e.axis_rotated?t.margin.left-t.rotated_padding_left-t.rotated_padding_right:t.width,t.height2=e.axis_rotated?t.height:t.currentHeight-t.margin2.top-t.margin2.bottom,t.width2<0&&(t.width2=0),t.height2<0&&(t.height2=0),t.arcWidth=t.width-(t.isLegendRight?n+10:0),t.arcHeight=t.height-(t.isLegendRight?0:10),t.hasType("gauge")&&!e.gauge_fullCircle&&(t.arcHeight+=t.height-t.getGaugeLabelHeight()),t.updateRadius&&t.updateRadius(),t.isLegendRight&&r&&(t.margin3.left=t.arcWidth/2+1.1*t.radiusExpanded)},b.updateTargets=function(t){var e=this;e.updateTargetsForText(t),e.updateTargetsForBar(t),e.updateTargetsForLine(t),e.hasArcType()&&e.updateTargetsForArc&&e.updateTargetsForArc(t),e.updateTargetsForSubchart&&e.updateTargetsForSubchart(t),e.showTargets()},b.showTargets=function(){var t=this;t.svg.selectAll("."+o.target).filter(function(e){return t.isTargetToShow(e.id)}).transition().duration(t.config.transition_duration).style("opacity",1)},b.redraw=function(t,e){var i,n,a,r,s,c,d,l,u,h,g,p,f,_,x,m,S,w,v,b,A,T,P,L,C,V,G,E,I,O=this,R=O.main,D=O.d3,F=O.config,X=O.getShapeIndices(O.isAreaType),k=O.getShapeIndices(O.isBarType),M=O.getShapeIndices(O.isLineType),H=O.hasArcType(),z=O.filterTargetsToShow(O.data.targets),B=O.xv.bind(O);if(t=t||{},i=y(t,"withY",!0),n=y(t,"withSubchart",!0),a=y(t,"withTransition",!0),c=y(t,"withTransform",!1),d=y(t,"withUpdateXDomain",!1),l=y(t,"withUpdateOrgXDomain",!1),u=y(t,"withTrimXDomain",!0),f=y(t,"withUpdateXAxis",d),h=y(t,"withLegend",!1),g=y(t,"withEventRect",!0),p=y(t,"withDimension",!0),r=y(t,"withTransitionForExit",a),s=y(t,"withTransitionForAxis",a),v=a?F.transition_duration:0,b=r?v:0,A=s?v:0,e=e||O.axis.generateTransitions(A),h&&F.legend_show?O.updateLegend(O.mapToIds(O.data.targets),t,e):p&&O.updateDimension(!0),O.isCategorized()&&0===z.length&&O.x.domain([0,O.axes.x.selectAll(".tick").size()]),z.length?(O.updateXDomain(z,d,l,u),F.axis_x_tick_values||(L=O.axis.updateXAxisTickValues(z))):(O.xAxis.tickValues([]),O.subXAxis.tickValues([])),F.zoom_rescale&&!t.flow&&(G=O.x.orgDomain()),O.y.domain(O.getYDomain(z,"y",G)),O.y2.domain(O.getYDomain(z,"y2",G)),!F.axis_y_tick_values&&F.axis_y_tick_count&&O.yAxis.tickValues(O.axis.generateTickValues(O.y.domain(),F.axis_y_tick_count)),!F.axis_y2_tick_values&&F.axis_y2_tick_count&&O.y2Axis.tickValues(O.axis.generateTickValues(O.y2.domain(),F.axis_y2_tick_count)),O.axis.redraw(e,H),O.axis.updateLabels(a),(d||f)&&z.length)if(F.axis_x_tick_culling&&L){for(C=1;C<L.length;C++)if(L.length/C<F.axis_x_tick_culling_max){V=C;break}O.svg.selectAll("."+o.axisX+" .tick text").each(function(t){var e=L.indexOf(t);e>=0&&D.select(this).style("display",e%V?"none":"block")})}else O.svg.selectAll("."+o.axisX+" .tick text").style("display","block");_=O.generateDrawArea?O.generateDrawArea(X,!1):void 0,x=O.generateDrawBar?O.generateDrawBar(k):void 0,m=O.generateDrawLine?O.generateDrawLine(M,!1):void 0,S=O.generateXYForText(X,k,M,!0),w=O.generateXYForText(X,k,M,!1),i&&(O.subY.domain(O.getYDomain(z,"y")),O.subY2.domain(O.getYDomain(z,"y2"))),O.updateXgridFocus(),R.select("text."+o.text+"."+o.empty).attr("x",O.width/2).attr("y",O.height/2).text(F.data_empty_label_text).transition().style("opacity",z.length?0:1),O.updateGrid(v),O.updateRegion(v),O.updateBar(b),O.updateLine(b),O.updateArea(b),O.updateCircle(),O.hasDataLabel()&&O.updateText(b),O.redrawTitle&&O.redrawTitle(),O.redrawArc&&O.redrawArc(v,b,c),O.redrawSubchart&&O.redrawSubchart(n,e,v,b,X,k,M),R.selectAll("."+o.selectedCircles).filter(O.isBarType.bind(O)).selectAll("circle").remove(),F.interaction_enabled&&!t.flow&&g&&(O.redrawEventRect(),O.updateZoom&&O.updateZoom()),O.updateCircleY(),E=(O.config.axis_rotated?O.circleY:O.circleX).bind(O),I=(O.config.axis_rotated?O.circleX:O.circleY).bind(O),t.flow&&(P=O.generateFlow({targets:z,flow:t.flow,duration:t.flow.duration,drawBar:x,drawLine:m,drawArea:_,cx:E,cy:I,xv:B,xForText:S,yForText:w})),(v||P)&&O.isTabVisible()?D.transition().duration(v).each(function(){var e=[];[O.redrawBar(x,!0),O.redrawLine(m,!0),O.redrawArea(_,!0),O.redrawCircle(E,I,!0),O.redrawText(S,w,t.flow,!0),O.redrawRegion(!0),O.redrawGrid(!0)].forEach(function(t){t.forEach(function(t){e.push(t)})}),T=O.generateWait(),e.forEach(function(t){T.add(t)})}).call(T,function(){P&&P(),F.onrendered&&F.onrendered.call(O)}):(O.redrawBar(x),O.redrawLine(m),O.redrawArea(_),O.redrawCircle(E,I),O.redrawText(S,w,t.flow),O.redrawRegion(),O.redrawGrid(),F.onrendered&&F.onrendered.call(O)),O.mapToIds(O.data.targets).forEach(function(t){O.withoutFadeIn[t]=!0})},b.updateAndRedraw=function(t){var e,i=this,n=i.config;(t=t||{}).withTransition=y(t,"withTransition",!0),t.withTransform=y(t,"withTransform",!1),t.withLegend=y(t,"withLegend",!1),t.withUpdateXDomain=!0,t.withUpdateOrgXDomain=!0,t.withTransitionForExit=!1,t.withTransitionForTransform=y(t,"withTransitionForTransform",t.withTransition),i.updateSizes(),t.withLegend&&n.legend_show||(e=i.axis.generateTransitions(t.withTransitionForAxis?n.transition_duration:0),i.updateScales(),i.updateSvgSize(),i.transformAll(t.withTransitionForTransform,e)),i.redraw(t,e)},b.redrawWithoutRescale=function(){this.redraw({withY:!1,withSubchart:!1,withEventRect:!1,withTransitionForAxis:!1})},b.isTimeSeries=function(){return"timeseries"===this.config.axis_x_type},b.isCategorized=function(){return this.config.axis_x_type.indexOf("categor")>=0},b.isCustomX=function(){var t=this,e=t.config;return!t.isTimeSeries()&&(e.data_x||x(e.data_xs))},b.isTimeSeriesY=function(){return"timeseries"===this.config.axis_y_type},b.getTranslate=function(t){var e,i,n=this,a=n.config;return"main"===t?(e=p(n.margin.left),i=p(n.margin.top)):"context"===t?(e=p(n.margin2.left),i=p(n.margin2.top)):"legend"===t?(e=n.margin3.left,i=n.margin3.top):"x"===t?(e=0,i=a.axis_rotated?0:n.height):"y"===t?(e=0,i=a.axis_rotated?n.height:0):"y2"===t?(e=a.axis_rotated?0:n.width,i=a.axis_rotated?1:0):"subx"===t?(e=0,i=a.axis_rotated?0:n.height2):"arc"===t&&(e=n.arcWidth/2,i=n.arcHeight/2),"translate("+e+","+i+")"},b.initialOpacity=function(t){return null!==t.value&&this.withoutFadeIn[t.id]?1:0},b.initialOpacityForCircle=function(t){return null!==t.value&&this.withoutFadeIn[t.id]?this.opacityForCircle(t):0},b.opacityForCircle=function(t){var e=(c(this.config.point_show)?this.config.point_show(t):this.config.point_show)?1:0;return s(t.value)?this.isScatterType(t)?.5:e:0},b.opacityForText=function(){return this.hasDataLabel()?1:0},b.xx=function(t){return t?this.x(t.x):null},b.xv=function(t){var e=this,i=t.value;return e.isTimeSeries()?i=e.parseDate(t.value):e.isCategorized()&&"string"==typeof t.value&&(i=e.config.axis_x_categories.indexOf(t.value)),Math.ceil(e.x(i))},b.yv=function(t){var e=this,i=t.axis&&"y2"===t.axis?e.y2:e.y;return Math.ceil(i(t.value))},b.subxx=function(t){return t?this.subX(t.x):null},b.transformMain=function(t,e){var i,n,a,r=this;e&&e.axisX?i=e.axisX:(i=r.main.select("."+o.axisX),t&&(i=i.transition())),e&&e.axisY?n=e.axisY:(n=r.main.select("."+o.axisY),t&&(n=n.transition())),e&&e.axisY2?a=e.axisY2:(a=r.main.select("."+o.axisY2),t&&(a=a.transition())),(t?r.main.transition():r.main).attr("transform",r.getTranslate("main")),i.attr("transform",r.getTranslate("x")),n.attr("transform",r.getTranslate("y")),a.attr("transform",r.getTranslate("y2")),r.main.select("."+o.chartArcs).attr("transform",r.getTranslate("arc"))},b.transformAll=function(t,e){var i=this;i.transformMain(t,e),i.config.subchart_show&&i.transformContext(t,e),i.legend&&i.transformLegend(t)},b.updateSvgSize=function(){var t=this,e=t.svg.select(".c3-brush .background");t.svg.attr("width",t.currentWidth).attr("height",t.currentHeight),t.svg.selectAll(["#"+t.clipId,"#"+t.clipIdForGrid]).select("rect").attr("width",t.width).attr("height",t.height),t.svg.select("#"+t.clipIdForXAxis).select("rect").attr("x",t.getXAxisClipX.bind(t)).attr("y",t.getXAxisClipY.bind(t)).attr("width",t.getXAxisClipWidth.bind(t)).attr("height",t.getXAxisClipHeight.bind(t)),t.svg.select("#"+t.clipIdForYAxis).select("rect").attr("x",t.getYAxisClipX.bind(t)).attr("y",t.getYAxisClipY.bind(t)).attr("width",t.getYAxisClipWidth.bind(t)).attr("height",t.getYAxisClipHeight.bind(t)),t.svg.select("#"+t.clipIdForSubchart).select("rect").attr("width",t.width).attr("height",e.size()?e.attr("height"):0),t.svg.select("."+o.zoomRect).attr("width",t.width).attr("height",t.height),t.selectChart.style("max-height",t.currentHeight+"px")},b.updateDimension=function(t){var e=this;t||(e.config.axis_rotated?(e.axes.x.call(e.xAxis),e.axes.subx.call(e.subXAxis)):(e.axes.y.call(e.yAxis),e.axes.y2.call(e.y2Axis))),e.updateSizes(),e.updateScales(),e.updateSvgSize(),e.transformAll(!1)},b.observeInserted=function(t){var e,i=this;"undefined"!=typeof MutationObserver?(e=new MutationObserver(function(n){n.forEach(function(n){"childList"===n.type&&n.previousSibling&&(e.disconnect(),i.intervalForObserveInserted=window.setInterval(function(){t.node().parentNode&&(window.clearInterval(i.intervalForObserveInserted),i.updateDimension(),i.brush&&i.brush.update(),i.config.oninit.call(i),i.redraw({withTransform:!0,withUpdateXDomain:!0,withUpdateOrgXDomain:!0,withTransition:!1,withTransitionForTransform:!1,withLegend:!0}),t.transition().style("opacity",1))},10))})})).observe(t.node(),{attributes:!0,childList:!0,characterData:!0}):window.console.error("MutationObserver not defined.")},b.bindResize=function(){var t=this,e=t.config;if(t.resizeFunction=t.generateResize(),t.resizeFunction.add(function(){e.onresize.call(t)}),e.resize_auto&&t.resizeFunction.add(function(){void 0!==t.resizeTimeout&&window.clearTimeout(t.resizeTimeout),t.resizeTimeout=window.setTimeout(function(){delete t.resizeTimeout,t.api.flush()},100)}),t.resizeFunction.add(function(){e.onresized.call(t)}),window.attachEvent)window.attachEvent("onresize",t.resizeFunction);else if(window.addEventListener)window.addEventListener("resize",t.resizeFunction,!1);else{var i=window.onresize;i?i.add&&i.remove||(i=t.generateResize()).add(window.onresize):i=t.generateResize(),i.add(t.resizeFunction),window.onresize=i}},b.generateResize=function(){function t(){e.forEach(function(t){t()})}var e=[];return t.add=function(t){e.push(t)},t.remove=function(t){for(var i=0;i<e.length;i++)if(e[i]===t){e.splice(i,1);break}},t},b.endall=function(t,e){var i=0;t.each(function(){++i}).each("end",function(){--i||e.apply(this,arguments)})},b.generateWait=function(){var t=[],e=function(e,i){var n=setInterval(function(){var e=0;t.forEach(function(t){if(t.empty())e+=1;else try{t.transition()}catch(t){e+=1}}),e===t.length&&(clearInterval(n),i&&i())},10)};return e.add=function(e){t.push(e)},e},b.parseDate=function(t){var e,i=this;return t instanceof Date?e=t:"string"==typeof t?e=i.dataTimeFormat(i.config.data_xFormat).parse(t):"object"==typeof t?e=new Date(+t):"number"!=typeof t||isNaN(t)||(e=new Date(+t)),e&&!isNaN(+e)||window.console.error("Failed to parse x '"+t+"' to Date object"),e},b.isTabVisible=function(){var t;return void 0!==document.hidden?t="hidden":void 0!==document.mozHidden?t="mozHidden":void 0!==document.msHidden?t="msHidden":void 0!==document.webkitHidden&&(t="webkitHidden"),!document[t]},b.isValue=s,b.isFunction=c,b.isString=l,b.isUndefined=u,b.isDefined=h,b.ceil10=g,b.asHalfPixel=p,b.diffDomain=f,b.isEmpty=_,b.notEmpty=x,b.notEmpty=x,b.getOption=y,b.hasValue=m,b.sanitise=S,b.getPathBox=w,b.CLASS=o,Function.prototype.bind||(Function.prototype.bind=function(t){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var e=Array.prototype.slice.call(arguments,1),i=this,n=function(){},a=function(){return i.apply(this instanceof n?this:t,e.concat(Array.prototype.slice.call(arguments)))};return n.prototype=this.prototype,a.prototype=new n,a}),"SVGPathSeg"in window||(window.SVGPathSeg=function(t,e,i){this.pathSegType=t,this.pathSegTypeAsLetter=e,this._owningPathSegList=i},window.SVGPathSeg.prototype.classname="SVGPathSeg",window.SVGPathSeg.PATHSEG_UNKNOWN=0,window.SVGPathSeg.PATHSEG_CLOSEPATH=1,window.SVGPathSeg.PATHSEG_MOVETO_ABS=2,window.SVGPathSeg.PATHSEG_MOVETO_REL=3,window.SVGPathSeg.PATHSEG_LINETO_ABS=4,window.SVGPathSeg.PATHSEG_LINETO_REL=5,window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS=6,window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL=7,window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS=8,window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL=9,window.SVGPathSeg.PATHSEG_ARC_ABS=10,window.SVGPathSeg.PATHSEG_ARC_REL=11,window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS=12,window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL=13,window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS=14,window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL=15,window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS=16,window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL=17,window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS=18,window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL=19,window.SVGPathSeg.prototype._segmentChanged=function(){this._owningPathSegList&&this._owningPathSegList.segmentChanged(this)},window.SVGPathSegClosePath=function(t){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_CLOSEPATH,"z",t)},window.SVGPathSegClosePath.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegClosePath.prototype.toString=function(){return"[object SVGPathSegClosePath]"},window.SVGPathSegClosePath.prototype._asPathString=function(){return this.pathSegTypeAsLetter},window.SVGPathSegClosePath.prototype.clone=function(){return new window.SVGPathSegClosePath(void 0)},window.SVGPathSegMovetoAbs=function(t,e,i){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_MOVETO_ABS,"M",t),this._x=e,this._y=i},window.SVGPathSegMovetoAbs.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegMovetoAbs.prototype.toString=function(){return"[object SVGPathSegMovetoAbs]"},window.SVGPathSegMovetoAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x+" "+this._y},window.SVGPathSegMovetoAbs.prototype.clone=function(){return new window.SVGPathSegMovetoAbs(void 0,this._x,this._y)},Object.defineProperty(window.SVGPathSegMovetoAbs.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegMovetoAbs.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegMovetoRel=function(t,e,i){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_MOVETO_REL,"m",t),this._x=e,this._y=i},window.SVGPathSegMovetoRel.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegMovetoRel.prototype.toString=function(){return"[object SVGPathSegMovetoRel]"},window.SVGPathSegMovetoRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x+" "+this._y},window.SVGPathSegMovetoRel.prototype.clone=function(){return new window.SVGPathSegMovetoRel(void 0,this._x,this._y)},Object.defineProperty(window.SVGPathSegMovetoRel.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegMovetoRel.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegLinetoAbs=function(t,e,i){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_LINETO_ABS,"L",t),this._x=e,this._y=i},window.SVGPathSegLinetoAbs.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegLinetoAbs.prototype.toString=function(){return"[object SVGPathSegLinetoAbs]"},window.SVGPathSegLinetoAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x+" "+this._y},window.SVGPathSegLinetoAbs.prototype.clone=function(){return new window.SVGPathSegLinetoAbs(void 0,this._x,this._y)},Object.defineProperty(window.SVGPathSegLinetoAbs.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegLinetoAbs.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegLinetoRel=function(t,e,i){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_LINETO_REL,"l",t),this._x=e,this._y=i},window.SVGPathSegLinetoRel.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegLinetoRel.prototype.toString=function(){return"[object SVGPathSegLinetoRel]"},window.SVGPathSegLinetoRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x+" "+this._y},window.SVGPathSegLinetoRel.prototype.clone=function(){return new window.SVGPathSegLinetoRel(void 0,this._x,this._y)},Object.defineProperty(window.SVGPathSegLinetoRel.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegLinetoRel.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegCurvetoCubicAbs=function(t,e,i,n,a,r,o){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS,"C",t),this._x=e,this._y=i,this._x1=n,this._y1=a,this._x2=r,this._y2=o},window.SVGPathSegCurvetoCubicAbs.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegCurvetoCubicAbs.prototype.toString=function(){return"[object SVGPathSegCurvetoCubicAbs]"},window.SVGPathSegCurvetoCubicAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x1+" "+this._y1+" "+this._x2+" "+this._y2+" "+this._x+" "+this._y},window.SVGPathSegCurvetoCubicAbs.prototype.clone=function(){return new window.SVGPathSegCurvetoCubicAbs(void 0,this._x,this._y,this._x1,this._y1,this._x2,this._y2)},Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype,"x1",{get:function(){return this._x1},set:function(t){this._x1=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype,"y1",{get:function(){return this._y1},set:function(t){this._y1=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype,"x2",{get:function(){return this._x2},set:function(t){this._x2=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype,"y2",{get:function(){return this._y2},set:function(t){this._y2=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegCurvetoCubicRel=function(t,e,i,n,a,r,o){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL,"c",t),this._x=e,this._y=i,this._x1=n,this._y1=a,this._x2=r,this._y2=o},window.SVGPathSegCurvetoCubicRel.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegCurvetoCubicRel.prototype.toString=function(){return"[object SVGPathSegCurvetoCubicRel]"},window.SVGPathSegCurvetoCubicRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x1+" "+this._y1+" "+this._x2+" "+this._y2+" "+this._x+" "+this._y},window.SVGPathSegCurvetoCubicRel.prototype.clone=function(){return new window.SVGPathSegCurvetoCubicRel(void 0,this._x,this._y,this._x1,this._y1,this._x2,this._y2)},Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype,"x1",{get:function(){return this._x1},set:function(t){this._x1=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype,"y1",{get:function(){return this._y1},set:function(t){this._y1=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype,"x2",{get:function(){return this._x2},set:function(t){this._x2=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype,"y2",{get:function(){return this._y2},set:function(t){this._y2=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegCurvetoQuadraticAbs=function(t,e,i,n,a){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS,"Q",t),this._x=e,this._y=i,this._x1=n,this._y1=a},window.SVGPathSegCurvetoQuadraticAbs.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegCurvetoQuadraticAbs.prototype.toString=function(){return"[object SVGPathSegCurvetoQuadraticAbs]"},window.SVGPathSegCurvetoQuadraticAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x1+" "+this._y1+" "+this._x+" "+this._y},window.SVGPathSegCurvetoQuadraticAbs.prototype.clone=function(){return new window.SVGPathSegCurvetoQuadraticAbs(void 0,this._x,this._y,this._x1,this._y1)},Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype,"x1",{get:function(){return this._x1},set:function(t){this._x1=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype,"y1",{get:function(){return this._y1},set:function(t){this._y1=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegCurvetoQuadraticRel=function(t,e,i,n,a){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL,"q",t),this._x=e,this._y=i,this._x1=n,this._y1=a},window.SVGPathSegCurvetoQuadraticRel.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegCurvetoQuadraticRel.prototype.toString=function(){return"[object SVGPathSegCurvetoQuadraticRel]"},window.SVGPathSegCurvetoQuadraticRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x1+" "+this._y1+" "+this._x+" "+this._y},window.SVGPathSegCurvetoQuadraticRel.prototype.clone=function(){return new window.SVGPathSegCurvetoQuadraticRel(void 0,this._x,this._y,this._x1,this._y1)},Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype,"x1",{get:function(){return this._x1},set:function(t){this._x1=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype,"y1",{get:function(){return this._y1},set:function(t){this._y1=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegArcAbs=function(t,e,i,n,a,r,o,s){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_ARC_ABS,"A",t),this._x=e,this._y=i,this._r1=n,this._r2=a,this._angle=r,this._largeArcFlag=o,this._sweepFlag=s},window.SVGPathSegArcAbs.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegArcAbs.prototype.toString=function(){return"[object SVGPathSegArcAbs]"},window.SVGPathSegArcAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._r1+" "+this._r2+" "+this._angle+" "+(this._largeArcFlag?"1":"0")+" "+(this._sweepFlag?"1":"0")+" "+this._x+" "+this._y},window.SVGPathSegArcAbs.prototype.clone=function(){return new window.SVGPathSegArcAbs(void 0,this._x,this._y,this._r1,this._r2,this._angle,this._largeArcFlag,this._sweepFlag)},Object.defineProperty(window.SVGPathSegArcAbs.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcAbs.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcAbs.prototype,"r1",{get:function(){return this._r1},set:function(t){this._r1=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcAbs.prototype,"r2",{get:function(){return this._r2},set:function(t){this._r2=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcAbs.prototype,"angle",{get:function(){return this._angle},set:function(t){this._angle=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcAbs.prototype,"largeArcFlag",{get:function(){return this._largeArcFlag},set:function(t){this._largeArcFlag=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcAbs.prototype,"sweepFlag",{get:function(){return this._sweepFlag},set:function(t){this._sweepFlag=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegArcRel=function(t,e,i,n,a,r,o,s){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_ARC_REL,"a",t),this._x=e,this._y=i,this._r1=n,this._r2=a,this._angle=r,this._largeArcFlag=o,this._sweepFlag=s},window.SVGPathSegArcRel.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegArcRel.prototype.toString=function(){return"[object SVGPathSegArcRel]"},window.SVGPathSegArcRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._r1+" "+this._r2+" "+this._angle+" "+(this._largeArcFlag?"1":"0")+" "+(this._sweepFlag?"1":"0")+" "+this._x+" "+this._y},window.SVGPathSegArcRel.prototype.clone=function(){return new window.SVGPathSegArcRel(void 0,this._x,this._y,this._r1,this._r2,this._angle,this._largeArcFlag,this._sweepFlag)},Object.defineProperty(window.SVGPathSegArcRel.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcRel.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcRel.prototype,"r1",{get:function(){return this._r1},set:function(t){this._r1=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcRel.prototype,"r2",{get:function(){return this._r2},set:function(t){this._r2=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcRel.prototype,"angle",{get:function(){return this._angle},set:function(t){this._angle=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcRel.prototype,"largeArcFlag",{get:function(){return this._largeArcFlag},set:function(t){this._largeArcFlag=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcRel.prototype,"sweepFlag",{get:function(){return this._sweepFlag},set:function(t){this._sweepFlag=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegLinetoHorizontalAbs=function(t,e){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS,"H",t),this._x=e},window.SVGPathSegLinetoHorizontalAbs.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegLinetoHorizontalAbs.prototype.toString=function(){return"[object SVGPathSegLinetoHorizontalAbs]"},window.SVGPathSegLinetoHorizontalAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x},window.SVGPathSegLinetoHorizontalAbs.prototype.clone=function(){return new window.SVGPathSegLinetoHorizontalAbs(void 0,this._x)},Object.defineProperty(window.SVGPathSegLinetoHorizontalAbs.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegLinetoHorizontalRel=function(t,e){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL,"h",t),this._x=e},window.SVGPathSegLinetoHorizontalRel.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegLinetoHorizontalRel.prototype.toString=function(){return"[object SVGPathSegLinetoHorizontalRel]"},window.SVGPathSegLinetoHorizontalRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x},window.SVGPathSegLinetoHorizontalRel.prototype.clone=function(){return new window.SVGPathSegLinetoHorizontalRel(void 0,this._x)},Object.defineProperty(window.SVGPathSegLinetoHorizontalRel.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegLinetoVerticalAbs=function(t,e){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS,"V",t),this._y=e},window.SVGPathSegLinetoVerticalAbs.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegLinetoVerticalAbs.prototype.toString=function(){return"[object SVGPathSegLinetoVerticalAbs]"},window.SVGPathSegLinetoVerticalAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._y},window.SVGPathSegLinetoVerticalAbs.prototype.clone=function(){return new window.SVGPathSegLinetoVerticalAbs(void 0,this._y)},Object.defineProperty(window.SVGPathSegLinetoVerticalAbs.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegLinetoVerticalRel=function(t,e){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL,"v",t),this._y=e},window.SVGPathSegLinetoVerticalRel.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegLinetoVerticalRel.prototype.toString=function(){return"[object SVGPathSegLinetoVerticalRel]"},window.SVGPathSegLinetoVerticalRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._y},window.SVGPathSegLinetoVerticalRel.prototype.clone=function(){return new window.SVGPathSegLinetoVerticalRel(void 0,this._y)},Object.defineProperty(window.SVGPathSegLinetoVerticalRel.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegCurvetoCubicSmoothAbs=function(t,e,i,n,a){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS,"S",t),this._x=e,this._y=i,this._x2=n,this._y2=a},window.SVGPathSegCurvetoCubicSmoothAbs.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegCurvetoCubicSmoothAbs.prototype.toString=function(){return"[object SVGPathSegCurvetoCubicSmoothAbs]"},window.SVGPathSegCurvetoCubicSmoothAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x2+" "+this._y2+" "+this._x+" "+this._y},window.SVGPathSegCurvetoCubicSmoothAbs.prototype.clone=function(){return new window.SVGPathSegCurvetoCubicSmoothAbs(void 0,this._x,this._y,this._x2,this._y2)},Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype,"x2",{get:function(){return this._x2},set:function(t){this._x2=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype,"y2",{get:function(){return this._y2},set:function(t){this._y2=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegCurvetoCubicSmoothRel=function(t,e,i,n,a){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL,"s",t),this._x=e,this._y=i,this._x2=n,this._y2=a},window.SVGPathSegCurvetoCubicSmoothRel.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegCurvetoCubicSmoothRel.prototype.toString=function(){return"[object SVGPathSegCurvetoCubicSmoothRel]"},window.SVGPathSegCurvetoCubicSmoothRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x2+" "+this._y2+" "+this._x+" "+this._y},window.SVGPathSegCurvetoCubicSmoothRel.prototype.clone=function(){return new window.SVGPathSegCurvetoCubicSmoothRel(void 0,this._x,this._y,this._x2,this._y2)},Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype,"x2",{get:function(){return this._x2},set:function(t){this._x2=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype,"y2",{get:function(){return this._y2},set:function(t){this._y2=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegCurvetoQuadraticSmoothAbs=function(t,e,i){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS,"T",t),this._x=e,this._y=i},window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.toString=function(){return"[object SVGPathSegCurvetoQuadraticSmoothAbs]"},window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x+" "+this._y},window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.clone=function(){return new window.SVGPathSegCurvetoQuadraticSmoothAbs(void 0,this._x,this._y)},Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegCurvetoQuadraticSmoothRel=function(t,e,i){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL,"t",t),this._x=e,this._y=i},window.SVGPathSegCurvetoQuadraticSmoothRel.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.toString=function(){return"[object SVGPathSegCurvetoQuadraticSmoothRel]"},window.SVGPathSegCurvetoQuadraticSmoothRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x+" "+this._y},window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.clone=function(){return new window.SVGPathSegCurvetoQuadraticSmoothRel(void 0,this._x,this._y)},Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),window.SVGPathElement.prototype.createSVGPathSegClosePath=function(){return new window.SVGPathSegClosePath(void 0)},window.SVGPathElement.prototype.createSVGPathSegMovetoAbs=function(t,e){return new window.SVGPathSegMovetoAbs(void 0,t,e)},window.SVGPathElement.prototype.createSVGPathSegMovetoRel=function(t,e){return new window.SVGPathSegMovetoRel(void 0,t,e)},window.SVGPathElement.prototype.createSVGPathSegLinetoAbs=function(t,e){return new window.SVGPathSegLinetoAbs(void 0,t,e)},window.SVGPathElement.prototype.createSVGPathSegLinetoRel=function(t,e){return new window.SVGPathSegLinetoRel(void 0,t,e)},window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs=function(t,e,i,n,a,r){return new window.SVGPathSegCurvetoCubicAbs(void 0,t,e,i,n,a,r)},window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel=function(t,e,i,n,a,r){return new window.SVGPathSegCurvetoCubicRel(void 0,t,e,i,n,a,r)},window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs=function(t,e,i,n){return new window.SVGPathSegCurvetoQuadraticAbs(void 0,t,e,i,n)},window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel=function(t,e,i,n){return new window.SVGPathSegCurvetoQuadraticRel(void 0,t,e,i,n)},window.SVGPathElement.prototype.createSVGPathSegArcAbs=function(t,e,i,n,a,r,o){return new window.SVGPathSegArcAbs(void 0,t,e,i,n,a,r,o)},window.SVGPathElement.prototype.createSVGPathSegArcRel=function(t,e,i,n,a,r,o){return new window.SVGPathSegArcRel(void 0,t,e,i,n,a,r,o)},window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs=function(t){return new window.SVGPathSegLinetoHorizontalAbs(void 0,t)},window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel=function(t){return new window.SVGPathSegLinetoHorizontalRel(void 0,t)},window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs=function(t){return new window.SVGPathSegLinetoVerticalAbs(void 0,t)},window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel=function(t){return new window.SVGPathSegLinetoVerticalRel(void 0,t)},window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs=function(t,e,i,n){return new window.SVGPathSegCurvetoCubicSmoothAbs(void 0,t,e,i,n)},window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel=function(t,e,i,n){return new window.SVGPathSegCurvetoCubicSmoothRel(void 0,t,e,i,n)},window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs=function(t,e){return new window.SVGPathSegCurvetoQuadraticSmoothAbs(void 0,t,e)},window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel=function(t,e){return new window.SVGPathSegCurvetoQuadraticSmoothRel(void 0,t,e)},"getPathSegAtLength"in window.SVGPathElement.prototype||(window.SVGPathElement.prototype.getPathSegAtLength=function(t){if(void 0===t||!isFinite(t))throw"Invalid arguments.";var e=document.createElementNS("http://www.w3.org/2000/svg","path");e.setAttribute("d",this.getAttribute("d"));var i=e.pathSegList.numberOfItems-1;if(i<=0)return 0;do{if(e.pathSegList.removeItem(i),t>e.getTotalLength())break;i--}while(i>0);return i})),"SVGPathSegList"in window||(window.SVGPathSegList=function(t){this._pathElement=t,this._list=this._parsePath(this._pathElement.getAttribute("d")),this._mutationObserverConfig={attributes:!0,attributeFilter:["d"]},this._pathElementMutationObserver=new MutationObserver(this._updateListFromPathMutations.bind(this)),this._pathElementMutationObserver.observe(this._pathElement,this._mutationObserverConfig)},window.SVGPathSegList.prototype.classname="SVGPathSegList",Object.defineProperty(window.SVGPathSegList.prototype,"numberOfItems",{get:function(){return this._checkPathSynchronizedToList(),this._list.length},enumerable:!0}),Object.defineProperty(window.SVGPathElement.prototype,"pathSegList",{get:function(){return this._pathSegList||(this._pathSegList=new window.SVGPathSegList(this)),this._pathSegList},enumerable:!0}),Object.defineProperty(window.SVGPathElement.prototype,"normalizedPathSegList",{get:function(){return this.pathSegList},enumerable:!0}),Object.defineProperty(window.SVGPathElement.prototype,"animatedPathSegList",{get:function(){return this.pathSegList},enumerable:!0}),Object.defineProperty(window.SVGPathElement.prototype,"animatedNormalizedPathSegList",{get:function(){return this.pathSegList},enumerable:!0}),window.SVGPathSegList.prototype._checkPathSynchronizedToList=function(){this._updateListFromPathMutations(this._pathElementMutationObserver.takeRecords())},window.SVGPathSegList.prototype._updateListFromPathMutations=function(t){if(this._pathElement){var e=!1;t.forEach(function(t){"d"==t.attributeName&&(e=!0)}),e&&(this._list=this._parsePath(this._pathElement.getAttribute("d")))}},window.SVGPathSegList.prototype._writeListToPath=function(){this._pathElementMutationObserver.disconnect(),this._pathElement.setAttribute("d",window.SVGPathSegList._pathSegArrayAsString(this._list)),this._pathElementMutationObserver.observe(this._pathElement,this._mutationObserverConfig)},window.SVGPathSegList.prototype.segmentChanged=function(t){this._writeListToPath()},window.SVGPathSegList.prototype.clear=function(){this._checkPathSynchronizedToList(),this._list.forEach(function(t){t._owningPathSegList=null}),this._list=[],this._writeListToPath()},window.SVGPathSegList.prototype.initialize=function(t){return this._checkPathSynchronizedToList(),this._list=[t],t._owningPathSegList=this,this._writeListToPath(),t},window.SVGPathSegList.prototype._checkValidIndex=function(t){if(isNaN(t)||t<0||t>=this.numberOfItems)throw"INDEX_SIZE_ERR"},window.SVGPathSegList.prototype.getItem=function(t){return this._checkPathSynchronizedToList(),this._checkValidIndex(t),this._list[t]},window.SVGPathSegList.prototype.insertItemBefore=function(t,e){return this._checkPathSynchronizedToList(),e>this.numberOfItems&&(e=this.numberOfItems),t._owningPathSegList&&(t=t.clone()),this._list.splice(e,0,t),t._owningPathSegList=this,this._writeListToPath(),t},window.SVGPathSegList.prototype.replaceItem=function(t,e){return this._checkPathSynchronizedToList(),t._owningPathSegList&&(t=t.clone()),this._checkValidIndex(e),this._list[e]=t,t._owningPathSegList=this,this._writeListToPath(),t},window.SVGPathSegList.prototype.removeItem=function(t){this._checkPathSynchronizedToList(),this._checkValidIndex(t);var e=this._list[t];return this._list.splice(t,1),this._writeListToPath(),e},window.SVGPathSegList.prototype.appendItem=function(t){return this._checkPathSynchronizedToList(),t._owningPathSegList&&(t=t.clone()),this._list.push(t),t._owningPathSegList=this,this._writeListToPath(),t},window.SVGPathSegList._pathSegArrayAsString=function(t){var e="",i=!0;return t.forEach(function(t){i?(i=!1,e+=t._asPathString()):e+=" "+t._asPathString()}),e},window.SVGPathSegList.prototype._parsePath=function(t){if(!t||0==t.length)return[];var e=this,i=function(){this.pathSegList=[]};i.prototype.appendSegment=function(t){this.pathSegList.push(t)};var n=function(t){this._string=t,this._currentIndex=0,this._endIndex=this._string.length,this._previousCommand=window.SVGPathSeg.PATHSEG_UNKNOWN,this._skipOptionalSpaces()};n.prototype._isCurrentSpace=function(){var t=this._string[this._currentIndex];return t<=" "&&(" "==t||"\n"==t||"\t"==t||"\r"==t||"\f"==t)},n.prototype._skipOptionalSpaces=function(){for(;this._currentIndex<this._endIndex&&this._isCurrentSpace();)this._currentIndex++;return this._currentIndex<this._endIndex},n.prototype._skipOptionalSpacesOrDelimiter=function(){return!(this._currentIndex<this._endIndex&&!this._isCurrentSpace()&&","!=this._string.charAt(this._currentIndex))&&(this._skipOptionalSpaces()&&this._currentIndex<this._endIndex&&","==this._string.charAt(this._currentIndex)&&(this._currentIndex++,this._skipOptionalSpaces()),this._currentIndex<this._endIndex)},n.prototype.hasMoreData=function(){return this._currentIndex<this._endIndex},n.prototype.peekSegmentType=function(){var t=this._string[this._currentIndex];return this._pathSegTypeFromChar(t)},n.prototype._pathSegTypeFromChar=function(t){switch(t){case"Z":case"z":return window.SVGPathSeg.PATHSEG_CLOSEPATH;case"M":return window.SVGPathSeg.PATHSEG_MOVETO_ABS;case"m":return window.SVGPathSeg.PATHSEG_MOVETO_REL;case"L":return window.SVGPathSeg.PATHSEG_LINETO_ABS;case"l":return window.SVGPathSeg.PATHSEG_LINETO_REL;case"C":return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS;case"c":return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL;case"Q":return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS;case"q":return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL;case"A":return window.SVGPathSeg.PATHSEG_ARC_ABS;case"a":return window.SVGPathSeg.PATHSEG_ARC_REL;case"H":return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS;case"h":return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL;case"V":return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS;case"v":return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL;case"S":return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS;case"s":return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL;case"T":return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS;case"t":return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL;default:return window.SVGPathSeg.PATHSEG_UNKNOWN}},n.prototype._nextCommandHelper=function(t,e){return("+"==t||"-"==t||"."==t||t>="0"&&t<="9")&&e!=window.SVGPathSeg.PATHSEG_CLOSEPATH?e==window.SVGPathSeg.PATHSEG_MOVETO_ABS?window.SVGPathSeg.PATHSEG_LINETO_ABS:e==window.SVGPathSeg.PATHSEG_MOVETO_REL?window.SVGPathSeg.PATHSEG_LINETO_REL:e:window.SVGPathSeg.PATHSEG_UNKNOWN},n.prototype.initialCommandIsMoveTo=function(){if(!this.hasMoreData())return!0;var t=this.peekSegmentType();return t==window.SVGPathSeg.PATHSEG_MOVETO_ABS||t==window.SVGPathSeg.PATHSEG_MOVETO_REL},n.prototype._parseNumber=function(){var t=0,e=0,i=1,n=0,a=1,r=1,o=this._currentIndex;if(this._skipOptionalSpaces(),this._currentIndex<this._endIndex&&"+"==this._string.charAt(this._currentIndex)?this._currentIndex++:this._currentIndex<this._endIndex&&"-"==this._string.charAt(this._currentIndex)&&(this._currentIndex++,a=-1),!(this._currentIndex==this._endIndex||(this._string.charAt(this._currentIndex)<"0"||this._string.charAt(this._currentIndex)>"9")&&"."!=this._string.charAt(this._currentIndex))){for(var s=this._currentIndex;this._currentIndex<this._endIndex&&this._string.charAt(this._currentIndex)>="0"&&this._string.charAt(this._currentIndex)<="9";)this._currentIndex++;if(this._currentIndex!=s)for(var c=this._currentIndex-1,d=1;c>=s;)e+=d*(this._string.charAt(c--)-"0"),d*=10;if(this._currentIndex<this._endIndex&&"."==this._string.charAt(this._currentIndex)){if(++this._currentIndex>=this._endIndex||this._string.charAt(this._currentIndex)<"0"||this._string.charAt(this._currentIndex)>"9")return;for(;this._currentIndex<this._endIndex&&this._string.charAt(this._currentIndex)>="0"&&this._string.charAt(this._currentIndex)<="9";)i*=10,n+=(this._string.charAt(this._currentIndex)-"0")/i,this._currentIndex+=1}if(this._currentIndex!=o&&this._currentIndex+1<this._endIndex&&("e"==this._string.charAt(this._currentIndex)||"E"==this._string.charAt(this._currentIndex))&&"x"!=this._string.charAt(this._currentIndex+1)&&"m"!=this._string.charAt(this._currentIndex+1)){if(this._currentIndex++,"+"==this._string.charAt(this._currentIndex)?this._currentIndex++:"-"==this._string.charAt(this._currentIndex)&&(this._currentIndex++,r=-1),this._currentIndex>=this._endIndex||this._string.charAt(this._currentIndex)<"0"||this._string.charAt(this._currentIndex)>"9")return;for(;this._currentIndex<this._endIndex&&this._string.charAt(this._currentIndex)>="0"&&this._string.charAt(this._currentIndex)<="9";)t*=10,t+=this._string.charAt(this._currentIndex)-"0",this._currentIndex++}var l=e+n;if(l*=a,t&&(l*=Math.pow(10,r*t)),o!=this._currentIndex)return this._skipOptionalSpacesOrDelimiter(),l}},n.prototype._parseArcFlag=function(){if(!(this._currentIndex>=this._endIndex)){var t=!1,e=this._string.charAt(this._currentIndex++);if("0"==e)t=!1;else{if("1"!=e)return;t=!0}return this._skipOptionalSpacesOrDelimiter(),t}},n.prototype.parseSegment=function(){var t=this._string[this._currentIndex],i=this._pathSegTypeFromChar(t);if(i==window.SVGPathSeg.PATHSEG_UNKNOWN){if(this._previousCommand==window.SVGPathSeg.PATHSEG_UNKNOWN)return null;if((i=this._nextCommandHelper(t,this._previousCommand))==window.SVGPathSeg.PATHSEG_UNKNOWN)return null}else this._currentIndex++;switch(this._previousCommand=i,i){case window.SVGPathSeg.PATHSEG_MOVETO_REL:return new window.SVGPathSegMovetoRel(e,this._parseNumber(),this._parseNumber());case window.SVGPathSeg.PATHSEG_MOVETO_ABS:return new window.SVGPathSegMovetoAbs(e,this._parseNumber(),this._parseNumber());case window.SVGPathSeg.PATHSEG_LINETO_REL:return new window.SVGPathSegLinetoRel(e,this._parseNumber(),this._parseNumber());case window.SVGPathSeg.PATHSEG_LINETO_ABS:return new window.SVGPathSegLinetoAbs(e,this._parseNumber(),this._parseNumber());case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:return new window.SVGPathSegLinetoHorizontalRel(e,this._parseNumber());case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:return new window.SVGPathSegLinetoHorizontalAbs(e,this._parseNumber());case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:return new window.SVGPathSegLinetoVerticalRel(e,this._parseNumber());case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:return new window.SVGPathSegLinetoVerticalAbs(e,this._parseNumber());case window.SVGPathSeg.PATHSEG_CLOSEPATH:return this._skipOptionalSpaces(),new window.SVGPathSegClosePath(e);case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:return n={x1:this._parseNumber(),y1:this._parseNumber(),x2:this._parseNumber(),y2:this._parseNumber(),x:this._parseNumber(),y:this._parseNumber()},new window.SVGPathSegCurvetoCubicRel(e,n.x,n.y,n.x1,n.y1,n.x2,n.y2);case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:return n={x1:this._parseNumber(),y1:this._parseNumber(),x2:this._parseNumber(),y2:this._parseNumber(),x:this._parseNumber(),y:this._parseNumber()},new window.SVGPathSegCurvetoCubicAbs(e,n.x,n.y,n.x1,n.y1,n.x2,n.y2);case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:return n={x2:this._parseNumber(),y2:this._parseNumber(),x:this._parseNumber(),y:this._parseNumber()},new window.SVGPathSegCurvetoCubicSmoothRel(e,n.x,n.y,n.x2,n.y2);case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:return n={x2:this._parseNumber(),y2:this._parseNumber(),x:this._parseNumber(),y:this._parseNumber()},new window.SVGPathSegCurvetoCubicSmoothAbs(e,n.x,n.y,n.x2,n.y2);case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:return n={x1:this._parseNumber(),y1:this._parseNumber(),x:this._parseNumber(),y:this._parseNumber()},new window.SVGPathSegCurvetoQuadraticRel(e,n.x,n.y,n.x1,n.y1);case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:return n={x1:this._parseNumber(),y1:this._parseNumber(),x:this._parseNumber(),y:this._parseNumber()},new window.SVGPathSegCurvetoQuadraticAbs(e,n.x,n.y,n.x1,n.y1);case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:return new window.SVGPathSegCurvetoQuadraticSmoothRel(e,this._parseNumber(),this._parseNumber());case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:return new window.SVGPathSegCurvetoQuadraticSmoothAbs(e,this._parseNumber(),this._parseNumber());case window.SVGPathSeg.PATHSEG_ARC_REL:return n={x1:this._parseNumber(),y1:this._parseNumber(),arcAngle:this._parseNumber(),arcLarge:this._parseArcFlag(),arcSweep:this._parseArcFlag(),x:this._parseNumber(),y:this._parseNumber()},new window.SVGPathSegArcRel(e,n.x,n.y,n.x1,n.y1,n.arcAngle,n.arcLarge,n.arcSweep);case window.SVGPathSeg.PATHSEG_ARC_ABS:var n={x1:this._parseNumber(),y1:this._parseNumber(),arcAngle:this._parseNumber(),arcLarge:this._parseArcFlag(),arcSweep:this._parseArcFlag(),x:this._parseNumber(),y:this._parseNumber()};return new window.SVGPathSegArcAbs(e,n.x,n.y,n.x1,n.y1,n.arcAngle,n.arcLarge,n.arcSweep);default:throw"Unknown path seg type."}};var a=new i,r=new n(t);if(!r.initialCommandIsMoveTo())return[];for(;r.hasMoreData();){var o=r.parseSegment();if(!o)return[];a.appendSegment(o)}return a.pathSegList}),v.axis=function(){},v.axis.labels=function(t){var e=this.internal;arguments.length&&(Object.keys(t).forEach(function(i){e.axis.setLabelText(i,t[i])}),e.axis.updateLabels())},v.axis.max=function(t){var e=this.internal,i=e.config;if(!arguments.length)return{x:i.axis_x_max,y:i.axis_y_max,y2:i.axis_y2_max};"object"==typeof t?(s(t.x)&&(i.axis_x_max=t.x),s(t.y)&&(i.axis_y_max=t.y),s(t.y2)&&(i.axis_y2_max=t.y2)):i.axis_y_max=i.axis_y2_max=t,e.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0})},v.axis.min=function(t){var e=this.internal,i=e.config;if(!arguments.length)return{x:i.axis_x_min,y:i.axis_y_min,y2:i.axis_y2_min};"object"==typeof t?(s(t.x)&&(i.axis_x_min=t.x),s(t.y)&&(i.axis_y_min=t.y),s(t.y2)&&(i.axis_y2_min=t.y2)):i.axis_y_min=i.axis_y2_min=t,e.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0})},v.axis.range=function(t){if(!arguments.length)return{max:this.axis.max(),min:this.axis.min()};void 0!==t.max&&this.axis.max(t.max),void 0!==t.min&&this.axis.min(t.min)},v.category=function(t,e){var i=this.internal,n=i.config;return arguments.length>1&&(n.axis_x_categories[t]=e,i.redraw()),n.axis_x_categories[t]},v.categories=function(t){var e=this.internal,i=e.config;return arguments.length?(i.axis_x_categories=t,e.redraw(),i.axis_x_categories):i.axis_x_categories},v.resize=function(t){var e=this.internal.config;e.size_width=t?t.width:null,e.size_height=t?t.height:null,this.flush()},v.flush=function(){this.internal.updateAndRedraw({withLegend:!0,withTransition:!1,withTransitionForTransform:!1})},v.destroy=function(){var t=this.internal;if(window.clearInterval(t.intervalForObserveInserted),void 0!==t.resizeTimeout&&window.clearTimeout(t.resizeTimeout),window.detachEvent)window.detachEvent("onresize",t.resizeFunction);else if(window.removeEventListener)window.removeEventListener("resize",t.resizeFunction);else{var e=window.onresize;e&&e.add&&e.remove&&e.remove(t.resizeFunction)}return t.selectChart.classed("c3",!1).html(""),Object.keys(t).forEach(function(e){t[e]=null}),null},v.color=function(t){return this.internal.color(t)},v.data=function(t){var e=this.internal.data.targets;return void 0===t?e:e.filter(function(e){return[].concat(t).indexOf(e.id)>=0})},v.data.shown=function(t){return this.internal.filterTargetsToShow(this.data(t))},v.data.values=function(t){var e,i=null;return t&&(i=(e=this.data(t))[0]?e[0].values.map(function(t){return t.value}):null),i},v.data.names=function(t){return this.internal.clearLegendItemTextBoxCache(),this.internal.updateDataAttributes("names",t)},v.data.colors=function(t){return this.internal.updateDataAttributes("colors",t)},v.data.axes=function(t){return this.internal.updateDataAttributes("axes",t)},v.flow=function(t){var e,i,n,a,r,o,c,d=this.internal,l=[],u=d.getMaxDataCount(),h=0,g=0;if(t.json)i=d.convertJsonToData(t.json,t.keys);else if(t.rows)i=d.convertRowsToData(t.rows);else{if(!t.columns)return;i=d.convertColumnsToData(t.columns)}e=d.convertDataToTargets(i,!0),d.data.targets.forEach(function(t){var i,n,a=!1;for(i=0;i<e.length;i++)if(t.id===e[i].id){for(a=!0,t.values[t.values.length-1]&&(g=t.values[t.values.length-1].index+1),h=e[i].values.length,n=0;n<h;n++)e[i].values[n].index=g+n,d.isTimeSeries()||(e[i].values[n].x=g+n);t.values=t.values.concat(e[i].values),e.splice(i,1);break}a||l.push(t.id)}),d.data.targets.forEach(function(t){var e,i;for(e=0;e<l.length;e++)if(t.id===l[e])for(g=t.values[t.values.length-1].index+1,i=0;i<h;i++)t.values.push({id:t.id,index:g+i,x:d.isTimeSeries()?d.getOtherTargetX(g+i):g+i,value:null})}),d.data.targets.length&&e.forEach(function(t){var e,i=[];for(e=d.data.targets[0].values[0].index;e<g;e++)i.push({id:t.id,index:e,x:d.isTimeSeries()?d.getOtherTargetX(e):e,value:null});t.values.forEach(function(t){t.index+=g,d.isTimeSeries()||(t.x+=g)}),t.values=i.concat(t.values)}),d.data.targets=d.data.targets.concat(e),d.getMaxDataCount(),r=(a=d.data.targets[0]).values[0],void 0!==t.to?(h=0,c=d.isTimeSeries()?d.parseDate(t.to):t.to,a.values.forEach(function(t){t.x<c&&h++})):void 0!==t.length&&(h=t.length),u?1===u&&d.isTimeSeries()&&(o=(a.values[a.values.length-1].x-r.x)/2,n=[new Date(+r.x-o),new Date(+r.x+o)],d.updateXDomain(null,!0,!0,!1,n)):(o=d.isTimeSeries()?a.values.length>1?a.values[a.values.length-1].x-r.x:r.x-d.getXDomain(d.data.targets)[0]:1,n=[r.x-o,r.x],d.updateXDomain(null,!0,!0,!1,n)),d.updateTargets(d.data.targets),d.redraw({flow:{index:r.index,length:h,duration:s(t.duration)?t.duration:d.config.transition_duration,done:t.done,orgDataCount:u},withLegend:!0,withTransition:u>1,withTrimXDomain:!1,withUpdateXAxis:!0})},b.generateFlow=function(t){var e=this,i=e.config,n=e.d3;return function(){var a,r,s,c=t.targets,d=t.flow,l=t.drawBar,u=t.drawLine,h=t.drawArea,g=t.cx,p=t.cy,_=t.xv,x=t.xForText,y=t.yForText,m=t.duration,S=1,w=d.index,v=d.length,b=e.getValueOnIndex(e.data.targets[0].values,w),A=e.getValueOnIndex(e.data.targets[0].values,w+v),T=e.x.domain(),P=d.duration||m,L=d.done||function(){},C=e.generateWait(),V=e.xgrid||n.selectAll([]),G=e.xgridLines||n.selectAll([]),E=e.mainRegion||n.selectAll([]),I=e.mainText||n.selectAll([]),O=e.mainBar||n.selectAll([]),R=e.mainLine||n.selectAll([]),D=e.mainArea||n.selectAll([]),F=e.mainCircle||n.selectAll([]);e.flowing=!0,e.data.targets.forEach(function(t){t.values.splice(0,v)}),s=e.updateXDomain(c,!0,!0),e.updateXGrid&&e.updateXGrid(!0),d.orgDataCount?a=1===d.orgDataCount||(b&&b.x)===(A&&A.x)?e.x(T[0])-e.x(s[0]):e.isTimeSeries()?e.x(T[0])-e.x(s[0]):e.x(b.x)-e.x(A.x):1!==e.data.targets[0].values.length?a=e.x(T[0])-e.x(s[0]):e.isTimeSeries()?(b=e.getValueOnIndex(e.data.targets[0].values,0),A=e.getValueOnIndex(e.data.targets[0].values,e.data.targets[0].values.length-1),a=e.x(b.x)-e.x(A.x)):a=f(s)/2,S=f(T)/f(s),r="translate("+a+",0) scale("+S+",1)",e.hideXGridFocus(),n.transition().ease("linear").duration(P).each(function(){C.add(e.axes.x.transition().call(e.xAxis)),C.add(O.transition().attr("transform",r)),C.add(R.transition().attr("transform",r)),C.add(D.transition().attr("transform",r)),C.add(F.transition().attr("transform",r)),C.add(I.transition().attr("transform",r)),C.add(E.filter(e.isRegionOnX).transition().attr("transform",r)),C.add(V.transition().attr("transform",r)),C.add(G.transition().attr("transform",r))}).call(C,function(){var t,n=[],a=[],r=[];if(v){for(t=0;t<v;t++)n.push("."+o.shape+"-"+(w+t)),a.push("."+o.text+"-"+(w+t)),r.push("."+o.eventRect+"-"+(w+t));e.svg.selectAll("."+o.shapes).selectAll(n).remove(),e.svg.selectAll("."+o.texts).selectAll(a).remove(),e.svg.selectAll("."+o.eventRects).selectAll(r).remove(),e.svg.select("."+o.xgrid).remove()}V.attr("transform",null).attr(e.xgridAttr),G.attr("transform",null),G.select("line").attr("x1",i.axis_rotated?0:_).attr("x2",i.axis_rotated?e.width:_),G.select("text").attr("x",i.axis_rotated?e.width:0).attr("y",_),O.attr("transform",null).attr("d",l),R.attr("transform",null).attr("d",u),D.attr("transform",null).attr("d",h),F.attr("transform",null).attr("cx",g).attr("cy",p),I.attr("transform",null).attr("x",x).attr("y",y).style("fill-opacity",e.opacityForText.bind(e)),E.attr("transform",null),E.select("rect").filter(e.isRegionOnX).attr("x",e.regionX.bind(e)).attr("width",e.regionWidth.bind(e)),i.interaction_enabled&&e.redrawEventRect(),L(),e.flowing=!1})}},v.focus=function(t){var e,i=this.internal;t=i.mapToTargetIds(t),e=i.svg.selectAll(i.selectorTargets(t.filter(i.isTargetToShow,i))),this.revert(),this.defocus(),e.classed(o.focused,!0).classed(o.defocused,!1),i.hasArcType()&&i.expandArc(t),i.toggleFocusLegend(t,!0),i.focusedTargetIds=t,i.defocusedTargetIds=i.defocusedTargetIds.filter(function(e){return t.indexOf(e)<0})},v.defocus=function(t){var e=this.internal;t=e.mapToTargetIds(t),e.svg.selectAll(e.selectorTargets(t.filter(e.isTargetToShow,e))).classed(o.focused,!1).classed(o.defocused,!0),e.hasArcType()&&e.unexpandArc(t),e.toggleFocusLegend(t,!1),e.focusedTargetIds=e.focusedTargetIds.filter(function(e){return t.indexOf(e)<0}),e.defocusedTargetIds=t},v.revert=function(t){var e=this.internal;t=e.mapToTargetIds(t),e.svg.selectAll(e.selectorTargets(t)).classed(o.focused,!1).classed(o.defocused,!1),e.hasArcType()&&e.unexpandArc(t),e.config.legend_show&&(e.showLegend(t.filter(e.isLegendToShow.bind(e))),e.legend.selectAll(e.selectorLegends(t)).filter(function(){return e.d3.select(this).classed(o.legendItemFocused)}).classed(o.legendItemFocused,!1)),e.focusedTargetIds=[],e.defocusedTargetIds=[]},v.xgrids=function(t){var e=this.internal,i=e.config;return t?(i.grid_x_lines=t,e.redrawWithoutRescale(),i.grid_x_lines):i.grid_x_lines},v.xgrids.add=function(t){var e=this.internal;return this.xgrids(e.config.grid_x_lines.concat(t||[]))},v.xgrids.remove=function(t){this.internal.removeGridLines(t,!0)},v.ygrids=function(t){var e=this.internal,i=e.config;return t?(i.grid_y_lines=t,e.redrawWithoutRescale(),i.grid_y_lines):i.grid_y_lines},v.ygrids.add=function(t){var e=this.internal;return this.ygrids(e.config.grid_y_lines.concat(t||[]))},v.ygrids.remove=function(t){this.internal.removeGridLines(t,!1)},v.groups=function(t){var e=this.internal,i=e.config;return void 0===t?i.data_groups:(i.data_groups=t,e.redraw(),i.data_groups)},v.legend=function(){},v.legend.show=function(t){var e=this.internal;e.showLegend(e.mapToTargetIds(t)),e.updateAndRedraw({withLegend:!0})},v.legend.hide=function(t){var e=this.internal;e.hideLegend(e.mapToTargetIds(t)),e.updateAndRedraw({withLegend:!0})},v.load=function(t){var e=this.internal,i=e.config;t.xs&&e.addXs(t.xs),"names"in t&&v.data.names.bind(this)(t.names),"classes"in t&&Object.keys(t.classes).forEach(function(e){i.data_classes[e]=t.classes[e]}),"categories"in t&&e.isCategorized()&&(i.axis_x_categories=t.categories),"axes"in t&&Object.keys(t.axes).forEach(function(e){i.data_axes[e]=t.axes[e]}),"colors"in t&&Object.keys(t.colors).forEach(function(e){i.data_colors[e]=t.colors[e]}),"cacheIds"in t&&e.hasCaches(t.cacheIds)?e.load(e.getCaches(t.cacheIds),t.done):"unload"in t?e.unload(e.mapToTargetIds("boolean"==typeof t.unload&&t.unload?null:t.unload),function(){e.loadFromArgs(t)}):e.loadFromArgs(t)},v.unload=function(t){var e=this.internal;(t=t||{})instanceof Array?t={ids:t}:"string"==typeof t&&(t={ids:[t]}),e.unload(e.mapToTargetIds(t.ids),function(){e.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0,withLegend:!0}),t.done&&t.done()})},v.regions=function(t){var e=this.internal,i=e.config;return t?(i.regions=t,e.redrawWithoutRescale(),i.regions):i.regions},v.regions.add=function(t){var e=this.internal,i=e.config;return t?(i.regions=i.regions.concat(t),e.redrawWithoutRescale(),i.regions):i.regions},v.regions.remove=function(t){var e,i,n,a=this.internal,r=a.config;return t=t||{},e=a.getOption(t,"duration",r.transition_duration),i=a.getOption(t,"classes",[o.region]),n=a.main.select("."+o.regions).selectAll(i.map(function(t){return"."+t})),(e?n.transition().duration(e):n).style("opacity",0).remove(),r.regions=r.regions.filter(function(t){var e=!1;return!t.class||(t.class.split(" ").forEach(function(t){i.indexOf(t)>=0&&(e=!0)}),!e)}),r.regions},v.selected=function(t){var e=this.internal,i=e.d3;return i.merge(e.main.selectAll("."+o.shapes+e.getTargetSelectorSuffix(t)).selectAll("."+o.shape).filter(function(){return i.select(this).classed(o.SELECTED)}).map(function(t){return t.map(function(t){var e=t.__data__;return e.data?e.data:e})}))},v.select=function(t,e,i){var n=this.internal,a=n.d3,r=n.config;r.data_selection_enabled&&n.main.selectAll("."+o.shapes).selectAll("."+o.shape).each(function(s,c){var d=a.select(this),l=s.data?s.data.id:s.id,u=n.getToggle(this,s).bind(n),h=r.data_selection_grouped||!t||t.indexOf(l)>=0,g=!e||e.indexOf(c)>=0,p=d.classed(o.SELECTED);d.classed(o.line)||d.classed(o.area)||(h&&g?r.data_selection_isselectable(s)&&!p&&u(!0,d.classed(o.SELECTED,!0),s,c):void 0!==i&&i&&p&&u(!1,d.classed(o.SELECTED,!1),s,c))})},v.unselect=function(t,e){var i=this.internal,n=i.d3,a=i.config;a.data_selection_enabled&&i.main.selectAll("."+o.shapes).selectAll("."+o.shape).each(function(r,s){var c=n.select(this),d=r.data?r.data.id:r.id,l=i.getToggle(this,r).bind(i),u=a.data_selection_grouped||!t||t.indexOf(d)>=0,h=!e||e.indexOf(s)>=0,g=c.classed(o.SELECTED);c.classed(o.line)||c.classed(o.area)||u&&h&&a.data_selection_isselectable(r)&&g&&l(!1,c.classed(o.SELECTED,!1),r,s)})},v.show=function(t,e){var i,n=this.internal;t=n.mapToTargetIds(t),e=e||{},n.removeHiddenTargetIds(t),(i=n.svg.selectAll(n.selectorTargets(t))).transition().style("opacity",1,"important").call(n.endall,function(){i.style("opacity",null).style("opacity",1)}),e.withLegend&&n.showLegend(t),n.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0,withLegend:!0})},v.hide=function(t,e){var i,n=this.internal;t=n.mapToTargetIds(t),e=e||{},n.addHiddenTargetIds(t),(i=n.svg.selectAll(n.selectorTargets(t))).transition().style("opacity",0,"important").call(n.endall,function(){i.style("opacity",null).style("opacity",0)}),e.withLegend&&n.hideLegend(t),n.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0,withLegend:!0})},v.toggle=function(t,e){var i=this,n=this.internal;n.mapToTargetIds(t).forEach(function(t){n.isTargetToShow(t)?i.hide(t,e):i.show(t,e)})},v.tooltip=function(){},v.tooltip.show=function(t){var e,i,n=this.internal;t.mouse&&(i=t.mouse),t.data?n.isMultipleX()?(i=[n.x(t.data.x),n.getYScale(t.data.id)(t.data.value)],e=null):e=s(t.data.index)?t.data.index:n.getIndexByX(t.data.x):void 0!==t.x?e=n.getIndexByX(t.x):void 0!==t.index&&(e=t.index),n.dispatchEvent("mouseover",e,i),n.dispatchEvent("mousemove",e,i),n.config.tooltip_onshow.call(n,t.data)},v.tooltip.hide=function(){this.internal.dispatchEvent("mouseout",0),this.internal.config.tooltip_onhide.call(this)},v.transform=function(t,e){var i=this.internal,n=["pie","donut"].indexOf(t)>=0?{withTransform:!0}:null;i.transformTo(e,t,n)},b.transformTo=function(t,e,i){var n=this,a=!n.hasArcType(),r=i||{withTransitionForAxis:a};r.withTransitionForTransform=!1,n.transiting=!1,n.setTargetType(t,e),n.updateTargets(n.data.targets),n.updateAndRedraw(r)},v.x=function(t){var e=this.internal;return arguments.length&&(e.updateTargetX(e.data.targets,t),e.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0})),e.data.xs},v.xs=function(t){var e=this.internal;return arguments.length&&(e.updateTargetXs(e.data.targets,t),e.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0})),e.data.xs},v.zoom=function(t){var e=this.internal;return t&&(e.isTimeSeries()&&(t=t.map(function(t){return e.parseDate(t)})),e.brush.extent(t),e.redraw({withUpdateXDomain:!0,withY:e.config.zoom_rescale}),e.config.zoom_onzoom.call(this,e.x.orgDomain())),e.brush.extent()},v.zoom.enable=function(t){var e=this.internal;e.config.zoom_enabled=t,e.updateAndRedraw()},v.unzoom=function(){var t=this.internal;t.brush.clear().update(),t.redraw({withUpdateXDomain:!0})},v.zoom.max=function(t){var e=this.internal,i=e.config,n=e.d3;if(0!==t&&!t)return i.zoom_x_max;i.zoom_x_max=n.max([e.orgXDomain[1],t])},v.zoom.min=function(t){var e=this.internal,i=e.config,n=e.d3;if(0!==t&&!t)return i.zoom_x_min;i.zoom_x_min=n.min([e.orgXDomain[0],t])},v.zoom.range=function(t){if(!arguments.length)return{max:this.domain.max(),min:this.domain.min()};void 0!==t.max&&this.domain.max(t.max),void 0!==t.min&&this.domain.min(t.min)},b.initPie=function(){var t=this,e=t.d3,i=t.config;t.pie=e.layout.pie().value(function(t){return t.values.reduce(function(t,e){return t+e.value},0)}),i.data_order||t.pie.sort(null)},b.updateRadius=function(){var t=this,e=t.config,i=e.gauge_width||e.donut_width;t.radiusExpanded=Math.min(t.arcWidth,t.arcHeight)/2,t.radius=.95*t.radiusExpanded,t.innerRadiusRatio=i?(t.radius-i)/t.radius:.6,t.innerRadius=t.hasType("donut")||t.hasType("gauge")?t.radius*t.innerRadiusRatio:0},b.updateArc=function(){var t=this;t.svgArc=t.getSvgArc(),t.svgArcExpanded=t.getSvgArcExpanded(),t.svgArcExpandedSub=t.getSvgArcExpanded(.98)},b.updateAngle=function(t){var e,i,n,a,r=this,o=r.config,s=!1,c=0;return o?(r.pie(r.filterTargetsToShow(r.data.targets)).forEach(function(e){s||e.data.id!==t.data.id||(s=!0,(t=e).index=c),c++}),isNaN(t.startAngle)&&(t.startAngle=0),isNaN(t.endAngle)&&(t.endAngle=t.startAngle),r.isGaugeType(t.data)&&(e=o.gauge_min,i=o.gauge_max,n=Math.PI*(o.gauge_fullCircle?2:1)/(i-e),a=t.value<e?0:t.value<i?t.value-e:i-e,t.startAngle=o.gauge_startingAngle,t.endAngle=t.startAngle+n*a),s?t:null):null},b.getSvgArc=function(){var t=this,e=t.d3.svg.arc().outerRadius(t.radius).innerRadius(t.innerRadius),i=function(i,n){var a;return n?e(i):(a=t.updateAngle(i),a?e(a):"M 0 0")};return i.centroid=e.centroid,i},b.getSvgArcExpanded=function(t){var e=this,i=e.d3.svg.arc().outerRadius(e.radiusExpanded*(t||1)).innerRadius(e.innerRadius);return function(t){var n=e.updateAngle(t);return n?i(n):"M 0 0"}},b.getArc=function(t,e,i){return i||this.isArcType(t.data)?this.svgArc(t,e):"M 0 0"},b.transformForArcLabel=function(t){var e,i,n,a,r,o=this,s=o.config,d=o.updateAngle(t),l="";return d&&!o.hasType("gauge")&&(e=this.svgArc.centroid(d),i=isNaN(e[0])?0:e[0],n=isNaN(e[1])?0:e[1],a=Math.sqrt(i*i+n*n),l="translate("+i*(r=o.hasType("donut")&&s.donut_label_ratio?c(s.donut_label_ratio)?s.donut_label_ratio(t,o.radius,a):s.donut_label_ratio:o.hasType("pie")&&s.pie_label_ratio?c(s.pie_label_ratio)?s.pie_label_ratio(t,o.radius,a):s.pie_label_ratio:o.radius&&a?(36/o.radius>.375?1.175-36/o.radius:.8)*o.radius/a:0)+","+n*r+")"),l},b.getArcRatio=function(t){var e=this,i=e.config,n=Math.PI*(e.hasType("gauge")&&!i.gauge_fullCircle?1:2);return t?(t.endAngle-t.startAngle)/n:null},b.convertToArcData=function(t){return this.addName({id:t.data.id,value:t.value,ratio:this.getArcRatio(t),index:t.index})},b.textForArcLabel=function(t){var e,i,n,a,r,o=this;return o.shouldShowArcLabel()?(e=o.updateAngle(t),i=e?e.value:null,n=o.getArcRatio(e),a=t.data.id,o.hasType("gauge")||o.meetsArcLabelThreshold(n)?(r=o.getArcLabelFormat(),r?r(i,n,a):o.defaultArcValueFormat(i,n)):""):""},b.textForGaugeMinMax=function(t,e){var i=this.getGaugeLabelExtents();return i?i(t,e):t},b.expandArc=function(t){var e,i=this;i.transiting?e=window.setInterval(function(){i.transiting||(window.clearInterval(e),i.legend.selectAll(".c3-legend-item-focused").size()>0&&i.expandArc(t))},10):(t=i.mapToTargetIds(t),i.svg.selectAll(i.selectorTargets(t,"."+o.chartArc)).each(function(t){i.shouldExpand(t.data.id)&&i.d3.select(this).selectAll("path").transition().duration(i.expandDuration(t.data.id)).attr("d",i.svgArcExpanded).transition().duration(2*i.expandDuration(t.data.id)).attr("d",i.svgArcExpandedSub).each(function(t){i.isDonutType(t.data)})}))},b.unexpandArc=function(t){var e=this;e.transiting||(t=e.mapToTargetIds(t),e.svg.selectAll(e.selectorTargets(t,"."+o.chartArc)).selectAll("path").transition().duration(function(t){return e.expandDuration(t.data.id)}).attr("d",e.svgArc),e.svg.selectAll("."+o.arc).style("opacity",1))},b.expandDuration=function(t){var e=this,i=e.config;return e.isDonutType(t)?i.donut_expand_duration:e.isGaugeType(t)?i.gauge_expand_duration:e.isPieType(t)?i.pie_expand_duration:50},b.shouldExpand=function(t){var e=this,i=e.config;return e.isDonutType(t)&&i.donut_expand||e.isGaugeType(t)&&i.gauge_expand||e.isPieType(t)&&i.pie_expand},b.shouldShowArcLabel=function(){var t=this,e=t.config,i=!0;return t.hasType("donut")?i=e.donut_label_show:t.hasType("pie")&&(i=e.pie_label_show),i},b.meetsArcLabelThreshold=function(t){var e=this,i=e.config;return t>=(e.hasType("donut")?i.donut_label_threshold:i.pie_label_threshold)},b.getArcLabelFormat=function(){var t=this,e=t.config,i=e.pie_label_format;return t.hasType("gauge")?i=e.gauge_label_format:t.hasType("donut")&&(i=e.donut_label_format),i},b.getGaugeLabelExtents=function(){return this.config.gauge_label_extents},b.getArcTitle=function(){var t=this;return t.hasType("donut")?t.config.donut_title:""},b.updateTargetsForArc=function(t){var e,i=this,n=i.main,a=i.classChartArc.bind(i),r=i.classArcs.bind(i),s=i.classFocus.bind(i);(e=n.select("."+o.chartArcs).selectAll("."+o.chartArc).data(i.pie(t)).attr("class",function(t){return a(t)+s(t.data)}).enter().append("g").attr("class",a)).append("g").attr("class",r),e.append("text").attr("dy",i.hasType("gauge")?"-.1em":".35em").style("opacity",0).style("text-anchor","middle").style("pointer-events","none")},b.initArc=function(){var t=this;t.arcs=t.main.select("."+o.chart).append("g").attr("class",o.chartArcs).attr("transform",t.getTranslate("arc")),t.arcs.append("text").attr("class",o.chartArcsTitle).style("text-anchor","middle").text(t.getArcTitle())},b.redrawArc=function(t,e,i){var n,a=this,r=a.d3,s=a.config,c=a.main;(n=c.selectAll("."+o.arcs).selectAll("."+o.arc).data(a.arcData.bind(a))).enter().append("path").attr("class",a.classArc.bind(a)).style("fill",function(t){return a.color(t.data)}).style("cursor",function(t){return s.interaction_enabled&&s.data_selection_isselectable(t)?"pointer":null}).style("opacity",0).each(function(t){a.isGaugeType(t.data)&&(t.startAngle=t.endAngle=s.gauge_startingAngle),this._current=t}),n.attr("transform",function(t){return!a.isGaugeType(t.data)&&i?"scale(0)":""}).style("opacity",function(t){return t===this._current?0:1}).on("mouseover",s.interaction_enabled?function(t){var e,i;a.transiting||(e=a.updateAngle(t))&&(i=a.convertToArcData(e),a.expandArc(e.data.id),a.api.focus(e.data.id),a.toggleFocusLegend(e.data.id,!0),a.config.data_onmouseover(i,this))}:null).on("mousemove",s.interaction_enabled?function(t){var e,i=a.updateAngle(t);i&&(e=[a.convertToArcData(i)],a.showTooltip(e,this))}:null).on("mouseout",s.interaction_enabled?function(t){var e,i;a.transiting||(e=a.updateAngle(t))&&(i=a.convertToArcData(e),a.unexpandArc(e.data.id),a.api.revert(),a.revertLegend(),a.hideTooltip(),a.config.data_onmouseout(i,this))}:null).on("click",s.interaction_enabled?function(t,e){var i,n=a.updateAngle(t);n&&(i=a.convertToArcData(n),a.toggleShape&&a.toggleShape(this,i,e),a.config.data_onclick.call(a.api,i,this))}:null).each(function(){a.transiting=!0}).transition().duration(t).attrTween("d",function(t){var e,i=a.updateAngle(t);return i?(isNaN(this._current.startAngle)&&(this._current.startAngle=0),isNaN(this._current.endAngle)&&(this._current.endAngle=this._current.startAngle),e=r.interpolate(this._current,i),this._current=e(0),function(i){var n=e(i);return n.data=t.data,a.getArc(n,!0)}):function(){return"M 0 0"}}).attr("transform",i?"scale(1)":"").style("fill",function(t){return a.levelColor?a.levelColor(t.data.values[0].value):a.color(t.data.id)}).style("opacity",1).call(a.endall,function(){a.transiting=!1}),n.exit().transition().duration(e).style("opacity",0).remove(),c.selectAll("."+o.chartArc).select("text").style("opacity",0).attr("class",function(t){return a.isGaugeType(t.data)?o.gaugeValue:""}).text(a.textForArcLabel.bind(a)).attr("transform",a.transformForArcLabel.bind(a)).style("font-size",function(t){return a.isGaugeType(t.data)?Math.round(a.radius/5)+"px":""}).transition().duration(t).style("opacity",function(t){return a.isTargetToShow(t.data.id)&&a.isArcType(t.data)?1:0}),c.select("."+o.chartArcsTitle).style("opacity",a.hasType("donut")||a.hasType("gauge")?1:0),a.hasType("gauge")&&(a.arcs.select("."+o.chartArcsBackground).attr("d",function(){var t={data:[{value:s.gauge_max}],startAngle:s.gauge_startingAngle,endAngle:-1*s.gauge_startingAngle};return a.getArc(t,!0,!0)}),a.arcs.select("."+o.chartArcsGaugeUnit).attr("dy",".75em").text(s.gauge_label_show?s.gauge_units:""),a.arcs.select("."+o.chartArcsGaugeMin).attr("dx",-1*(a.innerRadius+(a.radius-a.innerRadius)/(s.gauge_fullCircle?1:2))+"px").attr("dy","1.2em").text(s.gauge_label_show?a.textForGaugeMinMax(s.gauge_min,!1):""),a.arcs.select("."+o.chartArcsGaugeMax).attr("dx",a.innerRadius+(a.radius-a.innerRadius)/(s.gauge_fullCircle?1:2)+"px").attr("dy","1.2em").text(s.gauge_label_show?a.textForGaugeMinMax(s.gauge_max,!0):""))},b.initGauge=function(){var t=this.arcs;this.hasType("gauge")&&(t.append("path").attr("class",o.chartArcsBackground),t.append("text").attr("class",o.chartArcsGaugeUnit).style("text-anchor","middle").style("pointer-events","none"),t.append("text").attr("class",o.chartArcsGaugeMin).style("text-anchor","middle").style("pointer-events","none"),t.append("text").attr("class",o.chartArcsGaugeMax).style("text-anchor","middle").style("pointer-events","none"))},b.getGaugeLabelHeight=function(){return this.config.gauge_label_show?20:0},b.hasCaches=function(t){for(var e=0;e<t.length;e++)if(!(t[e]in this.cache))return!1;return!0},b.addCache=function(t,e){this.cache[t]=this.cloneTarget(e)},b.getCaches=function(t){var e,i=[];for(e=0;e<t.length;e++)t[e]in this.cache&&i.push(this.cloneTarget(this.cache[t[e]]));return i},b.categoryName=function(t){var e=this.config;return t<e.axis_x_categories.length?e.axis_x_categories[t]:t},b.generateClass=function(t,e){return" "+t+" "+t+this.getTargetSelectorSuffix(e)},b.classText=function(t){return this.generateClass(o.text,t.index)},b.classTexts=function(t){return this.generateClass(o.texts,t.id)},b.classShape=function(t){return this.generateClass(o.shape,t.index)},b.classShapes=function(t){return this.generateClass(o.shapes,t.id)},b.classLine=function(t){return this.classShape(t)+this.generateClass(o.line,t.id)},b.classLines=function(t){return this.classShapes(t)+this.generateClass(o.lines,t.id)},b.classCircle=function(t){return this.classShape(t)+this.generateClass(o.circle,t.index)},b.classCircles=function(t){return this.classShapes(t)+this.generateClass(o.circles,t.id)},b.classBar=function(t){return this.classShape(t)+this.generateClass(o.bar,t.index)},b.classBars=function(t){return this.classShapes(t)+this.generateClass(o.bars,t.id)},b.classArc=function(t){return this.classShape(t.data)+this.generateClass(o.arc,t.data.id)},b.classArcs=function(t){return this.classShapes(t.data)+this.generateClass(o.arcs,t.data.id)},b.classArea=function(t){return this.classShape(t)+this.generateClass(o.area,t.id)},b.classAreas=function(t){return this.classShapes(t)+this.generateClass(o.areas,t.id)},b.classRegion=function(t,e){return this.generateClass(o.region,e)+" "+("class"in t?t.class:"")},b.classEvent=function(t){return this.generateClass(o.eventRect,t.index)},b.classTarget=function(t){var e=this,i=e.config.data_classes[t],n="";return i&&(n=" "+o.target+"-"+i),e.generateClass(o.target,t)+n},b.classFocus=function(t){return this.classFocused(t)+this.classDefocused(t)},b.classFocused=function(t){return" "+(this.focusedTargetIds.indexOf(t.id)>=0?o.focused:"")},b.classDefocused=function(t){return" "+(this.defocusedTargetIds.indexOf(t.id)>=0?o.defocused:"")},b.classChartText=function(t){return o.chartText+this.classTarget(t.id)},b.classChartLine=function(t){return o.chartLine+this.classTarget(t.id)},b.classChartBar=function(t){return o.chartBar+this.classTarget(t.id)},b.classChartArc=function(t){return o.chartArc+this.classTarget(t.data.id)},b.getTargetSelectorSuffix=function(t){return t||0===t?("-"+t).replace(/[\s?!@#$%^&*()_=+,.<>'":;\[\]\/|~`{}\\]/g,"-"):""},b.selectorTarget=function(t,e){return(e||"")+"."+o.target+this.getTargetSelectorSuffix(t)},b.selectorTargets=function(t,e){var i=this;return t=t||[],t.length?t.map(function(t){return i.selectorTarget(t,e)}):null},b.selectorLegend=function(t){return"."+o.legendItem+this.getTargetSelectorSuffix(t)},b.selectorLegends=function(t){var e=this;return t&&t.length?t.map(function(t){return e.selectorLegend(t)}):null},b.getClipPath=function(t){return"url("+(window.navigator.appVersion.toLowerCase().indexOf("msie 9.")>=0?"":document.URL.split("#")[0])+"#"+t+")"},b.appendClip=function(t,e){return t.append("clipPath").attr("id",e).append("rect")},b.getAxisClipX=function(t){var e=Math.max(30,this.margin.left);return t?-(1+e):-(e-1)},b.getAxisClipY=function(t){return t?-20:-this.margin.top},b.getXAxisClipX=function(){var t=this;return t.getAxisClipX(!t.config.axis_rotated)},b.getXAxisClipY=function(){var t=this;return t.getAxisClipY(!t.config.axis_rotated)},b.getYAxisClipX=function(){var t=this;return t.config.axis_y_inner?-1:t.getAxisClipX(t.config.axis_rotated)},b.getYAxisClipY=function(){var t=this;return t.getAxisClipY(t.config.axis_rotated)},b.getAxisClipWidth=function(t){var e=this,i=Math.max(30,e.margin.left),n=Math.max(30,e.margin.right);return t?e.width+2+i+n:e.margin.left+20},b.getAxisClipHeight=function(t){return(t?this.margin.bottom:this.margin.top+this.height)+20},b.getXAxisClipWidth=function(){var t=this;return t.getAxisClipWidth(!t.config.axis_rotated)},b.getXAxisClipHeight=function(){var t=this;return t.getAxisClipHeight(!t.config.axis_rotated)},b.getYAxisClipWidth=function(){var t=this;return t.getAxisClipWidth(t.config.axis_rotated)+(t.config.axis_y_inner?20:0)},b.getYAxisClipHeight=function(){var t=this;return t.getAxisClipHeight(t.config.axis_rotated)},b.generateColor=function(){var t=this,e=t.config,i=t.d3,n=e.data_colors,a=x(e.color_pattern)?e.color_pattern:i.scale.category10().range(),r=e.data_color,o=[];return function(t){var e,i=t.id||t.data&&t.data.id||t;return n[i]instanceof Function?e=n[i](t):n[i]?e=n[i]:(o.indexOf(i)<0&&o.push(i),e=a[o.indexOf(i)%a.length],n[i]=e),r instanceof Function?r(e,t):e}},b.generateLevelColor=function(){var t=this.config,e=t.color_pattern,i=t.color_threshold,n="value"===i.unit,a=i.values&&i.values.length?i.values:[],r=i.max||100;return x(t.color_threshold)?function(t){var i,o=e[e.length-1];for(i=0;i<a.length;i++)if((n?t:100*t/r)<a[i]){o=e[i];break}return o}:null},b.getDefaultConfig=function(){var t={bindto:"#chart",svg_classname:void 0,size_width:void 0,size_height:void 0,padding_left:void 0,padding_right:void 0,padding_top:void 0,padding_bottom:void 0,resize_auto:!0,zoom_enabled:!1,zoom_extent:void 0,zoom_privileged:!1,zoom_rescale:!1,zoom_onzoom:function(){},zoom_onzoomstart:function(){},zoom_onzoomend:function(){},zoom_x_min:void 0,zoom_x_max:void 0,interaction_brighten:!0,interaction_enabled:!0,onmouseover:function(){},onmouseout:function(){},onresize:function(){},onresized:function(){},oninit:function(){},onrendered:function(){},transition_duration:350,data_x:void 0,data_xs:{},data_xFormat:"%Y-%m-%d",data_xLocaltime:!0,data_xSort:!0,data_idConverter:function(t){return t},data_names:{},data_classes:{},data_groups:[],data_axes:{},data_type:void 0,data_types:{},data_labels:{},data_order:"desc",data_regions:{},data_color:void 0,data_colors:{},data_hide:!1,data_filter:void 0,data_selection_enabled:!1,data_selection_grouped:!1,data_selection_isselectable:function(){return!0},data_selection_multiple:!0,data_selection_draggable:!1,data_onclick:function(){},data_onmouseover:function(){},data_onmouseout:function(){},data_onselected:function(){},data_onunselected:function(){},data_url:void 0,data_headers:void 0,data_json:void 0,data_rows:void 0,data_columns:void 0,data_mimeType:void 0,data_keys:void 0,data_empty_label_text:"",subchart_show:!1,subchart_size_height:60,subchart_axis_x_show:!0,subchart_onbrush:function(){},color_pattern:[],color_threshold:{},legend_show:!0,legend_hide:!1,legend_position:"bottom",legend_inset_anchor:"top-left",legend_inset_x:10,legend_inset_y:0,legend_inset_step:void 0,legend_item_onclick:void 0,legend_item_onmouseover:void 0,legend_item_onmouseout:void 0,legend_equally:!1,legend_padding:0,legend_item_tile_width:10,legend_item_tile_height:10,axis_rotated:!1,axis_x_show:!0,axis_x_type:"indexed",axis_x_localtime:!0,axis_x_categories:[],axis_x_tick_centered:!1,axis_x_tick_format:void 0,axis_x_tick_culling:{},axis_x_tick_culling_max:10,axis_x_tick_count:void 0,axis_x_tick_fit:!0,axis_x_tick_values:null,axis_x_tick_rotate:0,axis_x_tick_outer:!0,axis_x_tick_multiline:!0,axis_x_tick_width:null,axis_x_max:void 0,axis_x_min:void 0,axis_x_padding:{},axis_x_height:void 0,axis_x_extent:void 0,axis_x_label:{},axis_y_show:!0,axis_y_type:void 0,axis_y_max:void 0,axis_y_min:void 0,axis_y_inverted:!1,axis_y_center:void 0,axis_y_inner:void 0,axis_y_label:{},axis_y_tick_format:void 0,axis_y_tick_outer:!0,axis_y_tick_values:null,axis_y_tick_rotate:0,axis_y_tick_count:void 0,axis_y_tick_time_value:void 0,axis_y_tick_time_interval:void 0,axis_y_padding:{},axis_y_default:void 0,axis_y2_show:!1,axis_y2_max:void 0,axis_y2_min:void 0,axis_y2_inverted:!1,axis_y2_center:void 0,axis_y2_inner:void 0,axis_y2_label:{},axis_y2_tick_format:void 0,axis_y2_tick_outer:!0,axis_y2_tick_values:null,axis_y2_tick_count:void 0,axis_y2_padding:{},axis_y2_default:void 0,grid_x_show:!1,grid_x_type:"tick",grid_x_lines:[],grid_y_show:!1,grid_y_lines:[],grid_y_ticks:10,grid_focus_show:!0,grid_lines_front:!0,point_show:!0,point_r:2.5,point_sensitivity:10,point_focus_expand_enabled:!0,point_focus_expand_r:void 0,point_select_r:void 0,line_connectNull:!1,line_step_type:"step",bar_width:void 0,bar_width_ratio:.6,bar_width_max:void 0,bar_zerobased:!0,area_zerobased:!0,area_above:!1,pie_label_show:!0,pie_label_format:void 0,pie_label_threshold:.05,pie_label_ratio:void 0,pie_expand:{},pie_expand_duration:50,gauge_fullCircle:!1,gauge_label_show:!0,gauge_label_format:void 0,gauge_min:0,gauge_max:100,gauge_startingAngle:-1*Math.PI/2,gauge_label_extents:void 0,gauge_units:void 0,gauge_width:void 0,gauge_expand:{},gauge_expand_duration:50,donut_label_show:!0,donut_label_format:void 0,donut_label_threshold:.05,donut_label_ratio:void 0,donut_width:void 0,donut_title:"",donut_expand:{},donut_expand_duration:50,spline_interpolation_type:"cardinal",regions:[],tooltip_show:!0,tooltip_grouped:!0,tooltip_order:void 0,tooltip_format_title:void 0,tooltip_format_name:void 0,tooltip_format_value:void 0,tooltip_position:void 0,tooltip_contents:function(t,e,i,n){return this.getTooltipContent?this.getTooltipContent(t,e,i,n):""},tooltip_init_show:!1,tooltip_init_x:0,tooltip_init_position:{top:"0px",left:"50px"},tooltip_onshow:function(){},tooltip_onhide:function(){},title_text:void 0,title_padding:{top:0,right:0,bottom:0,left:0},title_position:"top-center"};return Object.keys(this.additionalConfig).forEach(function(e){t[e]=this.additionalConfig[e]},this),t},b.additionalConfig={},b.loadConfig=function(t){function e(){var t=n.shift();return t&&i&&"object"==typeof i&&t in i?(i=i[t],e()):t?void 0:i}var i,n,a,r=this.config;Object.keys(r).forEach(function(o){i=t,n=o.split("_"),void 0!==(a=e())&&(r[o]=a)})},b.convertUrlToData=function(t,e,i,n,a){var r=this,o=e||"csv",s=r.d3.xhr(t);i&&Object.keys(i).forEach(function(t){s.header(t,i[t])}),s.get(function(t,e){var i,s=e.response||e.responseText;if(!e)throw new Error(t.responseURL+" "+t.status+" ("+t.statusText+")");i="json"===o?r.convertJsonToData(JSON.parse(s),n):"tsv"===o?r.convertTsvToData(s):r.convertCsvToData(s),a.call(r,i)})},b.convertXsvToData=function(t,e){var i,n=e.parseRows(t);return 1===n.length?(i=[{}],n[0].forEach(function(t){i[0][t]=null})):i=e.parse(t),i},b.convertCsvToData=function(t){return this.convertXsvToData(t,this.d3.csv)},b.convertTsvToData=function(t){return this.convertXsvToData(t,this.d3.tsv)},b.convertJsonToData=function(t,e){var i,n,a=this,r=[];return e?(e.x?(i=e.value.concat(e.x),a.config.data_x=e.x):i=e.value,r.push(i),t.forEach(function(t){var e=[];i.forEach(function(i){var n=a.findValueInJson(t,i);u(n)&&(n=null),e.push(n)}),r.push(e)}),n=a.convertRowsToData(r)):(Object.keys(t).forEach(function(e){r.push([e].concat(t[e]))}),n=a.convertColumnsToData(r)),n},b.findValueInJson=function(t,e){for(var i=(e=(e=e.replace(/\[(\w+)\]/g,".$1")).replace(/^\./,"")).split("."),n=0;n<i.length;++n){var a=i[n];if(!(a in t))return;t=t[a]}return t},b.convertRowsToData=function(t){var e,i,n=t[0],a={},r=[];for(e=1;e<t.length;e++){for(a={},i=0;i<t[e].length;i++){if(void 0===t[e][i])throw new Error("Source data is missing a component at ("+e+","+i+")!");a[n[i]]=t[e][i]}r.push(a)}return r},b.convertColumnsToData=function(t){var e,i,n,a=[];for(e=0;e<t.length;e++)for(n=t[e][0],i=1;i<t[e].length;i++){if(void 0===a[i-1]&&(a[i-1]={}),void 0===t[e][i])throw new Error("Source data is missing a component at ("+e+","+i+")!");a[i-1][n]=t[e][i]}return a},b.convertDataToTargets=function(t,e){var i,n=this,a=n.config,r=n.d3.keys(t[0]).filter(n.isNotX,n),o=n.d3.keys(t[0]).filter(n.isX,n);return r.forEach(function(i){var r=n.getXKey(i);n.isCustomX()||n.isTimeSeries()?o.indexOf(r)>=0?n.data.xs[i]=(e&&n.data.xs[i]?n.data.xs[i]:[]).concat(t.map(function(t){return t[r]}).filter(s).map(function(t,e){return n.generateTargetX(t,i,e)})):a.data_x?n.data.xs[i]=n.getOtherTargetXs():x(a.data_xs)&&(n.data.xs[i]=n.getXValuesOfXKey(r,n.data.targets)):n.data.xs[i]=t.map(function(t,e){return e})}),r.forEach(function(t){if(!n.data.xs[t])throw new Error('x is not defined for id = "'+t+'".')}),(i=r.map(function(e,i){var r=a.data_idConverter(e);return{id:r,id_org:e,values:t.map(function(t,o){var s,c=t[n.getXKey(e)],d=null===t[e]||isNaN(t[e])?null:+t[e];return n.isCustomX()&&n.isCategorized()&&void 0!==c?(0===i&&0===o&&(a.axis_x_categories=[]),-1===(s=a.axis_x_categories.indexOf(c))&&(s=a.axis_x_categories.length,a.axis_x_categories.push(c))):s=n.generateTargetX(c,e,o),(void 0===t[e]||n.data.xs[e].length<=o)&&(s=void 0),{x:s,value:d,id:r}}).filter(function(t){return h(t.x)})}})).forEach(function(t){var e;a.data_xSort&&(t.values=t.values.sort(function(t,e){return(t.x||0===t.x?t.x:1/0)-(e.x||0===e.x?e.x:1/0)})),e=0,t.values.forEach(function(t){t.index=e++}),n.data.xs[t.id].sort(function(t,e){return t-e})}),n.hasNegativeValue=n.hasNegativeValueInTargets(i),n.hasPositiveValue=n.hasPositiveValueInTargets(i),a.data_type&&n.setTargetType(n.mapToIds(i).filter(function(t){return!(t in a.data_types)}),a.data_type),i.forEach(function(t){n.addCache(t.id_org,t)}),i},b.isX=function(t){var e=this.config;return e.data_x&&t===e.data_x||x(e.data_xs)&&m(e.data_xs,t)},b.isNotX=function(t){return!this.isX(t)},b.getXKey=function(t){var e=this.config;return e.data_x?e.data_x:x(e.data_xs)?e.data_xs[t]:null},b.getXValuesOfXKey=function(t,e){var i,n=this;return(e&&x(e)?n.mapToIds(e):[]).forEach(function(e){n.getXKey(e)===t&&(i=n.data.xs[e])}),i},b.getIndexByX=function(t){var e=this,i=e.filterByX(e.data.targets,t);return i.length?i[0].index:null},b.getXValue=function(t,e){var i=this;return t in i.data.xs&&i.data.xs[t]&&s(i.data.xs[t][e])?i.data.xs[t][e]:e},b.getOtherTargetXs=function(){var t=this,e=Object.keys(t.data.xs);return e.length?t.data.xs[e[0]]:null},b.getOtherTargetX=function(t){var e=this.getOtherTargetXs();return e&&t<e.length?e[t]:null},b.addXs=function(t){var e=this;Object.keys(t).forEach(function(i){e.config.data_xs[i]=t[i]})},b.hasMultipleX=function(t){return this.d3.set(Object.keys(t).map(function(e){return t[e]})).size()>1},b.isMultipleX=function(){return x(this.config.data_xs)||!this.config.data_xSort||this.hasType("scatter")},b.addName=function(t){var e,i=this;return t&&(e=i.config.data_names[t.id],t.name=void 0!==e?e:t.id),t},b.getValueOnIndex=function(t,e){var i=t.filter(function(t){return t.index===e});return i.length?i[0]:null},b.updateTargetX=function(t,e){var i=this;t.forEach(function(t){t.values.forEach(function(n,a){n.x=i.generateTargetX(e[a],t.id,a)}),i.data.xs[t.id]=e})},b.updateTargetXs=function(t,e){var i=this;t.forEach(function(t){e[t.id]&&i.updateTargetX([t],e[t.id])})},b.generateTargetX=function(t,e,i){var n=this;return n.isTimeSeries()?t?n.parseDate(t):n.parseDate(n.getXValue(e,i)):n.isCustomX()&&!n.isCategorized()?s(t)?+t:n.getXValue(e,i):i},b.cloneTarget=function(t){return{id:t.id,id_org:t.id_org,values:t.values.map(function(t){return{x:t.x,value:t.value,id:t.id}})}},b.updateXs=function(){var t=this;t.data.targets.length&&(t.xs=[],t.data.targets[0].values.forEach(function(e){t.xs[e.index]=e.x}))},b.getPrevX=function(t){var e=this.xs[t-1];return void 0!==e?e:null},b.getNextX=function(t){var e=this.xs[t+1];return void 0!==e?e:null},b.getMaxDataCount=function(){var t=this;return t.d3.max(t.data.targets,function(t){return t.values.length})},b.getMaxDataCountTarget=function(t){var e,i=t.length,n=0;return i>1?t.forEach(function(t){t.values.length>n&&(e=t,n=t.values.length)}):e=i?t[0]:null,e},b.getEdgeX=function(t){var e=this;return t.length?[e.d3.min(t,function(t){return t.values[0].x}),e.d3.max(t,function(t){return t.values[t.values.length-1].x})]:[0,0]},b.mapToIds=function(t){return t.map(function(t){return t.id})},b.mapToTargetIds=function(t){var e=this;return t?[].concat(t):e.mapToIds(e.data.targets)},b.hasTarget=function(t,e){var i,n=this.mapToIds(t);for(i=0;i<n.length;i++)if(n[i]===e)return!0;return!1},b.isTargetToShow=function(t){return this.hiddenTargetIds.indexOf(t)<0},b.isLegendToShow=function(t){return this.hiddenLegendIds.indexOf(t)<0},b.filterTargetsToShow=function(t){var e=this;return t.filter(function(t){return e.isTargetToShow(t.id)})},b.mapTargetsToUniqueXs=function(t){var e=this,i=e.d3.set(e.d3.merge(t.map(function(t){return t.values.map(function(t){return+t.x})}))).values();return(i=e.isTimeSeries()?i.map(function(t){return new Date(+t)}):i.map(function(t){return+t})).sort(function(t,e){return t<e?-1:t>e?1:t>=e?0:NaN})},b.addHiddenTargetIds=function(t){t=t instanceof Array?t:new Array(t);for(var e=0;e<t.length;e++)this.hiddenTargetIds.indexOf(t[e])<0&&(this.hiddenTargetIds=this.hiddenTargetIds.concat(t[e]))},b.removeHiddenTargetIds=function(t){this.hiddenTargetIds=this.hiddenTargetIds.filter(function(e){return t.indexOf(e)<0})},b.addHiddenLegendIds=function(t){t=t instanceof Array?t:new Array(t);for(var e=0;e<t.length;e++)this.hiddenLegendIds.indexOf(t[e])<0&&(this.hiddenLegendIds=this.hiddenLegendIds.concat(t[e]))},b.removeHiddenLegendIds=function(t){this.hiddenLegendIds=this.hiddenLegendIds.filter(function(e){return t.indexOf(e)<0})},b.getValuesAsIdKeyed=function(t){var e={};return t.forEach(function(t){e[t.id]=[],t.values.forEach(function(i){e[t.id].push(i.value)})}),e},b.checkValueInTargets=function(t,e){var i,n,a,r=Object.keys(t);for(i=0;i<r.length;i++)for(a=t[r[i]].values,n=0;n<a.length;n++)if(e(a[n].value))return!0;return!1},b.hasNegativeValueInTargets=function(t){return this.checkValueInTargets(t,function(t){return t<0})},b.hasPositiveValueInTargets=function(t){return this.checkValueInTargets(t,function(t){return t>0})},b.isOrderDesc=function(){var t=this.config;return"string"==typeof t.data_order&&"desc"===t.data_order.toLowerCase()},b.isOrderAsc=function(){var t=this.config;return"string"==typeof t.data_order&&"asc"===t.data_order.toLowerCase()},b.orderTargets=function(t){var e=this,i=e.config,n=e.isOrderAsc(),a=e.isOrderDesc();return n||a?t.sort(function(t,e){var i=function(t,e){return t+Math.abs(e.value)},a=t.values.reduce(i,0),r=e.values.reduce(i,0);return n?r-a:a-r}):c(i.data_order)?t.sort(i.data_order):d(i.data_order)&&t.sort(function(t,e){return i.data_order.indexOf(t.id)-i.data_order.indexOf(e.id)}),t},b.filterByX=function(t,e){return this.d3.merge(t.map(function(t){return t.values})).filter(function(t){return t.x-e==0})},b.filterRemoveNull=function(t){return t.filter(function(t){return s(t.value)})},b.filterByXDomain=function(t,e){return t.map(function(t){return{id:t.id,id_org:t.id_org,values:t.values.filter(function(t){return e[0]<=t.x&&t.x<=e[1]})}})},b.hasDataLabel=function(){var t=this.config;return!("boolean"!=typeof t.data_labels||!t.data_labels)||!("object"!=typeof t.data_labels||!x(t.data_labels))},b.getDataLabelLength=function(t,e,i){var n=this,a=[0,0];return n.selectChart.select("svg").selectAll(".dummy").data([t,e]).enter().append("text").text(function(t){return n.dataLabelFormat(t.id)(t)}).each(function(t,e){a[e]=1.3*this.getBoundingClientRect()[i]}).remove(),a},b.isNoneArc=function(t){return this.hasTarget(this.data.targets,t.id)},b.isArc=function(t){return"data"in t&&this.hasTarget(this.data.targets,t.data.id)},b.findSameXOfValues=function(t,e){var i,n=t[e].x,a=[];for(i=e-1;i>=0&&n===t[i].x;i--)a.push(t[i]);for(i=e;i<t.length&&n===t[i].x;i++)a.push(t[i]);return a},b.findClosestFromTargets=function(t,e){var i,n=this;return i=t.map(function(t){return n.findClosest(t.values,e)}),n.findClosest(i,e)},b.findClosest=function(t,e){var i,n=this,a=n.config.point_sensitivity;return t.filter(function(t){return t&&n.isBarType(t.id)}).forEach(function(t){var e=n.main.select("."+o.bars+n.getTargetSelectorSuffix(t.id)+" ."+o.bar+"-"+t.index).node();!i&&n.isWithinBar(e)&&(i=t)}),t.filter(function(t){return t&&!n.isBarType(t.id)}).forEach(function(t){var r=n.dist(t,e);r<a&&(a=r,i=t)}),i},b.dist=function(t,e){var i=this,n=i.config,a=n.axis_rotated?1:0,r=n.axis_rotated?0:1,o=i.circleY(t,t.index),s=i.x(t.x);return Math.sqrt(Math.pow(s-e[a],2)+Math.pow(o-e[r],2))},b.convertValuesToStep=function(t){var e,i=[].concat(t);if(!this.isCategorized())return t;for(e=t.length+1;0<e;e--)i[e]=i[e-1];return i[0]={x:i[0].x-1,value:i[0].value,id:i[0].id},i[t.length+1]={x:i[t.length].x+1,value:i[t.length].value,id:i[t.length].id},i},b.updateDataAttributes=function(t,e){var i=this,n=i.config["data_"+t];return void 0===e?n:(Object.keys(e).forEach(function(t){n[t]=e[t]}),i.redraw({withLegend:!0}),n)},b.load=function(t,e){var i=this;t&&(e.filter&&(t=t.filter(e.filter)),(e.type||e.types)&&t.forEach(function(t){var n=e.types&&e.types[t.id]?e.types[t.id]:e.type;i.setTargetType(t.id,n)}),i.data.targets.forEach(function(e){for(var i=0;i<t.length;i++)if(e.id===t[i].id){e.values=t[i].values,t.splice(i,1);break}}),i.data.targets=i.data.targets.concat(t)),i.updateTargets(i.data.targets),i.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0,withLegend:!0}),e.done&&e.done()},b.loadFromArgs=function(t){var e=this;t.data?e.load(e.convertDataToTargets(t.data),t):t.url?e.convertUrlToData(t.url,t.mimeType,t.headers,t.keys,function(i){e.load(e.convertDataToTargets(i),t)}):t.json?e.load(e.convertDataToTargets(e.convertJsonToData(t.json,t.keys)),t):t.rows?e.load(e.convertDataToTargets(e.convertRowsToData(t.rows)),t):t.columns?e.load(e.convertDataToTargets(e.convertColumnsToData(t.columns)),t):e.load(null,t)},b.unload=function(t,e){var i=this;e||(e=function(){}),(t=t.filter(function(t){return i.hasTarget(i.data.targets,t)}))&&0!==t.length?(i.svg.selectAll(t.map(function(t){return i.selectorTarget(t)})).transition().style("opacity",0).remove().call(i.endall,e),t.forEach(function(t){i.withoutFadeIn[t]=!1,i.legend&&i.legend.selectAll("."+o.legendItem+i.getTargetSelectorSuffix(t)).remove(),i.data.targets=i.data.targets.filter(function(e){return e.id!==t})})):e()},b.getYDomainMin=function(t){var e,i,n,a,r,o,s=this,c=s.config,d=s.mapToIds(t),l=s.getValuesAsIdKeyed(t);if(c.data_groups.length>0)for(o=s.hasNegativeValueInTargets(t),e=0;e<c.data_groups.length;e++)if(0!==(a=c.data_groups[e].filter(function(t){return d.indexOf(t)>=0})).length)for(n=a[0],o&&l[n]&&l[n].forEach(function(t,e){l[n][e]=t<0?t:0}),i=1;i<a.length;i++)r=a[i],l[r]&&l[r].forEach(function(t,e){s.axis.getId(r)!==s.axis.getId(n)||!l[n]||o&&+t>0||(l[n][e]+=+t)});return s.d3.min(Object.keys(l).map(function(t){return s.d3.min(l[t])}))},b.getYDomainMax=function(t){var e,i,n,a,r,o,s=this,c=s.config,d=s.mapToIds(t),l=s.getValuesAsIdKeyed(t);if(c.data_groups.length>0)for(o=s.hasPositiveValueInTargets(t),e=0;e<c.data_groups.length;e++)if(0!==(a=c.data_groups[e].filter(function(t){return d.indexOf(t)>=0})).length)for(n=a[0],o&&l[n]&&l[n].forEach(function(t,e){l[n][e]=t>0?t:0}),i=1;i<a.length;i++)r=a[i],l[r]&&l[r].forEach(function(t,e){s.axis.getId(r)!==s.axis.getId(n)||!l[n]||o&&+t<0||(l[n][e]+=+t)});return s.d3.max(Object.keys(l).map(function(t){return s.d3.max(l[t])}))},b.getYDomain=function(t,e,i){var n,a,r,o,c,d,l,u,h,g,p=this,_=p.config,y=t.filter(function(t){return p.axis.getId(t.id)===e}),m=i?p.filterByXDomain(y,i):y,S="y2"===e?_.axis_y2_min:_.axis_y_min,w="y2"===e?_.axis_y2_max:_.axis_y_max,v=p.getYDomainMin(m),b=p.getYDomainMax(m),A="y2"===e?_.axis_y2_center:_.axis_y_center,T=p.hasType("bar",m)&&_.bar_zerobased||p.hasType("area",m)&&_.area_zerobased,P="y2"===e?_.axis_y2_inverted:_.axis_y_inverted,L=p.hasDataLabel()&&_.axis_rotated,C=p.hasDataLabel()&&!_.axis_rotated;return v=s(S)?S:s(w)?v<w?v:w-10:v,b=s(w)?w:s(S)?S<b?b:S+10:b,0===m.length?"y2"===e?p.y2.domain():p.y.domain():(isNaN(v)&&(v=0),isNaN(b)&&(b=v),v===b&&(v<0?b=0:v=0),h=v>=0&&b>=0,g=v<=0&&b<=0,(s(S)&&h||s(w)&&g)&&(T=!1),T&&(h&&(v=0),g&&(b=0)),a=Math.abs(b-v),r=o=.1*a,void 0!==A&&(b=A+(c=Math.max(Math.abs(v),Math.abs(b))),v=A-c),L?(d=p.getDataLabelLength(v,b,"width"),l=f(p.y.range()),r+=a*((u=[d[0]/l,d[1]/l])[1]/(1-u[0]-u[1])),o+=a*(u[0]/(1-u[0]-u[1]))):C&&(d=p.getDataLabelLength(v,b,"height"),r+=p.axis.convertPixelsToAxisPadding(d[1],a),o+=p.axis.convertPixelsToAxisPadding(d[0],a)),"y"===e&&x(_.axis_y_padding)&&(r=p.axis.getPadding(_.axis_y_padding,"top",r,a),o=p.axis.getPadding(_.axis_y_padding,"bottom",o,a)),"y2"===e&&x(_.axis_y2_padding)&&(r=p.axis.getPadding(_.axis_y2_padding,"top",r,a),o=p.axis.getPadding(_.axis_y2_padding,"bottom",o,a)),T&&(h&&(o=v),g&&(r=-b)),n=[v-o,b+r],P?n.reverse():n)},b.getXDomainMin=function(t){var e=this,i=e.config;return void 0!==i.axis_x_min?e.isTimeSeries()?this.parseDate(i.axis_x_min):i.axis_x_min:e.d3.min(t,function(t){return e.d3.min(t.values,function(t){return t.x})})},b.getXDomainMax=function(t){var e=this,i=e.config;return void 0!==i.axis_x_max?e.isTimeSeries()?this.parseDate(i.axis_x_max):i.axis_x_max:e.d3.max(t,function(t){return e.d3.max(t.values,function(t){return t.x})})},b.getXDomainPadding=function(t){var e,i,n,a,r=this,o=r.config,c=t[1]-t[0];return i=r.isCategorized()?0:r.hasType("bar")?(e=r.getMaxDataCount())>1?c/(e-1)/2:.5:.01*c,"object"==typeof o.axis_x_padding&&x(o.axis_x_padding)?(n=s(o.axis_x_padding.left)?o.axis_x_padding.left:i,a=s(o.axis_x_padding.right)?o.axis_x_padding.right:i):n=a="number"==typeof o.axis_x_padding?o.axis_x_padding:i,{left:n,right:a}},b.getXDomain=function(t){var e=this,i=[e.getXDomainMin(t),e.getXDomainMax(t)],n=i[0],a=i[1],r=e.getXDomainPadding(i),o=0,s=0;return n-a!=0||e.isCategorized()||(e.isTimeSeries()?(n=new Date(.5*n.getTime()),a=new Date(1.5*a.getTime())):(n=0===n?1:.5*n,a=0===a?-1:1.5*a)),(n||0===n)&&(o=e.isTimeSeries()?new Date(n.getTime()-r.left):n-r.left),(a||0===a)&&(s=e.isTimeSeries()?new Date(a.getTime()+r.right):a+r.right),[o,s]},b.updateXDomain=function(t,e,i,n,a){var r=this,o=r.config;return i&&(r.x.domain(a||r.d3.extent(r.getXDomain(t))),r.orgXDomain=r.x.domain(),o.zoom_enabled&&r.zoom.scale(r.x).updateScaleExtent(),r.subX.domain(r.x.domain()),r.brush&&r.brush.scale(r.subX)),e&&(r.x.domain(a||(!r.brush||r.brush.empty()?r.orgXDomain:r.brush.extent())),o.zoom_enabled&&r.zoom.scale(r.x).updateScaleExtent()),n&&r.x.domain(r.trimXDomain(r.x.orgDomain())),r.x.domain()},b.trimXDomain=function(t){var e=this.getZoomDomain(),i=e[0],n=e[1];return t[0]<=i&&(t[1]=+t[1]+(i-t[0]),t[0]=i),n<=t[1]&&(t[0]=+t[0]-(t[1]-n),t[1]=n),t},b.drag=function(t){var e,i,n,a,r,s,c,d,l=this,u=l.config,h=l.main,g=l.d3;l.hasArcType()||u.data_selection_enabled&&(u.zoom_enabled&&!l.zoom.altDomain||u.data_selection_multiple&&(e=l.dragStart[0],i=l.dragStart[1],n=t[0],a=t[1],r=Math.min(e,n),s=Math.max(e,n),c=u.data_selection_grouped?l.margin.top:Math.min(i,a),d=u.data_selection_grouped?l.height:Math.max(i,a),h.select("."+o.dragarea).attr("x",r).attr("y",c).attr("width",s-r).attr("height",d-c),h.selectAll("."+o.shapes).selectAll("."+o.shape).filter(function(t){return u.data_selection_isselectable(t)}).each(function(t,e){var i,n,a,u,h,p,f=g.select(this),_=f.classed(o.SELECTED),x=f.classed(o.INCLUDED),y=!1;if(f.classed(o.circle))i=1*f.attr("cx"),n=1*f.attr("cy"),h=l.togglePoint,y=r<i&&i<s&&c<n&&n<d;else{if(!f.classed(o.bar))return;i=(p=w(this)).x,n=p.y,a=p.width,u=p.height,h=l.togglePath,y=!(s<i||i+a<r||d<n||n+u<c)}y^x&&(f.classed(o.INCLUDED,!x),f.classed(o.SELECTED,!_),h.call(l,!_,f,t,e))})))},b.dragstart=function(t){var e=this,i=e.config;e.hasArcType()||i.data_selection_enabled&&(e.dragStart=t,e.main.select("."+o.chart).append("rect").attr("class",o.dragarea).style("opacity",.1),e.dragging=!0)},b.dragend=function(){var t=this,e=t.config;t.hasArcType()||e.data_selection_enabled&&(t.main.select("."+o.dragarea).transition().duration(100).style("opacity",0).remove(),t.main.selectAll("."+o.shape).classed(o.INCLUDED,!1),t.dragging=!1)},b.getYFormat=function(t){var e=this,i=t&&!e.hasType("gauge")?e.defaultArcValueFormat:e.yFormat,n=t&&!e.hasType("gauge")?e.defaultArcValueFormat:e.y2Format;return function(t,a,r){return("y2"===e.axis.getId(r)?n:i).call(e,t,a)}},b.yFormat=function(t){var e=this,i=e.config;return(i.axis_y_tick_format?i.axis_y_tick_format:e.defaultValueFormat)(t)},b.y2Format=function(t){var e=this,i=e.config;return(i.axis_y2_tick_format?i.axis_y2_tick_format:e.defaultValueFormat)(t)},b.defaultValueFormat=function(t){return s(t)?+t:""},b.defaultArcValueFormat=function(t,e){return(100*e).toFixed(1)+"%"},b.dataLabelFormat=function(t){var e=this.config.data_labels,i=function(t){return s(t)?+t:""};return"function"==typeof e.format?e.format:"object"==typeof e.format?e.format[t]?!0===e.format[t]?i:e.format[t]:function(){return""}:i},b.initGrid=function(){var t=this,e=t.config,i=t.d3;t.grid=t.main.append("g").attr("clip-path",t.clipPathForGrid).attr("class",o.grid),e.grid_x_show&&t.grid.append("g").attr("class",o.xgrids),e.grid_y_show&&t.grid.append("g").attr("class",o.ygrids),e.grid_focus_show&&t.grid.append("g").attr("class",o.xgridFocus).append("line").attr("class",o.xgridFocus),t.xgrid=i.selectAll([]),e.grid_lines_front||t.initGridLines()},b.initGridLines=function(){var t=this,e=t.d3;t.gridLines=t.main.append("g").attr("clip-path",t.clipPathForGrid).attr("class",o.grid+" "+o.gridLines),t.gridLines.append("g").attr("class",o.xgridLines),t.gridLines.append("g").attr("class",o.ygridLines),t.xgridLines=e.selectAll([])},b.updateXGrid=function(t){var e=this,i=e.config,n=e.d3,a=e.generateGridData(i.grid_x_type,e.x),r=e.isCategorized()?e.xAxis.tickOffset():0;e.xgridAttr=i.axis_rotated?{x1:0,x2:e.width,y1:function(t){return e.x(t)-r},y2:function(t){return e.x(t)-r}}:{x1:function(t){return e.x(t)+r},x2:function(t){return e.x(t)+r},y1:0,y2:e.height},e.xgrid=e.main.select("."+o.xgrids).selectAll("."+o.xgrid).data(a),e.xgrid.enter().append("line").attr("class",o.xgrid),t||e.xgrid.attr(e.xgridAttr).style("opacity",function(){return+n.select(this).attr(i.axis_rotated?"y1":"x1")===(i.axis_rotated?e.height:0)?0:1}),e.xgrid.exit().remove()},b.updateYGrid=function(){var t=this,e=t.config,i=t.yAxis.tickValues()||t.y.ticks(e.grid_y_ticks);t.ygrid=t.main.select("."+o.ygrids).selectAll("."+o.ygrid).data(i),t.ygrid.enter().append("line").attr("class",o.ygrid),t.ygrid.attr("x1",e.axis_rotated?t.y:0).attr("x2",e.axis_rotated?t.y:t.width).attr("y1",e.axis_rotated?0:t.y).attr("y2",e.axis_rotated?t.height:t.y),t.ygrid.exit().remove(),t.smoothLines(t.ygrid,"grid")},b.gridTextAnchor=function(t){return t.position?t.position:"end"},b.gridTextDx=function(t){return"start"===t.position?4:"middle"===t.position?0:-4},b.xGridTextX=function(t){return"start"===t.position?-this.height:"middle"===t.position?-this.height/2:0},b.yGridTextX=function(t){return"start"===t.position?0:"middle"===t.position?this.width/2:this.width},b.updateGrid=function(t){var e,i,n,a=this,r=a.main,s=a.config;a.grid.style("visibility",a.hasArcType()?"hidden":"visible"),r.select("line."+o.xgridFocus).style("visibility","hidden"),s.grid_x_show&&a.updateXGrid(),a.xgridLines=r.select("."+o.xgridLines).selectAll("."+o.xgridLine).data(s.grid_x_lines),(e=a.xgridLines.enter().append("g").attr("class",function(t){return o.xgridLine+(t.class?" "+t.class:"")})).append("line").style("opacity",0),e.append("text").attr("text-anchor",a.gridTextAnchor).attr("transform",s.axis_rotated?"":"rotate(-90)").attr("dx",a.gridTextDx).attr("dy",-5).style("opacity",0),a.xgridLines.exit().transition().duration(t).style("opacity",0).remove(),s.grid_y_show&&a.updateYGrid(),a.ygridLines=r.select("."+o.ygridLines).selectAll("."+o.ygridLine).data(s.grid_y_lines),(i=a.ygridLines.enter().append("g").attr("class",function(t){return o.ygridLine+(t.class?" "+t.class:"")})).append("line").style("opacity",0),i.append("text").attr("text-anchor",a.gridTextAnchor).attr("transform",s.axis_rotated?"rotate(-90)":"").attr("dx",a.gridTextDx).attr("dy",-5).style("opacity",0),n=a.yv.bind(a),a.ygridLines.select("line").transition().duration(t).attr("x1",s.axis_rotated?n:0).attr("x2",s.axis_rotated?n:a.width).attr("y1",s.axis_rotated?0:n).attr("y2",s.axis_rotated?a.height:n).style("opacity",1),a.ygridLines.select("text").transition().duration(t).attr("x",s.axis_rotated?a.xGridTextX.bind(a):a.yGridTextX.bind(a)).attr("y",n).text(function(t){return t.text}).style("opacity",1),a.ygridLines.exit().transition().duration(t).style("opacity",0).remove()},b.redrawGrid=function(t){var e=this,i=e.config,n=e.xv.bind(e),a=e.xgridLines.select("line"),r=e.xgridLines.select("text");return[(t?a.transition():a).attr("x1",i.axis_rotated?0:n).attr("x2",i.axis_rotated?e.width:n).attr("y1",i.axis_rotated?n:0).attr("y2",i.axis_rotated?n:e.height).style("opacity",1),(t?r.transition():r).attr("x",i.axis_rotated?e.yGridTextX.bind(e):e.xGridTextX.bind(e)).attr("y",n).text(function(t){return t.text}).style("opacity",1)]},b.showXGridFocus=function(t){var e=this,i=e.config,n=t.filter(function(t){return t&&s(t.value)}),a=e.main.selectAll("line."+o.xgridFocus),r=e.xx.bind(e);i.tooltip_show&&(e.hasType("scatter")||e.hasArcType()||(a.style("visibility","visible").data([n[0]]).attr(i.axis_rotated?"y1":"x1",r).attr(i.axis_rotated?"y2":"x2",r),e.smoothLines(a,"grid")))},b.hideXGridFocus=function(){this.main.select("line."+o.xgridFocus).style("visibility","hidden")},b.updateXgridFocus=function(){var t=this,e=t.config;t.main.select("line."+o.xgridFocus).attr("x1",e.axis_rotated?0:-10).attr("x2",e.axis_rotated?t.width:-10).attr("y1",e.axis_rotated?-10:0).attr("y2",e.axis_rotated?-10:t.height)},b.generateGridData=function(t,e){var i,n,a,r,s=this,c=[],d=s.main.select("."+o.axisX).selectAll(".tick").size();if("year"===t)for(n=(i=s.getXDomain())[0].getFullYear(),a=i[1].getFullYear(),r=n;r<=a;r++)c.push(new Date(r+"-01-01 00:00:00"));else(c=e.ticks(10)).length>d&&(c=c.filter(function(t){return(""+t).indexOf(".")<0}));return c},b.getGridFilterToRemove=function(t){return t?function(e){var i=!1;return[].concat(t).forEach(function(t){("value"in t&&e.value===t.value||"class"in t&&e.class===t.class)&&(i=!0)}),i}:function(){return!0}},b.removeGridLines=function(t,e){var i=this,n=i.config,a=i.getGridFilterToRemove(t),r=function(t){return!a(t)},s=e?o.xgridLines:o.ygridLines,c=e?o.xgridLine:o.ygridLine;i.main.select("."+s).selectAll("."+c).filter(a).transition().duration(n.transition_duration).style("opacity",0).remove(),e?n.grid_x_lines=n.grid_x_lines.filter(r):n.grid_y_lines=n.grid_y_lines.filter(r)},b.initEventRect=function(){this.main.select("."+o.chart).append("g").attr("class",o.eventRects).style("fill-opacity",0)},b.redrawEventRect=function(){var t,e,i=this,n=i.config,a=i.isMultipleX(),r=i.main.select("."+o.eventRects).style("cursor",n.zoom_enabled?n.axis_rotated?"ns-resize":"ew-resize":null).classed(o.eventRectsMultiple,a).classed(o.eventRectsSingle,!a);r.selectAll("."+o.eventRect).remove(),i.eventRect=r.selectAll("."+o.eventRect),a?(t=i.eventRect.data([0]),i.generateEventRectsForMultipleXs(t.enter()),i.updateEventRect(t)):(e=i.getMaxDataCountTarget(i.data.targets),r.datum(e?e.values:[]),i.eventRect=r.selectAll("."+o.eventRect),t=i.eventRect.data(function(t){return t}),i.generateEventRectsForSingleX(t.enter()),i.updateEventRect(t),t.exit().remove())},b.updateEventRect=function(t){var e,i,n,a,r,o,s=this,c=s.config;t=t||s.eventRect.data(function(t){return t}),s.isMultipleX()?(e=0,i=0,n=s.width,a=s.height):(!s.isCustomX()&&!s.isTimeSeries()||s.isCategorized()?(r=s.getEventRectWidth(),o=function(t){return s.x(t.x)-r/2}):(s.updateXs(),r=function(t){var e=s.getPrevX(t.index),i=s.getNextX(t.index);return null===e&&null===i?c.axis_rotated?s.height:s.width:(null===e&&(e=s.x.domain()[0]),null===i&&(i=s.x.domain()[1]),Math.max(0,(s.x(i)-s.x(e))/2))},o=function(t){var e=s.getPrevX(t.index),i=s.getNextX(t.index),n=s.data.xs[t.id][t.index];return null===e&&null===i?0:(null===e&&(e=s.x.domain()[0]),(s.x(n)+s.x(e))/2)}),e=c.axis_rotated?0:o,i=c.axis_rotated?o:0,n=c.axis_rotated?s.width:r,a=c.axis_rotated?r:s.height),t.attr("class",s.classEvent.bind(s)).attr("x",e).attr("y",i).attr("width",n).attr("height",a)},b.generateEventRectsForSingleX=function(t){var e=this,i=e.d3,n=e.config;t.append("rect").attr("class",e.classEvent.bind(e)).style("cursor",n.data_selection_enabled&&n.data_selection_grouped?"pointer":null).on("mouseover",function(t){var i=t.index;e.dragging||e.flowing||e.hasArcType()||(n.point_focus_expand_enabled&&e.expandCircles(i,null,!0),e.expandBars(i,null,!0),e.main.selectAll("."+o.shape+"-"+i).each(function(t){n.data_onmouseover.call(e.api,t)}))}).on("mouseout",function(t){var i=t.index;e.config&&(e.hasArcType()||(e.hideXGridFocus(),e.hideTooltip(),e.unexpandCircles(),e.unexpandBars(),e.main.selectAll("."+o.shape+"-"+i).each(function(t){n.data_onmouseout.call(e.api,t)})))}).on("mousemove",function(t){var a,r=t.index,s=e.svg.select("."+o.eventRect+"-"+r);e.dragging||e.flowing||e.hasArcType()||(e.isStepType(t)&&"step-after"===e.config.line_step_type&&i.mouse(this)[0]<e.x(e.getXValue(t.id,r))&&(r-=1),a=e.filterTargetsToShow(e.data.targets).map(function(t){return e.addName(e.getValueOnIndex(t.values,r))}),n.tooltip_grouped&&(e.showTooltip(a,this),e.showXGridFocus(a)),(!n.tooltip_grouped||n.data_selection_enabled&&!n.data_selection_grouped)&&e.main.selectAll("."+o.shape+"-"+r).each(function(){i.select(this).classed(o.EXPANDED,!0),n.data_selection_enabled&&s.style("cursor",n.data_selection_grouped?"pointer":null),n.tooltip_grouped||(e.hideXGridFocus(),e.hideTooltip(),n.data_selection_grouped||(e.unexpandCircles(r),e.unexpandBars(r)))}).filter(function(t){return e.isWithinShape(this,t)}).each(function(t){n.data_selection_enabled&&(n.data_selection_grouped||n.data_selection_isselectable(t))&&s.style("cursor","pointer"),n.tooltip_grouped||(e.showTooltip([t],this),e.showXGridFocus([t]),n.point_focus_expand_enabled&&e.expandCircles(r,t.id,!0),e.expandBars(r,t.id,!0))}))}).on("click",function(t){var a=t.index;!e.hasArcType()&&e.toggleShape&&(e.cancelClick?e.cancelClick=!1:(e.isStepType(t)&&"step-after"===n.line_step_type&&i.mouse(this)[0]<e.x(e.getXValue(t.id,a))&&(a-=1),e.main.selectAll("."+o.shape+"-"+a).each(function(t){(n.data_selection_grouped||e.isWithinShape(this,t))&&(e.toggleShape(this,t,a),e.config.data_onclick.call(e.api,t,this))})))}).call(n.data_selection_draggable&&e.drag?i.behavior.drag().origin(Object).on("drag",function(){e.drag(i.mouse(this))}).on("dragstart",function(){e.dragstart(i.mouse(this))}).on("dragend",function(){e.dragend()}):function(){})},b.generateEventRectsForMultipleXs=function(t){function e(){i.svg.select("."+o.eventRect).style("cursor",null),i.hideXGridFocus(),i.hideTooltip(),i.unexpandCircles(),i.unexpandBars()}var i=this,n=i.d3,a=i.config;t.append("rect").attr("x",0).attr("y",0).attr("width",i.width).attr("height",i.height).attr("class",o.eventRect).on("mouseout",function(){i.config&&(i.hasArcType()||e())}).on("mousemove",function(){var t,r,s,c=i.filterTargetsToShow(i.data.targets);i.dragging||i.hasArcType(c)||(t=n.mouse(this),r=i.findClosestFromTargets(c,t),!i.mouseover||r&&r.id===i.mouseover.id||(a.data_onmouseout.call(i.api,i.mouseover),i.mouseover=void 0),r?(s=(i.isScatterType(r)||!a.tooltip_grouped?[r]:i.filterByX(c,r.x)).map(function(t){return i.addName(t)}),i.showTooltip(s,this),a.point_focus_expand_enabled&&i.expandCircles(r.index,r.id,!0),i.expandBars(r.index,r.id,!0),i.showXGridFocus(s),(i.isBarType(r.id)||i.dist(r,t)<a.point_sensitivity)&&(i.svg.select("."+o.eventRect).style("cursor","pointer"),i.mouseover||(a.data_onmouseover.call(i.api,r),i.mouseover=r))):e())}).on("click",function(){var t,e,r=i.filterTargetsToShow(i.data.targets);i.hasArcType(r)||(t=n.mouse(this),(e=i.findClosestFromTargets(r,t))&&(i.isBarType(e.id)||i.dist(e,t)<a.point_sensitivity)&&i.main.selectAll("."+o.shapes+i.getTargetSelectorSuffix(e.id)).selectAll("."+o.shape+"-"+e.index).each(function(){(a.data_selection_grouped||i.isWithinShape(this,e))&&(i.toggleShape(this,e,e.index),i.config.data_onclick.call(i.api,e,this))}))}).call(a.data_selection_draggable&&i.drag?n.behavior.drag().origin(Object).on("drag",function(){i.drag(n.mouse(this))}).on("dragstart",function(){i.dragstart(n.mouse(this))}).on("dragend",function(){i.dragend()}):function(){})},b.dispatchEvent=function(t,e,i){var n=this,a="."+o.eventRect+(n.isMultipleX()?"":"-"+e),r=n.main.select(a).node(),s=r.getBoundingClientRect(),c=s.left+(i?i[0]:0),d=s.top+(i?i[1]:0),l=document.createEvent("MouseEvents");l.initMouseEvent(t,!0,!0,window,0,c,d,c,d,!1,!1,!1,!1,0,null),r.dispatchEvent(l)},b.initLegend=function(){var t=this;if(t.legendItemTextBox={},t.legendHasRendered=!1,t.legend=t.svg.append("g").attr("transform",t.getTranslate("legend")),!t.config.legend_show)return t.legend.style("visibility","hidden"),void(t.hiddenLegendIds=t.mapToIds(t.data.targets));t.updateLegendWithDefaults()},b.updateLegendWithDefaults=function(){var t=this;t.updateLegend(t.mapToIds(t.data.targets),{withTransform:!1,withTransitionForTransform:!1,withTransition:!1})},b.updateSizeForLegend=function(t,e){var i=this,n=i.config,a={top:i.isLegendTop?i.getCurrentPaddingTop()+n.legend_inset_y+5.5:i.currentHeight-t-i.getCurrentPaddingBottom()-n.legend_inset_y,left:i.isLegendLeft?i.getCurrentPaddingLeft()+n.legend_inset_x+.5:i.currentWidth-e-i.getCurrentPaddingRight()-n.legend_inset_x+.5};i.margin3={top:i.isLegendRight?0:i.isLegendInset?a.top:i.currentHeight-t,right:NaN,bottom:0,left:i.isLegendRight?i.currentWidth-e:i.isLegendInset?a.left:0}},b.transformLegend=function(t){var e=this;(t?e.legend.transition():e.legend).attr("transform",e.getTranslate("legend"))},b.updateLegendStep=function(t){this.legendStep=t},b.updateLegendItemWidth=function(t){this.legendItemWidth=t},b.updateLegendItemHeight=function(t){this.legendItemHeight=t},b.getLegendWidth=function(){var t=this;return t.config.legend_show?t.isLegendRight||t.isLegendInset?t.legendItemWidth*(t.legendStep+1):t.currentWidth:0},b.getLegendHeight=function(){var t=this,e=0;return t.config.legend_show&&(e=t.isLegendRight?t.currentHeight:Math.max(20,t.legendItemHeight)*(t.legendStep+1)),e},b.opacityForLegend=function(t){return t.classed(o.legendItemHidden)?null:1},b.opacityForUnfocusedLegend=function(t){return t.classed(o.legendItemHidden)?null:.3},b.toggleFocusLegend=function(t,e){var i=this;t=i.mapToTargetIds(t),i.legend.selectAll("."+o.legendItem).filter(function(e){return t.indexOf(e)>=0}).classed(o.legendItemFocused,e).transition().duration(100).style("opacity",function(){return(e?i.opacityForLegend:i.opacityForUnfocusedLegend).call(i,i.d3.select(this))})},b.revertLegend=function(){var t=this,e=t.d3;t.legend.selectAll("."+o.legendItem).classed(o.legendItemFocused,!1).transition().duration(100).style("opacity",function(){return t.opacityForLegend(e.select(this))})},b.showLegend=function(t){var e=this,i=e.config;i.legend_show||(i.legend_show=!0,e.legend.style("visibility","visible"),e.legendHasRendered||e.updateLegendWithDefaults()),e.removeHiddenLegendIds(t),e.legend.selectAll(e.selectorLegends(t)).style("visibility","visible").transition().style("opacity",function(){return e.opacityForLegend(e.d3.select(this))})},b.hideLegend=function(t){var e=this,i=e.config;i.legend_show&&_(t)&&(i.legend_show=!1,e.legend.style("visibility","hidden")),e.addHiddenLegendIds(t),e.legend.selectAll(e.selectorLegends(t)).style("opacity",0).style("visibility","hidden")},b.clearLegendItemTextBoxCache=function(){this.legendItemTextBox={}},b.updateLegend=function(t,e,i){function n(t,e){return b.legendItemTextBox[e]||(b.legendItemTextBox[e]=b.getTextRect(t.textContent,o.legendItem,t)),b.legendItemTextBox[e]}function a(e,i,a){function r(t,e){e||(o=(p-E-g)/2)<V&&(o=(p-g)/2,E=0,X++),F[t]=X,D[X]=b.isLegendInset?10:o,I[t]=E,E+=g}var o,s,c=0===a,d=a===t.length-1,l=n(e,i),u=l.width+G+(!d||b.isLegendRight||b.isLegendInset?P:0)+A.legend_padding,h=l.height+T,g=b.isLegendRight||b.isLegendInset?h:u,p=b.isLegendRight||b.isLegendInset?b.getLegendHeight():b.getLegendWidth();c&&(E=0,X=0,L=0,C=0),!A.legend_show||b.isLegendToShow(i)?(O[i]=u,R[i]=h,(!L||u>=L)&&(L=u),(!C||h>=C)&&(C=h),s=b.isLegendRight||b.isLegendInset?C:L,A.legend_equally?(Object.keys(O).forEach(function(t){O[t]=L}),Object.keys(R).forEach(function(t){R[t]=C}),(o=(p-s*t.length)/2)<V?(E=0,X=0,t.forEach(function(t){r(t)})):r(i,!0)):r(i)):O[i]=R[i]=F[i]=I[i]=0}var r,s,c,d,l,u,h,g,p,f,_,x,m,S,w,v,b=this,A=b.config,T=4,P=10,L=0,C=0,V=10,G=A.legend_item_tile_width+5,E=0,I={},O={},R={},D=[0],F={},X=0;t=t.filter(function(t){return!(void 0!==A.data_names[t])||null!==A.data_names[t]}),_=y(e=e||{},"withTransition",!0),x=y(e,"withTransitionForTransform",!0),b.isLegendInset&&(X=A.legend_inset_step?A.legend_inset_step:t.length,b.updateLegendStep(X)),b.isLegendRight?(r=function(t){return L*F[t]},d=function(t){return D[F[t]]+I[t]}):b.isLegendInset?(r=function(t){return L*F[t]+10},d=function(t){return D[F[t]]+I[t]}):(r=function(t){return D[F[t]]+I[t]},d=function(t){return C*F[t]}),s=function(t,e){return r(t,e)+4+A.legend_item_tile_width},l=function(t,e){return d(t,e)+9},c=function(t,e){return r(t,e)},u=function(t,e){return d(t,e)-5},h=function(t,e){return r(t,e)-2},g=function(t,e){return r(t,e)-2+A.legend_item_tile_width},p=function(t,e){return d(t,e)+4},(f=b.legend.selectAll("."+o.legendItem).data(t).enter().append("g").attr("class",function(t){return b.generateClass(o.legendItem,t)}).style("visibility",function(t){return b.isLegendToShow(t)?"visible":"hidden"}).style("cursor","pointer").on("click",function(t){A.legend_item_onclick?A.legend_item_onclick.call(b,t):b.d3.event.altKey?(b.api.hide(),b.api.show(t)):(b.api.toggle(t),b.isTargetToShow(t)?b.api.focus(t):b.api.revert())}).on("mouseover",function(t){A.legend_item_onmouseover?A.legend_item_onmouseover.call(b,t):(b.d3.select(this).classed(o.legendItemFocused,!0),!b.transiting&&b.isTargetToShow(t)&&b.api.focus(t))}).on("mouseout",function(t){A.legend_item_onmouseout?A.legend_item_onmouseout.call(b,t):(b.d3.select(this).classed(o.legendItemFocused,!1),b.api.revert())})).append("text").text(function(t){return void 0!==A.data_names[t]?A.data_names[t]:t}).each(function(t,e){a(this,t,e)}).style("pointer-events","none").attr("x",b.isLegendRight||b.isLegendInset?s:-200).attr("y",b.isLegendRight||b.isLegendInset?-200:l),f.append("rect").attr("class",o.legendItemEvent).style("fill-opacity",0).attr("x",b.isLegendRight||b.isLegendInset?c:-200).attr("y",b.isLegendRight||b.isLegendInset?-200:u),f.append("line").attr("class",o.legendItemTile).style("stroke",b.color).style("pointer-events","none").attr("x1",b.isLegendRight||b.isLegendInset?h:-200).attr("y1",b.isLegendRight||b.isLegendInset?-200:p).attr("x2",b.isLegendRight||b.isLegendInset?g:-200).attr("y2",b.isLegendRight||b.isLegendInset?-200:p).attr("stroke-width",A.legend_item_tile_height),v=b.legend.select("."+o.legendBackground+" rect"),b.isLegendInset&&L>0&&0===v.size()&&(v=b.legend.insert("g","."+o.legendItem).attr("class",o.legendBackground).append("rect")),m=b.legend.selectAll("text").data(t).text(function(t){return void 0!==A.data_names[t]?A.data_names[t]:t}).each(function(t,e){a(this,t,e)}),(_?m.transition():m).attr("x",s).attr("y",l),S=b.legend.selectAll("rect."+o.legendItemEvent).data(t),(_?S.transition():S).attr("width",function(t){return O[t]}).attr("height",function(t){return R[t]}).attr("x",c).attr("y",u),w=b.legend.selectAll("line."+o.legendItemTile).data(t),(_?w.transition():w).style("stroke",b.color).attr("x1",h).attr("y1",p).attr("x2",g).attr("y2",p),v&&(_?v.transition():v).attr("height",b.getLegendHeight()-12).attr("width",L*(X+1)+10),b.legend.selectAll("."+o.legendItem).classed(o.legendItemHidden,function(t){return!b.isTargetToShow(t)}),b.updateLegendItemWidth(L),b.updateLegendItemHeight(C),b.updateLegendStep(X),b.updateSizes(),b.updateScales(),b.updateSvgSize(),b.transformAll(x,i),b.legendHasRendered=!0},b.initRegion=function(){var t=this;t.region=t.main.append("g").attr("clip-path",t.clipPath).attr("class",o.regions)},b.updateRegion=function(t){var e=this,i=e.config;e.region.style("visibility",e.hasArcType()?"hidden":"visible"),e.mainRegion=e.main.select("."+o.regions).selectAll("."+o.region).data(i.regions),e.mainRegion.enter().append("g").append("rect").style("fill-opacity",0),e.mainRegion.attr("class",e.classRegion.bind(e)),e.mainRegion.exit().transition().duration(t).style("opacity",0).remove()},b.redrawRegion=function(t){var e=this,i=e.mainRegion.selectAll("rect").each(function(){var t=e.d3.select(this.parentNode).datum();e.d3.select(this).datum(t)}),n=e.regionX.bind(e),a=e.regionY.bind(e),r=e.regionWidth.bind(e),o=e.regionHeight.bind(e);return[(t?i.transition():i).attr("x",n).attr("y",a).attr("width",r).attr("height",o).style("fill-opacity",function(t){return s(t.opacity)?t.opacity:.1})]},b.regionX=function(t){var e=this,i=e.config,n="y"===t.axis?e.y:e.y2;return"y"===t.axis||"y2"===t.axis?i.axis_rotated&&"start"in t?n(t.start):0:i.axis_rotated?0:"start"in t?e.x(e.isTimeSeries()?e.parseDate(t.start):t.start):0},b.regionY=function(t){var e=this,i=e.config,n="y"===t.axis?e.y:e.y2;return"y"===t.axis||"y2"===t.axis?i.axis_rotated?0:"end"in t?n(t.end):0:i.axis_rotated&&"start"in t?e.x(e.isTimeSeries()?e.parseDate(t.start):t.start):0},b.regionWidth=function(t){var e,i=this,n=i.config,a=i.regionX(t),r="y"===t.axis?i.y:i.y2;return e="y"===t.axis||"y2"===t.axis?n.axis_rotated&&"end"in t?r(t.end):i.width:n.axis_rotated?i.width:"end"in t?i.x(i.isTimeSeries()?i.parseDate(t.end):t.end):i.width,e<a?0:e-a},b.regionHeight=function(t){var e,i=this,n=i.config,a=this.regionY(t),r="y"===t.axis?i.y:i.y2;return e="y"===t.axis||"y2"===t.axis?n.axis_rotated?i.height:"start"in t?r(t.start):i.height:n.axis_rotated&&"end"in t?i.x(i.isTimeSeries()?i.parseDate(t.end):t.end):i.height,e<a?0:e-a},b.isRegionOnX=function(t){return!t.axis||"x"===t.axis},b.getScale=function(t,e,i){return(i?this.d3.time.scale():this.d3.scale.linear()).range([t,e])},b.getX=function(t,e,i,n){var a,r=this,o=r.getScale(t,e,r.isTimeSeries()),s=i?o.domain(i):o;r.isCategorized()?(n=n||function(){return 0},o=function(t,e){var i=s(t)+n(t);return e?i:Math.ceil(i)}):o=function(t,e){var i=s(t);return e?i:Math.ceil(i)};for(a in s)o[a]=s[a];return o.orgDomain=function(){return s.domain()},r.isCategorized()&&(o.domain=function(t){return arguments.length?(s.domain(t),o):(t=this.orgDomain(),[t[0],t[1]+1])}),o},b.getY=function(t,e,i){var n=this.getScale(t,e,this.isTimeSeriesY());return i&&n.domain(i),n},b.getYScale=function(t){return"y2"===this.axis.getId(t)?this.y2:this.y},b.getSubYScale=function(t){return"y2"===this.axis.getId(t)?this.subY2:this.subY},b.updateScales=function(){var t=this,e=t.config,i=!t.x;t.xMin=e.axis_rotated?1:0,t.xMax=e.axis_rotated?t.height:t.width,t.yMin=e.axis_rotated?0:t.height,t.yMax=e.axis_rotated?t.width:1,t.subXMin=t.xMin,t.subXMax=t.xMax,t.subYMin=e.axis_rotated?0:t.height2,t.subYMax=e.axis_rotated?t.width2:1,t.x=t.getX(t.xMin,t.xMax,i?void 0:t.x.orgDomain(),function(){return t.xAxis.tickOffset()}),t.y=t.getY(t.yMin,t.yMax,i?e.axis_y_default:t.y.domain()),t.y2=t.getY(t.yMin,t.yMax,i?e.axis_y2_default:t.y2.domain()),t.subX=t.getX(t.xMin,t.xMax,t.orgXDomain,function(e){return e%1?0:t.subXAxis.tickOffset()}),t.subY=t.getY(t.subYMin,t.subYMax,i?e.axis_y_default:t.subY.domain()),t.subY2=t.getY(t.subYMin,t.subYMax,i?e.axis_y2_default:t.subY2.domain()),t.xAxisTickFormat=t.axis.getXAxisTickFormat(),t.xAxisTickValues=t.axis.getXAxisTickValues(),t.yAxisTickValues=t.axis.getYAxisTickValues(),t.y2AxisTickValues=t.axis.getY2AxisTickValues(),t.xAxis=t.axis.getXAxis(t.x,t.xOrient,t.xAxisTickFormat,t.xAxisTickValues,e.axis_x_tick_outer),t.subXAxis=t.axis.getXAxis(t.subX,t.subXOrient,t.xAxisTickFormat,t.xAxisTickValues,e.axis_x_tick_outer),t.yAxis=t.axis.getYAxis(t.y,t.yOrient,e.axis_y_tick_format,t.yAxisTickValues,e.axis_y_tick_outer),t.y2Axis=t.axis.getYAxis(t.y2,t.y2Orient,e.axis_y2_tick_format,t.y2AxisTickValues,e.axis_y2_tick_outer),i||(t.brush&&t.brush.scale(t.subX),e.zoom_enabled&&t.zoom.scale(t.x)),t.updateArc&&t.updateArc()},b.selectPoint=function(t,e,i){var n=this,a=n.config,r=(a.axis_rotated?n.circleY:n.circleX).bind(n),s=(a.axis_rotated?n.circleX:n.circleY).bind(n),c=n.pointSelectR.bind(n);a.data_onselected.call(n.api,e,t.node()),n.main.select("."+o.selectedCircles+n.getTargetSelectorSuffix(e.id)).selectAll("."+o.selectedCircle+"-"+i).data([e]).enter().append("circle").attr("class",function(){return n.generateClass(o.selectedCircle,i)}).attr("cx",r).attr("cy",s).attr("stroke",function(){return n.color(e)}).attr("r",function(t){return 1.4*n.pointSelectR(t)}).transition().duration(100).attr("r",c)},b.unselectPoint=function(t,e,i){var n=this;n.config.data_onunselected.call(n.api,e,t.node()),n.main.select("."+o.selectedCircles+n.getTargetSelectorSuffix(e.id)).selectAll("."+o.selectedCircle+"-"+i).transition().duration(100).attr("r",0).remove()},b.togglePoint=function(t,e,i,n){t?this.selectPoint(e,i,n):this.unselectPoint(e,i,n)},b.selectPath=function(t,e){var i=this;i.config.data_onselected.call(i,e,t.node()),i.config.interaction_brighten&&t.transition().duration(100).style("fill",function(){return i.d3.rgb(i.color(e)).brighter(.75)})},b.unselectPath=function(t,e){var i=this;i.config.data_onunselected.call(i,e,t.node()),i.config.interaction_brighten&&t.transition().duration(100).style("fill",function(){return i.color(e)})},b.togglePath=function(t,e,i,n){t?this.selectPath(e,i,n):this.unselectPath(e,i,n)},b.getToggle=function(t,e){var i,n=this;return"circle"===t.nodeName?i=n.isStepType(e)?function(){}:n.togglePoint:"path"===t.nodeName&&(i=n.togglePath),i},b.toggleShape=function(t,e,i){var n=this,a=n.d3,r=n.config,s=a.select(t),c=s.classed(o.SELECTED),d=n.getToggle(t,e).bind(n);r.data_selection_enabled&&r.data_selection_isselectable(e)&&(r.data_selection_multiple||n.main.selectAll("."+o.shapes+(r.data_selection_grouped?n.getTargetSelectorSuffix(e.id):"")).selectAll("."+o.shape).each(function(t,e){var i=a.select(this);i.classed(o.SELECTED)&&d(!1,i.classed(o.SELECTED,!1),t,e)}),s.classed(o.SELECTED,!c),d(!c,s,e,i))},b.initBar=function(){this.main.select("."+o.chart).append("g").attr("class",o.chartBars)},b.updateTargetsForBar=function(t){var e=this,i=e.config,n=e.classChartBar.bind(e),a=e.classBars.bind(e),r=e.classFocus.bind(e);e.main.select("."+o.chartBars).selectAll("."+o.chartBar).data(t).attr("class",function(t){return n(t)+r(t)}).enter().append("g").attr("class",n).style("opacity",0).style("pointer-events","none").append("g").attr("class",a).style("cursor",function(t){return i.data_selection_isselectable(t)?"pointer":null})},b.updateBar=function(t){var e=this,i=e.barData.bind(e),n=e.classBar.bind(e),a=e.initialOpacity.bind(e),r=function(t){return e.color(t.id)};e.mainBar=e.main.selectAll("."+o.bars).selectAll("."+o.bar).data(i),e.mainBar.enter().append("path").attr("class",n).style("stroke",r).style("fill",r),e.mainBar.style("opacity",a),e.mainBar.exit().transition().duration(t).style("opacity",0).remove()},b.redrawBar=function(t,e){return[(e?this.mainBar.transition(Math.random().toString()):this.mainBar).attr("d",t).style("fill",this.color).style("opacity",1)]},b.getBarW=function(t,e){var i=this.config,n="number"==typeof i.bar_width?i.bar_width:e?t.tickInterval()*i.bar_width_ratio/e:0;return i.bar_width_max&&n>i.bar_width_max?i.bar_width_max:n},b.getBars=function(t,e){var i=this;return(e?i.main.selectAll("."+o.bars+i.getTargetSelectorSuffix(e)):i.main).selectAll("."+o.bar+(s(t)?"-"+t:""))},b.expandBars=function(t,e,i){var n=this;i&&n.unexpandBars(),n.getBars(t,e).classed(o.EXPANDED,!0)},b.unexpandBars=function(t){this.getBars(t).classed(o.EXPANDED,!1)},b.generateDrawBar=function(t,e){var i=this,n=i.config,a=i.generateGetBarPoints(t,e);return function(t,e){var i=a(t,e),r=n.axis_rotated?1:0,o=n.axis_rotated?0:1;return"M "+i[0][r]+","+i[0][o]+" L"+i[1][r]+","+i[1][o]+" L"+i[2][r]+","+i[2][o]+" L"+i[3][r]+","+i[3][o]+" z"}},b.generateGetBarPoints=function(t,e){var i=this,n=e?i.subXAxis:i.xAxis,a=t.__max__+1,r=i.getBarW(n,a),o=i.getShapeX(r,a,t,!!e),s=i.getShapeY(!!e),c=i.getShapeOffset(i.isBarType,t,!!e),d=e?i.getSubYScale:i.getYScale;return function(t,e){var n=d.call(i,t.id)(0),a=c(t,e)||n,l=o(t),u=s(t);return i.config.axis_rotated&&(0<t.value&&u<n||t.value<0&&n<u)&&(u=n),[[l,a],[l,u-(n-a)],[l+r,u-(n-a)],[l+r,a]]}},b.isWithinBar=function(t){var e=this.d3.mouse(t),i=t.getBoundingClientRect(),n=t.pathSegList.getItem(0),a=t.pathSegList.getItem(1),r=Math.min(n.x,a.x),o=Math.min(n.y,a.y),s=r+i.width+2,c=o+i.height+2,d=o-2;return r-2<e[0]&&e[0]<s&&d<e[1]&&e[1]<c},b.getShapeIndices=function(t){var e,i,n=this,a=n.config,r={},o=0;return n.filterTargetsToShow(n.data.targets.filter(t,n)).forEach(function(t){for(e=0;e<a.data_groups.length;e++)if(!(a.data_groups[e].indexOf(t.id)<0))for(i=0;i<a.data_groups[e].length;i++)if(a.data_groups[e][i]in r){r[t.id]=r[a.data_groups[e][i]];break}void 0===r[t.id]&&(r[t.id]=o++)}),r.__max__=o-1,r},b.getShapeX=function(t,e,i,n){var a=this,r=n?a.subX:a.x;return function(n){var a=n.id in i?i[n.id]:0;return n.x||0===n.x?r(n.x)-t*(e/2-a):0}},b.getShapeY=function(t){var e=this;return function(i){return(t?e.getSubYScale(i.id):e.getYScale(i.id))(i.value)}},b.getShapeOffset=function(t,e,i){var n=this,a=n.orderTargets(n.filterTargetsToShow(n.data.targets.filter(t,n))),r=a.map(function(t){return t.id});return function(t,o){var s=i?n.getSubYScale(t.id):n.getYScale(t.id),c=s(0),d=c;return a.forEach(function(i){var a=n.isStepType(t)?n.convertValuesToStep(i.values):i.values;i.id!==t.id&&e[i.id]===e[t.id]&&r.indexOf(i.id)<r.indexOf(t.id)&&(void 0!==a[o]&&+a[o].x==+t.x||(o=-1,a.forEach(function(e,i){e.x===t.x&&(o=i)})),o in a&&a[o].value*t.value>=0&&(d+=s(a[o].value)-c))}),d}},b.isWithinShape=function(t,e){var i,n=this,a=n.d3.select(t);return n.isTargetToShow(e.id)?"circle"===t.nodeName?i=n.isStepType(e)?n.isWithinStep(t,n.getYScale(e.id)(e.value)):n.isWithinCircle(t,1.5*n.pointSelectR(e)):"path"===t.nodeName&&(i=!a.classed(o.bar)||n.isWithinBar(t)):i=!1,i},b.getInterpolate=function(t){var e=this,i=e.isInterpolationType(e.config.spline_interpolation_type)?e.config.spline_interpolation_type:"cardinal";return e.isSplineType(t)?i:e.isStepType(t)?e.config.line_step_type:"linear"},b.initLine=function(){this.main.select("."+o.chart).append("g").attr("class",o.chartLines)},b.updateTargetsForLine=function(t){var e,i=this,n=i.config,a=i.classChartLine.bind(i),r=i.classLines.bind(i),s=i.classAreas.bind(i),c=i.classCircles.bind(i),d=i.classFocus.bind(i);(e=i.main.select("."+o.chartLines).selectAll("."+o.chartLine).data(t).attr("class",function(t){return a(t)+d(t)}).enter().append("g").attr("class",a).style("opacity",0).style("pointer-events","none")).append("g").attr("class",r),e.append("g").attr("class",s),e.append("g").attr("class",function(t){return i.generateClass(o.selectedCircles,t.id)}),e.append("g").attr("class",c).style("cursor",function(t){return n.data_selection_isselectable(t)?"pointer":null}),t.forEach(function(t){i.main.selectAll("."+o.selectedCircles+i.getTargetSelectorSuffix(t.id)).selectAll("."+o.selectedCircle).each(function(e){e.value=t.values[e.index].value})})},b.updateLine=function(t){var e=this;e.mainLine=e.main.selectAll("."+o.lines).selectAll("."+o.line).data(e.lineData.bind(e)),e.mainLine.enter().append("path").attr("class",e.classLine.bind(e)).style("stroke",e.color),e.mainLine.style("opacity",e.initialOpacity.bind(e)).style("shape-rendering",function(t){return e.isStepType(t)?"crispEdges":""}).attr("transform",null),e.mainLine.exit().transition().duration(t).style("opacity",0).remove()},b.redrawLine=function(t,e){return[(e?this.mainLine.transition(Math.random().toString()):this.mainLine).attr("d",t).style("stroke",this.color).style("opacity",1)]},b.generateDrawLine=function(t,e){var i=this,n=i.config,a=i.d3.svg.line(),r=i.generateGetLinePoints(t,e),o=e?i.getSubYScale:i.getYScale,s=function(t){return(e?i.subxx:i.xx).call(i,t)},c=function(t,e){return n.data_groups.length>0?r(t,e)[0][1]:o.call(i,t.id)(t.value)};return a=n.axis_rotated?a.x(c).y(s):a.x(s).y(c),n.line_connectNull||(a=a.defined(function(t){return null!=t.value})),function(t){var r,s=n.line_connectNull?i.filterRemoveNull(t.values):t.values,c=e?i.x:i.subX,d=o.call(i,t.id),l=0,u=0;return i.isLineType(t)?n.data_regions[t.id]?r=i.lineWithRegions(s,c,d,n.data_regions[t.id]):(i.isStepType(t)&&(s=i.convertValuesToStep(s)),r=a.interpolate(i.getInterpolate(t))(s)):(s[0]&&(l=c(s[0].x),u=d(s[0].value)),r=n.axis_rotated?"M "+u+" "+l:"M "+l+" "+u),r||"M 0 0"}},b.generateGetLinePoints=function(t,e){var i=this,n=i.config,a=t.__max__+1,r=i.getShapeX(0,a,t,!!e),o=i.getShapeY(!!e),s=i.getShapeOffset(i.isLineType,t,!!e),c=e?i.getSubYScale:i.getYScale;return function(t,e){var a=c.call(i,t.id)(0),d=s(t,e)||a,l=r(t),u=o(t);return n.axis_rotated&&(0<t.value&&u<a||t.value<0&&a<u)&&(u=a),[[l,u-(a-d)],[l,u-(a-d)],[l,u-(a-d)],[l,u-(a-d)]]}},b.lineWithRegions=function(t,e,i,n){function a(t){return"M"+t[0][0]+" "+t[0][1]+" "+t[1][0]+" "+t[1][1]}var r,o,s,c,d,l,u,h,g,p,f,_=this,x=_.config,y="M",m=_.isCategorized()?.5:0,S=[];if(void 0!==n)for(r=0;r<n.length;r++)S[r]={},void 0===n[r].start?S[r].start=t[0].x:S[r].start=_.isTimeSeries()?_.parseDate(n[r].start):n[r].start,void 0===n[r].end?S[r].end=t[t.length-1].x:S[r].end=_.isTimeSeries()?_.parseDate(n[r].end):n[r].end;for(p=x.axis_rotated?function(t){return i(t.value)}:function(t){return e(t.x)},f=x.axis_rotated?function(t){return e(t.x)}:function(t){return i(t.value)},s=_.isTimeSeries()?function(t,n,r,o){var s,c=t.x.getTime(),l=n.x-t.x,u=new Date(c+l*r),h=new Date(c+l*(r+o));return s=x.axis_rotated?[[i(d(r)),e(u)],[i(d(r+o)),e(h)]]:[[e(u),i(d(r))],[e(h),i(d(r+o))]],a(s)}:function(t,n,r,o){var s;return s=x.axis_rotated?[[i(d(r),!0),e(c(r))],[i(d(r+o),!0),e(c(r+o))]]:[[e(c(r),!0),i(d(r))],[e(c(r+o),!0),i(d(r+o))]],a(s)},r=0;r<t.length;r++){if(void 0!==S&&function(t,e){var i;for(i=0;i<e.length;i++)if(e[i].start<t&&t<=e[i].end)return!0;return!1}(t[r].x,S))for(c=_.getScale(t[r-1].x+m,t[r].x+m,_.isTimeSeries()),d=_.getScale(t[r-1].value,t[r].value),l=e(t[r].x)-e(t[r-1].x),u=i(t[r].value)-i(t[r-1].value),g=2*(h=2/Math.sqrt(Math.pow(l,2)+Math.pow(u,2))),o=h;o<=1;o+=g)y+=s(t[r-1],t[r],o,h);else y+=" "+p(t[r])+" "+f(t[r]);t[r].x}return y},b.updateArea=function(t){var e=this,i=e.d3;e.mainArea=e.main.selectAll("."+o.areas).selectAll("."+o.area).data(e.lineData.bind(e)),e.mainArea.enter().append("path").attr("class",e.classArea.bind(e)).style("fill",e.color).style("opacity",function(){return e.orgAreaOpacity=+i.select(this).style("opacity"),0}),e.mainArea.style("opacity",e.orgAreaOpacity),e.mainArea.exit().transition().duration(t).style("opacity",0).remove()},b.redrawArea=function(t,e){return[(e?this.mainArea.transition(Math.random().toString()):this.mainArea).attr("d",t).style("fill",this.color).style("opacity",this.orgAreaOpacity)]},b.generateDrawArea=function(t,e){var i=this,n=i.config,a=i.d3.svg.area(),r=i.generateGetAreaPoints(t,e),o=e?i.getSubYScale:i.getYScale,s=function(t){return(e?i.subxx:i.xx).call(i,t)},c=function(t,e){return n.data_groups.length>0?r(t,e)[0][1]:o.call(i,t.id)(i.getAreaBaseValue(t.id))},d=function(t,e){return n.data_groups.length>0?r(t,e)[1][1]:o.call(i,t.id)(t.value)};return a=n.axis_rotated?a.x0(c).x1(d).y(s):a.x(s).y0(n.area_above?0:c).y1(d),n.line_connectNull||(a=a.defined(function(t){return null!==t.value})),function(t){var e,r=n.line_connectNull?i.filterRemoveNull(t.values):t.values,o=0,s=0;return i.isAreaType(t)?(i.isStepType(t)&&(r=i.convertValuesToStep(r)),e=a.interpolate(i.getInterpolate(t))(r)):(r[0]&&(o=i.x(r[0].x),s=i.getYScale(t.id)(r[0].value)),e=n.axis_rotated?"M "+s+" "+o:"M "+o+" "+s),e||"M 0 0"}},b.getAreaBaseValue=function(){return 0},b.generateGetAreaPoints=function(t,e){var i=this,n=i.config,a=t.__max__+1,r=i.getShapeX(0,a,t,!!e),o=i.getShapeY(!!e),s=i.getShapeOffset(i.isAreaType,t,!!e),c=e?i.getSubYScale:i.getYScale;return function(t,e){var a=c.call(i,t.id)(0),d=s(t,e)||a,l=r(t),u=o(t);return n.axis_rotated&&(0<t.value&&u<a||t.value<0&&a<u)&&(u=a),[[l,d],[l,u-(a-d)],[l,u-(a-d)],[l,d]]}},b.updateCircle=function(){var t=this;t.mainCircle=t.main.selectAll("."+o.circles).selectAll("."+o.circle).data(t.lineOrScatterData.bind(t)),t.mainCircle.enter().append("circle").attr("class",t.classCircle.bind(t)).attr("r",t.pointR.bind(t)).style("fill",t.color),t.mainCircle.style("opacity",t.initialOpacityForCircle.bind(t)),t.mainCircle.exit().remove()},b.redrawCircle=function(t,e,i){var n=this.main.selectAll("."+o.selectedCircle);return[(i?this.mainCircle.transition(Math.random().toString()):this.mainCircle).style("opacity",this.opacityForCircle.bind(this)).style("fill",this.color).attr("cx",t).attr("cy",e),(i?n.transition(Math.random().toString()):n).attr("cx",t).attr("cy",e)]},b.circleX=function(t){return t.x||0===t.x?this.x(t.x):null},b.updateCircleY=function(){var t,e,i=this;i.config.data_groups.length>0?(t=i.getShapeIndices(i.isLineType),e=i.generateGetLinePoints(t),i.circleY=function(t,i){return e(t,i)[0][1]}):i.circleY=function(t){return i.getYScale(t.id)(t.value)}},b.getCircles=function(t,e){var i=this;return(e?i.main.selectAll("."+o.circles+i.getTargetSelectorSuffix(e)):i.main).selectAll("."+o.circle+(s(t)?"-"+t:""))},b.expandCircles=function(t,e,i){var n=this,a=n.pointExpandedR.bind(n);i&&n.unexpandCircles(),n.getCircles(t,e).classed(o.EXPANDED,!0).attr("r",a)},b.unexpandCircles=function(t){var e=this,i=e.pointR.bind(e);e.getCircles(t).filter(function(){return e.d3.select(this).classed(o.EXPANDED)}).classed(o.EXPANDED,!1).attr("r",i)},b.pointR=function(t){var e=this,i=e.config;return e.isStepType(t)?0:c(i.point_r)?i.point_r(t):i.point_r},b.pointExpandedR=function(t){var e=this,i=e.config;return i.point_focus_expand_enabled?i.point_focus_expand_r?i.point_focus_expand_r:1.75*e.pointR(t):e.pointR(t)},b.pointSelectR=function(t){var e=this,i=e.config;return c(i.point_select_r)?i.point_select_r(t):i.point_select_r?i.point_select_r:4*e.pointR(t)},b.isWithinCircle=function(t,e){var i=this.d3,n=i.mouse(t),a=i.select(t),r=+a.attr("cx"),o=+a.attr("cy");return Math.sqrt(Math.pow(r-n[0],2)+Math.pow(o-n[1],2))<e},b.isWithinStep=function(t,e){return Math.abs(e-this.d3.mouse(t)[1])<30},b.getCurrentWidth=function(){var t=this,e=t.config;return e.size_width?e.size_width:t.getParentWidth()},b.getCurrentHeight=function(){var t=this,e=t.config,i=e.size_height?e.size_height:t.getParentHeight();return i>0?i:320/(t.hasType("gauge")&&!e.gauge_fullCircle?2:1)},b.getCurrentPaddingTop=function(){var t=this,e=t.config,i=s(e.padding_top)?e.padding_top:0;return t.title&&t.title.node()&&(i+=t.getTitlePadding()),i},b.getCurrentPaddingBottom=function(){var t=this.config;return s(t.padding_bottom)?t.padding_bottom:0},b.getCurrentPaddingLeft=function(t){var e=this,i=e.config;return s(i.padding_left)?i.padding_left:i.axis_rotated?i.axis_x_show?Math.max(g(e.getAxisWidthByAxisId("x",t)),40):1:!i.axis_y_show||i.axis_y_inner?e.axis.getYAxisLabelPosition().isOuter?30:1:g(e.getAxisWidthByAxisId("y",t))},b.getCurrentPaddingRight=function(){var t=this,e=t.config,i=t.isLegendRight?t.getLegendWidth()+20:0;return s(e.padding_right)?e.padding_right+1:e.axis_rotated?10+i:!e.axis_y2_show||e.axis_y2_inner?2+i+(t.axis.getY2AxisLabelPosition().isOuter?20:0):g(t.getAxisWidthByAxisId("y2"))+i},b.getParentRectValue=function(t){for(var e,i=this.selectChart.node();i&&"BODY"!==i.tagName;){try{e=i.getBoundingClientRect()[t]}catch(n){"width"===t&&(e=i.offsetWidth)}if(e)break;i=i.parentNode}return e},b.getParentWidth=function(){return this.getParentRectValue("width")},b.getParentHeight=function(){var t=this.selectChart.style("height");return t.indexOf("px")>0?+t.replace("px",""):0},b.getSvgLeft=function(t){var e=this,i=e.config,n=i.axis_rotated||!i.axis_rotated&&!i.axis_y_inner,a=i.axis_rotated?o.axisX:o.axisY,r=e.main.select("."+a).node(),s=r&&n?r.getBoundingClientRect():{right:0},c=e.selectChart.node().getBoundingClientRect(),d=e.hasArcType(),l=s.right-c.left-(d?0:e.getCurrentPaddingLeft(t));return l>0?l:0},b.getAxisWidthByAxisId=function(t,e){var i=this,n=i.axis.getLabelPositionById(t);return i.axis.getMaxTickWidth(t,e)+(n.isInner?20:40)},b.getHorizontalAxisHeight=function(t){var e=this,i=e.config,n=30;return"x"!==t||i.axis_x_show?"x"===t&&i.axis_x_height?i.axis_x_height:"y"!==t||i.axis_y_show?"y2"!==t||i.axis_y2_show?("x"===t&&!i.axis_rotated&&i.axis_x_tick_rotate&&(n=30+e.axis.getMaxTickWidth(t)*Math.cos(Math.PI*(90-i.axis_x_tick_rotate)/180)),"y"===t&&i.axis_rotated&&i.axis_y_tick_rotate&&(n=30+e.axis.getMaxTickWidth(t)*Math.cos(Math.PI*(90-i.axis_y_tick_rotate)/180)),n+(e.axis.getLabelPositionById(t).isInner?0:10)+("y2"===t?-10:0)):e.rotated_padding_top:!i.legend_show||e.isLegendRight||e.isLegendInset?1:10:8},b.getEventRectWidth=function(){return Math.max(0,this.xAxis.tickInterval())},b.initBrush=function(){var t=this,e=t.d3;t.brush=e.svg.brush().on("brush",function(){t.redrawForBrush()}),t.brush.update=function(){return t.context&&t.context.select("."+o.brush).call(this),this},t.brush.scale=function(e){return t.config.axis_rotated?this.y(e):this.x(e)}},b.initSubchart=function(){var t=this,e=t.config,i=t.context=t.svg.append("g").attr("transform",t.getTranslate("context")),n=e.subchart_show?"visible":"hidden";i.style("visibility",n),i.append("g").attr("clip-path",t.clipPathForSubchart).attr("class",o.chart),i.select("."+o.chart).append("g").attr("class",o.chartBars),i.select("."+o.chart).append("g").attr("class",o.chartLines),i.append("g").attr("clip-path",t.clipPath).attr("class",o.brush).call(t.brush),t.axes.subx=i.append("g").attr("class",o.axisX).attr("transform",t.getTranslate("subx")).attr("clip-path",e.axis_rotated?"":t.clipPathForXAxis).style("visibility",e.subchart_axis_x_show?n:"hidden")},b.updateTargetsForSubchart=function(t){var e,i=this,n=i.context,a=i.config,r=i.classChartBar.bind(i),s=i.classBars.bind(i),c=i.classChartLine.bind(i),d=i.classLines.bind(i),l=i.classAreas.bind(i);a.subchart_show&&(n.select("."+o.chartBars).selectAll("."+o.chartBar).data(t).attr("class",r).enter().append("g").style("opacity",0).attr("class",r).append("g").attr("class",s),(e=n.select("."+o.chartLines).selectAll("."+o.chartLine).data(t).attr("class",c).enter().append("g").style("opacity",0).attr("class",c)).append("g").attr("class",d),e.append("g").attr("class",l),n.selectAll("."+o.brush+" rect").attr(a.axis_rotated?"width":"height",a.axis_rotated?i.width2:i.height2))},b.updateBarForSubchart=function(t){var e=this;e.contextBar=e.context.selectAll("."+o.bars).selectAll("."+o.bar).data(e.barData.bind(e)),e.contextBar.enter().append("path").attr("class",e.classBar.bind(e)).style("stroke","none").style("fill",e.color),e.contextBar.style("opacity",e.initialOpacity.bind(e)),e.contextBar.exit().transition().duration(t).style("opacity",0).remove()},b.redrawBarForSubchart=function(t,e,i){(e?this.contextBar.transition(Math.random().toString()).duration(i):this.contextBar).attr("d",t).style("opacity",1)},b.updateLineForSubchart=function(t){var e=this;e.contextLine=e.context.selectAll("."+o.lines).selectAll("."+o.line).data(e.lineData.bind(e)),e.contextLine.enter().append("path").attr("class",e.classLine.bind(e)).style("stroke",e.color),e.contextLine.style("opacity",e.initialOpacity.bind(e)),e.contextLine.exit().transition().duration(t).style("opacity",0).remove()},b.redrawLineForSubchart=function(t,e,i){(e?this.contextLine.transition(Math.random().toString()).duration(i):this.contextLine).attr("d",t).style("opacity",1)},b.updateAreaForSubchart=function(t){var e=this,i=e.d3;e.contextArea=e.context.selectAll("."+o.areas).selectAll("."+o.area).data(e.lineData.bind(e)),e.contextArea.enter().append("path").attr("class",e.classArea.bind(e)).style("fill",e.color).style("opacity",function(){return e.orgAreaOpacity=+i.select(this).style("opacity"),0}),e.contextArea.style("opacity",0),e.contextArea.exit().transition().duration(t).style("opacity",0).remove()},b.redrawAreaForSubchart=function(t,e,i){(e?this.contextArea.transition(Math.random().toString()).duration(i):this.contextArea).attr("d",t).style("fill",this.color).style("opacity",this.orgAreaOpacity)},b.redrawSubchart=function(t,e,i,n,a,r,o){var s,c,d,l=this,u=l.d3,h=l.config;l.context.style("visibility",h.subchart_show?"visible":"hidden"),h.subchart_show&&(u.event&&"zoom"===u.event.type&&l.brush.extent(l.x.orgDomain()).update(),t&&(l.brush.empty()||l.brush.extent(l.x.orgDomain()).update(),s=l.generateDrawArea(a,!0),c=l.generateDrawBar(r,!0),d=l.generateDrawLine(o,!0),l.updateBarForSubchart(i),l.updateLineForSubchart(i),l.updateAreaForSubchart(i),l.redrawBarForSubchart(c,i,i),l.redrawLineForSubchart(d,i,i),l.redrawAreaForSubchart(s,i,i)))},b.redrawForBrush=function(){var t=this,e=t.x;t.redraw({withTransition:!1,withY:t.config.zoom_rescale,withSubchart:!1,withUpdateXDomain:!0,withDimension:!1}),t.config.subchart_onbrush.call(t.api,e.orgDomain())},b.transformContext=function(t,e){var i,n=this;e&&e.axisSubX?i=e.axisSubX:(i=n.context.select("."+o.axisX),t&&(i=i.transition())),n.context.attr("transform",n.getTranslate("context")),i.attr("transform",n.getTranslate("subx"))},b.getDefaultExtent=function(){var t=this,e=t.config,i=c(e.axis_x_extent)?e.axis_x_extent(t.getXDomain(t.data.targets)):e.axis_x_extent;return t.isTimeSeries()&&(i=[t.parseDate(i[0]),t.parseDate(i[1])]),i},b.initText=function(){var t=this;t.main.select("."+o.chart).append("g").attr("class",o.chartTexts),t.mainText=t.d3.selectAll([])},b.updateTargetsForText=function(t){var e=this,i=e.classChartText.bind(e),n=e.classTexts.bind(e),a=e.classFocus.bind(e);e.main.select("."+o.chartTexts).selectAll("."+o.chartText).data(t).attr("class",function(t){return i(t)+a(t)}).enter().append("g").attr("class",i).style("opacity",0).style("pointer-events","none").append("g").attr("class",n)},b.updateText=function(t){var e=this,i=e.config,n=e.barOrLineData.bind(e),a=e.classText.bind(e);e.mainText=e.main.selectAll("."+o.texts).selectAll("."+o.text).data(n),e.mainText.enter().append("text").attr("class",a).attr("text-anchor",function(t){return i.axis_rotated?t.value<0?"end":"start":"middle"}).style("stroke","none").style("fill",function(t){return e.color(t)}).style("fill-opacity",0),e.mainText.text(function(t,i,n){return e.dataLabelFormat(t.id)(t.value,t.id,i,n)}),e.mainText.exit().transition().duration(t).style("fill-opacity",0).remove()},b.redrawText=function(t,e,i,n){return[(n?this.mainText.transition():this.mainText).attr("x",t).attr("y",e).style("fill",this.color).style("fill-opacity",i?0:this.opacityForText.bind(this))]},b.getTextRect=function(t,e,i){var n,a=this.d3.select("body").append("div").classed("c3",!0),r=a.append("svg").style("visibility","hidden").style("position","fixed").style("top",0).style("left",0),o=this.d3.select(i).style("font");return r.selectAll(".dummy").data([t]).enter().append("text").classed(e||"",!0).style("font",o).text(t).each(function(){n=this.getBoundingClientRect()}),a.remove(),n},b.generateXYForText=function(t,e,i,n){var a=this,r=a.generateGetAreaPoints(t,!1),o=a.generateGetBarPoints(e,!1),s=a.generateGetLinePoints(i,!1),c=n?a.getXForText:a.getYForText;return function(t,e){var i=a.isAreaType(t)?r:a.isBarType(t)?o:s;return c.call(a,i(t,e),t,this)}},b.getXForText=function(t,e,i){var n,a,r=this,o=i.getBoundingClientRect();return r.config.axis_rotated?(a=r.isBarType(e)?4:6,n=t[2][1]+a*(e.value<0?-1:1)):n=r.hasType("bar")?(t[2][0]+t[0][0])/2:t[0][0],null===e.value&&(n>r.width?n=r.width-o.width:n<0&&(n=4)),n},b.getYForText=function(t,e,i){var n,a=this,r=i.getBoundingClientRect();return a.config.axis_rotated?n=(t[0][0]+t[2][0]+.6*r.height)/2:(n=t[2][1],e.value<0||0===e.value&&!a.hasPositiveValue?(n+=r.height,a.isBarType(e)&&a.isSafari()?n-=3:!a.isBarType(e)&&a.isChrome()&&(n+=3)):n+=a.isBarType(e)?-3:-6),null!==e.value||a.config.axis_rotated||(n<r.height?n=r.height:n>this.height&&(n=this.height-4)),n},b.initTitle=function(){var t=this;t.title=t.svg.append("text").text(t.config.title_text).attr("class",t.CLASS.title)},b.redrawTitle=function(){var t=this;t.title.attr("x",t.xForTitle.bind(t)).attr("y",t.yForTitle.bind(t))},b.xForTitle=function(){var t=this,e=t.config,i=e.title_position||"left";return i.indexOf("right")>=0?t.currentWidth-t.getTextRect(t.title.node().textContent,t.CLASS.title,t.title.node()).width-e.title_padding.right:i.indexOf("center")>=0?(t.currentWidth-t.getTextRect(t.title.node().textContent,t.CLASS.title,t.title.node()).width)/2:e.title_padding.left},b.yForTitle=function(){var t=this;return t.config.title_padding.top+t.getTextRect(t.title.node().textContent,t.CLASS.title,t.title.node()).height},b.getTitlePadding=function(){var t=this;return t.yForTitle()+t.config.title_padding.bottom},b.initTooltip=function(){var t,e=this,i=e.config;if(e.tooltip=e.selectChart.style("position","relative").append("div").attr("class",o.tooltipContainer).style("position","absolute").style("pointer-events","none").style("display","none"),i.tooltip_init_show){if(e.isTimeSeries()&&l(i.tooltip_init_x)){for(i.tooltip_init_x=e.parseDate(i.tooltip_init_x),t=0;t<e.data.targets[0].values.length&&e.data.targets[0].values[t].x-i.tooltip_init_x!=0;t++);i.tooltip_init_x=t}e.tooltip.html(i.tooltip_contents.call(e,e.data.targets.map(function(t){return e.addName(t.values[i.tooltip_init_x])}),e.axis.getXAxisTickFormat(),e.getYFormat(e.hasArcType()),e.color)),e.tooltip.style("top",i.tooltip_init_position.top).style("left",i.tooltip_init_position.left).style("display","block")}},b.getTooltipSortFunction=function(){var t=this,e=t.config;if(0!==e.data_groups.length&&void 0===e.tooltip_order){var i=t.orderTargets(t.data.targets).map(function(t){return t.id});return(t.isOrderAsc()||t.isOrderDesc())&&(i=i.reverse()),function(t,e){return i.indexOf(t.id)-i.indexOf(e.id)}}var n=e.tooltip_order;void 0===n&&(n=e.data_order);var a=function(t){return t?t.value:null};if(l(n)&&"asc"===n.toLowerCase())return function(t,e){return a(t)-a(e)};if(l(n)&&"desc"===n.toLowerCase())return function(t,e){return a(e)-a(t)};if(c(n)){var r=n;return void 0===e.tooltip_order&&(r=function(t,e){return n(t?{id:t.id,values:[t]}:null,e?{id:e.id,values:[e]}:null)}),r}return d(n)?function(t,e){return n.indexOf(t.id)-n.indexOf(e.id)}:void 0},b.getTooltipContent=function(t,e,i,n){var a,r,o,s,c,d,l=this,u=l.config,h=u.tooltip_format_title||e,g=u.tooltip_format_name||function(t){return t},p=u.tooltip_format_value||i,f=this.getTooltipSortFunction();for(f&&t.sort(f),r=0;r<t.length;r++)if(t[r]&&(t[r].value||0===t[r].value)&&(a||(o=S(h?h(t[r].x):t[r].x),a="<table class='"+l.CLASS.tooltip+"'>"+(o||0===o?"<tr><th colspan='2'>"+o+"</th></tr>":"")),void 0!==(s=S(p(t[r].value,t[r].ratio,t[r].id,t[r].index,t))))){if(null===t[r].name)continue;c=S(g(t[r].name,t[r].ratio,t[r].id,t[r].index)),d=l.levelColor?l.levelColor(t[r].value):n(t[r].id),a+="<tr class='"+l.CLASS.tooltipName+"-"+l.getTargetSelectorSuffix(t[r].id)+"'>",a+="<td class='name'><span style='background-color:"+d+"'></span>"+c+"</td>",a+="<td class='value'>"+s+"</td>",a+="</tr>"}return a+"</table>"},b.tooltipPosition=function(t,e,i,n){var a,r,o,s,c,d=this,l=d.config,u=d.d3,h=d.hasArcType(),g=u.mouse(n);return h?(r=(d.width-(d.isLegendRight?d.getLegendWidth():0))/2+g[0],s=d.height/2+g[1]+20):(a=d.getSvgLeft(!0),l.axis_rotated?(o=(r=a+g[0]+100)+e,c=d.currentWidth-d.getCurrentPaddingRight(),s=d.x(t[0].x)+20):(o=(r=a+d.getCurrentPaddingLeft(!0)+d.x(t[0].x)+20)+e,c=a+d.currentWidth-d.getCurrentPaddingRight(),s=g[1]+15),o>c&&(r-=o-c+20),s+i>d.currentHeight&&(s-=i+30)),s<0&&(s=0),{top:s,left:r}},b.showTooltip=function(t,e){var i,n,a,r=this,o=r.config,c=r.hasArcType(),d=t.filter(function(t){return t&&s(t.value)}),l=o.tooltip_position||b.tooltipPosition;0!==d.length&&o.tooltip_show&&(r.tooltip.html(o.tooltip_contents.call(r,t,r.axis.getXAxisTickFormat(),r.getYFormat(c),r.color)).style("display","block"),i=r.tooltip.property("offsetWidth"),n=r.tooltip.property("offsetHeight"),a=l.call(this,d,i,n,e),r.tooltip.style("top",a.top+"px").style("left",a.left+"px"))},b.hideTooltip=function(){this.tooltip.style("display","none")},b.setTargetType=function(t,e){var i=this,n=i.config;i.mapToTargetIds(t).forEach(function(t){i.withoutFadeIn[t]=e===n.data_types[t],n.data_types[t]=e}),t||(n.data_type=e)},b.hasType=function(t,e){var i=this,n=i.config.data_types,a=!1;return e=e||i.data.targets,e&&e.length?e.forEach(function(e){var i=n[e.id];(i&&i.indexOf(t)>=0||!i&&"line"===t)&&(a=!0)}):Object.keys(n).length?Object.keys(n).forEach(function(e){n[e]===t&&(a=!0)}):a=i.config.data_type===t,a},b.hasArcType=function(t){return this.hasType("pie",t)||this.hasType("donut",t)||this.hasType("gauge",t)},b.isLineType=function(t){var e=this.config,i=l(t)?t:t.id;return!e.data_types[i]||["line","spline","area","area-spline","step","area-step"].indexOf(e.data_types[i])>=0},b.isStepType=function(t){var e=l(t)?t:t.id;return["step","area-step"].indexOf(this.config.data_types[e])>=0},b.isSplineType=function(t){var e=l(t)?t:t.id;return["spline","area-spline"].indexOf(this.config.data_types[e])>=0},b.isAreaType=function(t){var e=l(t)?t:t.id;return["area","area-spline","area-step"].indexOf(this.config.data_types[e])>=0},b.isBarType=function(t){var e=l(t)?t:t.id;return"bar"===this.config.data_types[e]},b.isScatterType=function(t){var e=l(t)?t:t.id;return"scatter"===this.config.data_types[e]},b.isPieType=function(t){var e=l(t)?t:t.id;return"pie"===this.config.data_types[e]},b.isGaugeType=function(t){var e=l(t)?t:t.id;return"gauge"===this.config.data_types[e]},b.isDonutType=function(t){var e=l(t)?t:t.id;return"donut"===this.config.data_types[e]},b.isArcType=function(t){return this.isPieType(t)||this.isDonutType(t)||this.isGaugeType(t)},b.lineData=function(t){return this.isLineType(t)?[t]:[]},b.arcData=function(t){return this.isArcType(t.data)?[t]:[]},b.barData=function(t){return this.isBarType(t)?t.values:[]},b.lineOrScatterData=function(t){return this.isLineType(t)||this.isScatterType(t)?t.values:[]},b.barOrLineData=function(t){return this.isBarType(t)||this.isLineType(t)?t.values:[]},b.isInterpolationType=function(t){return["linear","linear-closed","basis","basis-open","basis-closed","bundle","cardinal","cardinal-open","cardinal-closed","monotone"].indexOf(t)>=0},b.isSafari=function(){var t=window.navigator.userAgent;return t.indexOf("Safari")>=0&&t.indexOf("Chrome")<0},b.isChrome=function(){return window.navigator.userAgent.indexOf("Chrome")>=0},b.initZoom=function(){var t,e=this,i=e.d3,n=e.config;e.zoom=i.behavior.zoom().on("zoomstart",function(){t=i.event.sourceEvent,e.zoom.altDomain=i.event.sourceEvent.altKey?e.x.orgDomain():null,n.zoom_onzoomstart.call(e.api,i.event.sourceEvent)}).on("zoom",function(){e.redrawForZoom.call(e)}).on("zoomend",function(){var a=i.event.sourceEvent;a&&t.clientX===a.clientX&&t.clientY===a.clientY||(e.redrawEventRect(),e.updateZoom(),n.zoom_onzoomend.call(e.api,e.x.orgDomain()))}),e.zoom.scale=function(t){return n.axis_rotated?this.y(t):this.x(t)},e.zoom.orgScaleExtent=function(){var t=n.zoom_extent?n.zoom_extent:[1,10];return[t[0],Math.max(e.getMaxDataCount()/t[1],t[1])]},e.zoom.updateScaleExtent=function(){var t=f(e.x.orgDomain())/f(e.getZoomDomain()),i=this.orgScaleExtent();return this.scaleExtent([i[0]*t,i[1]*t]),this}},b.getZoomDomain=function(){var t=this,e=t.config,i=t.d3;return[i.min([t.orgXDomain[0],e.zoom_x_min]),i.max([t.orgXDomain[1],e.zoom_x_max])]},b.updateZoom=function(){var t=this,e=t.config.zoom_enabled?t.zoom:function(){};t.main.select("."+o.zoomRect).call(e).on("dblclick.zoom",null),t.main.selectAll("."+o.eventRect).call(e).on("dblclick.zoom",null)},b.redrawForZoom=function(){var t=this,e=t.d3,i=t.config,n=t.zoom,a=t.x;if(i.zoom_enabled&&0!==t.filterTargetsToShow(t.data.targets).length){if("mousemove"===e.event.sourceEvent.type&&n.altDomain)return a.domain(n.altDomain),void n.scale(a).updateScaleExtent();t.isCategorized()&&a.orgDomain()[0]===t.orgXDomain[0]&&a.domain([t.orgXDomain[0]-1e-10,a.orgDomain()[1]]),t.redraw({withTransition:!1,withY:i.zoom_rescale,withSubchart:!1,withEventRect:!1,withDimension:!1}),"mousemove"===e.event.sourceEvent.type&&(t.cancelClick=!0),i.zoom_onzoom.call(t.api,a.orgDomain())}},A}); !function(a){var b=/iPhone/i,c=/iPod/i,d=/iPad/i,e=/(?=.*\bAndroid\b)(?=.*\bMobile\b)/i,f=/Android/i,g=/(?=.*\bAndroid\b)(?=.*\bSD4930UR\b)/i,h=/(?=.*\bAndroid\b)(?=.*\b(?:KFOT|KFTT|KFJWI|KFJWA|KFSOWI|KFTHWI|KFTHWA|KFAPWI|KFAPWA|KFARWI|KFASWI|KFSAWI|KFSAWA)\b)/i,i=/IEMobile/i,j=/(?=.*\bWindows\b)(?=.*\bARM\b)/i,k=/BlackBerry/i,l=/BB10/i,m=/Opera Mini/i,n=/(CriOS|Chrome)(?=.*\bMobile\b)/i,o=/(?=.*\bFirefox\b)(?=.*\bMobile\b)/i,p=new RegExp("(?:Nexus 7|BNTV250|Kindle Fire|Silk|GT-P1000)","i"),q=function(a,b){return a.test(b)},r=function(a){var r=a||navigator.userAgent,s=r.split("[FBAN");return"undefined"!=typeof s[1]&&(r=s[0]),s=r.split("Twitter"),"undefined"!=typeof s[1]&&(r=s[0]),this.apple={phone:q(b,r),ipod:q(c,r),tablet:!q(b,r)&&q(d,r),device:q(b,r)||q(c,r)||q(d,r)},this.amazon={phone:q(g,r),tablet:!q(g,r)&&q(h,r),device:q(g,r)||q(h,r)},this.android={phone:q(g,r)||q(e,r),tablet:!q(g,r)&&!q(e,r)&&(q(h,r)||q(f,r)),device:q(g,r)||q(h,r)||q(e,r)||q(f,r)},this.windows={phone:q(i,r),tablet:q(j,r),device:q(i,r)||q(j,r)},this.other={blackberry:q(k,r),blackberry10:q(l,r),opera:q(m,r),firefox:q(o,r),chrome:q(n,r),device:q(k,r)||q(l,r)||q(m,r)||q(o,r)||q(n,r)},this.seven_inch=q(p,r),this.any=this.apple.device||this.android.device||this.windows.device||this.other.device||this.seven_inch,this.phone=this.apple.phone||this.android.phone||this.windows.phone,this.tablet=this.apple.tablet||this.android.tablet||this.windows.tablet,"undefined"==typeof window?this:void 0},s=function(){var a=new r;return a.Class=r,a};"undefined"!=typeof module&&module.exports&&"undefined"==typeof window?module.exports=r:"undefined"!=typeof module&&module.exports&&"undefined"!=typeof window?module.exports=s():"function"==typeof define&&define.amd?define("isMobile",[],a.isMobile=s()):a.isMobile=s()}(this); /** * marked - a markdown parser diff --git a/assets/sass/_activity_stream.sass b/assets/sass/_activity_stream.sass index 3ec201e6..7bd2de8e 100644 --- a/assets/sass/_activity_stream.sass +++ b/assets/sass/_activity_stream.sass @@ -4,8 +4,10 @@ .activity-event margin-bottom: 15px padding: 10px - &:hover + &:nth-child(even) background: #fafafa + &:hover + background: map-get($highlight-colors, 'background') .activity-date margin-left: 10px diff --git a/assets/sass/_dropdown.sass b/assets/sass/_dropdown.sass index 8ffe1142..364c5824 100644 --- a/assets/sass/_dropdown.sass +++ b/assets/sass/_dropdown.sass @@ -11,6 +11,9 @@ h2 ul display: none +.dropdown-smaller + font-size: 0.85em + ul.dropdown-submenu-open display: block position: absolute diff --git a/assets/sass/_filter_box.sass b/assets/sass/_filter_box.sass index 33a09696..83bfd559 100644 --- a/assets/sass/_filter_box.sass +++ b/assets/sass/_filter_box.sass @@ -1,2 +1,2 @@ .filter-box - max-width: 800px + max-width: 1024px diff --git a/assets/sass/_form.sass b/assets/sass/_form.sass index 00cea65a..adf20a1b 100644 --- a/assets/sass/_form.sass +++ b/assets/sass/_form.sass @@ -145,6 +145,10 @@ ul.form-errors li .form-login max-width: 350px margin: 5% auto 0 + + @include xs-device + margin-left: 5px + li margin-left: 25px line-height: 25px diff --git a/assets/sass/_gantt_chart.sass b/assets/sass/_gantt_chart.sass deleted file mode 100644 index eb6124d8..00000000 --- a/assets/sass/_gantt_chart.sass +++ /dev/null @@ -1,87 +0,0 @@ -@import variables - -div - &.ganttview-hzheader-month, &.ganttview-hzheader-day, &.ganttview-vtheader, &.ganttview-vtheader-item-name, &.ganttview-vtheader-series, &.ganttview-grid, &.ganttview-grid-row-cell - float: left - &.ganttview-hzheader-month, &.ganttview-hzheader-day - text-align: center - &.ganttview-grid-row-cell.last, &.ganttview-hzheader-day.last, &.ganttview-hzheader-month.last - border-right: none - &.ganttview - border: 1px solid #999 - &.ganttview-hzheader-month - width: 60px - height: 20px - border-right: 1px solid #d0d0d0 - line-height: 20px - overflow: hidden - &.ganttview-hzheader-day - width: 20px - height: 20px - border-right: 1px solid #f0f0f0 - border-top: 1px solid #d0d0d0 - line-height: 20px - color: color('medium') - &.ganttview-vtheader - margin-top: 41px - width: 400px - overflow: hidden - background-color: #fff - &.ganttview-vtheader-item - color: color('medium') - &.ganttview-vtheader-series-name - width: 400px - height: 31px - line-height: 31px - padding-left: 3px - border-top: 1px solid #d0d0d0 - text-overflow: ellipsis - overflow: hidden - white-space: nowrap - a - color: color('medium') - text-decoration: none - &:hover - color: color('primary') - text-decoration: underline - i - color: color('dark') - &:hover i - color: color('medium') - &.ganttview-slide-container - overflow: auto - border-left: 1px solid #999 - &.ganttview-grid-row-cell - width: 20px - height: 31px - border-right: 1px solid #f0f0f0 - border-top: 1px solid #f0f0f0 - &.ganttview-weekend - background-color: #fafafa - &.ganttview-blocks - margin-top: 40px - &.ganttview-block-container - height: 28px - padding-top: 4px - &.ganttview-block - position: relative - height: 25px - background-color: #E5ECF9 - border: 1px solid #c0c0c0 - border-radius: 3px - -.ganttview-block-movable - cursor: move - -div - &.ganttview-block-not-defined - border-color: #000 - background-color: #000 - &.ganttview-block-text - position: absolute - height: 12px - font-size: size('tiny') - color: color('light') - padding: 2px 3px - &.ganttview-block div.ui-resizable-handle.ui-resizable-s - bottom: -0 diff --git a/assets/sass/_header.sass b/assets/sass/_header.sass index 3e586fc7..2940fea3 100644 --- a/assets/sass/_header.sass +++ b/assets/sass/_header.sass @@ -2,35 +2,35 @@ @import mixins header - @include grid(100) - margin-top: 5px + display: flex + flex-wrap: wrap + padding: 5px 10px margin-bottom: 5px border-bottom: 1px solid #dedede + background-color: #fbfbfb - .menus-container - @include grid_width(10/100) - @include md-device - @include grid_width(15/100) + .title-container + flex: 1 + min-width: 300px @include sm-device - @include grid_width(20/100) - order: 2 - .header-creation-menu - display: none + order: 3 .board-selector-container - @include grid_width(25/100) - @include md-device - @include grid_width(20/100) + min-width: 320px + display: flex + align-items: center @include sm-device - @include grid_width(80/100) - order: 1 - margin-bottom: 5px + order: 2 - .title-container - @include grid_width(65/100) + .menus-container + min-width: 120px + display: flex + align-items: center + justify-content: flex-end @include sm-device - @include grid_width(1) - order: 3 + order: 1 + margin-bottom: 5px + margin-left: auto h1 font-size: size('title') diff --git a/assets/sass/_project_header.sass b/assets/sass/_project_header.sass index 0a4e08b8..1dc5f3b2 100644 --- a/assets/sass/_project_header.sass +++ b/assets/sass/_project_header.sass @@ -2,42 +2,24 @@ @import mixins .project-header - @include grid(100) margin-bottom: 8px .dropdown-component margin-top: 4px - @include grid_width(5/100) - @include md-device - @include grid_width(8/100) + margin-right: 5px + float: left + @include sm-device - @include grid_width(1) + float: none .views-switcher-component margin-top: 4px - @include grid_width(38/100) - @include custom-device(1300px) - @include grid_width(45/100) - @include md-device - @include grid_width(92/100) + float: left + @include sm-device - margin-top: 0 - @include grid_width(1) + float: none + margin-bottom: 10px .filter-box-component - margin: 0 - @include grid_width(55/100) - @include custom-device(1300px) - @include grid_width(50/100) - @include md-device - @include grid_width(1) - margin-top: 10px - .filter-box - max-width: 100% - @include sm-device - @include grid_width(1) - margin-top: 10px - .filter-box - max-width: 100% form margin: 0 diff --git a/assets/sass/_project_views_switcher.sass b/assets/sass/_project_views_switcher.sass index 591277ef..99d0aecc 100644 --- a/assets/sass/_project_views_switcher.sass +++ b/assets/sass/_project_views_switcher.sass @@ -4,8 +4,8 @@ $breakdown-switcher: 560px .views - display: inline-block margin-right: 10px + margin-top: 1px font-size: size('compact') @include custom-device($breakdown-switcher) width: 100% diff --git a/assets/sass/_reset.sass b/assets/sass/_reset.sass index c4568ce7..6906bee2 100644 --- a/assets/sass/_reset.sass +++ b/assets/sass/_reset.sass @@ -16,12 +16,11 @@ body font-size: 100% body - margin-left: 10px - margin-right: 10px padding-bottom: 10px color: color('primary') font-family: $text-font text-rendering: optimizeLegibility + background-color: color('default') small font-size: size('small') @@ -31,3 +30,7 @@ hr height: 0 border-top: 1px solid rgba(0, 0, 0, 0.1) border-bottom: 1px solid rgba(255, 255, 255, 0.3) + +.page + margin-left: 10px + margin-right: 10px diff --git a/assets/sass/_select_dropdown.sass b/assets/sass/_select_dropdown.sass index f0cef59c..ebfcffca 100644 --- a/assets/sass/_select_dropdown.sass +++ b/assets/sass/_select_dropdown.sass @@ -32,6 +32,7 @@ position: relative border: 1px solid #ccc border-radius: 5px + background-color: #fff input.select-dropdown-input margin: 0 0 0 5px border: none diff --git a/assets/sass/_sidebar.sass b/assets/sass/_sidebar.sass index 52177ba0..c872c1d7 100644 --- a/assets/sass/_sidebar.sass +++ b/assets/sass/_sidebar.sass @@ -2,22 +2,28 @@ @import mixins .sidebar-container - @include grid(100) + height: 100% + display: flex + flex-flow: row + + @include sm-device + flex-flow: wrap .sidebar-content padding-left: 10px - @include grid_width(82/100) - @include xs-device - @include grid_width(1) + flex: 1 100% + + @include sm-device + padding-left: 0 + order: 1 .sidebar - max-width: 240px - min-width: 190px - @include grid_width(18/100) - @include xs-device - @include grid_width(1) - max-width: 99% - min-width: 0 + width: 230px + + @include sm-device + flex: 1 auto + order: 2 + h2 margin-top: 0 > ul diff --git a/assets/sass/_table_list.sass b/assets/sass/_table_list.sass index 019a8296..b6118d10 100644 --- a/assets/sass/_table_list.sass +++ b/assets/sass/_table_list.sass @@ -74,6 +74,9 @@ content: ', ' &:last-child:after content: '' + strong + font-weight: 400 + color: color('medium') .table-list-details-with-icons float: left diff --git a/assets/sass/_task_summary.sass b/assets/sass/_task_summary.sass index 1135133f..7f736bd9 100644 --- a/assets/sass/_task_summary.sass +++ b/assets/sass/_task_summary.sass @@ -12,18 +12,15 @@ .task-summary-container border: 2px solid #000 border-radius: 8px - padding: 15px + padding: 10px .task-summary-columns - display: -webkit-flex display: flex - -webkit-flex-direction: row - flex-direction: row - -webkit-justify-content: space-between + flex-flow: row justify-content: space-between - @include xs-device - display: block + @include sm-device + flex-flow: column .task-summary-column color: color('primary') diff --git a/assets/sass/_tooltip.sass b/assets/sass/_tooltip.sass index 19365a33..14b91d51 100644 --- a/assets/sass/_tooltip.sass +++ b/assets/sass/_tooltip.sass @@ -45,8 +45,5 @@ div.ui-tooltip .ui-tooltip-content .markdown p margin-bottom: 0 -.ui-tooltip li - list-style-type: none - .tooltip .fa-info-circle color: color('light') diff --git a/assets/sass/_variables.sass b/assets/sass/_variables.sass index 3ad4bc7a..a42d2cd6 100644 --- a/assets/sass/_variables.sass +++ b/assets/sass/_variables.sass @@ -4,7 +4,7 @@ $md-device-width: 1150px $colors: ('primary': #333, 'light': #999, 'lighter': #dedede, 'dark': #000, 'medium': #555, 'error': #b94a48) $link-colors: ('primary': #3366CC, 'focus': #DF5353, 'hover': #333) -$background-colors: ('primary': #fbfbfb, 'light': #fcfcfc, 'lighter': #fefefe) +$background-colors: ('primary': #fbfbfb, 'light': #fcfcfc, 'lighter': #fefefe, 'default': #ffffff) $alert-colors: ('default': #c09853, 'success': #468847, 'error': #b94a48, 'info': #3a87ad, 'normal': #333) $alert-background-colors: ('default': #fcf8e3, 'success': #dff0d8, 'error': #f2dede, 'info': #d9edf7, 'normal': #f0f0f0) diff --git a/assets/sass/app.sass b/assets/sass/app.sass index cb554430..7a58ac77 100644 --- a/assets/sass/app.sass +++ b/assets/sass/app.sass @@ -50,6 +50,5 @@ @import documentation @import panel @import activity_stream -@import gantt_chart @import user_mentions @import image_slideshow @@ -9,7 +9,6 @@ ], "dependencies": { "jquery": "^2.2.3", - "fullcalendar": "^3.0.1", "c3": "^0.4.11", "jquery-ui": "^1.11.4", "jqueryui-touch-punch": "*", diff --git a/composer.json b/composer.json index 03f7114b..d6fc9e01 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ "erusev/parsedown" : "1.6.0", "fguillot/json-rpc" : "1.2.1", "fguillot/picodb" : "1.0.14", - "fguillot/picofeed": "0.1.31", + "miniflux/picofeed": "0.1.34", "fguillot/simpleLogger" : "1.0.1", "fguillot/simple-validator" : "1.0.1", "fguillot/simple-queue" : "1.0.1", diff --git a/composer.lock b/composer.lock index 1b51e88a..44146752 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "dc50383a3d1f75257044a2977c0c70af", - "content-hash": "811bd1ea5a2a0fed780794bc6ee7ea82", + "hash": "e88e6605fbf5cf9547d209428e6b83ef", + "content-hash": "4bc386d092cedede740e8690169b2256", "packages": [ { "name": "aferrandini/phpqrcode", @@ -328,59 +328,6 @@ "time": "2016-07-16 22:59:59" }, { - "name": "fguillot/picofeed", - "version": "v0.1.31", - "source": { - "type": "git", - "url": "https://github.com/fguillot/picoFeed.git", - "reference": "b753961879d0b92c284971d902355e00cad1fd9b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/fguillot/picoFeed/zipball/b753961879d0b92c284971d902355e00cad1fd9b", - "reference": "b753961879d0b92c284971d902355e00cad1fd9b", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-iconv": "*", - "ext-libxml": "*", - "ext-simplexml": "*", - "ext-xml": "*", - "php": ">=5.3.0", - "zendframework/zendxml": "^1.0" - }, - "require-dev": { - "phpdocumentor/reflection-docblock": "2.0.4", - "phpunit/phpunit": "4.8.26", - "symfony/yaml": "2.8.7" - }, - "suggest": { - "ext-curl": "PicoFeed will use cURL if present" - }, - "bin": [ - "picofeed" - ], - "type": "library", - "autoload": { - "psr-0": { - "PicoFeed": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Frédéric Guillot" - } - ], - "description": "Modern library to handle RSS/Atom feeds", - "homepage": "https://github.com/fguillot/picoFeed", - "time": "2017-01-16 03:10:21" - }, - { "name": "fguillot/simple-queue", "version": "v1.0.1", "source": { @@ -551,6 +498,59 @@ "time": "2015-09-11 15:23:20" }, { + "name": "miniflux/picofeed", + "version": "v0.1.34", + "source": { + "type": "git", + "url": "https://github.com/miniflux/picoFeed.git", + "reference": "5c8a731d4e7a3589e562e4fdaa98bcb57fa8a2ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/miniflux/picoFeed/zipball/5c8a731d4e7a3589e562e4fdaa98bcb57fa8a2ea", + "reference": "5c8a731d4e7a3589e562e4fdaa98bcb57fa8a2ea", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-iconv": "*", + "ext-libxml": "*", + "ext-simplexml": "*", + "ext-xml": "*", + "php": ">=5.3.0", + "zendframework/zendxml": "^1.0" + }, + "require-dev": { + "phpdocumentor/reflection-docblock": "2.0.4", + "phpunit/phpunit": "4.8.26", + "symfony/yaml": "2.8.7" + }, + "suggest": { + "ext-curl": "PicoFeed will use cURL if present" + }, + "bin": [ + "picofeed" + ], + "type": "library", + "autoload": { + "psr-0": { + "PicoFeed": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frédéric Guillot" + } + ], + "description": "Modern library to handle RSS/Atom feeds", + "homepage": "https://github.com/miniflux/picoFeed", + "time": "2017-06-12 00:22:06" + }, + { "name": "paragonie/random_compat", "version": "v2.0.2", "source": { @@ -912,16 +912,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.3.0", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4" + "reference": "f29dca382a6485c3cbe6379f0c61230167681937" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e79d363049d1c2128f133a2667e4f4190904f7f4", - "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f29dca382a6485c3cbe6379f0c61230167681937", + "reference": "f29dca382a6485c3cbe6379f0c61230167681937", "shasum": "" }, "require": { @@ -933,7 +933,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "1.4-dev" } }, "autoload": { @@ -967,7 +967,7 @@ "portable", "shim" ], - "time": "2016-11-14 01:06:16" + "time": "2017-06-09 14:24:12" }, { "name": "zendframework/zendxml", @@ -1688,23 +1688,23 @@ }, { "name": "sebastian/diff", - "version": "1.4.1", + "version": "1.4.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" + "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", - "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.3.3 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.8" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" }, "type": "library", "extra": { @@ -1736,7 +1736,7 @@ "keywords": [ "diff" ], - "time": "2015-12-08 07:14:41" + "time": "2017-05-22 07:24:03" }, { "name": "sebastian/environment", diff --git a/config.default.php b/config.default.php index 1f08fbad..fb3aa477 100644 --- a/config.default.php +++ b/config.default.php @@ -237,3 +237,6 @@ define('HTTP_VERIFY_SSL_CERTIFICATE', true); // TOTP (2FA) issuer name define('TOTP_ISSUER', 'Kanboard'); + +// Maximum number of events stored in the table "project_activities" +define('PROJECT_ACTIVITIES_MAX_EVENTS', 10000); diff --git a/doc/cs_CZ/board-collapsed-expanded.markdown b/doc/cs_CZ/board-collapsed-expanded.markdown new file mode 100644 index 00000000..c49149cc --- /dev/null +++ b/doc/cs_CZ/board-collapsed-expanded.markdown @@ -0,0 +1,18 @@ +Sbalený a rozbalený režim +=========================== + +Úkoly na desce mohou být zobrazeny ve sbaleném nebo v rozbaleném režimu. +PÅ™echod z jednoho pohledu na druhý lze provést pomocà klávesové zkratky **"s"** nebo pomocà rozbalovacà nabÃdky vlevo. + +Sbalený režim +-------------- + + + +- Pokud je úkol nÄ›komu pÅ™iÅ™azen, zobrazà se vedle ÄÃsla úlohy iniciály osoby. +- Pokud je název úlohy pÅ™ÃliÅ¡ dlouhý, můžete svou myÅ¡ umÃstit pÅ™es úkol, aby se zobrazil popis s celým názvem. + +Rozbalený režim +------------- + +
\ No newline at end of file diff --git a/doc/cs_CZ/board-horizontal-scrolling-and-compact-view.markdown b/doc/cs_CZ/board-horizontal-scrolling-and-compact-view.markdown new file mode 100644 index 00000000..6ee739aa --- /dev/null +++ b/doc/cs_CZ/board-horizontal-scrolling-and-compact-view.markdown @@ -0,0 +1,10 @@ +Horizontálnà rolovánà a kompaktnà režim +===================================== + +Pokud se nástÄ›nka nevejde na obrazovku, zobrazà se v dolnà Äásti vodorovná posuvná liÅ¡ta. + +Je vÅ¡ak možné pÅ™epnout na kompaktnà zobrazenÃ, aby se zobrazily vÅ¡echny sloupce na obrazovce. + + + +PÅ™epÃnánà mezi horizontálnÃm rolovánÃm a kompaktnÃm zobrazenÃm lze provést pomocà klávesové zkratky **"c"** nebo pomocà rozbalovacà nabÃdky vlevo nahoÅ™e. diff --git a/doc/cs_CZ/board-show-hide-columns.markdown b/doc/cs_CZ/board-show-hide-columns.markdown new file mode 100644 index 00000000..81fac467 --- /dev/null +++ b/doc/cs_CZ/board-show-hide-columns.markdown @@ -0,0 +1,12 @@ +Zobrazit a skrýt sloupce na nástÄ›nce +================================== + +Sloupce můžete velmi snadno skrýt nebo zobrazit na nástÄ›nce: + + + +Chcete-li sloupec skrýt, kliknÄ›te na rozbalovacà nabÃdku sloupců a zvolte "Skrýt tento sloupec". + + + +Chcete-li znovu zobrazit sloupec, kliknÄ›te na ikonu plus. diff --git a/doc/cs_CZ/closing-tasks.markdown b/doc/cs_CZ/closing-tasks.markdown new file mode 100644 index 00000000..f8383237 --- /dev/null +++ b/doc/cs_CZ/closing-tasks.markdown @@ -0,0 +1,16 @@ +UzavÃránà úkolů +============= + +Když je úkol uzavÅ™en, je skrytý z nástÄ›nky. + +Vždy vÅ¡ak můžete zÃskat pÅ™Ãstup k seznamu uzavÅ™ených úkolů pomocà dotazu **status:closed** v libovolném vyhledávacÃm formuláři nebo jednoduÅ¡e vybrat **UzavÅ™ené úkoly** z rozevÃracà nabÃdky filtru. + +Existujà dva různé způsoby jak zavÅ™Ãt úkol z rozbalovacà nabÃdky úloh na nástÄ›nce: + + + +Nebo z nabÃdky postrannÃho panelu úloh v detailnÃm zobrazenà úkolu: + + + +Poznámka: Když zavÅ™ete úkol, vÅ¡echny dÃlÄà úkoly, které nebyly dokonÄeny, budou zmÄ›nÄ›ny na stav "DokonÄeno". diff --git a/doc/cs_CZ/creating-projects.markdown b/doc/cs_CZ/creating-projects.markdown new file mode 100644 index 00000000..ac5982aa --- /dev/null +++ b/doc/cs_CZ/creating-projects.markdown @@ -0,0 +1,39 @@ +Vytvářenà projektů +================= + +Kanboard dokáže zpracovat vÃce projektů. Existujà dva druhy projektů: + +- Týmové projekty +- Soukromý projekt pro jednoho uživatele + +Vytvářenà projektů pro vÃce uživatelů +------------------------------------- + +- Tyto projekty mohou vytvářet pouze správci a správci projektů +- Správa uživatelů je k dispozici + +Na nástÄ›nce kliknÄ›te na odkaz **Nový projekt**: + + + +Je to velmi snadné: staÄà najÃt jméno pro svůj projekt! + +Vytvářenà soukromého projektu +----------------------------- + +- Každý může vytvoÅ™it soukromý projekt +- **NenÃ** zde možnost správy uživatelů +- Do projektu mohou pÅ™istupovat pouze vlastnÃci a správci + +Na nástÄ›nce kliknÄ›te na odkaz **Nový soukromý projekt**. + +Vytvářenà projektů z jiného projektu +-------------------------------------- + +PÅ™i vytvářenà nového projektu můžete zvolit duplikovánà vlastnostà jiného projektu: + +- OprávnÄ›nà +- Akce +- Dráhy +- Kategorie +- Úkoly
\ No newline at end of file diff --git a/doc/cs_CZ/creating-tasks.markdown b/doc/cs_CZ/creating-tasks.markdown new file mode 100644 index 00000000..2eb904a5 --- /dev/null +++ b/doc/cs_CZ/creating-tasks.markdown @@ -0,0 +1,30 @@ +Vytvářenà úkolů +============== + +Na nástÄ›nce kliknÄ›te na znaménko plus vedle názvu sloupce: + + + +Poté se zobrazà formulář vytvoÅ™enà úkolu: + + + +Popis pole: + +- **Název**: Název vaÅ¡eho úkolu, který bude zobrazen na nástÄ›nce. +- **Popis**: Popis, který použÃvá formát [Markdown](syntax-guide.markdown). +- **Å tÃtky**: Seznam Å¡tÃtků pÅ™idružených k úkolům. +- **VytvoÅ™enà dalÅ¡Ãho úkolu**: ZaÅ¡krtnÄ›te toto polÃÄko, pokud chcete vytvoÅ™it podobný úkol (nÄ›které pole budou pÅ™edem naplnÄ›ny). +- **Barva**: Vyberte barvu karty. +- **VlastnÃk**: Osoba, která bude na úkolu pracovat. +- **Kategorie**: K úloze lze pÅ™iÅ™adit pouze jednu kategorii (viditelné pouze v pÅ™ÃpadÄ›, že projekty majà kategorie). +- **Sloupec**: Sloupec, ve kterém bude úloha vytvoÅ™ena, úkol bude umÃstÄ›n v dolnà Äásti. +- **Priorita**: Priorita úkolu, rozsah lze definovat v nastavenà projektu, výchozà hodnoty jsou P0 až P3. +- **Složitost**: PoužÃvá se v agilnÃm Å™Ãzenà projektů (Scrum), složitost nebo tzv. "story-points", to je ÄÃslo, které Å™Ãká týmu, jak těžký je úkol. ÄŒasto lidé použÃvajà Fibonacciho posloupnost. +- **Reference**: Externà ID úkolu, napÅ™. to může být ÄÃslo ticketu, které pocházà z jiného systému +- **ÄŒasový odhad**: Odhad v hodinách pro dokonÄenà úkolu. +- **ÄŒas strávený**: Doba strávená pÅ™i práci na úkolu. +- **Datum zahájenÃ**: Toto je pole pro zadánà data a Äasu zahájenÃ. +- **Datum splnÄ›nÃ**: Opakované úkoly budou mÃt Äervený termÃn splnÄ›nà a nadcházejÃcà termÃny budou Äerné na nástÄ›nce. Pro zadánà datumů je k dispozici nÄ›kolik formátů datumu. + +Pomocà odkazu náhled můžete vidÄ›t popis úlohy pÅ™evedený ze syntaxe Markdown.
\ No newline at end of file diff --git a/doc/cs_CZ/duplicate-move-tasks.markdown b/doc/cs_CZ/duplicate-move-tasks.markdown new file mode 100644 index 00000000..5f73aa4b --- /dev/null +++ b/doc/cs_CZ/duplicate-move-tasks.markdown @@ -0,0 +1,58 @@ +KopÃrovat a pÅ™esunout úkoly +======================== + +KopÃrovat úkol do stejného projektu +-------------------------------------- + +PÅ™ejdÄ›te do zobrazenà úkolů a vlevo vyberte položku **VytvoÅ™it kopii**. + + + +Nový úkol bude vytvoÅ™en se stejnými vlastnostmi jako originál. + +KopÃrovat úkol do jiného projektu +----------------------------------- + +PÅ™ejdÄ›te do zobrazenà úkolů a vyberte možnost **VytvoÅ™it kopii v jiném projektu **. + + + +V rozbalovacà nabÃdce se zobrazà pouze projekty, ve kterých jste Älenem. + +Než provedete kopÃrovánà úkolů, Kanboard se vás zeptá na cÃlové vlastnosti, které nejsou spoleÄné mezi zdrojovým a cÃlovým projektem. + +V zásadÄ› musÃte definovat: + +- cÃlová dráha +- Sloupec +- Kategorie +- VlastnÃk + +PÅ™esunout úkol do jiného projektu +------------------------------ + +PÅ™ejdÄ›te do zobrazenà úkolů a vyberte možnost **PÅ™esunout do jiného projektu**. + +PÅ™esunutà úkolu do jiného projektu pracuje stejným způsobem jako kopÃrovánÃ, musÃte zvolit nové vlastnosti úkolu. + +Seznam kopÃrovaných polà +------------------------- + +Zde je seznam kopÃrovaných vlastnostÃ: + +- název +- popis +- datum splnÄ›nà +- barva +- projekt +- sloupec +- vlastnÃk +- skóre +- kategorie +- strávený Äas +- dráha +- stav opakovánà +- spouÅ¡tÄ›Ä opakovánà +- faktor pro opakovánà +- Äasové okno pro opakovánà +- výchozà datum pro výpoÄet opakovánà diff --git a/doc/cs_CZ/editing-projects.markdown b/doc/cs_CZ/editing-projects.markdown new file mode 100644 index 00000000..d5f25b71 --- /dev/null +++ b/doc/cs_CZ/editing-projects.markdown @@ -0,0 +1,15 @@ +Úpravy projektů +================ + +Projekty lze kdykoli pÅ™ejmenovat nebo zakázat. + +Chcete-li projekt pÅ™ejmenovat, kliknÄ›te na odkaz "Upravit projekt" vlevo. + + + +- Datum zahájenà a datum ukonÄenà se použÃvajà k vygenerovánà Ganttova grafu projektu. +- Popis je viditelný jako nápovÄ›da na nástÄ›nce a na stránce výpisů projektů. +- Správci a správci projektů mohou pÅ™evádÄ›t soukromý projekt na projekt s vÃce uživateli zmÄ›nou zaÅ¡krtávacÃho polÃÄka "Soukromý projekt". +- Můžete také pÅ™evést týmový projekt na soukromý projekt. + +Poznámka: Když pÅ™evedete projekt na soukromý, vÅ¡ichni stávajÃcà uživatelé budou stále mÃt pÅ™Ãstup k projektu. Seznam uživatelů můžete upravit podle vaÅ¡ich potÅ™eb.
\ No newline at end of file diff --git a/doc/cs_CZ/index.markdown b/doc/cs_CZ/index.markdown new file mode 100644 index 00000000..8797e099 --- /dev/null +++ b/doc/cs_CZ/index.markdown @@ -0,0 +1,149 @@ +Obsah +============= + +PoužÃvánà Kanboard +-------------- + +### Úvod + +- [Co je Kanban?](what-is-kanban.markdown) +- [Kanban vs Todo Lists and Scrum](kanban-vs-todo-and-scrum.markdown) +- [Usage examples](usage-examples.markdown) + +### PoužÃvánà nástÄ›nky + +- [Zobrazenà NástÄ›nka, Kalendář, Seznam a Gantt](project-views.markdown) +- [Sbalený a rozbalený režim](board-collapsed-expanded.markdown) +- [Horizontálnà rolovánà a kompaktnà režim](board-horizontal-scrolling-and-compact-view.markdown) +- [Zobrazit a skrýt sloupce](board-show-hide-columns.markdown) + +### Práce s projekty + +- [Typy projektů](project-types.markdown) +- [Vytvářenà projektů](creating-projects.markdown) +- [Úpravy projektů](editing-projects.markdown) +- [OdstranÄ›nà projektů](removing-projects.markdown) +- [Sharing boards and tasks](sharing-projects.markdown) +- [Automatic actions](automatic-actions.markdown) +- [OprávnÄ›nà projektu](project-permissions.markdown) +- [Swimlanes](swimlanes.markdown) +- [Calendar](calendar.markdown) +- [Analytics](analytics.markdown) +- [Gantt chart for tasks](gantt-chart-tasks.markdown) +- [Gantt chart for projects](gantt-chart-projects.markdown) +- [Custom filters](custom-filters.markdown) +- [Custom project roles](custom-project-roles.markdown) + +### Práce s úkoly + +- [Vytvářenà úkolů](creating-tasks.markdown) +- [UzavÃránà úkolů](closing-tasks.markdown) +- [KopÃrovat a pÅ™esunout úkoly](duplicate-move-tasks.markdown) +- [Adding screenshots](screenshots.markdown) +- [Internal Task links](task-links.markdown) +- [Transitions](transitions.markdown) +- [Time tracking](time-tracking.markdown) +- [Recurring tasks](recurring-tasks.markdown) +- [Create tasks by email](create-tasks-by-email.markdown) +- [Subtasks](subtasks.markdown) +- [Analytics for tasks](analytics-tasks.markdown) +- [User mentions](user-mentions.markdown) +- [Tags](tags.markdown) + +### Working with users and groups + +- [Roles](roles.markdown) +- [User Types](user-types.markdown) +- [Group management](groups.markdown) +- [User management](user-management.markdown) +- [Notifications](notifications.markdown) +- [Two factor authentication](2fa.markdown) + +### Settings + +- [Keyboard shortcuts](keyboard-shortcuts.markdown) +- [Application settings](application-configuration.markdown) +- [Nastavenà projektu](project-configuration.markdown) +- [Board settings](board-configuration.markdown) +- [Calendar settings](calendar-configuration.markdown) +- [Link settings](link-labels.markdown) +- [Currency rate](currency-rate.markdown) + +### Integrations + +- [iCalendar subscriptions](ical.markdown) +- [RSS/Atom subscriptions](rss.markdown) +- [Json-RPC API](api-json-rpc.markdown) +- [Webhooks](webhooks.markdown) +- [Plugins](plugins.markdown) + +### More information + +- [Advanced Search Syntax](search.markdown) +- [Command line interface](cli.markdown) +- [Syntax guide](syntax-guide.markdown) +- [Brute force protection](bruteforce-protection.markdown) +- [Frequently asked questions](faq.markdown) + +### Troubleshooting + +- [Solving database migration issues](solving-database-migration-issues.markdown) +- [Blank page after upgrading](solving-blank-page-issue.markdown) +- [Unable to open a session on Windows IIS and Internet Explorer](solving-session-issue-windows-iis-ie.markdown) +- [Performances](performances.markdown) + +Technical details +----------------- + +### Installation + +- [Requirements](requirements.markdown) +- [Installation instructions](installation.markdown) +- [Upgrade Kanboard to a new version](update.markdown) +- [Installation on Ubuntu](ubuntu-installation.markdown) +- [Installation on Debian](debian-installation.markdown) +- [Installation on Centos](centos-installation.markdown) +- [Installation on OpenSuse](suse-installation.markdown) +- [Installation on FreeBSD](freebsd-installation.markdown) +- [Installation on Windows Server with IIS](windows-iis-installation.markdown) +- [Installation on Windows Server with Apache](windows-apache-installation.markdown) +- [Installation on Heroku](heroku.markdown) +- [Run Kanboard with Docker](docker.markdown) +- [Run Kanboard with Vagrant](vagrant.markdown) +- [Run Kanboard on Cloudron](cloudron.markdown) + +### Configuration + +- [Daily background job](cronjob.markdown) +- [Background Worker](worker.markdown) +- [Config file](config.markdown) +- [Environment variables](env.markdown) +- [Email configuration](email-configuration.markdown) +- [URL rewriting](nice-urls.markdown) +- [Plugin Directory](plugin-directory.markdown) + +### Database + +- [Sqlite database management](sqlite-database.markdown) +- [How to use Mysql](mysql-configuration.markdown) +- [How to use Postgresql](postgresql-configuration.markdown) + +### Authentication + +- [LDAP authentication](ldap-authentication.markdown) +- [LDAP group synchronization](ldap-group-sync.markdown) +- [LDAP profile picture](ldap-profile-picture.markdown) +- [LDAP parameters](ldap-parameters.markdown) +- [LDAP configuration examples](ldap-configuration-examples.markdown) +- [Reverse proxy authentication](reverse-proxy-authentication.markdown) + +### Contributors + +- [Contributor guide](contributing.markdown) +- [Translations](translations.markdown) +- [Coding standards](coding-standards.markdown) +- [Running tests](tests.markdown) +- [Build assets](assets.markdown) + +The documentation is written in [Markdown](http://en.wikipedia.org/wiki/Markdown). +If you want to improve the documentation, just send a pull-request. diff --git a/doc/cs_CZ/project-configuration.markdown b/doc/cs_CZ/project-configuration.markdown new file mode 100644 index 00000000..6f8dfb14 --- /dev/null +++ b/doc/cs_CZ/project-configuration.markdown @@ -0,0 +1,41 @@ +Nastavenà projektu +================ + +PÅ™ejdÄ›te do nabÃdky **NastavenÃ** a poté vlevo vyberte možnost **Nastavenà projektu**. + + + +### Výchozà sloupce pro nové projekty + +Zde můžete zmÄ›nit výchozà názvy sloupců. +Toto je užiteÄné, pokud vždy vytvářÃte projekty se stejnými sloupci. + +Každé jméno sloupce musà být oddÄ›leno Äárkou. + +Ve výchozÃm nastavenà Kanboard použÃvá tyto názvy sloupců: NevyÅ™Ãzené, PÅ™ipraveno, V Å™eÅ¡enà a DokonÄeno. + +### Výchozà kategorie pro nové projekty + +Kategorie nejsou globálnà pro aplikaci, ale jsou pÅ™ipojeny k projektu. +Každý projekt může mÃt různé kategorie. + +Pokud vÅ¡ak vždy vytvoÅ™Ãte stejné kategorie pro vÅ¡echny vaÅ¡e projekty, můžete zde definovat seznam kategoriÃ, které chcete vytvoÅ™it automaticky. + +### Povolit souÄasnÄ› pouze jednu dÃlÄà úlohu pro uživatele + +Je-li tato volba povolena, může uživatel pracovat pouze s jednou dÃlÄà úlohou v daném okamžiku. + +Pokud má dalšà dÃlÄà úloha stav "probÃhajÃcÃ", zobrazà se toto dialogové okno: + + + +### Spustit automaticky sledovánà Äasu + +- Pokud je povoleno, když je stav dÃlÄà úlohy zmÄ›nÄ›n na "probÃhajÃcÃ", ÄasovaÄ se spustà automaticky. +- Tuto možnost deaktivujte, pokud nepoužÃváte sledovánà Äasu. + +### Zahrnout uzavÅ™ené úkoly v kumulativnÃm vývojovém diagramu + +- Pokud je povoleno, uzavÅ™ené úkoly budou zahrnuty do kumulativnÃho diagramu. +- Pokud je zakázáno, budou zahrnuty pouze otevÅ™ené úkoly. +- Tato volba ovlivňuje sloupec "celkem" tabulky "project_daily_column_stats" diff --git a/doc/cs_CZ/project-permissions.markdown b/doc/cs_CZ/project-permissions.markdown new file mode 100644 index 00000000..6ced801b --- /dev/null +++ b/doc/cs_CZ/project-permissions.markdown @@ -0,0 +1,20 @@ +OprávnÄ›nà projektu +=================== + +Každý projekt je izolován od jiných projektů. +PÅ™Ãstup k projektu musà být povolen vlastnÃkem projektu. + +Každý uživatel a každá skupina mohou mÃt pÅ™iÅ™azenou jinou roli. +Existujà 3 typy [rolà projektu](roles.markdown): + +- Správce projektu +- ÄŒlen projektu +- ÄŒtenář projektu + +Pouze správci majà pÅ™Ãstup ke vÅ¡emu. + +PÅ™iÅ™azenà rolà je viditelné v **Nastavenà projektu > OprávnÄ›nÃ**: + + + +U soukromých projektů nelze definovat oprávnÄ›nÃ.
\ No newline at end of file diff --git a/doc/cs_CZ/project-types.markdown b/doc/cs_CZ/project-types.markdown new file mode 100644 index 00000000..c0bd29b9 --- /dev/null +++ b/doc/cs_CZ/project-types.markdown @@ -0,0 +1,14 @@ +Typy projektů +============= + +Existujà dva druhy projektů: + +| Type | Description | +|-------------------|-----------------------------------------------------------------------| +| Týmový projekt | Projekt s Å™ÃzenÃm uživatelů a skupin | +| Soukromý projekt | Projekt, který patřà pouze jedné osobÄ›, nemá správu uživatelů | + +- Pouze správci a aplikaÄnà správci mohou vytvářet týmové projekty. +- Soukromé projekty může vytvářet každý. + +[PÅ™eÄtÄ›te si dokumentaci o rolÃch v Kanboard](roles.markdown) diff --git a/doc/cs_CZ/project-views.markdown b/doc/cs_CZ/project-views.markdown new file mode 100644 index 00000000..b41f3f40 --- /dev/null +++ b/doc/cs_CZ/project-views.markdown @@ -0,0 +1,58 @@ +NástÄ›nka, kalendář a zobrazenà seznamu +============================== + +Pro každý projekt je možné úkoly vizualizovat s nÄ›kolika pohledy: ** NástÄ›nka, Kalendář, Seznam a Gantt **. +V každém zobrazenà se zobrazà výsledek pole filtru v hornà Äásti. +VyhledávaÄ použÃvá [advanced syntax](search.markdown). + +Zobrazenà nástÄ›nky +----------------- + + + +- S tÃmto pohledem můžete snadno pÅ™etáhnout úkoly mezi sloupci. +- Můžete také použÃt klávesovou zkratku **"v b"** pro pÅ™epnutà na nástÄ›nku. +- Úkoly se stÃnem byly nedávno upraveny. + + + +Po dosaženà limitu úkolů pro sloupec se pozadà stává Äerveným. To znamená, že probÃhá pÅ™ÃliÅ¡ mnoho úkolů ve stejnou dobu. + +[Dalšà informace o konfiguraci nástÄ›nky](board-configuration.markdown) + +Zobrazenà kalendáře +-------------- + + + +- Pomocà tohoto pohledu můžete vizualizovat úkoly s datem splnÄ›nÃ. +- V závislosti na nastavenà můžete také vidÄ›t probÃhajÃcà úlohy. +- K pÅ™epnutà do zobrazenà kalendáře můžete také použÃt klávesovou zkratku **"v c"**. +- [Dalšà informace o konfiguraci kalendáře](calendar-configuration.markdown) + +Zobrazenà seznamu +--------- + + + +- V tomto zobrazenà jsou vÅ¡echny výsledky vaÅ¡eho vyhledávánà zobrazeny v tabulce. +- K pÅ™epnutà do zobrazenà seznamu můžete také použÃt klávesovou zkratku **"v l"**. + +Zobrazenà jako Gantt +----------------- + + + +- Zobrazenà Gantt zobrazuje úkoly na horizontálnà Äasové ose. +- Datum zahájenà a datum splnÄ›nà se použÃvajà k zobrazenà grafu. +- Pro rychlý pÅ™Ãstup použijte klávesovou zkratku: **v g**. + +PÅ™ehled projektu +---------------- + + + +- Zobrazenà popisu projektu. +- PÅ™ipojit a nahrát dokumenty do projektu. +- Zobrazit seznam Älenů projektu. +- Zobrazit poslednà aktivity projektu.
\ No newline at end of file diff --git a/doc/cs_CZ/removing-projects.markdown b/doc/cs_CZ/removing-projects.markdown new file mode 100644 index 00000000..31a613e3 --- /dev/null +++ b/doc/cs_CZ/removing-projects.markdown @@ -0,0 +1,10 @@ +OdstranÄ›nà projektů +================= + +Chcete-li projekt odebrat, musÃte být správcem projektu nebo správcem. + +PÅ™ejdÄ›te na položku **"Nastavenà projektu"** a v nabÃdce vlevo, v dolnà Äásti zvolte možnost "Odstranit"**. + + + +OdstranÄ›nà projektu odstranà vÅ¡echny úkoly, které patřà tomuto projektu.
\ No newline at end of file diff --git a/doc/cs_CZ/what-is-kanban.markdown b/doc/cs_CZ/what-is-kanban.markdown new file mode 100644 index 00000000..e84aa71a --- /dev/null +++ b/doc/cs_CZ/what-is-kanban.markdown @@ -0,0 +1,32 @@ +Co je Kanban? +=============== + +Kanban je metodika původnÄ› vyvinutá spoleÄnostà Toyota aby byla efektivnÄ›jÅ¡Ã. + +Kanban si klade za cÃl dva úkoly: + +- Vizualizujte svůj pracovnà postup +- Omezte množstvà své rozdÄ›lané práce + +Vizualizujte svůj pracovnà postup +----------------------- + +- VaÅ¡e práce je zobrazena na nástÄ›nce, takže máte pÅ™ehled o vaÅ¡em projektu +- Každý sloupec pÅ™edstavuje krok ve vaÅ¡em pracovnÃm postupu + +Omezte množstvà své rozdÄ›lané práce +-------------------------------------------------- +- SlepÅ¡uje soustÅ™edÄ›nÃ, tÃm že se vyhne souběžné práci +- Každá fáze může mÃt limity pro rozdÄ›lanou práci +- Limity pomáhajà identifikovat pÅ™ekážky +- Limity pomáhajà vyhnout se práci na pÅ™ÃliÅ¡ mnoha úlohách souÄasnÄ› + +Měřenà výkonu +======================= + +Kanban využÃvá doby potÅ™ebné k realizaci a doby cyklů k měřenà výkonu: + +- ** Doba potÅ™ebná k realizaci **: ÄŒas mezi vytvoÅ™enÃm a dokonÄenÃm úlohy +- ** Doba cyklu **: ÄŒas mezi zahájenÃm a dokonÄenÃm úlohy + +Můžete napÅ™Ãklad mÃt 100 dnà doby potÅ™ebné k realizaci, ale musÃte dokonÄit úkol pouze za 1 hodinu. diff --git a/doc/en_US/api-me-procedures.markdown b/doc/en_US/api-me-procedures.markdown index e90bee61..c2476904 100644 --- a/doc/en_US/api-me-procedures.markdown +++ b/doc/en_US/api-me-procedures.markdown @@ -78,7 +78,6 @@ Response example: "last_modified": "1438205337", "is_public": "0", "is_private": "1", - "is_everybody_allowed": "0", "default_swimlane": "Default swimlane", "show_default_swimlane": "1", "description": null, @@ -369,7 +368,6 @@ Response example: "last_modified": "1436119570", "is_public": "0", "is_private": "0", - "is_everybody_allowed": "0", "default_swimlane": "Default swimlane", "show_default_swimlane": "1", "description": null, diff --git a/doc/en_US/api-project-procedures.markdown b/doc/en_US/api-project-procedures.markdown index 831c4b60..91793803 100644 --- a/doc/en_US/api-project-procedures.markdown +++ b/doc/en_US/api-project-procedures.markdown @@ -70,7 +70,6 @@ Response example: "last_modified": "1436119135", "is_public": "0", "is_private": "0", - "is_everybody_allowed": "0", "default_swimlane": "Default swimlane", "show_default_swimlane": "1", "description": "test", @@ -119,7 +118,6 @@ Response example: "last_modified": "1436119135", "is_public": "0", "is_private": "0", - "is_everybody_allowed": "0", "default_swimlane": "Default swimlane", "show_default_swimlane": "1", "description": "test", @@ -168,7 +166,6 @@ Response example: "last_modified": "1436119135", "is_public": "0", "is_private": "0", - "is_everybody_allowed": "0", "default_swimlane": "Default swimlane", "show_default_swimlane": "1", "description": "test", @@ -217,7 +214,6 @@ Response example: "last_modified": "1436119135", "is_public": "0", "is_private": "0", - "is_everybody_allowed": "0", "default_swimlane": "Default swimlane", "show_default_swimlane": "1", "description": "test", @@ -265,7 +261,6 @@ Response example: "last_modified": "1436119570", "is_public": "0", "is_private": "0", - "is_everybody_allowed": "0", "default_swimlane": "Default swimlane", "show_default_swimlane": "1", "description": null, diff --git a/doc/en_US/api-task-procedures.markdown b/doc/en_US/api-task-procedures.markdown index a5691c12..cec6e0e0 100644 --- a/doc/en_US/api-task-procedures.markdown +++ b/doc/en_US/api-task-procedures.markdown @@ -255,7 +255,15 @@ Response example: "recurrence_basedate": "0", "recurrence_parent": null, "recurrence_child": null, - "url": "http:\/\/127.0.0.1:8000\/?controller=task&action=show&task_id=1&project_id=1" + "priority": "0", + "external_provider": null, + "external_uri": null, + "url": "http:\/\/127.0.0.1:8000\/?controller=task&action=show&task_id=1&project_id=1", + "color": { + "name": "Blue", + "background": "rgb(219, 235, 255)", + "border": "rgb(168, 207, 255)" + } }, { "id": "2", @@ -287,7 +295,15 @@ Response example: "recurrence_basedate": "0", "recurrence_parent": null, "recurrence_child": null, - "url": "http:\/\/127.0.0.1:8000\/?controller=task&action=show&task_id=2&project_id=1" + "priority": "0", + "external_provider": null, + "external_uri": null, + "url": "http:\/\/127.0.0.1:8000\/?controller=task&action=show&task_id=2&project_id=1", + "color": { + "name": "Green", + "background": "rgb(189, 244, 203)", + "border": "rgb(74, 227, 113)" + } }, ... ] diff --git a/doc/en_US/cli.markdown b/doc/en_US/cli.markdown index b1722308..b7d60705 100644 --- a/doc/en_US/cli.markdown +++ b/doc/en_US/cli.markdown @@ -133,11 +133,12 @@ Optional parameters: - `--show`: Display notifications sent - `--group`: Group all overdue tasks for one user (from all projects) in one email - `--manager`: Send all overdue tasks to project manager(s) in one email +- `-p|--project project_id|identifier`: Send notifications only for the given project You can also display the overdue tasks with the flag `--show`: ```bash -./kanboard notification:overdue-tasks --show +./cli notification:overdue-tasks --show +-----+---------+------------+------------+--------------+----------+ | Id | Title | Due date | Project Id | Project name | Assignee | +-----+---------+------------+------------+--------------+----------+ @@ -146,6 +147,18 @@ You can also display the overdue tasks with the flag `--show`: +-----+---------+------------+------------+--------------+----------+ ``` +Example to filter by project: + +```bash +./cli notification:overdue-tasks --project 123 +``` + +Or if you have defined a project identifier: + +```bash +./cli notification:overdue-tasks --project MY_PROJECT +``` + ### Run daily project stats calculation This command calculate the statistics of each project: diff --git a/doc/en_US/config.markdown b/doc/en_US/config.markdown index 4ccaec27..4fbb9548 100644 --- a/doc/en_US/config.markdown +++ b/doc/en_US/config.markdown @@ -10,6 +10,9 @@ Enable/Disable debug mode ```php define('DEBUG', true); define('LOG_DRIVER', 'file'); // Other drivers are: syslog, stdout, stderr or file + +// By default, the log file is in data/debug.log but you can change the path: +define('LOG_FILE', '/path/to/debug.log'); ``` The log driver must be defined if you enable the debug mode. @@ -332,4 +335,7 @@ define('API_AUTHENTICATION_TOKEN', 'My unique API Token'); // TOTP (2FA) issuer name define('TOTP_ISSUER', 'Kanboard'); + +// Maximum number of events stored in the table "project_activities" +define('PROJECT_ACTIVITIES_MAX_EVENTS', 10000); ``` diff --git a/doc/en_US/create-tasks-by-email.markdown b/doc/en_US/create-tasks-by-email.markdown index b46e5797..f0ac4753 100644 --- a/doc/en_US/create-tasks-by-email.markdown +++ b/doc/en_US/create-tasks-by-email.markdown @@ -18,28 +18,17 @@ All complicated works are already handled by those services. Incoming emails workflow ------------------------ -1. You send an email to a specific address, for example **something+myproject@inbound.mydomain.tld** +1. You send an email to a specific address, for example **myproject@inbound.mydomain.tld** 2. Your email is forwarded to the third-party SMTP servers 3. The SMTP provider call the Kanboard web hook with the email in JSON or multipart/form-data formats 4. Kanboard parses the received email and create the task to the right project -Note: New tasks are automatically created in the first column. - -Email format ------------- - -- The local part of the email address must use the plus separator, for example **kanboard+project123** -- The string defined after the plus sign must match a project identifier, for example **project123** is the identifier of the project **Project 123** -- The email subject becomes the task title -- The email body becomes the task description (Markdown format) - -Incoming emails can be written in text or HTML formats. -**Kanboard is able to convert simple HTML emails to Markdown**. - -Security and requirements -------------------------- +Notes +----- - The Kanboard web hook is protected by a random token - The sender email address must match a Kanboard user -- The Kanboard project must have a unique identifier, for example **MYPROJECT** - The Kanboard user must be a member of the project +- Kanboard is able to convert only simple HTML emails to Markdown +- New tasks are automatically created in the first column +- Refer to the documentation of each plugin for the configuration diff --git a/doc/en_US/cronjob.markdown b/doc/en_US/cronjob.markdown index 743aac95..fd7c2b1e 100644 --- a/doc/en_US/cronjob.markdown +++ b/doc/en_US/cronjob.markdown @@ -39,7 +39,7 @@ Before to configure the recurring task, create a batch file (*.bat or *.cmd) tha Here an example (`C:\kanboard.bat`): ``` -"C:\php\php.exe" -f "C:\inetpub\wwwroot\kanboard\kanboard" cronjob +"C:\php\php.exe" -f "C:\inetpub\wwwroot\kanboard\cli" cronjob ``` **You must change the path of the PHP executable and the path of the Kanboard's script according to your installation.** diff --git a/doc/en_US/docker.markdown b/doc/en_US/docker.markdown index 5b77da76..698cf5e5 100644 --- a/doc/en_US/docker.markdown +++ b/doc/en_US/docker.markdown @@ -86,6 +86,7 @@ Config files - The container already include a custom config file located at `/var/www/app/config.php`. - You can store your own config file on the data volume: `/var/www/app/data/config.php`. +- If you change your config file values, you must restart the container to take into account the new parameters. References ---------- diff --git a/doc/en_US/env.markdown b/doc/en_US/env.markdown index 902066d7..6e241f8b 100644 --- a/doc/en_US/env.markdown +++ b/doc/en_US/env.markdown @@ -3,7 +3,8 @@ Environment Variables Environment variables maybe useful when Kanboard is deployed as container (Docker). -| Variable | Description | -|---------------|---------------------------------------------------------------------------------------------------------------------------------| -| DATABASE_URL | `[database type]://[username]:[password]@[host]:[port]/[database name]`, example: `postgres://foo:foo@myserver:5432/kanboard` | -| DEBUG | Enable/Disable debug mode: "true" or "false" | +| Variable | Description | +|--------------------------|---------------------------------------------------------------------------------------------------------------------------------| +| DATABASE_URL | `[database type]://[username]:[password]@[host]:[port]/[database name]`, example: `postgres://foo:foo@myserver:5432/kanboard` | +| DEBUG | Enable/Disable debug mode: "true" or "false" | +| API_AUTHENTICATION_TOKEN | Custom API token | diff --git a/doc/en_US/heroku.markdown b/doc/en_US/heroku.markdown index 1891efb0..fa8d29b9 100644 --- a/doc/en_US/heroku.markdown +++ b/doc/en_US/heroku.markdown @@ -35,5 +35,9 @@ heroku open Limitations ----------- -- The storage of Heroku is ephemeral, that means uploaded files through Kanboard are not persistent after a restart. You may want to install a plugin to store your files in a cloud storage provider like [Amazon S3](https://github.com/kanboard/plugin-s3). -- Some features of Kanboard require that you run [a daily background job](cronjob.markdown). +Local disk storage on Heroku is ephemeral: + +- Uploaded files are not persistent after a restart. You may want to install a plugin to store your files in a cloud storage provider like [Amazon S3](https://github.com/kanboard/plugin-s3). +- Plugins installed via the web interface are stored on the local filesystem. You should include and deploy plugins with your own copy of Kanboard. + +Some features of Kanboard require that you run [a daily background job](cronjob.markdown). diff --git a/doc/en_US/installation.markdown b/doc/en_US/installation.markdown index d8969c1c..543ca4d0 100644 --- a/doc/en_US/installation.markdown +++ b/doc/en_US/installation.markdown @@ -1,7 +1,7 @@ Installation ============ -Firstly, check the [requirements](requirements.markdown) before to go further. +First, check the [requirements](requirements.markdown) before going further. From the archive (stable version) --------------------------------- @@ -11,26 +11,23 @@ From the archive (stable version) 3. Check if the directory `data` is writeable by the web server user 4. With your browser go to <http://yourpersonalserver/kanboard> 5. The default login and password is **admin/admin** -6. Start to use the software +6. Start using the software 7. Don't forget to change your password! The `data` folder is used to store: - Sqlite database: `db.sqlite` -- Debug file: `debug.log` (if debug mode enabled) +- Debug file: `debug.log` (if debug mode is enabled) - Uploaded files: `files/*` - Image thumbnails: `files/thumbnails/*` -People who are using a remote database (Mysql/Postgresql) and a remote object storage (Aws S3 or similar) don't necessarily need to have a persistent local data folder or to change the permission. +People who are using a remote database (Mysql/Postgresql) and a remote object storage (Aws S3 or similar) don't necessarily need to have a persistent local data folder or to change the permissions for the folder. From the git repository (development version) --------------------------------------------- -You must install [composer](https://getcomposer.org/) to use this method. - 1. `git clone https://github.com/kanboard/kanboard.git` -2. `composer install --no-dev` -3. Go to the third step just above +2. Go to the third step just above Note: This method will install the **current development version**, use at your own risk. @@ -67,10 +64,10 @@ Optional Installation --------------------- - Some features of Kanboard require that you run [a daily background job](cronjob.markdown) (Reports and analytics) -- [Install the background worker](worker.markdown) to improve the performances +- [Install the background worker](worker.markdown) to improve performance Security -------- - Don't forget to change the default user/password -- Don't allow everybody to access to the directory `data` from the URL. There is already a `.htaccess` for Apache and a `web.config` file for IIS but nothing for other web servers. +- Don't allow everybody to access to the directory `data` from the URL. A `.htaccess` file for Apache and a `web.config` file for IIS is included but other web servers will have to be configured manually. diff --git a/doc/en_US/ldap-authentication.markdown b/doc/en_US/ldap-authentication.markdown index e994c149..6d80e9db 100644 --- a/doc/en_US/ldap-authentication.markdown +++ b/doc/en_US/ldap-authentication.markdown @@ -153,6 +153,33 @@ define('LDAP_USER_BASE_DN', 'ou=People,dc=example,dc=com'); define('LDAP_USER_FILTER', 'uid=%s'); ``` +Example for LDAPS (SSL-encryption) +---------------------------------- + +Some LDAP servers are configured for "LDAPS" connectivity only (on port 636). This is different to TLS, which starts off in cleartext (port 389 by default) and then sets up encryption over the same channel. + +To tell PHP to use LDAPS, you need to prefix the name of your LDAP server with "ldaps://", as in the example below: + +Our LDAP server is `myserver.example.com` and is only accessible via LDAPS. Most likely we won't want to validate the server cert, and we DON'T want TLS. + +For this example we use the anonymous binding. + +```php +<?php + +// Enable LDAP authentication (false by default) +define('LDAP_AUTH', true); + +// LDAP server hostname +define('LDAP_SERVER', 'ldaps://myserver.example.com'); + +// By default, require certificate to be verified for ldaps:// style URL. Set to false to skip the verification +define('LDAP_SSL_VERIFY', false); + +// Enable LDAP START_TLS +define('LDAP_START_TLS', false);; +``` + Disable automatic account creation ----------------------------------- diff --git a/doc/en_US/mysql-configuration.markdown b/doc/en_US/mysql-configuration.markdown index c199353e..27d5141d 100644 --- a/doc/en_US/mysql-configuration.markdown +++ b/doc/en_US/mysql-configuration.markdown @@ -55,6 +55,12 @@ mysql -u root -p my_database < app/Schema/Sql/mysql.sql The file `app/Schema/Sql/mysql.sql` is a SQL dump that represents the last version of the database. +If you would like to use Unix socket connection, try this: + +```php +define('DB_HOSTNAME', '127.0.0.1;unix_socket=/var/run/mysqld/mysqld.sock'); +``` + SSL configuration ----------------- diff --git a/doc/en_US/plugin-hooks.markdown b/doc/en_US/plugin-hooks.markdown index dedea934..c01da61b 100644 --- a/doc/en_US/plugin-hooks.markdown +++ b/doc/en_US/plugin-hooks.markdown @@ -211,10 +211,8 @@ List of template hooks: | `template:config:application ` | Application settings form | | `template:config:email` | Email settings page | | `template:config:integrations` | Integration page in global settings | -| `template:dashboard:sidebar` | Sidebar on dashboard page | | `template:dashboard:show` | Main page of the dashboard | -| `template:dashboard:subtasks:header:before-timetracking` | Header of Subtask table before Time Tracking | -| `template:dashboard:subtasks:rows` | Column on row of Subtask table of the dashboard | +| `template:dashboard:page-header:menu` | Dashboard submenu | | `template:header:dropdown` | Page header dropdown menu (user avatar icon) | | `template:header:creation-dropdown` | Page header dropdown menu (plus icon) | | `template:layout:head` | Page layout `<head/>` tag | @@ -229,6 +227,7 @@ List of template hooks: | `template:project-list:menu:before` | Project list: before menu entries | | `template:project-list:menu:after` | Project list: after menu entries | | `template:project-overview:before-description` | Project overview: before description | +| `template:project-header:view-switcher` | Project view switcher | | `template:task:layout:top` | Task layout top (after page header) | | `template:task:details:top` | Task summary top | | `template:task:details:bottom` | Task summary bottom | diff --git a/doc/en_US/project-permissions.markdown b/doc/en_US/project-permissions.markdown index 99e4caa2..05c81ef9 100644 --- a/doc/en_US/project-permissions.markdown +++ b/doc/en_US/project-permissions.markdown @@ -17,7 +17,4 @@ Role assignments are visible in **Project Settings > Permissions**:  -If you choose to allow everybody, all Kanboard users will be considered 'Project Member'. -With this setting, role management will have no effect. Permission per user or per group cannot be applied. - Private projects cannot define permissions. diff --git a/doc/en_US/requirements.markdown b/doc/en_US/requirements.markdown index 957d8750..78a0ced8 100644 --- a/doc/en_US/requirements.markdown +++ b/doc/en_US/requirements.markdown @@ -44,9 +44,13 @@ Do not use Sqlite on NFS mounts, only when you have a disk with fast I/O. | Apache HTTP Server | | Nginx | | Microsoft IIS | +| Caddy Server | Kanboard is pre-configured to work with Apache (URL rewriting). +- Note: Kanboard is not compatible with Apache `mod_security`. +- If you use Apache, you must have the module `mod_version`. + ### PHP Versions | PHP Version | diff --git a/doc/en_US/search.markdown b/doc/en_US/search.markdown index fefbe095..818a19ce 100644 --- a/doc/en_US/search.markdown +++ b/doc/en_US/search.markdown @@ -115,6 +115,7 @@ Attribute: **completed** The task reference is an external id of your task, by example a ticket number from another software. - Find tasks with a reference: `ref:1234` or `reference:TICKET-1234` +- Wildcard search: `ref:TICKET-*` ### Search by category @@ -165,6 +166,13 @@ Attribute: **tag** - Example: `tag:"My tag"` +### Search by score/complexity + +Attribute: **score** or **complexity** + +- `score:>=21` +- `complexity:8` + Activity stream search ---------------------- diff --git a/doc/en_US/update.markdown b/doc/en_US/update.markdown index 76cac27a..516b704b 100644 --- a/doc/en_US/update.markdown +++ b/doc/en_US/update.markdown @@ -30,8 +30,7 @@ From the repository (development version) ----------------------------------------- 1. `git pull` -2. `composer install --no-dev` -3. Login and check if everything is ok +2. Login and check if everything is ok Note: This method will install the **current development version**, use at your own risk. diff --git a/doc/es_ES/installation.markdown b/doc/es_ES/installation.markdown index f3dfc495..308b0e2b 100644 --- a/doc/es_ES/installation.markdown +++ b/doc/es_ES/installation.markdown @@ -27,12 +27,8 @@ Las personas que están utilizando una base de datos remota (MySQL / PostgreSQL) Desde el repositorio (versión de desarrollo) ----------------------------------------- - -Debe instalar [compositora] (https://getcomposer.org/)para utilizar este método. - 1. ` git clone https: // github.com / kanboard / kanboard.git` -2. ` composer instalar --no- dev` -3. Ir a la tercera etapa justo por encima +2. Ir a la tercera etapa justo por encima Nota: Este método se instalará la versión de **desarrollo actual** , utilice a su propio riesgo . diff --git a/doc/es_ES/update.markdown b/doc/es_ES/update.markdown index 06789d74..ae5d7a5e 100755 --- a/doc/es_ES/update.markdown +++ b/doc/es_ES/update.markdown @@ -28,7 +28,6 @@ Desde el repositorio (development version) ----------------------------------------- 1. `git pull` -2. `composer install --no-dev` -3. Inicia la sesión y comprobar si todo está bien +2. Inicia la sesión y comprobar si todo está bien Nota: Este método se instalará la **versión de desarrollo actual**, utilice a su propio riesgo. diff --git a/doc/fr_FR/installation.markdown b/doc/fr_FR/installation.markdown index 9e4cb47a..84626151 100644 --- a/doc/fr_FR/installation.markdown +++ b/doc/fr_FR/installation.markdown @@ -26,11 +26,8 @@ Les gens qui utilisent une base de données distante (Mysql/Postgresql) ou un sy Depuis le dépôt git (version de développement) ---------------------------------------------- -Vous devez installer [composer](https://getcomposer.org/) pour utiliser cette méthode. - 1. `git clone https://github.com/kanboard/kanboard.git` -2. `composer install --no-dev` -3. Allez à l'étape 3) juste au-dessus +2. Allez à l'étape 3) juste au-dessus Cette méthode va installer **la version en cours de développement**, utilisez là à vos risques. diff --git a/doc/fr_FR/project-permissions.markdown b/doc/fr_FR/project-permissions.markdown index c4ef4df4..d5e3662d 100644 --- a/doc/fr_FR/project-permissions.markdown +++ b/doc/fr_FR/project-permissions.markdown @@ -15,8 +15,4 @@ L'assignation des rôles est disponible depuis **Paramètres du projet > Permiss  -Si vous choisissez d'autoriser tout le monde, tous les utilisateurs de Kanboard seront considérés comme **Membre du projet**. -Ce qui signifie qu'il n'y a plus des gestion de rôles. -Les permissions par utilisateur ou par groupe ne peuvent plus être appliquées. - Les projets privés ne peuvent pas définir de permissions. diff --git a/doc/fr_FR/update.markdown b/doc/fr_FR/update.markdown index 68468e50..ed248b58 100644 --- a/doc/fr_FR/update.markdown +++ b/doc/fr_FR/update.markdown @@ -30,8 +30,7 @@ Depuis le dépôt git (version de développement) --------------------------------------------- 1. `git pull` -2. `composer install --no-dev` -3. Testez que tout fonctionne correctement +2. Testez que tout fonctionne correctement Cette méthode va installer **la version en cours de développement**, utilisez là à vos risques. diff --git a/doc/ru_RU/create-tasks-by-email.markdown b/doc/ru_RU/create-tasks-by-email.markdown index baddc682..2e380916 100644 --- a/doc/ru_RU/create-tasks-by-email.markdown +++ b/doc/ru_RU/create-tasks-by-email.markdown @@ -19,43 +19,18 @@ Обработка входÑщих email Ñообщений[¶](#incoming-emails-workflow "СÑылка на Ñтот заголовок") ------------------------------------------------------------------------------------------- - -1. Ð’Ñ‹ отправлÑете email Ñообщение на определенный адреÑ, например **something+myproject@inbound.mydomain.tld** +1. Ð’Ñ‹ отправлÑете email Ñообщение на определенный адреÑ, например **myproject@inbound.mydomain.tld** 2. Email Ñообщение перенаправлÑетÑÑ Ð½Ð° SMTP Ñервер 3. SMTP провайдер передает в веб ÑÐµÑ€Ð²Ð¸Ñ ÐšÐ°Ð½Ð±Ð¾Ñ€Ð´Ð° email Ñообщение в JSON формате или в формате multipart/form-data 4. Канборд обрабатывает полученное email Ñообщение и Ñоздает задачу в указанном проекте -**Заметка**: Ðовые задачи автоматичеÑки ÑоздаютÑÑ Ð² первой колонке. - - -Формат email ÑообщениÑ[¶](#email-format "СÑылка на Ñтот заголовок") -------------------------------------------------------------------- - -- Email Ð°Ð´Ñ€ÐµÑ Ð´Ð¾ знака **@** должен Ñодержать разделитель **плюÑ**, например **kanboard+project123** -- Строка ÑÐ»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ Ð¿Ð¾Ñле знака Ð¿Ð»ÑŽÑ Ð¾Ð·Ð½Ð°Ñ‡Ð°ÐµÑ‚ **Идентификатор проекта**, например, проект **Проект 123** может иметь идентификатор проекта **project123**. Идентификатор проекта можно задать в ÑвойÑтвах проекта **Меню** -\> **ÐаÑтройки** -\> **Изменить проект** -\> **Идентификатор**. **Идентификатор** должен быть из цифр и латинÑких букв. -- Тема из email ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ ÑтановитÑÑ Ð½Ð°Ð·Ð²Ð°Ð½Ð¸ÐµÐ¼ задачи -- ТекÑÑ‚ email ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ ÑтановитÑÑ Ð¾Ð¿Ð¸Ñанием задачи (в формате Markdown) - -Email ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ быть напиÑаны в текÑтовом или HTML формате. **Канборд Ñам переконвертирует формат ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð² Markdown** - - -БезопаÑтноÑть и требованиÑ[¶](#security-and-requirements "СÑылка на Ñтот заголовок") ------------------------------------------------------------------------------------- - -- Веб транÑлÑтор Канборд защищен Ñлучайным ключом -- Email Ð°Ð´Ñ€ÐµÑ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð¸Ñ‚ÐµÐ»Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ быть такой же как и у Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ ÐšÐ°Ð½Ð±Ð¾Ñ€Ð´ -- Проект в Канборде должен иметь уникальный идентификатор -- Отправитель email ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ быть учаÑтником проекта - - - - - - - - - - - -[РуÑÑÐºÐ°Ñ Ð´Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ð¸Ñ Kanboard](http://kanboard.ru/doc/) +Notes +----- +- Веб транÑлÑтор Канборд защищен Ñлучайным ключом +- Email Ð°Ð´Ñ€ÐµÑ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð¸Ñ‚ÐµÐ»Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ быть такой же как и у Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ ÐšÐ°Ð½Ð±Ð¾Ñ€Ð´ +- Проект в Канборде должен иметь уникальный идентификатор +- Отправитель email ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ быть учаÑтником проекта +- Email ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ быть напиÑаны в текÑтовом или HTML формате. **Канборд Ñам переконвертирует формат ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð² Markdown** +- Заметка**: Ðовые задачи автоматичеÑки ÑоздаютÑÑ Ð² первой колонке +- Refer to the documentation of each plugin for the configuration diff --git a/doc/ru_RU/installation.markdown b/doc/ru_RU/installation.markdown index ff989b19..5bc4f66c 100644 --- a/doc/ru_RU/installation.markdown +++ b/doc/ru_RU/installation.markdown @@ -47,16 +47,8 @@ ИнÑталÑÑ†Ð¸Ñ Ð¸Ð· Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ (Ñ€Ð°Ð·Ñ€Ð°Ð±Ð°Ñ‚Ñ‹Ð²Ð°ÐµÐ¼Ð°Ñ Ð²ÐµÑ€ÑиÑ)[¶](#from-the-repository-development-version "СÑылка на Ñтот заголовок") -------------------------------------------------------------------------------------------------------------------------- - - -Ð’Ñ‹ можете уÑтановить [composer](https://getcomposer.org/) Ð´Ð»Ñ Ñтого метода инÑталÑции. - - 1. `git clone https://github.com/fguillot/kanboard.git` - -2. `composer install --no-dev` - -3. Далее, перейдите к третьему шагу [ИнÑталÑÑ†Ð¸Ñ Ð¸Ð· архива](installation.html#from-the-archive-stable-version) +2. Далее, перейдите к третьему шагу [ИнÑталÑÑ†Ð¸Ñ Ð¸Ð· архива](installation.html#from-the-archive-stable-version) diff --git a/doc/ru_RU/project-permissions.markdown b/doc/ru_RU/project-permissions.markdown index 6859c1a8..0235c9f6 100644 --- a/doc/ru_RU/project-permissions.markdown +++ b/doc/ru_RU/project-permissions.markdown @@ -37,9 +37,6 @@ -ЕÑли вы выберите **Разрешить любому**, то вÑе пользователи Канборд будут ÑчитатьÑÑ ÑƒÑ‡Ð°Ñтниками Проекта. Ð’ таком Ñлучае, нет необходимоÑти назначать роли. Потому что, разрешениÑ, назначенные пользователÑм и группам, на доÑтуп к Проекту не будут работать. - - Приватный проект не позволÑет уÑтанавливать разрешениÑ. diff --git a/doc/ru_RU/update.markdown b/doc/ru_RU/update.markdown index 7cfabdb0..84483f7b 100644 --- a/doc/ru_RU/update.markdown +++ b/doc/ru_RU/update.markdown @@ -42,10 +42,7 @@ 1. `git pull` - -2. `composer install --no-dev` - -3. Выполните вход и проверьте, что вÑе работает корректно +2. Выполните вход и проверьте, что вÑе работает корректно **Внимание**: ВыполнÑÑ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ðµ из разрабатываемой верÑии, вы должны понимать, что Ñто неÑÑ‚Ð°Ð±Ð¸Ð»ÑŒÐ½Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ Ð¸ берете вÑе риÑки по работе Канборд на ÑебÑ. diff --git a/doc/tr_TR/cronjob.markdown b/doc/tr_TR/cronjob.markdown index 188b299c..65dc1b5d 100644 --- a/doc/tr_TR/cronjob.markdown +++ b/doc/tr_TR/cronjob.markdown @@ -39,7 +39,7 @@ Yinelenen görevi yapılandırmadan önce, Kanboard CLI komut dosyasını çalı İşte bir örnek (`C:\kanboard.bat`): ``` -"C:\php\php.exe" -f "C:\inetpub\wwwroot\kanboard\kanboard" cronjob +"C:\php\php.exe" -f "C:\inetpub\wwwroot\kanboard\cli" cronjob ``` **Kurulumunuza göre PHP yürütülebilir dosyanın yolunu ve Kanboard'un komut dosyasının yolunu deÄŸiÅŸtirmelisiniz.** diff --git a/doc/tr_TR/installation.markdown b/doc/tr_TR/installation.markdown index 23093e56..c46c379d 100644 --- a/doc/tr_TR/installation.markdown +++ b/doc/tr_TR/installation.markdown @@ -26,11 +26,8 @@ Uzak bir veritabanı (Mysql/Postgresql) ve uzak nesne depolama birimi (Aws S3 ve Git deposundan (geliÅŸtirme versiyonu) --------------------------------------------- -Bu yöntemi kullanmak için [composer](https://getcomposer.org/) yüklemelisiniz. - 1. `git clone https://github.com/kanboard/kanboard.git` -2. `composer install --no-dev` -3. Yukarıdaki üçüncü adıma geçin +2. Yukarıdaki üçüncü adıma geçin Not: Bu yöntem, **mevcut geliÅŸtirme sürümünü** yükleyecektir, kendi sorumluluÄŸunuzdadır. diff --git a/doc/tr_TR/project-permissions.markdown b/doc/tr_TR/project-permissions.markdown index 2440f085..19eebe51 100644 --- a/doc/tr_TR/project-permissions.markdown +++ b/doc/tr_TR/project-permissions.markdown @@ -17,7 +17,4 @@ Rol atamaları ** Proje Ayarları> İzinler ** 'den ulaşılabilir:  -Herkese izin vermeyi seçerseniz, tüm Kanboard kullanıcıları Proje Üyesi olarak deÄŸerlendirilecektir. -Bu da artık rol yönetimi olmadığını gösterir. Kullanıcı veya grup başına izin kullanılamaz. - Özel projeler izin tanımlayamaz. diff --git a/doc/tr_TR/search.markdown b/doc/tr_TR/search.markdown index c7bac5e6..1ddc26d1 100644 --- a/doc/tr_TR/search.markdown +++ b/doc/tr_TR/search.markdown @@ -111,6 +111,7 @@ DeÄŸiÅŸtirme tarihi sorguları aynı ÅŸekilde çalışır. Görev referansı, görevinizin harici bir kimliÄŸi, örneÄŸin baÅŸka bir yazılımdan gelen bir bilet numarasıdır. - Görevleri referans ile bulun: `ref:1234` veya `reference:TICKET-1234` +- Wildcard search: `ref:TICKET-*` ### Kategoriye göre ara diff --git a/doc/tr_TR/update.markdown b/doc/tr_TR/update.markdown index 842b1b5a..1ca0a654 100644 --- a/doc/tr_TR/update.markdown +++ b/doc/tr_TR/update.markdown @@ -30,8 +30,7 @@ Depodan-repository (geliÅŸtirme versiyonu) ----------------------------------------- 1. `git pull` -2. `composer install --no-dev` -3. GiriÅŸ yapın ve her ÅŸeyin yolunda olduÄŸunu kontrol edin +2. GiriÅŸ yapın ve her ÅŸeyin yolunda olduÄŸunu kontrol edin Not: Bu yöntem, **mevcut geliÅŸtirme sürümünü** yükleyecektir, bu versiyonu kullanmanız kendi sorumluluÄŸunuzdadır. diff --git a/docker/php/env.conf b/docker/php/env.conf index bcf2e37c..23f6ce3d 100644 --- a/docker/php/env.conf +++ b/docker/php/env.conf @@ -1,2 +1,3 @@ env[DATABASE_URL] = $DATABASE_URL env[DEBUG] = $DEBUG +env[API_AUTHENTICATION_TOKEN] = $API_AUTHENTICATION_TOKEN diff --git a/gulpfile.js b/gulpfile.js index 0f4e405d..d6577a9f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -24,7 +24,6 @@ var vendor = { 'bower_components/jquery-ui/themes/base/jquery-ui.min.css', 'bower_components/jqueryui-timepicker-addon/dist/jquery-ui-timepicker-addon.min.css', 'bower_components/select2/dist/css/select2.min.css', - 'bower_components/fullcalendar/dist/fullcalendar.min.css', 'bower_components/font-awesome/css/font-awesome.min.css', 'bower_components/c3/c3.min.css' ], @@ -34,6 +33,7 @@ var vendor = { 'bower_components/jquery-ui/ui/minified/core.min.js', 'bower_components/jquery-ui/ui/minified/autocomplete.min.js', 'bower_components/jquery-ui/ui/minified/datepicker.min.js', + 'bower_components/jquery-ui/ui/minified/i18n/*.js', 'bower_components/jquery-ui/ui/minified/draggable.min.js', 'bower_components/jquery-ui/ui/minified/droppable.min.js', 'bower_components/jquery-ui/ui/minified/resizable.min.js', @@ -44,9 +44,6 @@ var vendor = { 'bower_components/jqueryui-timepicker-addon/dist/i18n/jquery-ui-timepicker-addon-i18n.min.js', 'bower_components/jqueryui-touch-punch/jquery.ui.touch-punch.min.js', 'bower_components/select2/dist/js/select2.min.js', - 'bower_components/moment/min/moment.min.js', - 'bower_components/fullcalendar/dist/fullcalendar.min.js', - 'bower_components/fullcalendar/dist/locale-all.js', 'bower_components/d3/d3.min.js', 'bower_components/c3/c3.min.js', 'bower_components/isMobile/isMobile.min.js', diff --git a/tests/integration/MeProcedureTest.php b/tests/integration/MeProcedureTest.php index 2106419c..ed471b1b 100644 --- a/tests/integration/MeProcedureTest.php +++ b/tests/integration/MeProcedureTest.php @@ -53,11 +53,7 @@ class MeProcedureTest extends BaseProcedureTest { $dashboard = $this->user->getMyDashboard(); $this->assertNotEmpty($dashboard); - $this->assertArrayHasKey('projects', $dashboard); - $this->assertArrayHasKey('tasks', $dashboard); - $this->assertArrayHasKey('subtasks', $dashboard); - $this->assertNotEmpty($dashboard['projects']); - $this->assertNotEmpty($dashboard['tasks']); + $this->assertEquals('My task', $dashboard[0]['title']); } public function assertGetMyActivityStream() diff --git a/tests/integration/OverdueTaskProcedureTest.php b/tests/integration/OverdueTaskProcedureTest.php index 65f52301..0e31afa0 100644 --- a/tests/integration/OverdueTaskProcedureTest.php +++ b/tests/integration/OverdueTaskProcedureTest.php @@ -19,7 +19,7 @@ class OverdueTaskProcedureTest extends BaseProcedureTest $this->assertNotFalse($this->app->createTask(array( 'title' => 'overdue task', 'project_id' => $this->projectId, - 'date_due' => date('Y-m-d', strtotime('-2days')), + 'date_due' => date('Y-m-d H:i', strtotime('-2days')), ))); } diff --git a/tests/integration/ProjectProcedureTest.php b/tests/integration/ProjectProcedureTest.php index 69c2464f..12bb6d04 100644 --- a/tests/integration/ProjectProcedureTest.php +++ b/tests/integration/ProjectProcedureTest.php @@ -20,6 +20,7 @@ class ProjectProcedureTest extends BaseProcedureTest $this->assertEnableDisableProject(); $this->assertEnableDisablePublicAccess(); $this->assertRemoveProject(); + $this->assertCreateProjectWithOwnerId(); } public function assertGetProjectById() @@ -28,6 +29,8 @@ class ProjectProcedureTest extends BaseProcedureTest $this->assertNotNull($project); $this->assertEquals($this->projectName, $project['name']); $this->assertEquals('Description', $project['description']); + $this->assertArrayHasKey('board', $project['url']); + $this->assertArrayHasKey('list', $project['url']); } public function assertGetProjectByName() @@ -43,6 +46,9 @@ class ProjectProcedureTest extends BaseProcedureTest { $projects = $this->app->getAllProjects(); $this->assertNotEmpty($projects); + $this->assertInternalType('array', $projects); + $this->assertArrayHasKey('board', $projects[0]['url']); + $this->assertArrayHasKey('list', $projects[0]['url']); } public function assertGetProjectActivity() @@ -116,4 +122,23 @@ class ProjectProcedureTest extends BaseProcedureTest $this->assertTrue($this->app->removeProject($this->projectId)); $this->assertNull($this->app->getProjectById($this->projectId)); } + + public function assertCreateProjectWithOwnerId() + { + $this->assertFalse($this->app->createProject(array( + 'name' => 'My project with an owner', + 'owner_id' => 999, + ))); + + $projectId = $this->app->createProject(array( + 'name' => 'My project with an owner', + 'owner_id' => 1, + )); + + $this->assertNotFalse($projectId); + + $project = $this->app->getProjectById($projectId); + $this->assertEquals($projectId, $project['id']); + $this->assertEquals(1, $project['owner_id']); + } } diff --git a/tests/integration/TaskProcedureTest.php b/tests/integration/TaskProcedureTest.php index f456ae52..54527939 100644 --- a/tests/integration/TaskProcedureTest.php +++ b/tests/integration/TaskProcedureTest.php @@ -28,6 +28,7 @@ class TaskProcedureTest extends BaseProcedureTest $this->assertNotNull($task); $this->assertEquals('red', $task['color_id']); $this->assertEquals($this->taskTitle, $task['title']); + $this->assertArrayHasKey('url', $task); } public function assertGetTaskByReference() @@ -45,6 +46,7 @@ class TaskProcedureTest extends BaseProcedureTest $tasks = $this->app->getAllTasks($this->projectId); $this->assertInternalType('array', $tasks); $this->assertNotEmpty($tasks); + $this->assertArrayHasKey('url', $tasks[0]); } public function assertOpenCloseTask() diff --git a/tests/units/Action/TaskAssignColorOnDueDateTest.php b/tests/units/Action/TaskAssignColorOnDueDateTest.php index 4bb87c06..cf1373d5 100644 --- a/tests/units/Action/TaskAssignColorOnDueDateTest.php +++ b/tests/units/Action/TaskAssignColorOnDueDateTest.php @@ -34,4 +34,26 @@ class TaskAssignColorOnDueDateTest extends Base $this->assertEquals('red', $tasks[0]['color_id']); $this->assertEquals('yellow', $tasks[1]['color_id']); } + + public function testChangeColorOnlyWhenNecessary() + { + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $taskFinderModel = new TaskFinderModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test', 'date_due' => strtotime('-1 day'), 'color_id' => 'red'))); + + $tasks = $taskFinderModel->getAll(1); + $event = new TaskListEvent(array('tasks' => $tasks, 'project_id' => 1)); + + $action = new TaskAssignColorOnDueDate($this->container); + $action->setProjectId(1); + $action->setParam('color_id', 'red'); + + $this->assertFalse($action->execute($event, TaskModel::EVENT_DAILY_CRONJOB)); + + $tasks = $taskFinderModel->getAll(1); + $this->assertEquals('red', $tasks[0]['color_id']); + } } diff --git a/tests/units/Action/TaskAssignCreatorTest.php b/tests/units/Action/TaskAssignCreatorTest.php new file mode 100644 index 00000000..70bf9da5 --- /dev/null +++ b/tests/units/Action/TaskAssignCreatorTest.php @@ -0,0 +1,89 @@ +<?php + +require_once __DIR__.'/../Base.php'; + +use Kanboard\Action\TaskAssignCreator; +use Kanboard\Event\TaskEvent; +use Kanboard\Model\TaskCreationModel; +use Kanboard\Model\TaskFinderModel; +use Kanboard\Model\ProjectModel; +use Kanboard\Model\TaskModel; + +class TaskAssignCreatorTest extends Base +{ + public function testChangeUser() + { + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $taskFinderModel = new TaskFinderModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test', 'owner_id' => 0))); + + $event = new TaskEvent(array( + 'task_id' => 1, + 'task' => array( + 'project_id' => 1, + 'column_id' => 2, + 'creator_id' => 1, + ) + )); + + $action = new TaskAssignCreator($this->container); + $action->setProjectId(1); + $action->setParam('column_id', 2); + + $this->assertTrue($action->execute($event, TaskModel::EVENT_MOVE_COLUMN)); + + $task = $taskFinderModel->getById(1); + $this->assertNotEmpty($task); + $this->assertEquals(1, $task['owner_id']); + } + + public function testWithoutCreator() + { + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test', 'owner_id' => 0))); + + $event = new TaskEvent(array( + 'task_id' => 1, + 'task' => array( + 'project_id' => 1, + 'column_id' => 2, + ) + )); + + $action = new TaskAssignCreator($this->container); + $action->setProjectId(1); + $action->setParam('column_id', 2); + + $this->assertFalse($action->execute($event, TaskModel::EVENT_MOVE_COLUMN)); + } + + public function testWithWrongColumn() + { + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test'))); + + $event = new TaskEvent(array( + 'task_id' => 1, + 'task' => array( + 'project_id' => 1, + 'column_id' => 3, + 'creator_id' => 1, + ) + )); + + $action = new TaskAssignCreator($this->container); + $action->setProjectId(1); + $action->setParam('column_id', 2); + + $this->assertFalse($action->execute($event, TaskModel::EVENT_MOVE_COLUMN)); + } +} diff --git a/tests/units/Action/TaskAssignDueDateOnCreationTest.php b/tests/units/Action/TaskAssignDueDateOnCreationTest.php index 26c0584e..64625d2c 100644 --- a/tests/units/Action/TaskAssignDueDateOnCreationTest.php +++ b/tests/units/Action/TaskAssignDueDateOnCreationTest.php @@ -32,6 +32,6 @@ class TaskAssignDueDateOnCreationTest extends Base $task = $taskFinderModel->getById(1); $this->assertNotEmpty($task); - $this->assertEquals(date('Y-m-d', strtotime('+4days')), date('Y-m-d', $task['date_due'])); + $this->assertEquals(date('Y-m-d H:i', strtotime('+4days')), date('Y-m-d H:i', $task['date_due'])); } } diff --git a/tests/units/Action/TaskAssignSpecificUserTest.php b/tests/units/Action/TaskAssignSpecificUserTest.php index 0e63fc13..9b0b8f5d 100644 --- a/tests/units/Action/TaskAssignSpecificUserTest.php +++ b/tests/units/Action/TaskAssignSpecificUserTest.php @@ -2,7 +2,6 @@ require_once __DIR__.'/../Base.php'; -use Kanboard\Event\GenericEvent; use Kanboard\Event\TaskEvent; use Kanboard\Model\TaskCreationModel; use Kanboard\Model\TaskFinderModel; diff --git a/tests/units/Base.php b/tests/units/Base.php index 1c93e9b8..f7c99425 100644 --- a/tests/units/Base.php +++ b/tests/units/Base.php @@ -101,5 +101,6 @@ abstract class Base extends PHPUnit_Framework_TestCase public function tearDown() { $this->container['db']->closeConnection(); + unset ($this->container); } } diff --git a/tests/units/Export/TaskExportTest.php b/tests/units/Export/TaskExportTest.php index e8b347ac..ca0fb8de 100644 --- a/tests/units/Export/TaskExportTest.php +++ b/tests/units/Export/TaskExportTest.php @@ -37,12 +37,13 @@ class TaskExportTest extends Base 'swimlane_id' => 2, 'title' => 'Task 2', 'date_due' => time(), + 'tags' => array('tag 1', 'tag 2'), ))); $report = $taskExport->export(1, date('Y-m-d'), date('Y-m-d')); $this->assertCount(3, $report); - $this->assertCount(22, $report[0]); + $this->assertCount(24, $report[0]); $this->assertEquals('Task Id', $report[0][0]); $this->assertEquals(1, $report[1][0]); @@ -70,12 +71,14 @@ class TaskExportTest extends Base $this->assertEquals('Yellow', $report[2][8]); $this->assertEquals('', $report[1][9]); - $this->assertEquals(date('m/d/Y').' 00:00', $report[2][9]); + $this->assertEquals(date('m/d/Y H:i'), $report[2][9]); $this->assertEquals(3, $report[1][21]); $this->assertEquals(0, $report[2][21]); $this->assertEquals(2.5, $report[1][20]); $this->assertEquals(0, $report[2][20]); + + $this->assertEquals('tag 1, tag 2', $report[2][23]); } } diff --git a/tests/units/Filter/TaskReferenceFilterTest.php b/tests/units/Filter/TaskReferenceFilterTest.php new file mode 100644 index 00000000..49a86454 --- /dev/null +++ b/tests/units/Filter/TaskReferenceFilterTest.php @@ -0,0 +1,65 @@ +<?php + +use Kanboard\Filter\TaskReferenceFilter; +use Kanboard\Model\ProjectModel; +use Kanboard\Model\TaskCreationModel; +use Kanboard\Model\TaskFinderModel; + +require_once __DIR__.'/../Base.php'; + +class TaskReferenceFilterTest extends Base +{ + public function testWithoutMatch() + { + $taskFinder = new TaskFinderModel($this->container); + $taskCreation = new TaskCreationModel($this->container); + $projectModel = new ProjectModel($this->container); + $query = $taskFinder->getExtendedQuery(); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreation->create(array('title' => 'Test', 'project_id' => 1))); + + $filter = new TaskReferenceFilter(); + $filter->withQuery($query); + $filter->withValue('aaa-bbb'); + $filter->apply(); + + $this->assertCount(0, $query->findAll()); + } + + public function testWithExactMatch() + { + $taskFinder = new TaskFinderModel($this->container); + $taskCreation = new TaskCreationModel($this->container); + $projectModel = new ProjectModel($this->container); + $query = $taskFinder->getExtendedQuery(); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreation->create(array('title' => 'Test', 'project_id' => 1, 'reference' => 'aaa-bbb'))); + + $filter = new TaskReferenceFilter(); + $filter->withQuery($query); + $filter->withValue('aaa-bbb'); + $filter->apply(); + + $this->assertCount(1, $query->findAll()); + } + + public function testWithWildCard() + { + $taskFinder = new TaskFinderModel($this->container); + $taskCreation = new TaskCreationModel($this->container); + $projectModel = new ProjectModel($this->container); + $query = $taskFinder->getExtendedQuery(); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreation->create(array('title' => 'Test', 'project_id' => 1, 'reference' => 'aaa-bbb'))); + + $filter = new TaskReferenceFilter(); + $filter->withQuery($query); + $filter->withValue('aaa-*'); + $filter->apply(); + + $this->assertCount(1, $query->findAll()); + } +} diff --git a/tests/units/Helper/ProjectRoleHelperTest.php b/tests/units/Helper/ProjectRoleHelperTest.php index eb9b320c..d2335dfa 100644 --- a/tests/units/Helper/ProjectRoleHelperTest.php +++ b/tests/units/Helper/ProjectRoleHelperTest.php @@ -106,6 +106,116 @@ class ProjectRoleHelperTest extends Base $this->assertFalse($projectRoleHelper->canCreateTaskInColumn(1, 2)); } + public function testCanRemoveTaskWithCustomProjectRoleAndRestriction() + { + $projectRoleHelper = new ProjectRoleHelper($this->container); + $projectModel = new ProjectModel($this->container); + $projectUserRole = new ProjectUserRoleModel($this->container); + $userModel = new UserModel($this->container); + $projectRoleModel = new ProjectRoleModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $taskFinderModel = new TaskFinderModel($this->container); + $projectRoleRestrictionModel = new ProjectRoleRestrictionModel($this->container); + + $this->container['sessionStorage']->user = array( + 'id' => 2, + 'role' => Role::APP_USER, + ); + + $this->assertEquals(2, $userModel->create(array('username' => 'user'))); + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1 , 'title' => 'test'))); + + $this->assertEquals(1, $projectRoleModel->create(1, 'Custom Role')); + $this->assertTrue($projectUserRole->addUser(1, 2, 'Custom Role')); + + $this->assertEquals(1, $projectRoleRestrictionModel->create(1, 1, ProjectRoleRestrictionModel::RULE_TASK_SUPPRESSION)); + + $task = $taskFinderModel->getById(1); + $this->assertFalse($projectRoleHelper->canRemoveTask($task)); + } + + public function testCanRemoveTaskWithCustomProjectRole() + { + $projectRoleHelper = new ProjectRoleHelper($this->container); + $projectModel = new ProjectModel($this->container); + $projectUserRole = new ProjectUserRoleModel($this->container); + $userModel = new UserModel($this->container); + $projectRoleModel = new ProjectRoleModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $taskFinderModel = new TaskFinderModel($this->container); + + $this->container['sessionStorage']->user = array( + 'id' => 2, + 'role' => Role::APP_USER, + ); + + $this->assertEquals(2, $userModel->create(array('username' => 'user'))); + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1 , 'title' => 'test'))); + + $this->assertEquals(1, $projectRoleModel->create(1, 'Custom Role')); + $this->assertTrue($projectUserRole->addUser(1, 2, 'Custom Role')); + + $task = $taskFinderModel->getById(1); + $this->assertTrue($projectRoleHelper->canRemoveTask($task)); + } + + public function testCanChangeAssigneeWithCustomProjectRoleAndRestriction() + { + $projectRoleHelper = new ProjectRoleHelper($this->container); + $projectModel = new ProjectModel($this->container); + $projectUserRole = new ProjectUserRoleModel($this->container); + $userModel = new UserModel($this->container); + $projectRoleModel = new ProjectRoleModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $taskFinderModel = new TaskFinderModel($this->container); + $projectRoleRestrictionModel = new ProjectRoleRestrictionModel($this->container); + + $this->container['sessionStorage']->user = array( + 'id' => 2, + 'role' => Role::APP_USER, + ); + + $this->assertEquals(2, $userModel->create(array('username' => 'user'))); + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1 , 'title' => 'test'))); + + $this->assertEquals(1, $projectRoleModel->create(1, 'Custom Role')); + $this->assertTrue($projectUserRole->addUser(1, 2, 'Custom Role')); + + $this->assertEquals(1, $projectRoleRestrictionModel->create(1, 1, ProjectRoleRestrictionModel::RULE_TASK_CHANGE_ASSIGNEE)); + + $task = $taskFinderModel->getById(1); + $this->assertFalse($projectRoleHelper->canChangeAssignee($task)); + } + + public function testCanChangeAssigneeWithCustomProjectRole() + { + $projectRoleHelper = new ProjectRoleHelper($this->container); + $projectModel = new ProjectModel($this->container); + $projectUserRole = new ProjectUserRoleModel($this->container); + $userModel = new UserModel($this->container); + $projectRoleModel = new ProjectRoleModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $taskFinderModel = new TaskFinderModel($this->container); + + $this->container['sessionStorage']->user = array( + 'id' => 2, + 'role' => Role::APP_USER, + ); + + $this->assertEquals(2, $userModel->create(array('username' => 'user'))); + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1 , 'title' => 'test'))); + + $this->assertEquals(1, $projectRoleModel->create(1, 'Custom Role')); + $this->assertTrue($projectUserRole->addUser(1, 2, 'Custom Role')); + + $task = $taskFinderModel->getById(1); + $this->assertTrue($projectRoleHelper->canChangeAssignee($task)); + } + public function testCanChangeTaskStatusInColumnWithProjectViewer() { $projectRoleHelper = new ProjectRoleHelper($this->container); diff --git a/tests/units/Model/ProjectModelTest.php b/tests/units/Model/ProjectModelTest.php index 1f65cad7..7958ef0b 100644 --- a/tests/units/Model/ProjectModelTest.php +++ b/tests/units/Model/ProjectModelTest.php @@ -2,7 +2,13 @@ require_once __DIR__.'/../Base.php'; +use Kanboard\Api\Procedure\ProjectPermissionProcedure; +use Kanboard\Core\Security\Role; use Kanboard\Core\Translator; +use Kanboard\Model\ColumnModel; +use Kanboard\Model\ProjectPermissionModel; +use Kanboard\Model\SwimlaneModel; +use Kanboard\Model\TagModel; use Kanboard\Subscriber\ProjectModificationDateSubscriber; use Kanboard\Model\ProjectModel; use Kanboard\Model\UserModel; @@ -43,6 +49,21 @@ class ProjectModelTest extends Base $this->assertEmpty($project['end_date']); } + public function testCreationWithUserId() + { + $projectModel = new ProjectModel($this->container); + + $this->assertFalse($projectModel->create(array('name' => 'UnitTest'), 3)); + + $this->assertEquals(1, $projectModel->create(array('name' => 'UnitTest'), 1)); + $project = $projectModel->getById(1); + $this->assertEquals(1, $project['owner_id']); + + $this->assertEquals(2, $projectModel->create(array('name' => 'UnitTest'), 0)); + $project = $projectModel->getById(2); + $this->assertEquals(0, $project['owner_id']); + } + public function testProjectDate() { $projectModel = new ProjectModel($this->container); @@ -159,6 +180,26 @@ class ProjectModelTest extends Base $this->assertGreaterThan($now, $project['last_modified']); } + public function testUpdateOwnerId() + { + $projectModel = new ProjectModel($this->container); + $this->assertEquals(1, $projectModel->create(array('name' => 'UnitTest'))); + + $this->assertFalse($projectModel->update(array('id'=> 1, 'name' => 'test', 'owner_id' => 2))); + + $this->assertTrue($projectModel->update(array('id'=> 1, 'name' => 'test', 'owner_id' => 1))); + + $project = $projectModel->getById(1); + $this->assertNotEmpty($project); + $this->assertEquals(1, $project['owner_id']); + + $this->assertTrue($projectModel->update(array('id'=> 1, 'name' => 'test', 'owner_id' => 0))); + + $project = $projectModel->getById(1); + $this->assertNotEmpty($project); + $this->assertEquals(0, $project['owner_id']); + } + public function testGetAllIds() { $projectModel = new ProjectModel($this->container); @@ -207,6 +248,68 @@ class ProjectModelTest extends Base $this->assertFalse($projectModel->remove(1234)); } + public function testRemoveTagsOnProjectRemove() + { + $projectModel = new ProjectModel($this->container); + $tagModel = new TagModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'UnitTest'))); + $this->assertNotFalse($tagModel->create(1, 'TestTag')); + + $this->assertCount(1, $tagModel->getAllByProject(1)); + + $this->assertTrue($projectModel->remove(1)); + + $this->assertCount(0, $tagModel->getAllByProject(1)); + } + + public function testRemoveSwimlaneOnProjectRemove() + { + $projectModel = new ProjectModel($this->container); + $swimlaneModel = new SwimlaneModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'UnitTest'))); + + $swimlaneId = $swimlaneModel->create(1, 'TestSwimlane'); + $this->assertNotFalse($swimlaneId); + + $this->assertTrue($projectModel->remove(1)); + $this->assertNull($swimlaneModel->getById($swimlaneId)); + } + + public function testRemoveColumnOnProjectRemove() + { + $projectModel = new ProjectModel($this->container); + $columnModel = new ColumnModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'UnitTest'))); + + $columnId = $columnModel->create(1, 'TestColumn'); + $this->assertNotFalse($columnId); + + $this->assertTrue($projectModel->remove(1)); + $this->assertNull($columnModel->getById($columnId)); + } + + public function testRemovePermissionOnProjectRemove() + { + $projectModel = new ProjectModel($this->container); + $userModel = new UserModel($this->container); + + $permissionModel = new ProjectPermissionModel($this->container); + $permissionProcedure = new ProjectPermissionProcedure($this->container); + + $userId = $userModel->create(array('username' => 'user1')); + $this->assertNotFalse($userId); + + $this->assertEquals(1, $projectModel->create(array('name' => 'UnitTest'))); + $permissionProcedure->addProjectUser(1, $userId, Role::PROJECT_MEMBER); + + $this->assertTrue($permissionModel->isUserAllowed(1, $userId)); + $this->assertTrue($projectModel->remove(1)); + $this->assertFalse($permissionModel->isUserAllowed(1, $userId)); + } + public function testEnable() { $projectModel = new ProjectModel($this->container); @@ -354,4 +457,17 @@ class ProjectModelTest extends Base $this->assertEquals('', $project['owner_username']); $this->assertEquals(0, $project['owner_id']); } + + public function testGetList() + { + $projectModel = new ProjectModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Project B'), 1)); + $this->assertEquals(2, $projectModel->create(array('name' => 'Project A', 'is_private' => 1), 1)); + + $this->assertEquals(array(0 => 'None', 1 => 'Project B'), $projectModel->getList()); + $this->assertEquals(array(1 => 'Project B'), $projectModel->getList(false)); + $this->assertEquals(array(2 => 'Project A', 1 => 'Project B'), $projectModel->getList(false, false)); + $this->assertEquals(array(0 => 'None', 2 => 'Project A', 1 => 'Project B'), $projectModel->getList(true, false)); + } } diff --git a/tests/units/Model/ProjectPermissionModelTest.php b/tests/units/Model/ProjectPermissionModelTest.php index 7f604374..4b5a7476 100644 --- a/tests/units/Model/ProjectPermissionModelTest.php +++ b/tests/units/Model/ProjectPermissionModelTest.php @@ -120,18 +120,6 @@ class ProjectPermissionModelTest extends Base $this->assertEquals('Project 3', $users[0]['project_name']); } - public function testEverybodyAllowed() - { - $projectModel = new ProjectModel($this->container); - $projectPermission = new ProjectPermissionModel($this->container); - - $this->assertEquals(1, $projectModel->create(array('name' => 'Project 1'))); - $this->assertEquals(2, $projectModel->create(array('name' => 'Project 2', 'is_everybody_allowed' => 1))); - - $this->assertFalse($projectPermission->isEverybodyAllowed(1)); - $this->assertTrue($projectPermission->isEverybodyAllowed(2)); - } - public function testIsUserAllowed() { $userModel = new UserModel($this->container); diff --git a/tests/units/Model/ProjectRoleRestrictionModelTest.php b/tests/units/Model/ProjectRoleRestrictionModelTest.php index af0f9bf9..6f77f8a2 100644 --- a/tests/units/Model/ProjectRoleRestrictionModelTest.php +++ b/tests/units/Model/ProjectRoleRestrictionModelTest.php @@ -90,7 +90,7 @@ class ProjectRoleRestrictionModelTest extends Base $projectRoleRestrictionModel = new ProjectRoleRestrictionModel($this->container); $rules = $projectRoleRestrictionModel->getRules(); - $this->assertCount(3, $rules); + $this->assertCount(6, $rules); $this->assertArrayHasKey(ProjectRoleRestrictionModel::RULE_TASK_OPEN_CLOSE, $rules); } } diff --git a/tests/units/Model/ProjectUserRoleTest.php b/tests/units/Model/ProjectUserRoleTest.php index d59b15c9..e45080eb 100644 --- a/tests/units/Model/ProjectUserRoleTest.php +++ b/tests/units/Model/ProjectUserRoleTest.php @@ -101,26 +101,6 @@ class ProjectUserRoleTest extends Base $this->assertEquals('', $userRoleModel->getUserRole(1, 2)); } - public function testGetRoleWithPublicProject() - { - $projectModel = new ProjectModel($this->container); - $userRoleModel = new ProjectUserRoleModel($this->container); - $userModel = new UserModel($this->container); - - $this->assertEquals(2, $userModel->create(array('username' => 'user1', 'name' => 'User1'))); - $this->assertEquals(3, $userModel->create(array('username' => 'user2', 'name' => 'User2'))); - - $this->assertEquals(1, $projectModel->create(array('name' => 'Test'), 2, true)); - - $this->assertEquals(Role::PROJECT_MANAGER, $userRoleModel->getUserRole(1, 2)); - $this->assertEquals(null, $userRoleModel->getUserRole(1, 3)); - - $this->assertTrue($projectModel->update(array('id' => 1, 'is_everybody_allowed' => 1))); - - $this->assertEquals(Role::PROJECT_MANAGER, $userRoleModel->getUserRole(1, 2)); - $this->assertEquals(Role::PROJECT_MEMBER, $userRoleModel->getUserRole(1, 3)); - } - public function testGetAssignableUsersWithDisabledUsers() { $projectModel = new ProjectModel($this->container); @@ -247,59 +227,6 @@ class ProjectUserRoleTest extends Base $this->assertEquals('admin', $users[1]); } - public function testGetAssignableUsersWithEverybodyAllowed() - { - $projectModel = new ProjectModel($this->container); - $userModel = new UserModel($this->container); - $userRoleModel = new ProjectUserRoleModel($this->container); - - $this->assertEquals(1, $projectModel->create(array('name' => 'Test', 'is_everybody_allowed' => 1))); - - $this->assertEquals(2, $userModel->create(array('username' => 'user1', 'name' => 'User1'))); - $this->assertEquals(3, $userModel->create(array('username' => 'user2', 'name' => 'User2'))); - $this->assertEquals(4, $userModel->create(array('username' => 'user3', 'name' => 'User3'))); - $this->assertEquals(5, $userModel->create(array('username' => 'user4', 'name' => 'User4'))); - - $users = $userRoleModel->getAssignableUsers(1); - $this->assertCount(5, $users); - - $this->assertEquals('admin', $users[1]); - $this->assertEquals('User1', $users[2]); - $this->assertEquals('User2', $users[3]); - $this->assertEquals('User3', $users[4]); - $this->assertEquals('User4', $users[5]); - } - - public function testGetAssignableUsersWithDisabledUsersAndEverybodyAllowed() - { - $projectModel = new ProjectModel($this->container); - $projectPermission = new ProjectPermissionModel($this->container); - $userModel = new UserModel($this->container); - $userRoleModel = new ProjectUserRoleModel($this->container); - - $this->assertEquals(2, $userModel->create(array('username' => 'user1', 'name' => 'User1'))); - $this->assertEquals(3, $userModel->create(array('username' => 'user2', 'name' => 'User2'))); - - $this->assertEquals(1, $projectModel->create(array('name' => 'Project 1', 'is_everybody_allowed' => 1))); - - $this->assertTrue($projectPermission->isEverybodyAllowed(1)); - - $users = $userRoleModel->getAssignableUsers(1); - $this->assertCount(3, $users); - - $this->assertEquals('admin', $users[1]); - $this->assertEquals('User1', $users[2]); - $this->assertEquals('User2', $users[3]); - - $this->assertTrue($userModel->disable(2)); - - $users = $userRoleModel->getAssignableUsers(1); - $this->assertCount(2, $users); - - $this->assertEquals('admin', $users[1]); - $this->assertEquals('User2', $users[3]); - } - public function testGetProjectsByUser() { $userModel = new UserModel($this->container); diff --git a/tests/units/Model/SubtaskModelTest.php b/tests/units/Model/SubtaskModelTest.php index eed37cf3..8ad054d1 100644 --- a/tests/units/Model/SubtaskModelTest.php +++ b/tests/units/Model/SubtaskModelTest.php @@ -172,4 +172,33 @@ class SubtaskModelTest extends Base $this->assertEquals(1, $subtaskModel->getProjectId(1)); $this->assertEquals(0, $subtaskModel->getProjectId(2)); } + + public function testGetAllByTaskIds() + { + $taskCreationModel = new TaskCreationModel($this->container); + $subtaskModel = new SubtaskModel($this->container); + $projectModel = new ProjectModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1))); + $this->assertEquals(1, $subtaskModel->create(array('title' => 'subtask #1', 'task_id' => 1))); + + $this->assertCount(0, $subtaskModel->getAllByTaskIds(array())); + $this->assertCount(1, $subtaskModel->getAllByTaskIds(array(1))); + } + + public function testGetAllByTaskIdsAndAssignee() + { + $taskCreationModel = new TaskCreationModel($this->container); + $subtaskModel = new SubtaskModel($this->container); + $projectModel = new ProjectModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1))); + $this->assertEquals(1, $subtaskModel->create(array('title' => 'subtask #1', 'task_id' => 1, 'user_id' => 1))); + + $this->assertCount(0, $subtaskModel->getAllByTaskIdsAndAssignee(array(), 1)); + $this->assertCount(0, $subtaskModel->getAllByTaskIdsAndAssignee(array(1), 2)); + $this->assertCount(1, $subtaskModel->getAllByTaskIdsAndAssignee(array(1), 1)); + } } diff --git a/tests/units/Model/TaskCreationModelTest.php b/tests/units/Model/TaskCreationModelTest.php index ce9996d9..7723bece 100644 --- a/tests/units/Model/TaskCreationModelTest.php +++ b/tests/units/Model/TaskCreationModelTest.php @@ -248,7 +248,7 @@ class TaskCreationModelTest extends Base public function testDateDue() { - $date = '2014-11-23'; + $date = '2014-11-23 14:30'; $timestamp = strtotime('+2days'); $projectModel = new ProjectModel($this->container); $taskCreationModel = new TaskCreationModel($this->container); @@ -262,12 +262,12 @@ class TaskCreationModelTest extends Base $task = $taskFinderModel->getById(1); $this->assertNotEmpty($task); $this->assertEquals(1, $task['id']); - $this->assertEquals($date, date('Y-m-d', $task['date_due'])); + $this->assertEquals($date, date('Y-m-d H:i', $task['date_due'])); $task = $taskFinderModel->getById(2); $this->assertNotEmpty($task); $this->assertEquals(2, $task['id']); - $this->assertEquals(date('Y-m-d 00:00', $timestamp), date('Y-m-d 00:00', $task['date_due'])); + $this->assertEquals(date('Y-m-d H:i', $timestamp), date('Y-m-d H:i', $task['date_due'])); $task = $taskFinderModel->getById(3); $this->assertEquals(3, $task['id']); @@ -398,6 +398,6 @@ class TaskCreationModelTest extends Base $task = $taskFinderModel->getById(1); $this->assertNotEmpty($task); - $this->assertEquals('2050-01-10 00:00', date('Y-m-d H:i', $task['date_due'])); + $this->assertEquals('2050-01-10 12:30', date('Y-m-d H:i', $task['date_due'])); } } diff --git a/tests/units/Model/TaskDuplicationModelTest.php b/tests/units/Model/TaskDuplicationModelTest.php index 1fff110c..e4121ff9 100644 --- a/tests/units/Model/TaskDuplicationModelTest.php +++ b/tests/units/Model/TaskDuplicationModelTest.php @@ -93,7 +93,7 @@ class TaskDuplicationModelTest extends Base $this->assertEquals(1, $task['swimlane_id']); $this->assertEquals(3, $task['column_id']); $this->assertEquals(2, $task['position']); - $this->assertEquals('test', $task['title']); + $this->assertEquals('[DUPLICATE] test', $task['title']); $this->assertEquals(0, $task['time_spent']); } diff --git a/tests/units/Model/TaskModificationModelTest.php b/tests/units/Model/TaskModificationModelTest.php index f70561b3..a716cf3d 100644 --- a/tests/units/Model/TaskModificationModelTest.php +++ b/tests/units/Model/TaskModificationModelTest.php @@ -195,15 +195,15 @@ class TaskModificationModelTest extends Base $task = $taskFinderModel->getById(1); $this->assertEquals(0, $task['date_due']); - $this->assertTrue($taskModificationModel->update(array('id' => 1, 'date_due' => '2014-11-24'))); + $this->assertTrue($taskModificationModel->update(array('id' => 1, 'date_due' => '2014-11-24 14:30'))); $task = $taskFinderModel->getById(1); - $this->assertEquals('2014-11-24', date('Y-m-d', $task['date_due'])); + $this->assertEquals('2014-11-24 14:30', date('Y-m-d H:i', $task['date_due'])); $this->assertTrue($taskModificationModel->update(array('id' => 1, 'date_due' => time()))); $task = $taskFinderModel->getById(1); - $this->assertEquals(date('Y-m-d'), date('Y-m-d', $task['date_due'])); + $this->assertEquals(date('Y-m-d H:i'), date('Y-m-d H:i', $task['date_due'])); } public function testChangeStartedDate() diff --git a/tests/units/Model/TaskRecurrenceModelTest.php b/tests/units/Model/TaskRecurrenceModelTest.php index 85d77bc2..56f6cf84 100644 --- a/tests/units/Model/TaskRecurrenceModelTest.php +++ b/tests/units/Model/TaskRecurrenceModelTest.php @@ -106,7 +106,7 @@ class TaskRecurrenceModelTest extends Base $this->assertNotEmpty($task); $this->assertEquals(TaskModel::RECURRING_STATUS_PROCESSED, $task['recurrence_status']); $this->assertEquals(2, $task['recurrence_child']); - $this->assertEquals(1436486400, $task['date_due'], '', 2); + $this->assertEquals(1436561776, $task['date_due'], '', 2); $task = $taskFinderModel->getById(2); $this->assertNotEmpty($task); @@ -116,7 +116,7 @@ class TaskRecurrenceModelTest extends Base $this->assertEquals(TaskModel::RECURRING_BASEDATE_TRIGGERDATE, $task['recurrence_basedate']); $this->assertEquals(1, $task['recurrence_parent']); $this->assertEquals(2, $task['recurrence_factor']); - $this->assertEquals($dateParser->removeTimeFromTimestamp(strtotime('+2 days')), $task['date_due'], '', 2); + $this->assertEquals(strtotime('+2 days'), $task['date_due'], '', 2); $tags = $taskTagModel->getList(2); $this->assertCount(2, $tags); diff --git a/tests/units/Model/UserNotificationTest.php b/tests/units/Model/UserNotificationTest.php index 6ee5c875..e568d204 100644 --- a/tests/units/Model/UserNotificationTest.php +++ b/tests/units/Model/UserNotificationTest.php @@ -179,55 +179,21 @@ class UserNotificationTest extends Base $this->assertEquals('user3@here', $users[2]['email']); } - public function testGetUsersWithNotificationsWhenEverybodyAllowed() - { - $u = new UserModel($this->container); - $p = new ProjectModel($this->container); - $n = new UserNotificationModel($this->container); - $pp = new ProjectPermissionModel($this->container); - - $this->assertEquals(1, $p->create(array('name' => 'UnitTest1', 'is_everybody_allowed' => 1))); - $this->assertTrue($pp->isEverybodyAllowed(1)); - - // Email + Notifications enabled - $this->assertEquals(2, $u->create(array('username' => 'user1', 'email' => 'user1@here', 'notifications_enabled' => 1))); - - // No email + Notifications enabled - $this->assertEquals(3, $u->create(array('username' => 'user2', 'email' => '', 'notifications_enabled' => 1))); - - // Email + Notifications enabled - $this->assertEquals(4, $u->create(array('username' => 'user3', 'email' => 'user3@here', 'notifications_enabled' => 1))); - - // User disabled - $this->assertEquals(5, $u->create(array('username' => 'user4', 'email' => 'user3@here', 'notifications_enabled' => 1, 'is_active' => 0))); - - // No email + notifications disabled - $this->assertEquals(6, $u->create(array('username' => 'user5'))); - - $users = $n->getUsersWithNotificationEnabled(1); - - $this->assertNotEmpty($users); - $this->assertCount(3, $users); - $this->assertEquals('user1@here', $users[0]['email']); - $this->assertEquals('', $users[1]['email']); - $this->assertEquals('user3@here', $users[2]['email']); - } - public function testSendNotifications() { - $u = new UserModel($this->container); - $n = new UserNotificationModel($this->container); - $p = new ProjectModel($this->container); - $tc = new TaskCreationModel($this->container); - $tf = new TaskFinderModel($this->container); - $pp = new ProjectPermissionModel($this->container); + $userModel = new UserModel($this->container); + $userNotificationModel = new UserNotificationModel($this->container); + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $taskFinderModel = new TaskFinderModel($this->container); + $projectUserRoleModel = new ProjectUserRoleModel($this->container); - $this->assertEquals(1, $p->create(array('name' => 'UnitTest1', 'is_everybody_allowed' => 1))); - $this->assertEquals(1, $tc->create(array('title' => 'test', 'project_id' => 1))); - $this->assertTrue($u->update(array('id' => 1, 'email' => 'test@localhost'))); - $this->assertTrue($pp->isEverybodyAllowed(1)); + $this->assertEquals(1, $projectModel->create(array('name' => 'UnitTest1'))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test', 'project_id' => 1))); + $this->assertTrue($userModel->update(array('id' => 1, 'email' => 'test@localhost'))); + $this->assertTrue($projectUserRoleModel->addUser(1, 1, Role::PROJECT_MANAGER)); - $n->saveSettings(1, array( + $userNotificationModel->saveSettings(1, array( 'notifications_enabled' => 1, 'notifications_filter' => UserNotificationFilterModel::FILTER_NONE, 'notification_types' => array('web' => 1, 'email' => 1), @@ -259,6 +225,6 @@ class UserNotificationTest extends Base ->with($this->equalTo('web')) ->will($this->returnValue($notifier)); - $n->sendNotifications(TaskModel::EVENT_CREATE, array('task' => $tf->getDetails(1))); + $userNotificationModel->sendNotifications(TaskModel::EVENT_CREATE, array('task' => $taskFinderModel->getDetails(1))); } } diff --git a/tests/units/Pagination/DashboardPaginationTest.php b/tests/units/Pagination/DashboardPaginationTest.php new file mode 100644 index 00000000..6b0a40fb --- /dev/null +++ b/tests/units/Pagination/DashboardPaginationTest.php @@ -0,0 +1,121 @@ +<?php + +use Kanboard\Core\Security\Role; +use Kanboard\Model\ColumnModel; +use Kanboard\Model\ProjectModel; +use Kanboard\Model\ProjectUserRoleModel; +use Kanboard\Model\SubtaskModel; +use Kanboard\Model\TaskCreationModel; +use Kanboard\Model\UserModel; +use Kanboard\Pagination\DashboardPagination; + +require_once __DIR__.'/../Base.php'; + +class DashboardPaginationTest extends Base +{ + public function testDashboardPagination() + { + $projectModel = new ProjectModel($this->container); + $projectUserRoleModel = new ProjectUserRoleModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $subtaskModel = new SubtaskModel($this->container); + $dashboardPagination = new DashboardPagination($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Project #1'))); + $this->assertEquals(2, $projectModel->create(array('name' => 'Project #2'))); + + $this->assertTrue($projectUserRoleModel->addUser(1, 1, Role::PROJECT_MEMBER)); + + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'Task #1', 'project_id' => 2))); + $this->assertEquals(2, $taskCreationModel->create(array('title' => 'Task #2', 'project_id' => 1, 'column_id' => 2, 'owner_id' => 1))); + + $this->assertEquals(1, $subtaskModel->create(array('task_id' => 1, 'title' => 'subtask A', 'user_id' => 1))); + $this->assertEquals(2, $subtaskModel->create(array('task_id' => 2, 'title' => 'subtask B', 'user_id' => 1))); + $this->assertEquals(3, $subtaskModel->create(array('task_id' => 2, 'title' => 'subtask C'))); + + $dashboard = $dashboardPagination->getOverview(1); + $this->assertCount(1, $dashboard); + $this->assertEquals(1, $dashboard[0]['project_id']); + $this->assertEquals('Project #1', $dashboard[0]['project_name']); + $this->assertEquals(1, $dashboard[0]['paginator']->getTotal()); + + $tasks = $dashboard[0]['paginator']->getCollection(); + $this->assertCount(1, $tasks); + $this->assertCount(1, $tasks[0]['subtasks']); + $this->assertEquals('subtask B', $tasks[0]['subtasks'][0]['title']); + } + + public function testWhenUserIsNotAssignedToTask() + { + $userModel = new UserModel($this->container); + $projectModel = new ProjectModel($this->container); + $projectUserRoleModel = new ProjectUserRoleModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $subtaskModel = new SubtaskModel($this->container); + $dashboardPagination = new DashboardPagination($this->container); + + $this->assertEquals(2, $userModel->create(array('username' => 'user1'))); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Project #1'))); + $this->assertEquals(2, $projectModel->create(array('name' => 'Project #2'))); + + $this->assertTrue($projectUserRoleModel->addUser(1, 1, Role::PROJECT_MEMBER)); + $this->assertTrue($projectUserRoleModel->addUser(1, 2, Role::PROJECT_MEMBER)); + $this->assertTrue($projectUserRoleModel->addUser(2, 2, Role::PROJECT_MEMBER)); + + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'Task #1', 'project_id' => 2, 'priority' => 3))); + $this->assertEquals(2, $taskCreationModel->create(array('title' => 'Task #2', 'project_id' => 1, 'column_id' => 2, 'owner_id' => 2))); + $this->assertEquals(3, $taskCreationModel->create(array('title' => 'Task #3', 'project_id' => 2, 'owner_id' => 2))); + + $this->assertEquals(1, $subtaskModel->create(array('task_id' => 1, 'title' => 'subtask A', 'user_id' => 2))); + $this->assertEquals(2, $subtaskModel->create(array('task_id' => 2, 'title' => 'subtask B', 'user_id' => 1))); + $this->assertEquals(3, $subtaskModel->create(array('task_id' => 2, 'title' => 'subtask C'))); + + $dashboard = $dashboardPagination->getOverview(1); + $this->assertCount(1, $dashboard); + $this->assertEquals(1, $dashboard[0]['project_id']); + $this->assertEquals('Project #1', $dashboard[0]['project_name']); + $this->assertEquals(1, $dashboard[0]['paginator']->getTotal()); + + $tasks = $dashboard[0]['paginator']->getCollection(); + $this->assertCount(1, $tasks); + $this->assertCount(1, $tasks[0]['subtasks']); + $this->assertEquals('subtask B', $tasks[0]['subtasks'][0]['title']); + + $dashboard = $dashboardPagination->getOverview(2); + $this->assertCount(2, $dashboard); + $this->assertEquals('Project #1', $dashboard[0]['project_name']); + $this->assertEquals('Project #2', $dashboard[1]['project_name']); + $this->assertEquals(1, $dashboard[0]['paginator']->getTotal()); + + $tasks = $dashboard[0]['paginator']->getCollection(); + $this->assertCount(1, $tasks); + $this->assertCount(0, $tasks[0]['subtasks']); + + $tasks = $dashboard[1]['paginator']->getCollection(); + $this->assertCount(2, $tasks); + $this->assertCount(1, $tasks[0]['subtasks']); + $this->assertEquals('subtask A', $tasks[0]['subtasks'][0]['title']); + } + + public function testWhenColumnIsHidden() + { + $projectModel = new ProjectModel($this->container); + $projectUserRoleModel = new ProjectUserRoleModel($this->container); + $columnModel = new ColumnModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $subtaskModel = new SubtaskModel($this->container); + $dashboardPagination = new DashboardPagination($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Project #1'))); + $this->assertTrue($projectUserRoleModel->addUser(1, 1, Role::PROJECT_MEMBER)); + + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'Task #1', 'project_id' => 1, 'column_id' => 1))); + $this->assertEquals(1, $subtaskModel->create(array('task_id' => 1, 'title' => 'subtask #1', 'user_id' => 1))); + + $this->assertCount(1, $dashboardPagination->getOverview(1)); + + $this->assertTrue($columnModel->update(1, 'test', 0, '', 1)); + $this->assertCount(0, $dashboardPagination->getOverview(1)); + } +} diff --git a/tests/units/Pagination/SubtaskPaginationTest.php b/tests/units/Pagination/SubtaskPaginationTest.php deleted file mode 100644 index 1e16c985..00000000 --- a/tests/units/Pagination/SubtaskPaginationTest.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -use Kanboard\Model\ColumnModel; -use Kanboard\Model\ProjectModel; -use Kanboard\Model\SubtaskModel; -use Kanboard\Model\TaskCreationModel; -use Kanboard\Model\TaskModel; -use Kanboard\Pagination\SubtaskPagination; - -require_once __DIR__.'/../Base.php'; - -class SubtaskPaginationTest extends Base -{ - public function testDashboardPagination() - { - $taskCreationModel = new TaskCreationModel($this->container); - $projectModel = new ProjectModel($this->container); - $subtaskModel = new SubtaskModel($this->container); - $subtaskPagination = new SubtaskPagination($this->container); - - $this->assertEquals(1, $projectModel->create(array('name' => 'Project #1'))); - $this->assertEquals(1, $taskCreationModel->create(array('title' => 'Task #1', 'project_id' => 1))); - $this->assertEquals(2, $taskCreationModel->create(array('title' => 'Task #2', 'project_id' => 1, 'column_id' => 2, 'owner_id' => 1))); - $this->assertEquals(1, $subtaskModel->create(array('task_id' => 1, 'title' => 'subtask #1', 'user_id' => 1))); - $this->assertEquals(2, $subtaskModel->create(array('task_id' => 2, 'title' => 'subtask #1', 'user_id' => 1))); - $this->assertEquals(3, $subtaskModel->create(array('task_id' => 1, 'title' => 'subtask #1', 'user_id' => 1))); - $this->assertEquals(4, $subtaskModel->create(array('task_id' => 2, 'title' => 'subtask #1'))); - $this->assertEquals(5, $subtaskModel->create(array('task_id' => 1, 'title' => 'subtask #1'))); - - $this->assertCount(3, $subtaskPagination->getDashboardPaginator(1, 'subtasks', 5)->getCollection()); - $this->assertCount(0, $subtaskPagination->getDashboardPaginator(2, 'subtasks', 5)->getCollection()); - $this->assertCount(3, $subtaskPagination->getDashboardPaginator(1, 'subtasks', 5)->setOrder(TaskModel::TABLE.'.id')->getCollection()); - $this->assertCount(3, $subtaskPagination->getDashboardPaginator(1, 'subtasks', 5)->setOrder('project_name')->getCollection()); - $this->assertCount(3, $subtaskPagination->getDashboardPaginator(1, 'subtasks', 5)->setOrder('task_name')->getCollection()); - $this->assertCount(3, $subtaskPagination->getDashboardPaginator(1, 'subtasks', 5)->setOrder(SubtaskModel::TABLE.'.title')->getCollection()); - } - - public function testWhenColumnIsHidden() - { - $columnModel = new ColumnModel($this->container); - $taskCreationModel = new TaskCreationModel($this->container); - $projectModel = new ProjectModel($this->container); - $subtaskModel = new SubtaskModel($this->container); - $subtaskPagination = new SubtaskPagination($this->container); - - $this->assertEquals(1, $projectModel->create(array('name' => 'Project #1'))); - $this->assertTrue($columnModel->update(1, 'test', 0, '', 1)); - $this->assertEquals(1, $taskCreationModel->create(array('title' => 'Task #1', 'project_id' => 1, 'column_id' => 1))); - $this->assertEquals(1, $subtaskModel->create(array('task_id' => 1, 'title' => 'subtask #1', 'user_id' => 1))); - - $this->assertCount(0, $subtaskPagination->getDashboardPaginator(1, 'subtasks', 5)->getCollection()); - } -} diff --git a/tests/units/Validator/TaskValidatorTest.php b/tests/units/Validator/TaskValidatorTest.php index 5c1b9a52..12c5a497 100644 --- a/tests/units/Validator/TaskValidatorTest.php +++ b/tests/units/Validator/TaskValidatorTest.php @@ -68,10 +68,19 @@ class TaskValidatorTest extends Base $this->assertFalse($result[0]); } - public function testDateField() + public function testStartAndDueDateFields() { $taskValidator = new TaskValidator($this->container); + $result = $taskValidator->validateCreation(array('project_id' => 1, 'title' => 'test', 'date_due' => '09/11/2017 10:50', 'date_started' => '09/11/2017 9:50')); + $this->assertTrue($result[0]); + + $result = $taskValidator->validateCreation(array('project_id' => 1, 'title' => 'test', 'date_due' => '09/11/2017 10:50', 'date_started' => '09/11/2017 10:50')); + $this->assertTrue($result[0]); + + $result = $taskValidator->validateCreation(array('project_id' => 1, 'title' => 'test', 'date_due' => '09/11/2017 10:50', 'date_started' => '09/11/2017 11:50')); + $this->assertFalse($result[0]); + // date_due // ISO dates $result = $taskValidator->validateCreation(array('project_id' => 1, 'title' => 'test', 'date_due' => '2017-02-01')); diff --git a/vendor/aferrandini/phpqrcode/.gitignore b/vendor/aferrandini/phpqrcode/.gitignore new file mode 100644 index 00000000..485dee64 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/vendor/aferrandini/phpqrcode/LICENSE b/vendor/aferrandini/phpqrcode/LICENSE new file mode 100755 index 00000000..18833032 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/vendor/aferrandini/phpqrcode/VERSION b/vendor/aferrandini/phpqrcode/VERSION new file mode 100755 index 00000000..e0d3a2ee --- /dev/null +++ b/vendor/aferrandini/phpqrcode/VERSION @@ -0,0 +1,2 @@ +1.1.5
+2012021604
diff --git a/vendor/aferrandini/phpqrcode/cache/frame_1.dat b/vendor/aferrandini/phpqrcode/cache/frame_1.dat new file mode 100755 index 00000000..be28feac --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_1.dat @@ -0,0 +1,2 @@ +xÚÁ
À E9³u`³"PÅ„CÛ牗T!0$ +E•ɲQ™Ém½úhÛ¾9{kI"› 9Ln)Ap¤åÖ¾Ë>ß^‡Õz³mënÅ–;ü´mßn†ú¦Ë
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_1.png b/vendor/aferrandini/phpqrcode/cache/frame_1.png Binary files differnew file mode 100755 index 00000000..86ce6e98 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_1.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_10.dat b/vendor/aferrandini/phpqrcode/cache/frame_10.dat Binary files differnew file mode 100755 index 00000000..aff163f6 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_10.dat diff --git a/vendor/aferrandini/phpqrcode/cache/frame_10.png b/vendor/aferrandini/phpqrcode/cache/frame_10.png Binary files differnew file mode 100755 index 00000000..dbfcd70b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_10.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_11.dat b/vendor/aferrandini/phpqrcode/cache/frame_11.dat Binary files differnew file mode 100755 index 00000000..95af68a4 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_11.dat diff --git a/vendor/aferrandini/phpqrcode/cache/frame_11.png b/vendor/aferrandini/phpqrcode/cache/frame_11.png Binary files differnew file mode 100755 index 00000000..c07c761f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_11.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_12.dat b/vendor/aferrandini/phpqrcode/cache/frame_12.dat Binary files differnew file mode 100755 index 00000000..73228b36 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_12.dat diff --git a/vendor/aferrandini/phpqrcode/cache/frame_12.png b/vendor/aferrandini/phpqrcode/cache/frame_12.png Binary files differnew file mode 100755 index 00000000..8ba67822 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_12.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_13.dat b/vendor/aferrandini/phpqrcode/cache/frame_13.dat Binary files differnew file mode 100755 index 00000000..2256f0e3 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_13.dat diff --git a/vendor/aferrandini/phpqrcode/cache/frame_13.png b/vendor/aferrandini/phpqrcode/cache/frame_13.png Binary files differnew file mode 100755 index 00000000..6e49d35a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_13.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_14.dat b/vendor/aferrandini/phpqrcode/cache/frame_14.dat Binary files differnew file mode 100755 index 00000000..e9ae0932 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_14.dat diff --git a/vendor/aferrandini/phpqrcode/cache/frame_14.png b/vendor/aferrandini/phpqrcode/cache/frame_14.png Binary files differnew file mode 100755 index 00000000..efc36c03 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_14.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_15.dat b/vendor/aferrandini/phpqrcode/cache/frame_15.dat Binary files differnew file mode 100755 index 00000000..18727818 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_15.dat diff --git a/vendor/aferrandini/phpqrcode/cache/frame_15.png b/vendor/aferrandini/phpqrcode/cache/frame_15.png Binary files differnew file mode 100755 index 00000000..a9f416c7 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_15.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_16.dat b/vendor/aferrandini/phpqrcode/cache/frame_16.dat new file mode 100755 index 00000000..60af6784 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_16.dat @@ -0,0 +1 @@ +xÚí™A„ E]sëIX´;¸Ün6€È`‚q”êêW6ñ奚`Œ%A/3!¢°‚¢Š!g–ÈÌ¡’1N)éE¢Ï|;®—>6â¸Þ97$ëÄôëc]kköwé1Öü[·mCÍœcÊRºÄê¹>¦èµ¾šE,•hʼnp„#áxFyWÏÇVWGçòÕ3¼Õ+шþàË“úSŽâ}Äž#áG8b^c^cÏÀŽp„c&3YQ"ñŽ÷çÌvµù›…ñàÎþþ¼–¹kÞ9ŠÜ‡÷}”¹³ï×ú ¢Ä¿QäÿL—/ÝÔÀÏ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_16.png b/vendor/aferrandini/phpqrcode/cache/frame_16.png Binary files differnew file mode 100755 index 00000000..6ac8fe89 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_16.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_17.dat b/vendor/aferrandini/phpqrcode/cache/frame_17.dat Binary files differnew file mode 100755 index 00000000..87f0cf59 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_17.dat diff --git a/vendor/aferrandini/phpqrcode/cache/frame_17.png b/vendor/aferrandini/phpqrcode/cache/frame_17.png Binary files differnew file mode 100755 index 00000000..5b929ac7 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_17.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_18.dat b/vendor/aferrandini/phpqrcode/cache/frame_18.dat new file mode 100755 index 00000000..bb7138c1 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_18.dat @@ -0,0 +1,2 @@ +xÚí™A +ƒ0E]çÖ…,2;sƒä&ÉÍšh¥ÛêO¡ôÝÈàã1&09OIv@DDÒÌ&§Ù‰KXÈÕFv•<Ádqò9Ö<%h•¹Yïs!(d¥²ës;~||b(ÏøYůg#µ`œK ±S¼Åô¹Ä¶˜ùsàidßLg:Ó™Îtþ/gmª™ƒkÅMâ3³{4rTÈQýÿe¥·s·>ó<Ó™Ît¦3éÌ;ïH¼#Ñ™Ît¦3ÍYœ+og©hù¶óµÙ½¬lnðûF>Øi^»#awm;gè~pÛgìNs{6z’‘»ãºïÞäp¾Ê'
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_18.png b/vendor/aferrandini/phpqrcode/cache/frame_18.png Binary files differnew file mode 100755 index 00000000..ee0d6a35 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_18.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_19.dat b/vendor/aferrandini/phpqrcode/cache/frame_19.dat new file mode 100755 index 00000000..95e26adc --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_19.dat @@ -0,0 +1,3 @@ +xÚíšA +Ä E»öÖ.ÌNo 7Ñ›¶iiRÚN2‹áW%ðxÁ@ÚÚœê' +u6×êˆ.*S;}˜«Òà ÏTúèÌzrt¹ï%ç,ÒÅÚâÎ}ç;“âç)¹Ÿ˜âÝZÚîLåè¹÷¬Pçç$¯×÷ÏqËgœLÂôdJ‡;Üáw¸Ãý.]z#Ÿ¾«[ͽïOg‚Æô"ÐË áBíî¦}Ç}‡;Üáw¸Ãî˜#1GbŽ„;Üáw¸Ãý_ÝC+w¢@Dfî÷ïç™uø2™ÅÚÉNþû9R7|pWßkïû®¿“ßßkºö¿ºú»¼ÎÓ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_19.png b/vendor/aferrandini/phpqrcode/cache/frame_19.png Binary files differnew file mode 100755 index 00000000..20fddd84 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_19.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_2.dat b/vendor/aferrandini/phpqrcode/cache/frame_2.dat new file mode 100755 index 00000000..7e42f31c --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_2.dat @@ -0,0 +1 @@ +xÚÍ’Í
À F{vë& à&°Y+?Z1öÐSŸ'y!¢ŸÌÁa815&£•Û´ŽÙHå£Ùžc³•l«ÏFÆè1º#é6fÊÖü©§6Äø•O7ˆ¨†C¦«›ðÖžÏ8gI®ÏöfB¦ÃÄÿæ\DÔ»(
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_2.png b/vendor/aferrandini/phpqrcode/cache/frame_2.png Binary files differnew file mode 100755 index 00000000..9c150ebe --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_2.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_20.dat b/vendor/aferrandini/phpqrcode/cache/frame_20.dat Binary files differnew file mode 100755 index 00000000..d5ecc1d8 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_20.dat diff --git a/vendor/aferrandini/phpqrcode/cache/frame_20.png b/vendor/aferrandini/phpqrcode/cache/frame_20.png Binary files differnew file mode 100755 index 00000000..23a061d5 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_20.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_21.dat b/vendor/aferrandini/phpqrcode/cache/frame_21.dat new file mode 100755 index 00000000..1974dd9d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_21.dat @@ -0,0 +1 @@ +xÚíšA„ E]sëIX´;¹Ün6Up‚“в™ÿ]Ù˜þ<i-eWö‹¶˜)×äÅ•¼ÉÂ…H\jvqÙHL\6–šÝÐ…rI›¢LܹÜÕ%ÅÓ@´þ±V—vÆÂúý¤(ÏP4|ÎXnÒgÉß¼~]D¾ÉÕ×u1Us S\À°€,ÿÅ2Þ¢N§Ã?D›KºüF-:“eJ]p_À°€,˜a0Ã`†ÁÝXÀ°`†Áƒw,`X´]˜ˆ™‚¹‹˜°5‰®Y4{屿ñ2íûåvçJs†±Ûí9±˜í)õu±Û¹êÏØ,«]¸“‹Ù^_§7$ƒ_Í
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_21.png b/vendor/aferrandini/phpqrcode/cache/frame_21.png Binary files differnew file mode 100755 index 00000000..291598c7 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_21.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_22.dat b/vendor/aferrandini/phpqrcode/cache/frame_22.dat new file mode 100755 index 00000000..0f01802d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_22.dat @@ -0,0 +1,3 @@ +xÚíšA +„0E]{ë.’]{{{³©Z¥BepÆÞwe@V›ERZ3»Á"*2o€4¦y‰)i#dÒbdFÒ…´ŒI"ú‘—4ž½WIíuŠÓ45ßx«.ZSÙ{ÁŸ¯8åËÿk={o.±qÊÙ£[œÍ:帒q»õƒy +)t#á„N8ádCj-OOG}¼:/Ÿ:sz!Å)^<ùe½·S·uâ{ 'œp 'ú=ú=ú=¾'œp 'œp¢ß£ß£ßãN8á„Óÿ9©ªˆôpQQõ]HÔpz¾ØGœ^æ½Qº˜I|¾ß³u;9™ÎïÕëd;“X~$ËÙÑÉt¶ÊÛédy
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_22.png b/vendor/aferrandini/phpqrcode/cache/frame_22.png Binary files differnew file mode 100755 index 00000000..bc97bd01 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_22.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_23.dat b/vendor/aferrandini/phpqrcode/cache/frame_23.dat new file mode 100755 index 00000000..ee3b3707 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_23.dat @@ -0,0 +1,3 @@ +xÚíšA +à E³öÖfo 7Ñ›U) %M!ΔÂûYu(<šð“sK²“Tœ›Ó +É&§IÚ\i+¥Ðª™(m®´FQ¡¹¯h±æöüèv~n1„oÏ]sëçÖï¤_ÞŸÊ3`î_w2õȹ•lc[¼•;·Ûc֟ˤ’Nóª4ÜpÃ
7ÜpÃímTÿ¸œ›‘ÝêrÞiñä_ƒç¿pS=7Þ7ÜpÃ
7ÜpÃ>IŸ¤Oò-Á
7ÜpÃ
7ú$}’>É·7ÜpÃ
·tss‰Órs
§åVÍÎÜÆ÷’mýï¡Ò¹ò‡Þñ}R~7ôà&¾÷º?7ùÞýÔ¦Iïbhâ{æ»<ÀMi-
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_23.png b/vendor/aferrandini/phpqrcode/cache/frame_23.png Binary files differnew file mode 100755 index 00000000..b8f16ae2 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_23.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_24.dat b/vendor/aferrandini/phpqrcode/cache/frame_24.dat new file mode 100755 index 00000000..7b92e29c --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_24.dat @@ -0,0 +1 @@ +xÚí›Aƒ E»öÖMX0;¸ÜnVP4ÚHSS»xßU3±/O´ýLiJ4ž±VâJCŠ%ý‰6VR&ÃÞD‘BœHjDù‚JÏ??™¯êBlcDZñ½§'óUëXïUïÞ0æÃywÍį÷j¬é똳€3Å›¾ë˜cj†ù£{¨¥½:GqÄGÿÝñøŸûÚ°N†v;¹¶ç¬“J‡ÄÐ<û‡É]ŽêëÈóˆ#Ž8âˆ#Ž8âH'§“ÓÉùÍÁGqÄGéätr:9Ï#Ž8âˆ#Ž8âØ“hˆ¯NÔt”Œ´Ö_ÝØ>t¹eëìS¯¦æžù^\g¯õÎQe?ùvuöÌoïÕ;ˆï>ìˆ*ïwlò×mÑ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_24.png b/vendor/aferrandini/phpqrcode/cache/frame_24.png Binary files differnew file mode 100755 index 00000000..397c64f8 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_24.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_25.dat b/vendor/aferrandini/phpqrcode/cache/frame_25.dat new file mode 100755 index 00000000..ba125182 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_25.dat @@ -0,0 +1,3 @@ +xÚíÛA +à …á¬së‚‹™]rƒx½Y51mMÈBG +ÿ¸*Sx|Ua5Ƶ‚Z—Š„-,Ž1ä²HÑPÒRj–šX5§®i†©’áG©>W¥ŽžRïöÕ/Ëâ+uT廯åÏӯ嗴ªuæÏ¥Ú[Sía£[kví÷5•+5n§Á´JêÜ%+V¬X±bÅŠõ߬u'Á±þÔû SRýå÷štzZ»ì+÷+V¬X±bÅŠ•ٟٟٟûŠ+V¬X±bÅÊìÏìÏìÏ}ÅŠ+V¬X±ö±ª¤¥ÖVI©¢ÖÖ‘+k«qÿ[úËtŽ·oVZÍþvoNV³wÇ}µ{³r<ýRÞ"RÍÞ]ê
W«r}
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_25.png b/vendor/aferrandini/phpqrcode/cache/frame_25.png Binary files differnew file mode 100755 index 00000000..25bc4454 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_25.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_26.dat b/vendor/aferrandini/phpqrcode/cache/frame_26.dat new file mode 100755 index 00000000..d34a73f1 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_26.dat @@ -0,0 +1,2 @@ +xÚí›A +à E³öÖ…,t§7ˆ7Ñ›U E)i7ï»*~cÃüÅÄXÖEBÆè°FC–˜³6¡:&çL,å¬Mv.ŽÂÎæKgŸÕ¸ãYMç>ŸÎí>ûmÛš·?ª•vô¹¾mg?ßÒ±Îþ³æÎ·ªd˜“Cµ¹U¦ÏIk•ÚÚE\ÕÙMs†f˜a†f˜a>œ[sÓˆ9쬩ެ8bö<kÕÙ7œ}ç†k³™§õ™ÿ3Ì0Ã3Ì0Ã3Ìä*r¹Š\Å7f˜a†f˜a†fr¹Š\Å7f˜a†f˜a†YÆÙÎæd›4ƒ9kíÆÌÔÝyûX y‰gŒØÙ)›«dwnÌ¢ûU×>Ëî”]ßöLgÉÝÁ›³è¾äEo‚ w1
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_26.png b/vendor/aferrandini/phpqrcode/cache/frame_26.png Binary files differnew file mode 100755 index 00000000..f4a6b393 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_26.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_27.dat b/vendor/aferrandini/phpqrcode/cache/frame_27.dat Binary files differnew file mode 100755 index 00000000..b4d9ffd4 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_27.dat diff --git a/vendor/aferrandini/phpqrcode/cache/frame_27.png b/vendor/aferrandini/phpqrcode/cache/frame_27.png Binary files differnew file mode 100755 index 00000000..8419ec23 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_27.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_28.dat b/vendor/aferrandini/phpqrcode/cache/frame_28.dat Binary files differnew file mode 100755 index 00000000..8cbaa196 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_28.dat diff --git a/vendor/aferrandini/phpqrcode/cache/frame_28.png b/vendor/aferrandini/phpqrcode/cache/frame_28.png Binary files differnew file mode 100755 index 00000000..7609d8e1 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_28.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_29.dat b/vendor/aferrandini/phpqrcode/cache/frame_29.dat new file mode 100755 index 00000000..5e4a7110 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_29.dat @@ -0,0 +1,2 @@ +xÚíÜAƒ …a×Þº ‹™Ü@n7+*¶šÖÚ4‘!Í?®Jšðò ³”抮«]ª—ÉSŸâTf)–ÙsŠIÂ"…È”bžÝ0…Š|•"LuÙ¸î,Ž×EÇ1\6®*ÏuQÞ?¼>aÌÏ…ãþñŽÄRõ-r“÷n.ïꯋ\®T¿ü:Ó*)|)°À,°À,þÑâêóåéx_ã¬}:^R„ƒUoÉ¢‰uÁ~ÁÞ‰X`XÐÐÐа_`X`XÐÐа_`X`XÐÐÐаwbX`¿¥PUõö)DÔÞ"cÈ{‹zçÎõ3ê›é<}¸ó¡^?b÷mÿÎÂìžƒíº°»óaûŽ´’Âê.] +³{Q6uáT,9
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_29.png b/vendor/aferrandini/phpqrcode/cache/frame_29.png Binary files differnew file mode 100755 index 00000000..ffe072c8 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_29.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_3.dat b/vendor/aferrandini/phpqrcode/cache/frame_3.dat new file mode 100755 index 00000000..188d531c --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_3.dat @@ -0,0 +1 @@ +xÚí“Á
À E{vë& à&°Y+¢b¤öÐkŸ'yù‘¤¿ÌÁa :äÀTXlÞ¶$W+Óvû®îœ¢9}gRæ¬@H0YPB½ÆÃEmÚÚ?ûœÍ±ísœÖ"bµìt2cnÖé†É:½ïºë;¿Y§“ÃzÿQã«7¿Ô
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_3.png b/vendor/aferrandini/phpqrcode/cache/frame_3.png Binary files differnew file mode 100755 index 00000000..945ee7cb --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_3.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_30.dat b/vendor/aferrandini/phpqrcode/cache/frame_30.dat Binary files differnew file mode 100755 index 00000000..44cf3d31 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_30.dat diff --git a/vendor/aferrandini/phpqrcode/cache/frame_30.png b/vendor/aferrandini/phpqrcode/cache/frame_30.png Binary files differnew file mode 100755 index 00000000..75dbddd2 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_30.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_31.dat b/vendor/aferrandini/phpqrcode/cache/frame_31.dat new file mode 100755 index 00000000..ce429d0a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_31.dat @@ -0,0 +1 @@ +xÚíÜAƒ …a×Þº ØÉ
à&r³‚ Á´¸ªÎ4ù§«†´yù‚Ä·!¥mV3Iµv!ÒœÖ2¢i\NSSä4EF2+65Å¥‰e¾þÃ/Wœs]šñ¾‰!„Á?ÿpÅõû¦=S~ùüÄ?Ëý+þx¦Ö6r6yö³Ùƹ}“Ç´™ë×eR1-W•l°Ál°Á›ûÒŒÞXŸz/>Væ«·ù§:ñÒÒÄAš8üý-+mTíÎÎbl°Ál°ÁlèštMº&]“³l°Ál°Áº&]“®I×ä¼Ál°Ál°Áº&]“®Éyƒ
6Ø`ƒ
6Ø`ƒÍÝi¬uy´ØXWòè±Éi¬²\t†ýz•—Š>•.î”z¾kÊß
t²¿7©ß7òwJõÏ”¶4Òw‘ÒˆßÓÖÍ85‰
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_31.png b/vendor/aferrandini/phpqrcode/cache/frame_31.png Binary files differnew file mode 100755 index 00000000..b14d1fa2 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_31.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_32.dat b/vendor/aferrandini/phpqrcode/cache/frame_32.dat new file mode 100755 index 00000000..aaa0808e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_32.dat @@ -0,0 +1,2 @@ +xÚíÜÁ +„ …áÖ¾õ€‹ë.ß ßDßl¬,¦šMz‰ÿ6›†Ã‡ gcJËD;ô'.®A’IqžÞ‰ÄI,Ir¢Y¨»‘ËFk%‰DþOæy|EDªD×û(LÓ_YÊ>*ßš?aÊOƒ¿k±L_£<[c—ñ¶ï>Êc˘õuÔLIäÕ%Â#Œ0Â#Œ0Â#ŒÞotÑ¢šõµ}ÅÜ4Ífv_)‰ÂE¢pú¬h5R·88³1Â#Œ0Â#Œ0¢ÓÒié´tZÎ#Œ0Â#Œ0Â#Œ0¢ÓÒié´tZÎ#Œ0Â#Œ0Â#Œ0¢ÓÒié´tZÎlŒ0Â#Œ0£÷9q"¢ÉHÜœH™Qþµï"ÛÕL5}-ÝÜY×¾Óê¸kì`¤â>¶z鸳®þÖ4&Òp÷á!‘Šû!«ù`¿:5
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_32.png b/vendor/aferrandini/phpqrcode/cache/frame_32.png Binary files differnew file mode 100755 index 00000000..58d42db3 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_32.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_33.dat b/vendor/aferrandini/phpqrcode/cache/frame_33.dat new file mode 100755 index 00000000..a2613755 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_33.dat @@ -0,0 +1,14 @@ +xÚíÜAƒ …a×Þº‰‹™Ü@n7+*L++ÓæŸ®óò‰ÌbbÜ*LCï‘°‡‰ck™H¥rš”j•²J5Yíi~0•_«òŒû×TÊTõ}å—e©>ýö5‘b_åwÐÍŸ?¿¤ßìæ§ÖÜù†\ýRaÆi+7õßW©¦\ãþwLUNåL¦Â ++¬°Â ++¬°ÂêÿjßÒO·ŸkcëÞñôç\Ë©|%•o<á‹k–Lî+Î+Îv¬°Â ++¬°Â ++¬°ÂŠ>}úô8¯°Â ++¬°Â ++¬°Â ++úôè3Ðgà¼Â ++¬°Â ++¬°Â ++¬è3Ðg Ï@Ÿó ++¬°Â ++¬°Â ++¬°:R‰¨ªX³ÚB‰9«”IÔ=çkÞ±o/SwçØ˜™Ù¯Ï`g¶áÅÊÌÈr_Ù™™Y¾ƒVSY™ÅzIefnmQoz
>á
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_33.png b/vendor/aferrandini/phpqrcode/cache/frame_33.png Binary files differnew file mode 100755 index 00000000..924c728e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_33.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_34.dat b/vendor/aferrandini/phpqrcode/cache/frame_34.dat Binary files differnew file mode 100755 index 00000000..7ceb0259 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_34.dat diff --git a/vendor/aferrandini/phpqrcode/cache/frame_34.png b/vendor/aferrandini/phpqrcode/cache/frame_34.png Binary files differnew file mode 100755 index 00000000..a477042d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_34.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_35.dat b/vendor/aferrandini/phpqrcode/cache/frame_35.dat Binary files differnew file mode 100755 index 00000000..56bc3e28 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_35.dat diff --git a/vendor/aferrandini/phpqrcode/cache/frame_35.png b/vendor/aferrandini/phpqrcode/cache/frame_35.png Binary files differnew file mode 100755 index 00000000..d29806c6 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_35.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_36.dat b/vendor/aferrandini/phpqrcode/cache/frame_36.dat Binary files differnew file mode 100755 index 00000000..282c60d2 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_36.dat diff --git a/vendor/aferrandini/phpqrcode/cache/frame_36.png b/vendor/aferrandini/phpqrcode/cache/frame_36.png Binary files differnew file mode 100755 index 00000000..96ecb421 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_36.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_37.dat b/vendor/aferrandini/phpqrcode/cache/frame_37.dat Binary files differnew file mode 100755 index 00000000..015c0f24 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_37.dat diff --git a/vendor/aferrandini/phpqrcode/cache/frame_37.png b/vendor/aferrandini/phpqrcode/cache/frame_37.png Binary files differnew file mode 100755 index 00000000..fcc51627 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_37.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_38.dat b/vendor/aferrandini/phpqrcode/cache/frame_38.dat new file mode 100755 index 00000000..71cf53eb --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_38.dat @@ -0,0 +1 @@ +xÚíÝAªƒ0ÐŽÝuÁA2«;ÐèÎkü(üg¾Ày•tp9Äï$Ëò™¹Dœ”ò¼\ºe^'tÒ-aIºŠFMšSškÂðIóŤÓ:7®¤|LúkŸNã8N7®œöi}ö‡×Ÿi,Ÿ[W†¿g®Ó´Ì°ë?3ô1÷i™¾N·}}=ÂOM:4“”)S¦L™2eÊ”)S¦L#$½ÿ
ôÂJãþÂJM:}ý]˜•ÖL›Ù§ÎSÿQL™2eÊ”)S¦L™2Õ¡èPt(:Šó”)S¦L™2eÊ”)S¦:ŠE‡¢Cqž2eÊ”)S¦L™2eÊ”©E‡¢CÑ¡8O™2eÊ”)S¦L™2eÊT‡¢CÑ¡èPœ§L™2eÊ”)S¦L™2Ý“¦”sJCIKÖÔ‚iÍ93ônº_Ñòÿ¾¿ü¼“+R‡û®£“ièû£Žû4ö\Çg¿¥¤‘ïŽ;%
}ßaÞnŽ£
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_38.png b/vendor/aferrandini/phpqrcode/cache/frame_38.png Binary files differnew file mode 100755 index 00000000..89238f3c --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_38.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_39.dat b/vendor/aferrandini/phpqrcode/cache/frame_39.dat Binary files differnew file mode 100755 index 00000000..53511f73 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_39.dat diff --git a/vendor/aferrandini/phpqrcode/cache/frame_39.png b/vendor/aferrandini/phpqrcode/cache/frame_39.png Binary files differnew file mode 100755 index 00000000..1dc9cd1b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_39.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_4.dat b/vendor/aferrandini/phpqrcode/cache/frame_4.dat new file mode 100755 index 00000000..67b30e82 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_4.dat @@ -0,0 +1 @@ +xÚí”Á
À E=»uÐ
pجQ•ØCOMŸ'ÃË$ ³@à¨Ø3e–F©\FNXRyÉØ¾C{‰a8RæÅƒa2@ñ圉qküßÉH1ê(£ˆÅ`cç¦j³~Ë0ö¥¿ÃܨÖËÃØ¹nXÿGåÿÄ€
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_4.png b/vendor/aferrandini/phpqrcode/cache/frame_4.png Binary files differnew file mode 100755 index 00000000..b72f9e70 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_4.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_40.dat b/vendor/aferrandini/phpqrcode/cache/frame_40.dat new file mode 100755 index 00000000..90d36dd1 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_40.dat @@ -0,0 +1,2 @@ +xÚíÝAŠƒ@Ь½õ€‹îÞ@o¢7“˜`“Qfeºä•«PA>¦ÀÚô<?jjo5WNiz›yºWý‰ó´&]ß…C?“IœrþWâñ^;ï8·— +ãýs<ðûöS{Å9^gEß}>ã°<]ßÕÐëß³bZ«nã¥^A›öQ}[÷9^ª]«yþìnajM܇K̘1cÆŒ3f̘1ã¸Æ{ßW5}ç½{ÍÑ7lMßÒïÞšxÜI<¼áK½¨Æáαyl3f̘1cÆŒ3f̘1ã«Û»Ù»={·“αyl3f̘1cÆŒ3f̘1ã«Û»Ù»={·“αyl3f̘1cÆŒ3f̘1ã«Û»Ù»={·“αyl3f̘1cÆŒ3f̘1ã«Û»Ù»={·“αyl3f̘1cÆŒ3f̘ñ÷çœSÊ‘ŒÓ’7¥HÆKÞ¼g\ç¾âuõßÏ_ÿªr'4Ü[çÞ-Æ]›…qˆûL·ç8ƱÛY1q„»‹Ä!î—ÞÔ/(%û
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_40.png b/vendor/aferrandini/phpqrcode/cache/frame_40.png Binary files differnew file mode 100755 index 00000000..8034d862 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_40.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_5.dat b/vendor/aferrandini/phpqrcode/cache/frame_5.dat new file mode 100755 index 00000000..d5dafe18 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_5.dat @@ -0,0 +1 @@ +xÚí”1À E½u 7ЛÀÍZµ‡|N†—üDB0@R$l,-™>VKZ[<ýØÚz—qÆŽ¨ØYJ&ƒiåš‚‹ZyË:Y'ë¯YµÁVÿ&—e•RÄ"§sj©Ýrþö+Ëé‰ù.·MÆŽ»–Ó9ÓòzµsŽ”É,
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/frame_5.png b/vendor/aferrandini/phpqrcode/cache/frame_5.png Binary files differnew file mode 100755 index 00000000..96b6494f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_5.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_6.dat b/vendor/aferrandini/phpqrcode/cache/frame_6.dat Binary files differnew file mode 100755 index 00000000..0fc3d039 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_6.dat diff --git a/vendor/aferrandini/phpqrcode/cache/frame_6.png b/vendor/aferrandini/phpqrcode/cache/frame_6.png Binary files differnew file mode 100755 index 00000000..05ca358b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_6.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_7.dat b/vendor/aferrandini/phpqrcode/cache/frame_7.dat Binary files differnew file mode 100755 index 00000000..43375960 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_7.dat diff --git a/vendor/aferrandini/phpqrcode/cache/frame_7.png b/vendor/aferrandini/phpqrcode/cache/frame_7.png Binary files differnew file mode 100755 index 00000000..7d2ff4f3 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_7.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_8.dat b/vendor/aferrandini/phpqrcode/cache/frame_8.dat Binary files differnew file mode 100755 index 00000000..669b325f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_8.dat diff --git a/vendor/aferrandini/phpqrcode/cache/frame_8.png b/vendor/aferrandini/phpqrcode/cache/frame_8.png Binary files differnew file mode 100755 index 00000000..db1f1877 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_8.png diff --git a/vendor/aferrandini/phpqrcode/cache/frame_9.dat b/vendor/aferrandini/phpqrcode/cache/frame_9.dat Binary files differnew file mode 100755 index 00000000..d79295ee --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_9.dat diff --git a/vendor/aferrandini/phpqrcode/cache/frame_9.png b/vendor/aferrandini/phpqrcode/cache/frame_9.png Binary files differnew file mode 100755 index 00000000..74ddf08d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/frame_9.png diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_101_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_101_0.dat Binary files differnew file mode 100755 index 00000000..51deabae --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_101_0.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_105_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_105_0.dat Binary files differnew file mode 100755 index 00000000..97e9e5df --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_105_0.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_109_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_109_0.dat new file mode 100755 index 00000000..eadf83a2 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_109_0.dat @@ -0,0 +1,2 @@ +xÚíÚ= +€0н§iï9'Åb‡$ ¾tËýáÚû^#iª¥Ëi?³ÅôÛbúK[AUØF徯µÄ³x]mŸ]2Ž-Ä–KŽ~ÏVw}¶X›ûÆÆÆÆÆÆÆ&O²É“Þ666666yRž”'½%lllll/´åhœl…Ãîm ¹¤êádël™¶´3Ù+ïÛmÍ«
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_113_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_113_0.dat new file mode 100755 index 00000000..5eb7f5de --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_113_0.dat @@ -0,0 +1,2 @@ +xÚíÚ; +…0Ð>«Iö¿9+Eñƒ‚sá=ϤL1Ì„[¤÷¹FáZU‹4‡?i<ÿç;7çòç;‡ÆP¥Œ#ýW-[ñݯ6÷µddddddüc",;í"¼Ÿskæ‘‘‘‘‘‘‘‘Q&—Éerw######£L.¯Êäæ‘‘‘‘‘‘‘±Ð˜y¼1†^˲\òîÆØ3Æâ³ÚÓóøÏÆ ‘Ñv
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_117_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_117_0.dat new file mode 100755 index 00000000..781c7f87 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_117_0.dat @@ -0,0 +1,2 @@ +xÚíÚA +„0Ð}OÓÞÿr³R,#3öñ¥Ë,âÃþ¢½o5ŸCµØÐq:õõÖ;;¬wvNÁJZGÅ=Œm»û}Úö
ѱ¬¬¬¬¬¬¬¬µ¦2âÞi‹RïïkÆj_YYYYYYYYeÙ_ö·¯¬¬¬¬¬¬¬¬²¿ì/ûÛWVVVVVVÖkîáýd-úϺ,#¦ßÀOÖÎZc]|‡{ž¾Áúˆ$™
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_121_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_121_0.dat new file mode 100755 index 00000000..68810c34 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_121_0.dat @@ -0,0 +1 @@ +xÚíÛ1„ ОÓÀý/·•w
YMüSø Ä8>2SÐûÖF OEÓÈÌÓc«W\ûÛ¼š‹{c§æpK›GÕžB·ÐšþímûŽxhfffffffæ/s2ÛÇÚ2W|*÷d®1ÛÏÌÌÌÌÌÌÌÌÌê*5³ºÊÆÌÌÌÌÌÌÌ̬®RW©«œaÌÌÌÌÌÌÌÌsöòËÁ\xm~8ß®¸ƒr0wæjsdm÷ªýü&óâyÙ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_125_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_125_0.dat new file mode 100755 index 00000000..2c73ef1a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_125_0.dat @@ -0,0 +1,2 @@ +xÚíÛA +€ н§Ñû_®•‘TH`3AO—L4ükí£ÍÃ(áÍÛewö›GWÖÙ××.í #ÃÞ2¿û¡} \ëçYú»¤´gggggggggg_d>Ïïµòj^™ÕØsíö;;;;;;;;;;»'ÇÉqö;;;;;;;;;»'˰ËqþuììììììììµÇ_Pì‰Íçö—ÏóYwÄ{eÿš=dÍ×ÌýþGû/ý“¸
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_129_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_129_0.dat new file mode 100755 index 00000000..812ee8a6 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_129_0.dat @@ -0,0 +1,2 @@ +xÚíÛ1 +Å ÀÞÓèý/—*Ÿ„DøEØ'ÉhgõtÙ-ì}_£pV‹· \"Ìb=sþ—ÁÌå™ó[ƒÐJŒô=8DhÅoàºÛž'0XÐ ÑüÎÛ´©è‘¬e 0`À€˜ôÊÌj"0`ÀÀ¼ Wf`^P0`À€æ½2ó‚šÈ€¼Ò óÉôd07(ì“<OÁŠåo¡§ëÁ—
6
ÎSÞ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_133_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_133_0.dat new file mode 100755 index 00000000..03b41d36 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_133_0.dat @@ -0,0 +1,2 @@ +xÚíÛ1 +€0н§Iï9§Š¢ÚTyí–)<4äh§N¾‡SÒš¨·]°èZŒ?¶[¿µH<™u…ïâÐFIø7®·´žRÛ`Á‚,X°ø±EÖÞ¹×Ë´]»Wg±¦…yÁ‚,X°`!ØÁYÈ#æ,X°`Á‚…<"È#æ,X°`Á‚…<bg!˜,X°`Á‚‹WyÉO4Ñ·˜¸wf¿á>Y‹/XLÿGb…yÁ"êpT
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_137_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_137_0.dat new file mode 100755 index 00000000..f6d993b0 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_137_0.dat @@ -0,0 +1 @@ +xÚíÜ1„ ОÓÀý/·•+FÉZ?®JšÉLæ7Ôº¬ØÝ*ÑBÚa%L~šŒË»òä”ÉØéÊ“C“ðJ›´YîIWJ ½™ý.K]ñR˜0a„ “›$ç“ïINTwÎlLža¢Ÿè±L˜0a„ &Ld@PÔO˜0a„ &L˜0‘e@P?a„ &L˜0aÂD”e@ý„ &L˜0aÂäÏM²ŸIlL&)dl˜ígøÃacR™<É$övê,ý„ɺ?U2ç]
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_141_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_141_0.dat new file mode 100755 index 00000000..8c685c8e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_141_0.dat @@ -0,0 +1,2 @@ +xÚíÜ= +€0н§iï9'EÑúÖDx͘%<lð[Zë|ZPN ¦NÃæ‘MÌ7;·múfovmœ6-Ów³§Þ¥}•y¶ã°aÆ
6lذný_¼tÊeÒivØüËÆ¾±‹Ù°aÆ
6lØÈšòYÓ.fÆ
6lذa#kÊSldMû†
6lذaÆ
YSÖ”5í6lذaÆ
6ƒmâœÙØ$æÜ& 3dyãecSÙüÙ&ìNÕLû†Í¾&ëîº<
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_145_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_145_0.dat new file mode 100755 index 00000000..9c9c1ae1 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_145_0.dat @@ -0,0 +1,2 @@ +xÚíÜ; +…0Ð>«Iö¿9+EñÇ{ˆõ$åm†ƒ^˜&µŽ§u¼³S"µÍDŒþ6ÚŸ]9ùÉè8ìU“M£“bÔÒ¾£Y¤Òù[ß2拉Ĉ#FŒ1bĈ£Ï%ôµiRNšæ½–Ñ³ì#;›#FŒ1bĈ‘N«¯1Òií#FŒ1bĈ#FŒtZ}‘Nk1bĈ#FŒ1b¤ÓêkŒtZ;›#FŒ1bÄèõFV-ŒÂuè"IoD-Œ*£7uû×jÚ>b´MV“+
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_149_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_149_0.dat new file mode 100755 index 00000000..d2583502 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_149_0.dat @@ -0,0 +1,3 @@ +xÚíÜA +€ н§Ñû_®MEQXPà§ž.Ý|94³°ÖeµÁ{³JL¨všŠÕ#«^Ü÷Ïn[õß?;µ +ZIV-ñ»ÚÄ*wï¸Ë’1*+V¬X±bÅŠ«ßX¥ü·¯g¥ÛwŒèqX}ÇJ½RÛY±bÅŠ+V¬X±beÎ wfeÎ ^±bÅŠ+V¬X±bÅÊœAïÌÊœA½bÅŠ+V¬X±bÅŠ•9ƒÞÙœÁœA½bÅŠ+V¬X±bÅŠÕl•ó0ÞÎ*0Tßj`?˜öÝΪ²ú¢Õð;Xë«ë=zZr*
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_153_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_153_0.dat new file mode 100755 index 00000000..fc79e9ed --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_153_0.dat @@ -0,0 +1 @@ +xÚíÜ1€ ÐÓÀý/礑h¢&F¾ú`ìÒ¼@I;Pë¼ZÀ^•XÛÍŒÙmfÏÆ.™=Û5[if-õœR+!wr»Ëœg\j̘1cÆŒ3f̘1cf–Ôo.±2¬?Š1ûž™zæ
`ÆŒ3f̘1cÆŒ™¹†™¹†zÆŒ3f̘1cÆŒ3fæztfæê3f̘1cÆŒ3f̘™k˜k03×ð0cÆŒ3f̘1cö³¬Ï9;³ÐÄŽÍö›‰`vf•Ù̆ßÍšZϘ߶W9
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_157_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_157_0.dat new file mode 100755 index 00000000..ad749f30 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_157_0.dat @@ -0,0 +1,2 @@ +xÚíÜA +€ н§Ñû_®•QRù©§»Y
ùkí«…ìÍ*q͵ÓîØ=j7§~ÙnNýÔ.p%Úµäs·i¯ÝÕã.½×ÈöرcÇŽ;vìØ±{Ù.-W¬õ2={êì¾mgÞy+رcÇŽ;vìØÉ³2;yÖ¼cÇŽ;vìØ±cÇNž•ɨɳæ;vìØ±cÇŽ;v2™<ËNžõV°cÇŽ;vìØ±“ge2vò¬yÇŽ;vìØ±cÇî¦]ÞÂ;»àæÆv¹"õÞ]e÷'»˜;[“ç»û{¾|Aµ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_161_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_161_0.dat Binary files differnew file mode 100755 index 00000000..4bdc5fdd --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_161_0.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_165_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_165_0.dat new file mode 100755 index 00000000..3a17a051 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_165_0.dat @@ -0,0 +1,2 @@ +xÚíÝ; +€0À>§Iî9+ÅàDyÁIÊ4Ë ÷5Ö:¯¶W«ÄÙv«dù¢åqñߜܴ<þæd×2x%[¶žËU™%ðÝÞî2×]&K–,Y²dÉ’%Ë¡,S¿Õ—“r2ydÌ=,ÿk©_º{X²dÉ’%K–,Y²d)Û0³”mè—,Y²dÉ’%K–,Y²d)Û0³”mè—,Y²dÉ’%K–,Y²”m˜Çe,eú%K–,Y²dÉ’%K–,eæq–²
ýÒÝÃ’%K–,Y²dÉ’å…eî:ËŠ<³Œš!Óÿ¡ÐYV–,ßñ:B¿dù|O¨$*#
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_169_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_169_0.dat new file mode 100755 index 00000000..c4787d9d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_169_0.dat @@ -0,0 +1,2 @@ +xÚíÝ1 +€0À>¯Iþÿ9EQÑ=˜¤Lsæä¶IïË{³Zt¡ã´R¦™þ}òÀôï“SÓð•n:ª|§›R[è?î¶Ô_*S¦L™2eÊ”)S¦L™™&ϦëIšê¯O˜2ÕOý£˜2eÊ”)S¦L™2e*C1ïËP˜ÊPôS¦L™2eÊ”)S¦L™ÊPÌû2¦2ý”)S¦L™2eÊ”)S¦Le(æ}¦2ý”)S¦L™2eÊ”)S¦Le(æ}¦2ý”)S¦L™2eÊ”)S¦³iöc7;Ó"…Þ™FͦÞÙ™v¦LÜý^¥Ÿ2}oOäß'r
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_173_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_173_0.dat new file mode 100755 index 00000000..5ef85e7a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_173_0.dat @@ -0,0 +1 @@ +xÚíÝ1ƒ0ÀÞ¯±ÿÿ¹T ¥ˆ¬[4véæ2ŠØÆ½ok݇ÕÊ;¦Ó²]fûôÖžýdûä½ölj°lGÒÿö0n+œß»m³GŒË–-[¶lÙ²eû"Ûêï·ûY»}o¯öíÀ–íÕVÞÊ[¶lÙ²eË–-[¶lÙ²e«ÓÕ°ÕƒÉ[Ï2¶lÙ²eË–-[¶lÙ²e«ÓÕ°ÕƒÉ[Ï2¶lÙ²eË–-[¶lÙ²e«ÓÕ°ÕƒÉ[¶lÙ²eË–-[¶lÙ²eËV¦«a«“·lÙ²eË–-[¶lÙ²ef[ÿB°“mа÷¶Eû„”;¶N¶-ÛœT¸/r’·lÿ¿?ØÔ*Ñ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_177_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_177_0.dat new file mode 100755 index 00000000..78a26a77 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_177_0.dat @@ -0,0 +1,2 @@ +xÚíÝ1 +…0Ð>§Iî9+?вØèß—”iÖ‡dšÌ¹¯ÕxÖˆxÝNÌø/ÆÕ§|{öظ²ÿöìÖ8d¥¯´ÿø0òhž×=öùcFf̘1cÆŒ3f̘q¼qÂ=ïw6Ê;l·»4cÆ•±<–ÇŒ3f̘1cÆŒ3fÌXï¦Ò»1Ö»ÉcÆŒ3f̘1cÆŒ3fÌXï¦b¬w“ÇŒ3f̘1cÆŒ3f̘±ÞM'ÄXï&å1cÆŒ3f̘1cÆŒ3Ö»é„ônŒõnò˜1cÆŒ3f̘1cÆŒÃ3U<‡
\7í+’Þ(<OÆŒŸ·ÎŠ™–ÇŒßÛÕä4@
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_21_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_21_0.dat Binary files differnew file mode 100755 index 00000000..368c9941 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_21_0.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_25_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_25_0.dat Binary files differnew file mode 100755 index 00000000..e4a5b6d8 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_25_0.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_29_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_29_0.dat Binary files differnew file mode 100755 index 00000000..74a216b4 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_29_0.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_33_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_33_0.dat Binary files differnew file mode 100755 index 00000000..2ec712a7 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_33_0.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_37_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_37_0.dat Binary files differnew file mode 100755 index 00000000..1588cfce --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_37_0.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_41_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_41_0.dat Binary files differnew file mode 100755 index 00000000..e369027e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_41_0.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_45_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_45_0.dat Binary files differnew file mode 100755 index 00000000..452f126c --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_45_0.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_49_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_49_0.dat new file mode 100755 index 00000000..fdd2aac1 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_49_0.dat @@ -0,0 +1,2 @@ +xÚíÕKÀ EÑ9«ýo®#?H/›6g$Ï-ª,X] +˜›xݘ;Àð·˜XÔ°ì9ºˆ<ÜѺ¤åq¤Œ2 Af÷îHð7ø/5We„{Ô#ØáìfÞ¨ÁÐ?à®4û=N >Çæ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_53_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_53_0.dat new file mode 100755 index 00000000..572d279e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_53_0.dat @@ -0,0 +1,2 @@ +xÚíÖK +@!й«Ñýo®QÑϺ:(m&ܯžs-¹¬®É6õÑZ{¥m4Y†²Xâù®.FÀÙ‹ê¦XZi·Ðùj=:ÎÖ‹bå¿‘VH8–#¤[—ù²¾‹ãYвç¾^XÔ÷e
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_57_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_57_0.dat new file mode 100755 index 00000000..ea81e6dc --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_57_0.dat @@ -0,0 +1,4 @@ +xÚíÖA +À À{^“üÿs=YLÕš
( +ou¨– j)¬ª +Z7y„ÙëÍñv,Ô´ìwVQ ži»¤ìGiÒ¤¹ÅDfÂÛ•ÉÄwo4ùѤ¹ÐÄoÎLÿÌéLȼÁœ©·›Á³Õì}î4
hå
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_61_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_61_0.dat Binary files differnew file mode 100755 index 00000000..93d2444d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_61_0.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_65_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_65_0.dat Binary files differnew file mode 100755 index 00000000..df29d7bf --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_65_0.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_69_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_69_0.dat new file mode 100755 index 00000000..8a2cfbd7 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_69_0.dat @@ -0,0 +1 @@ +xÚí×KÀ Ð=§û_®+mÏBìà’d|Q"s+1®®È"¯),ß=“EÛàa TÄ"çÒÅãnæE-3,°ÀKY‹wÝ=ZšZïTÞ.,°ÀK1‹ÿ#<XBtËâ<ŠþaÿÇbº#Îx/;X.®‡Ô
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_73_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_73_0.dat new file mode 100755 index 00000000..3de46066 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_73_0.dat @@ -0,0 +1 @@ +xÚíØ1À …áÓÀý/×ÉFSM(´Æ7†—/JTmeÓ•¤‚lš´É×s™|½©)YY“UÝS%Á7{i¹é(L˜0moÊì§»'¯÷ãªw‚‰Ç„ {œ=ÎŒcÂô)÷2˜Š‚Ö¦À~ªøÃLz–)üvZ5O2]=Œó?
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_77_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_77_0.dat new file mode 100755 index 00000000..2717fd86 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_77_0.dat @@ -0,0 +1,2 @@ +xÚíØA€ DÑ}OÓÞÿr®0 ÎBÃ/;b†Hp/‹*K‡E7íG¶þã/_l}ïxˆ‹MP +[(犳ÄZÞ›•lI6lض´eÏ·³×Oû˜ìÝÍÆ^À†
6î,î,ö6lØ&lù³Æ&ÛÏ^ÕWcó}m©5uåÿöEÛ;âe
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_81_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_81_0.dat new file mode 100755 index 00000000..2d9a052f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_81_0.dat @@ -0,0 +1,2 @@ +xÚíØ1€ …á½§iï9'•Bß ñ‡áñE¤Mpo# +³& ŒaâÙ6ãõ)cæ~Úèb
•1Ôß±‹´âß§µ|Y$FŒ1Nõq_·´.¯Ôð¿¹31bĈ‘~M¿æÎ`Ĉco{B£807úŒòôdtŒ’³võÿøfãߨÜþ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_85_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_85_0.dat new file mode 100755 index 00000000..eb8197b2 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_85_0.dat @@ -0,0 +1,2 @@ +xÚíØ1 +À …á=§Iî¹NE‘Z_†À·¯m E÷Vqyº2Yh,S‹[ÏúŸgý¥UXJkd¼×.Öß~>Öž!ÅŠ+V¬å¬ª½õöí÷.Ýõ±2¯X±bÅŠ•ÿ¬Ì+V¬X±–²ê.ökBèÞz±·ÔwðƒÕ±¦Ì€gÌkë¡YZý
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_89_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_89_0.dat new file mode 100755 index 00000000..aaa4c526 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_89_0.dat @@ -0,0 +1 @@ +xÚíÙ1€ …á½§iï9'Œˆ€Ñ×ýËHl?Lšà^"ë&M—™?bî—qÃÜ?˸äÉ,µ9²þó!µ‰z¢]VêÈScÆŒ3æ_š•÷à¾cÓé!î`nÍô3f̘1cÆÌŒ„™ 3f̘1/fÖ>.Uæ¤ÄcóË»;ã
¨2;æ™YÒžÕÏ+™7Þý
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_93_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_93_0.dat new file mode 100755 index 00000000..e218fa0e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_93_0.dat @@ -0,0 +1,3 @@ +xÚíÙK +À EÑyV÷¿¹Ž,ÚO¬mÞ rãPHŽ0 {’´š°ôäå2ûbö¨ìýÞc{t¦Qáƒ] +{QÞ{“Þ{弬֒¤ÇŽ;vìØ_Ú³ßÕ}ÏÂ÷ýËL€}l§ß±cÇŽ;vì̑̑̑رcÇŽû¢öüÐÎ.LÛæÕegwì3ö´žqe¿ÿѾ@i
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_0/mask_97_0.dat b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_97_0.dat Binary files differnew file mode 100755 index 00000000..74ac719d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_0/mask_97_0.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_101_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_101_1.dat new file mode 100755 index 00000000..ec939b52 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_101_1.dat @@ -0,0 +1,2 @@ +xÚíÖ1 +À н§‰÷¿\Q÷ªEóþ¢d 1¢¦ÌNô<±#½ËÕ–¯Õ-7u™þ.ÃÓl²Ô¦eiXXXXXRZVíÊVeIo1û,,,,,v%‹?ŒÙgaaaa±÷YÌK&K=/·œ·+Ûå˱ó²Öò¼ÞŽã
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_105_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_105_1.dat new file mode 100755 index 00000000..e1f5c99b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_105_1.dat @@ -0,0 +1 @@ +xÚíÖ1€ ÐÝÓ”û_Î¥‡b€÷
KóB¬?"Ó*#WÌʘt€éÍùíügÓŽ“JîéqUM9¸•‡‰‰‰‰©ÔTùêçLLv“¾Ç¤ïÙLLLLLLz“¾gG01111šòy„iåÑߘ–4mð=Õ›n§¥+2
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_109_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_109_1.dat new file mode 100755 index 00000000..7e0d6d16 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_109_1.dat @@ -0,0 +1 @@ +xÚíÖ±
À À>Ó˜ý—Kƒ€°•À}“ˆÆ:!£èiY‰™'*3§]fÛsþÛÉÓÒîm¹ºb[ÞJÂÆÆÆÆÆÆ¶Å–ÝKÆ9›}ccccccÓ'u.6ûÆÆÆÆÆÆÆ¦s±é“Þ6666¶R[ÿ^gû{/lÇØÙ·ÛÞ7Í‚
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_113_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_113_1.dat new file mode 100755 index 00000000..1dd666d9 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_113_1.dat @@ -0,0 +1 @@ +xÚíÖ1€ ÀÞ×ÿÿœ
-8fL(pBŽlDM™è9";ýÄ-÷ò÷;?1îpâÐ{¼\åÆú%-ŒŒŒŒŒŒ“3:@Ûad4ŒŒŒŒŒŒŒŒú*£NîÍaddddddÔÉÍ#######ã#c]75®ÓåÚã¢Æåæ1ÏxYuñ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_117_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_117_1.dat new file mode 100755 index 00000000..8921f643 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_117_1.dat @@ -0,0 +1,2 @@ +xÚíÖ»
À À>Ó˜ý—Kƒ€ø$ø^’8Y–QSV'zž8‘þjzëÌʇ¬™^]Þסµ‡¬õekXYYYYYYYjݵ#¶ ++«yeeeeeeeeµ#²²šWVVVVVVVV;"+«yeeeeeeel'ëe;b»±&²^9¯{/ÊJ$p
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_121_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_121_1.dat new file mode 100755 index 00000000..64bd8ba0 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_121_1.dat @@ -0,0 +1,2 @@ +xÚí×1 +À н§‰÷¿\— ¥C¡PbÑ—EÁÁ<DñGdµŠŠQG̪±3óèð©û×k?3ï¸sÉ9_Žz¢9iåÅÌÌÌÌÌÌÌ|3WþÃú3³ûÌÌÌÌÌÌÌÌÌ,WÉÌr•7Œ™™™™™™™™YÆ`–«¼aÌÌÌÌÌÌÌÌ›sd^ü¿ÝgÌÛš¿Ïõæ4Øð
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_125_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_125_1.dat new file mode 100755 index 00000000..d5881dd5 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_125_1.dat @@ -0,0 +1,2 @@ +xÚí×1 +À н§‰÷¿\— :ŠòþÒ‚ƒ>$’DdÆ©ÄÊ•Y»³ÿŸtëÚ…öλ»÷ÏÕÛó0£$ìììììììììì›ì§ûù¹ÆÎ®ÞÙÙÙÙÙÙÙÙÙÙõóììêÝg–a7ÇyëØÙÙÙÙÙÙÙ¯³ç—½Y??ÿØÙ{Õ{ý¾D“
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_129_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_129_1.dat Binary files differnew file mode 100755 index 00000000..62cd1c9a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_129_1.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_133_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_133_1.dat new file mode 100755 index 00000000..18d68dce --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_133_1.dat @@ -0,0 +1 @@ +xÚí×1€ ÐÝÓ”û_Î…€“ºh«¼¿h˜šÒ|"zÚÛ‰™-*dNÁâÔâÙó¦H¼‡«QÄ¢ÕRÂ,X°`ñc‹¬Þ9ÎY°(na_°`Á‚,Xè,XØ,X°`Á‚Þ#:8ﻓ,X°`ÁBïdÁ¾`Á‚,X|Ï¢Y\X¬Ö;Ç7-ݹ;`
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_137_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_137_1.dat new file mode 100755 index 00000000..284d7bea --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_137_1.dat @@ -0,0 +1,3 @@ +xÚí×1 +€0ÐÝÓ¤÷¿œKh]D¥‘¾,-t ò#²ÚŒŠQ[T©Ñ “K“s§7_Š›è¤È?9|•B&ÙX›^L˜0a„ÉÂ&3÷“þ„ÉMÌ&L˜0a„ &2 ¼ÃD4c™0a„ &L˜È€òÐŒe„ &L˜0ab·—w˜È€f,&L˜0a„É7&y2¹a²ònßoL˜<01O +˜ì¾ç
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_141_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_141_1.dat new file mode 100755 index 00000000..83220ddb --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_141_1.dat @@ -0,0 +1,2 @@ +xÚí×1 +À À>¯9ÿÿ¹4¢6)ƒ3×$`s²¸uÊ®‰>Wdš¾
›)›g³•'°±MÂ{3\d6u¹’bذaÆ
6lؼn³û]ÜNذ9ÄFÞ°aÆ
6lذa£3°a#oذaÆ
6lØ°ÑØ°Ñ5e16lذaÆ
]SŸb£kÊ6lذaÆ
›mê—ͤÍß;CûcÃf‘¼IdsG¹ÿ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_145_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_145_1.dat new file mode 100755 index 00000000..6a9950f7 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_145_1.dat @@ -0,0 +1 @@ +xÚíÜ!€0@Ïk®ÿÿ¦¡4a)³qæ2i.YCUO{35²UZÆFŒn]ÏfN>bd£ðwtzJF}ÁFŒ1bĈ#FŒýÆ(¡¯FŒ6r1bĈ#FŒ1ÒE1ÒiÝlFŒ1bĈ#FŒtFŒÜ#FŒ1bĈ#FŒtZ}#÷ˆ#FŒ1bĈÑl£þeôÀHÉùG£µÜ£@£Ù’î
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_149_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_149_1.dat new file mode 100755 index 00000000..02a3cdc6 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_149_1.dat @@ -0,0 +1 @@ +xÚíØ1€ ÐÝÓ”û_Î…ˆƒqÁHÞ_Xšòc‰èi«#Gd̘ŠÕ”Õ›á÷g…¬LUè^Ý®VR«>dKV¬X±bÅŠ+VÛXeùo¿ÎX±ÚÌJ_±bÅŠ+V¬X±bÅÊ;ƒÝ™+}ÅŠ+V¬X±bÅŠ+VöAV¬ô•ngÅŠ+V¬X±bÅŠ•}+ïúŠ+V¬X±bÅŠ«VýËjÒÊ>øhewfõ›•¾*`uTqí
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_153_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_153_1.dat new file mode 100755 index 00000000..2abfca20 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_153_1.dat @@ -0,0 +1,2 @@ +xÚíØ1 +€0ÀÞ×\þÿ9›Ãbt³Ešc'›ªÎHHÍ•š9³efßžmff²MïÙíª›õ #.̘1cÆŒ3f̘1cf–Ô7¯3f̘Ùg̘1cÆŒ3f̘1c¦o2cæ]Ã?€3f̘1cÆŒ3fÞ5ôMfÌì3f̘1cÆŒ3f̘1Ó7utfÌì3f̘1cÆŒ3f̘=ÌúËl¡™¾ùj¦£3ûÕÌ>ÛÈì¤ÐVü
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_157_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_157_1.dat new file mode 100755 index 00000000..17344b89 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_157_1.dat @@ -0,0 +1,2 @@ +xÚí×1 +À À>¯9ÿÿ¹4S¦d/Ì5‚Õ1ÈÊVÍ)SkŽJžµ»vßÜ7´³ÝÞÝíé…ÛÍeGä°cÇŽ;vìØ±c÷²]Z¯¸îÙ±c'ïØ±cÇŽ;vìØ±Ó+رÓgå;vìØ±cÇŽ;}V¯`ÇNÞù+رcÇŽ;vìØé:;vòŽ;vìØ±cÇŽ;½‚;}VÞ±cÇŽ;vìØµ±›'»ÍvzÅ#;Œ]Œ¼klw¯oA`
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_161_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_161_1.dat new file mode 100755 index 00000000..669ade1b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_161_1.dat @@ -0,0 +1 @@ +xÚíØ1€0ÐÝÓÐû_Î…XӨšyi~…ˆ¬Q©bÖÕkvÈp±á³ê7'M
u¸Ù=¼]ņÙð([2dÈáÏ+þÛ\'2”‡2dÈ!C†2´s0/3d(½)2dÈ!C†24ë1dhç 2dÈ!C†2dhÖcÈ¡<dÈ!C†2dÈ¡ƒy™!CyèMaÈ!C†2dØÞ0¿—šõ^š—4”‡[ž‹E2
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_165_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_165_1.dat new file mode 100755 index 00000000..abb48f0b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_165_1.dat @@ -0,0 +1 @@ +xÚíØ1€ ÐÝÓ”û_Î¥LK^–æ…#²FµŠYWt¨Ù%Ë–ïÂÿœ4¶ÔåÁ÷r¹šM,³éQºX²dÉ’%K–,Y¶²¬ú¯þœ°dÉÒ¼dÉ’%K–,Y²dÉ’%K’%K»
ó’%K–,Y²dÉ’%K–v2$K–vÞ–,Y²dÉ’%K–,YÊò8K–楷‡%K–,Y²dÉ’%K–2$K–,ÍK–,Y²dÉ’%K–,ÛZæ—åVKò£¥<≥yy¤å
™ó)Î
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_169_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_169_1.dat new file mode 100755 index 00000000..ba21b710 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_169_1.dat @@ -0,0 +1 @@ +xÚíØ1À н§Áû_®ÑîM,ØÇâàB^æ‘5*V¬º¢KN™n2ýú¦¹©NòNOµ‘i6>ÊS¦L™2eÊ”)S¦L™2œMç
S¦LÍS¦L™2eÊ”)S¦L™2•Må}¦LÍSS¦L™2eÊ”)S¦LeSyŸ)SóÔÅ”)S¦L™2eÊ”)S;Ù”)S;ó”)S¦L™2eÊ”)S¦v(²)S¦v(æ)S¦L™2eÊ”)S¦íLódºÍT6}a*ï3mljžmzC'
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_173_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_173_1.dat new file mode 100755 index 00000000..436918c0 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_173_1.dat @@ -0,0 +1 @@ +xÚíØ1€ ÐÝÓ”û_Î¥''Ó@y]Xšò1?"g¬:1çŠfn˶ÌöË»ö¬m¼·¯«»™m.?¶¶lÙ²eË–-ÛF¶«ÿß>glÙ²•·Þ2¶lÙ²eË–-[¶lÙ²e«O`Ë–¼eË–-[¶lÙ²eË–-[¶lõ lÙ²•·lÙ²eË–-[¶lÙ²eËV¦O`Ë–¼eË–-[¶lÙ²eË–-[¶z0}[¶z0yË–-[¶lÙ²eË–-[¶E¶ùe[h«OøÍVWö…¼=Ìötÿ*|
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_177_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_177_1.dat new file mode 100755 index 00000000..12e2e522 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_177_1.dat @@ -0,0 +1 @@ +xÚíØ1€ ÀÞ×ÿÿœÍE¨¬Œp0×XÐà„,a#rÚÊ}®¨6}ÇŒ§¿Ùÿ»¶‰±~އ£\Ð8 •ÆŒ3f̘1cÆŒ—7®ðÎ{Ö3f,ÝyŒ3f̘1cÆŒ3fÌX_¡¯`ÌXï&3f̘1cÆŒ3f̘±ÞM_Á˜1cy̘1cÆŒ3f̘1cÆŒë+3f,ÝyŒ3f̘1cÆŒ3fÌX_¡bÌXï&3f̘1cÆŒ3fÌxãü2žd¬¯øÔX'Äx[cy|¨ñ
¸•3ë
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_21_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_21_1.dat Binary files differnew file mode 100755 index 00000000..f87e0a11 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_21_1.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_25_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_25_1.dat Binary files differnew file mode 100755 index 00000000..3a225e30 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_25_1.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_29_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_29_1.dat Binary files differnew file mode 100755 index 00000000..0a1cb3b5 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_29_1.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_33_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_33_1.dat Binary files differnew file mode 100755 index 00000000..318949df --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_33_1.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_37_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_37_1.dat Binary files differnew file mode 100755 index 00000000..5bd9e3aa --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_37_1.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_41_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_41_1.dat Binary files differnew file mode 100755 index 00000000..52e9e58f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_41_1.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_45_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_45_1.dat Binary files differnew file mode 100755 index 00000000..b35c567d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_45_1.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_49_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_49_1.dat Binary files differnew file mode 100755 index 00000000..d20d7171 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_49_1.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_53_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_53_1.dat Binary files differnew file mode 100755 index 00000000..a676d7df --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_53_1.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_57_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_57_1.dat Binary files differnew file mode 100755 index 00000000..896ed435 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_57_1.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_61_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_61_1.dat new file mode 100755 index 00000000..4165a4bd --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_61_1.dat @@ -0,0 +1 @@ +xÚ30€CbpPº™ÝØÅi`÷@ê&ÚßH^§Ðn¨a†dQ»Gíµ{Ôn<v“Z®ÁÅGíMk£vÚ=j7¶¶â ³›Öå9mäán7â›<»ª³Âh
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_65_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_65_1.dat Binary files differnew file mode 100755 index 00000000..db8db88a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_65_1.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_69_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_69_1.dat Binary files differnew file mode 100755 index 00000000..03bba657 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_69_1.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_73_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_73_1.dat Binary files differnew file mode 100755 index 00000000..a729fdf0 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_73_1.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_77_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_77_1.dat Binary files differnew file mode 100755 index 00000000..0fe0b03e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_77_1.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_81_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_81_1.dat Binary files differnew file mode 100755 index 00000000..eacbdb1a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_81_1.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_85_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_85_1.dat new file mode 100755 index 00000000..b8a20c75 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_85_1.dat @@ -0,0 +1,2 @@ +xÚíÙ1 +À н§‰÷¿\©]‹‰4ðþ˜AA hÄÌØM¬\Q‘µjsë·úAk§U·ûújm‘un2RÃÊÊÊÊÚΚ5·ž:k;«»ÅÊÊÊÊjƲº[¬¬¬¬eoÅí›[™oð¬ÿ[ÇÎk®õ¨ðZä
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_89_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_89_1.dat new file mode 100755 index 00000000..e9d226f3 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_89_1.dat @@ -0,0 +1 @@ +xÚíÙ1€ ÐÝÓ”û_Î…‹N…h}%Àƒ@ iDOËHŒ±*cä"æûµ<öØgþâÈ)û<mõBsŸ¨¥‡™™™™ù—æÌwðja.av™™™™™ý‘ü7˜ÝAffff極‰2æ·¾ÝÙ5 æuØç9ß|²Þä
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_93_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_93_1.dat new file mode 100755 index 00000000..f37836c6 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_93_1.dat @@ -0,0 +1,2 @@ +xÚíÙ; +À À>§Ñû_.Ä4I‘ìâ‡y킎¢¸`)-5*¥ç(™é£of[Ósm€}åÑÃöý¶õÉö6YM ;;;;;ûG{ô»zÕØ·µ»«ìììììììz1vw•}œ=úwuûìýLÆ%û?û"ç=Ç~e—i
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_1/mask_97_1.dat b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_97_1.dat new file mode 100755 index 00000000..24fa60fc --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_1/mask_97_1.dat @@ -0,0 +1,2 @@ +xÚíÙ1 +À0н§1÷¿\—ÐtncKÌû«ƒ<DŒèi3#Gdgt(lxVûÉP¡ÃÔ9ÜFñ¡7lia``````ØÞq/]5†všÁ½Äàne``````xùÛ-mXá^Êú±3Ì5,´y†-†ù#
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_101_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_101_2.dat new file mode 100755 index 00000000..e39fd2cf --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_101_2.dat @@ -0,0 +1,3 @@ +xÚíÚ1 +À н§‰÷¿œ‹C‡â ‰Ux!“’?1ªe÷«ž ä)Ÿç“ás”Ê*¼KÛ®LfòšB¡P(”ÝJÖ®¤Ü¨x/ +…B‘a(2…B¡P(2E†¡P(ʲR÷Så(%qWR®S¼—ÿ”iY‰
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_105_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_105_2.dat new file mode 100755 index 00000000..7b63e31b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_105_2.dat @@ -0,0 +1 @@ +xÚíÚ!À @ß×ÿÿ\Mƒ«iØp4sÁ€™ 6¬ ꙑYs®"¤7þÛù&)=ñ;
ÒÜŒð"‘H$Ò¥µ¯;é|IžH$‰DÒ÷t#}OžH$‰DÒ÷Húžä’H$©·”þÕIZß#HgKòÔJºt”$•
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_109_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_109_2.dat new file mode 100755 index 00000000..252f6d80 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_109_2.dat @@ -0,0 +1,2 @@ +xÚíÚ1 +À0н§1÷¿\—@¶NÖˆ<q’À#ƒð#vßúÔ´-e~_«¨Š¿[Ú÷«ä¦Ñh46JKžÓFjöF£Ñh4šÌE“'i4F£É“4y’F£Ñh´¾ZÁEcS-;—Ð&jö©öŒ³Æñ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_113_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_113_2.dat new file mode 100755 index 00000000..26b5d7ea --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_113_2.dat @@ -0,0 +1 @@ +xÚíÚ1€0ÐÝÓÐû_ÎÅ¡‰‰Ðª0óÚäD\5*{ª#ˆ½bò’H'o+vUÓÇR1ŸPÐD"‘H$øÑ>‰D"‘H$ÊäD™œH$‰D"Q&—WerûH$‰D"ñ±ç*x[±(Ë?/ÚÇ÷ˆ'ŸŠnd
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_117_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_117_2.dat new file mode 100755 index 00000000..b4dcce46 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_117_2.dat @@ -0,0 +1,2 @@ +xÚíÚ1 +À À>¯9ÿÿ¹4!m dOƒs\§0X,laÕ5#¿÷Eª¾>Z[íœÖ·ŽÉêã½ÄR©T*•J¥R?Q-œº«*¯T*•J¥R©º?U÷—W*•J¥R©TÝŸªûË+•J¥R©Ô¤Úùó~m5Õ©;ªòúSõ&+
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_121_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_121_2.dat Binary files differnew file mode 100755 index 00000000..a2a0097b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_121_2.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_125_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_125_2.dat new file mode 100755 index 00000000..0ea40fda --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_125_2.dat @@ -0,0 +1 @@ +xÚíÛ!À P¿Ó”û_ƒ@
U(Ûkp@^Mú£Ú®5ÕôªúËîòâ-ú‰:ðöVF_žÌ\t:N§Óétú÷õ¼yžN×qt:N§ÓétºG—ãô;N§Óét:.ËÐå8:N§ÓétzA}ÿµ‹ôÔyžN×q¿Ñ;+nŠ&
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_129_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_129_2.dat new file mode 100755 index 00000000..bf048394 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_129_2.dat @@ -0,0 +1,2 @@ +xÚíÛ1 +À0н§Ñû_®KÈVÚ¡¨'.Éôþ!«²¶w]A0XðÄú÷~˜ «ÚÞ ö!›š€€€€€€à£ fK# x˜Fy€À4Èvey€€€€€€€@^°+Èçº~™ž ¨Û L#ÁêveI
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_133_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_133_2.dat new file mode 100755 index 00000000..9e78b6de --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_133_2.dat @@ -0,0 +1,10 @@ +xÚíÛ1 +À н§‰÷¿œ‹à&´`¢ô…Lâð”üÁˆQ-½g=Aq•bëú¹ŠÊª<‹všb½+©)((((((((ÊIën*…<b§G(((((((((ä +yÄ{AAAAAAAA!˜Á)ä + + + + + + +Š•¢ð'ùeЬ¹“‚â½ÂMý©¢fv{
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_137_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_137_2.dat new file mode 100755 index 00000000..95c3c48c --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_137_2.dat @@ -0,0 +1,2 @@ +xÚíÜ1 +À н§‰÷¿\—N‚I,/d+Ê£`ð/F|5*zª'H.”,xëO[KšKª«øŸŒ–’õ‰MBBBBBBÒT’|?!!9”8Å$$$$$$$$$$2 ‰hžÈ€$2 yBBBBBBBBBB"eÈ€æ ÉŸ$µ¯IÜ%ɿۓœHœb’©_xêÛë
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_141_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_141_2.dat new file mode 100755 index 00000000..da07da2e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_141_2.dat @@ -0,0 +1,2 @@ +xÚíÜ= +À н§‰÷¿œ‹t*-ˆ?±¼M…‡á[ŒhUöõ]WÐüB¯ç;–ÎÔd¨wS’j>÷lš
††fŽÆ„ÓÐÐÐÐÐÐÐÐÐÈš44²&
¬)OÑÈšÞY“FÖ4á444444444c4~œ9S³:3ÐЌטpšÇ®>®
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_145_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_145_2.dat new file mode 100755 index 00000000..9ff2bbf3 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_145_2.dat @@ -0,0 +1,4 @@ +xÚíÜ1 +À н§ùÞÿr] +Ý,tˆQ^È&ÊCˆð“§ÆÒ~ë +щ¢Éj~mɾ¢.ÕåŽFgÑüìMDDDDDDDDDDDÔST™×ˆˆêD¦ŸˆˆˆˆˆˆˆˆˆH¦•ŽˆdZï‘L+¯É´DDDDDDDDDDD2-‘é'"""""""":BÔäת½EåY„ˆ¨Ddú‰¾û††Š
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_149_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_149_2.dat new file mode 100755 index 00000000..d52e0484 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_149_2.dat @@ -0,0 +1 @@ +xÚíÜ;€@ÀÞÓÀý/gcaGÖBXBç'“-ˆ¼Âˆ«²»ouÕ§UQ½dõRVOm¡šTƒÎ*Ç«Ê;;šŠŠŠŠŠŠŠŠŠj¨ªå»ŠªEe2PQQQQQQQQQQQɨ¨äTTTTTTTTTTTTr»3•œÁ¼¢¢¢¢¢¢¢¢¢¢¢’3ØR©ä&ÕßTsþŒ·»ªk¤¢z_e2P=ê ïd™
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_153_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_153_2.dat new file mode 100755 index 00000000..3b060410 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_153_2.dat @@ -0,0 +1,2 @@ +xÚíÜ1 +€0ÐÝÓ¤÷¿œ‹8ZP!¿úB¶ÒòèèZuÔÈè³¶"û“ìbuºñö™‹ÈÒ*îÎÆ²é)]MFFFFFFFFFFFFö†¬%= #‹‘™ddddddddddddr
ot2¹†yFFFFFFFFFFFF&× #“k‘‘‘‘‘‘‘‘‘‘‘‘É5ÈÈL
2222222222²(YÚµ½7ÉÈ"d¦ÙóÞ@H—
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_157_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_157_2.dat new file mode 100755 index 00000000..2baf535e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_157_2.dat @@ -0,0 +1,3 @@ +xÚíÜ1 +€0À>¯¹üÿs6‚M¹ÎqUH1¤XØ&Uç̘½fÝ/u-çûé'ñíæ.ºû[ÍKGGGGGGGGGG÷H×|NG¨“(ttttttttttú¬NF§ÏÊ;::::::::::}–ŽNŸ¥££££££££££Ózèè$ +>«“Ñé³…ŽŽŽŽŽŽŽŽnüAø#ºî^AG—§“(t¯ì =3{
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_161_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_161_2.dat Binary files differnew file mode 100755 index 00000000..d2df7594 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_161_2.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_165_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_165_2.dat new file mode 100755 index 00000000..2e6cd7c6 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_165_2.dat @@ -0,0 +1,2 @@ +xÚíÝ1 +€0ÐÝÓ¤÷¿œ‹Šè ?òBÆVü¥UG¼žµ%åùÐêþ¸åÙ×*“+ú_ŽfÊËs MIIIIIIIIIù–2d;£¤l¨4‰()))))))))))eöqJÙ†IDIIIIIIIIIII)Û ¤”mPRRRRRRRRRRRÊ6lº””²
JJJJJJJJJJJJÙ†}œR¶aQRRRRRRRRRRNeòK?R픔͔&åW½3U
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_169_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_169_2.dat Binary files differnew file mode 100755 index 00000000..4052062b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_169_2.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_173_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_173_2.dat new file mode 100755 index 00000000..0a30ba53 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_173_2.dat @@ -0,0 +1 @@ +xÚíÝ+€@PÏiº÷¿ÚfÝtóš:>y &dU߬ÔýÍS´´[íþª»ùé¡Õ]5Z;a¼Û5VÛž™´´´´´´´´´´AÚÀï[ZÚË´Œ–––––––––––––V¦«¡ÕƒI0ZZZZZZZZZZZZZZ=-L‚ÑÒÒÒÒÒÒÒÒÒÒÒÒÒêÁhiõ`Œ–––––––––––––VFK«“·´´´´´´´´´´´´ÉÚü?‚ݧÍìhioÒJ0Úà}³¢o
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_177_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_177_2.dat new file mode 100755 index 00000000..d2c52f99 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_177_2.dat @@ -0,0 +1,2 @@ +xÚíÝ1 +À EÁ>§Yï¹4’V$Í~±,CŠ…¼&UóŒÞ÷;OoŠk5b÷ÑÏ™áâ”óŽG°x9¥Û%&&&&&&&&&&&n$îöõOL|‡Øv#&&&&&&&&&&&&&ÖÝbbݘ˜˜˜˜˜˜˜˜˜˜˜˜˜Xw#&ÖÝl7bbbbbbbbbbbbbbÝM"ÖÝl7bbbbbbbbbbbbbbÝMa!&Ö݈‰‰‰‰‰‰‰‰‰‰‰‰3Ä)U<WܱWŸ/¶Ýˆ#în Â
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_21_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_21_2.dat Binary files differnew file mode 100755 index 00000000..7466be4b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_21_2.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_25_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_25_2.dat Binary files differnew file mode 100755 index 00000000..0bc44c03 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_25_2.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_29_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_29_2.dat Binary files differnew file mode 100755 index 00000000..5112d11e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_29_2.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_33_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_33_2.dat Binary files differnew file mode 100755 index 00000000..5bac0c80 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_33_2.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_37_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_37_2.dat Binary files differnew file mode 100755 index 00000000..bdfc0bd4 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_37_2.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_41_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_41_2.dat new file mode 100755 index 00000000..c55c63e8 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_41_2.dat @@ -0,0 +1 @@ +xÚ30€C¸†¯JB€&¶Ã¹†Ð¨ÊQ•£*‡ªJXi@lÉ0øUÂý> *F™>–
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_45_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_45_2.dat Binary files differnew file mode 100755 index 00000000..ad44ff18 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_45_2.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_49_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_49_2.dat Binary files differnew file mode 100755 index 00000000..6e8edff2 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_49_2.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_53_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_53_2.dat Binary files differnew file mode 100755 index 00000000..682cae2a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_53_2.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_57_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_57_2.dat Binary files differnew file mode 100755 index 00000000..66a5c056 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_57_2.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_61_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_61_2.dat Binary files differnew file mode 100755 index 00000000..77d3815e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_61_2.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_65_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_65_2.dat Binary files differnew file mode 100755 index 00000000..caf184ad --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_65_2.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_69_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_69_2.dat Binary files differnew file mode 100755 index 00000000..6a3801bf --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_69_2.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_73_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_73_2.dat Binary files differnew file mode 100755 index 00000000..74945b71 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_73_2.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_77_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_77_2.dat new file mode 100755 index 00000000..903cba4a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_77_2.dat @@ -0,0 +1 @@ +xÚíØ1À н§Áû_®‹CMŠØ>òGót Ñ«eó¨+Ž×FWÄZE¼mÓ&gÞ‡F£Ñ¶h‰ùF+×t/F£ÙYvF£´õ³ïµÜì¥Õjçô[*7òÕa¶
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_81_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_81_2.dat new file mode 100755 index 00000000..17a9ac2a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_81_2.dat @@ -0,0 +1,2 @@ +xÚíØ1 +À0н§Ñû_®KÉÒ©Vi!O\"á‘Aøˆ«ò]¯:‚x»‘çÃbWµ½1ÅuȦ&‰Ä_‰µT ΋6œH$‰ÒU^‰Dâ~bׯëb=gˆ³âžûXîÛÙ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_85_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_85_2.dat new file mode 100755 index 00000000..72c74ff9 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_85_2.dat @@ -0,0 +1,2 @@ +xÚíØ1 +À0Ð=§1÷¿\—B7¡ÖÖOþ$á‘A0$â¬ý8W ¦êþwjguÞu¿æ§Š¡R©T*u•÷S֧ލæ•J¥R©ÞTóJ¥R©ÿRö‡ÕêÞ¢N¨æµ˜çVû
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_89_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_89_2.dat new file mode 100755 index 00000000..06c9a4fe --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_89_2.dat @@ -0,0 +1 @@ +xÚíÙ± À0À>ÓÈû/—&E*câÇQqâëÃŃªzf$òš«ÈËrM…ßåô„ß<ŽÈsa#d2™L&’?ïArYŸÉd2™LöG"ë3™L&“{ÊÙëRygw“;ÈúÊ
ƒñÚI
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_93_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_93_2.dat Binary files differnew file mode 100755 index 00000000..f5202963 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_93_2.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_2/mask_97_2.dat b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_97_2.dat new file mode 100755 index 00000000..38842b98 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_2/mask_97_2.dat @@ -0,0 +1,2 @@ +xÚíÙ1 +À0н§Ñû_®Ki·vH4
<q Â#®Ê©ýÔ„ÂËôsÔ-TUÕ²S¸_YÔ@ k…‘¼DØI°@ äV‚ÜJ a©PtÅÿ—0œ—›ö¡C8%ó¬
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_101_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_101_3.dat new file mode 100755 index 00000000..fa992512 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_101_3.dat @@ -0,0 +1 @@ +xÚíÚK€ EÑyWSö¿9'D1ñŸ¾ªp ã /9±Ðîu•èÝ,kžu9–Ò¤¼$¶îû%‰ÝwAL¹¤b%ClA³ƒzqÛ´åë¶Œ‚bˆ!†ØÇĤwå™XÜ4`Áõ¶_ö'FWrŽ!†bˆõ&Æt1Ä<FWrŽ!†bˆýXŒé‚yŒ®äC1ĆÓý©²Ó‡Ì)E{WúÙ¯AÓ…gLž1]¼-&øÆ¼§®”æLqn‰
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_105_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_105_3.dat new file mode 100755 index 00000000..d8a28ce9 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_105_3.dat @@ -0,0 +1 @@ +xÚíÚ1€ DÑ~O3Üÿr6„Ð@(œ1ê'†„¬ûâÂJ}4Ï£¦gk 9’7ÐéA=÷ž¯Ÿèi»÷p}‘“w¸õZJo¬ýûÜ8Ë¢ÏJB=ôÐCÏs=
=÷;ô”Hê³zT.çz衇zô{ô{ô{T.çz衇zô{ô{ô{T.z衇zoÔS$P«T õhŽŽE©Ž%•Ô¿ôbßž¯r•ª\RåL$Ý
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_109_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_109_3.dat new file mode 100755 index 00000000..48d94040 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_109_3.dat @@ -0,0 +1 @@ +xÚíÚ1€ DÑ~O3Üÿr6Ñ‹Ý ÁO(‹Í¾™©V6ïÓsy±¹š¡¹Ð>’)ë_%s×_½Õƒd3KŽO1^ša†§LŸá,†$’H"‰$’KzRP—t[I&¯ÿX’ÝÍ9‰$’H"‰$’äIò$y’sI$‘DI$É“äIò$»I$‘DÉ%eëÌsò!¹¼=—™‚äLAZ5—œ'åÌ“IVý“rîn¸»/2oÆ…
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_113_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_113_3.dat new file mode 100755 index 00000000..023b2730 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_113_3.dat @@ -0,0 +1,2 @@ +xÚíÚA +€0DÑ}N“ÞÿrnDëÂFj2úK‚ÂÈCÂt?W«ÜݲîZ“iêÀ.qÕÁ‹ŒoýPô¯ªª%SmjÕÖÂ7ËÞ¦‹:·©ª¨¢Š*ª¨N©Ê:@¤:݇·ìýÁоº•*€¹Š*ª¨¢Š*ª¨f©ÒW9dª2˜«¨¢Š*ª¨¢Šj–*}•S@ Ê`®¢Š*ª¨¢Šj½ªæ¯à§ª6ðJlº¾êÑ¿Ã}ÕÕ}ÕÕ§€•T‹¿UßaÈ24hnt
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_117_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_117_3.dat new file mode 100755 index 00000000..79cc04d1 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_117_3.dat @@ -0,0 +1,4 @@ +xÚíÛ1 +Ã0Ð]§Qî¹.¡xIÚB$úŒ‡€Á?~!É<ÇQ?ß#–ç¶Ð5µ/tIÝJ8owøié{áßçâ¬}£Sø˜^ã~‡¢½q猉P„ &L˜0áJá¢î1ÊúϻݢðD×K'¬J¨Ã„ &L˜0á…õÃn +«ê0a„ &Lxgaý°Gƒ°*¡&L˜0aÂ{ +g{衟þ2¨ë‡s¢Î‰~˜ðì7\]%r¢Jôö9nZ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_121_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_121_3.dat Binary files differnew file mode 100755 index 00000000..aff5a7be --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_121_3.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_125_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_125_3.dat new file mode 100755 index 00000000..e2febdbd --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_125_3.dat @@ -0,0 +1,2 @@ +xÚíÛ1 +Ä Ð~N3ÞÿrÛ„]·‰BБÝ'?<Âà/̼VÛµ»Ýóþü¨ïÒ”¿ÿ´…<–’0-_±Jä[¥ü?† +wÔÄ^;*ÃyòäÉ“'Ož<ù’/9ÏÏɯì9±<áîyÓÆœ'Ož<yòäÉ“'¿J^“Òa—È›6¦
yòäÉ“'Ož<ùGòš”{–¼icΓ'Ož<yòäÉå÷ßPû–¯§·šó|Î]¨ZÖ¤²²Iee“:[~Ë?Ÿÿ<mJò_¶NŠ:
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_129_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_129_3.dat new file mode 100755 index 00000000..b1ce63b7 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_129_3.dat @@ -0,0 +1,8 @@ +xÚíÛA +€ н§ï¹6!n” ðG<q‚4øhð/ªºG?;ÇhÓs¤€¹‚LST¨õÞ÷ëOj»÷åúâ2#¥ÐÓ +DÛ×y`¶Ü«ïÙÒP @ +ÏÎ\†BêŽ4*yt$‰ +(P /Èò‚¼ #Q @ +äyA^t$ +(P @‚¼ /Èò‚ŽD +(ü\¡¢ô–.`ý§íÉ›j¥oªéC °Sˆç;R¥;Rî.€Iõ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_133_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_133_3.dat Binary files differnew file mode 100755 index 00000000..f4181507 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_133_3.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_137_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_137_3.dat new file mode 100755 index 00000000..e24ac5b5 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_137_3.dat @@ -0,0 +1,2 @@ +xÚíÜA +Â0Ð}N“ÞÿrnªFÔHSš™àYQ|øé‡ÒZ÷µEìf•æuì,%Ë Í$‰uj÷ÓG£:Ãßzèèóo»Âu¶,:O òsâY»Ä°ï’e:tèÐI¡sÅÕj÷¨|ãÌ+ê»Nø5ŽŽd“ltèСC‡:tÎèè;Úèb:’M²Ñ¡C‡:tèÐ9££ïh£ËëH6ÉF‡:tèСCǽ Ú¨{A%›d£C‡:tèü³NìÓ$^urò˜dV—èô±'\Ówj–¾S³´Ñt‚þ;U²½'[ø,7ÃÜ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_141_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_141_3.dat new file mode 100755 index 00000000..a3f6a248 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_141_3.dat @@ -0,0 +1,2 @@ +xÚíÜA +Â0Ð}Nó{ÿ˹)%]ØjÀf/d!ˆuÈÃOŠUûÚæícµîõô0}šùaº4ùR9ëòÊ_½õ¡T~é`˜ÓÙÌ_ R[’T‡Õ®¯ððn1öÝ’Â"EŠ)R¤H-*•p—~H…Ü¥ßJýäÞ„Ô°”égú‘"EŠ)R¤H‘J“Ò§4ß‘2ýL?R¤H‘"EŠ)RiRú”滀”égú‘"EŠ)R¤H‘ò>¥ùz†Öô#EŠ)R¤H‘Z@ªbÂl-)ÌÝ¿ñ<ß§*©OURŸ"5&•ö›š5ý*iúeÎJ]®+
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_145_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_145_3.dat new file mode 100755 index 00000000..338b7e7a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_145_3.dat @@ -0,0 +1,3 @@ +xÚíÜÁ +‚@Ðý|Íóÿ®”•:Œ^ñ³JyÌ¡¤ªyM§î×j‹×-' +9¤V—SÛýÖ¦K¶©9ëÇe¬µ)PíyUûû¹£wËeÞ-m jÔ¨Q£F5jÔ¨RËiÙ«ÔF4×_wk}·0+µµjRBR£F5jÔ¨Q»ƒš¾¦eßOMBJHjÔ¨Q£F5jwPÓ×´ì˪IH I5jÔ¨Q£FͳÇúš–íÙc éw5jÔ¨Q£FíµŠ:¡œÿÑzS«Ž*2¤¯UZ_«¾CÒ×*e_Oí´ïZ¥%dIȯûâ†b
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_149_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_149_3.dat new file mode 100755 index 00000000..30bc5fab --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_149_3.dat @@ -0,0 +1 @@ +xÚíÜA‚0Ð}Oó¹ÿåÜ â¦Hé ¯éÂô“'¬Z×2{oVÛ|Ι«%µ™ê>‚yû·R{‡!8îäÂI+JpI|#¶Ýfì–5κ[âPA‚ $H Á‚Q}ð)Õ&<¹÷E‚§ JQ)J A‚ $H Ás5zþ¥¨%H A‚ $Hà4A^£ÿ5A)*E $H A‚ $è½ Þ3ïMHQ‚ $H A‚ç æü3Þ§`ÞP¯©®o¢Ý>X{טÔè+±ÑWb£¿¡àìß`IÑã)5׈%dš
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_153_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_153_3.dat new file mode 100755 index 00000000..89cdec03 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_153_3.dat @@ -0,0 +1,2 @@ +xÚíÜA +Â0Ð}Nósÿ˹)7mJ¦õ…,Å}8µêX=c¿W^G“e’ÝNó<v]]bíIͯ_½Ïû¸gY+M³§j íò“»vË‹tì–Œ&Mš4iÒ¤I“&MšÿªxÞœÑÜu~o[¯~ö4W55¦¥I“&Mš4iÒ¤I“æS5ML¯©i5-Mš4iÒ¤I“&Mš4oiz`z@SÓjZš4iÒ¤I“&Mš4izÅôÀôÀs(šVÓÒ¤I“&Mš4iÒ¤¹¬Y‘ÁzK
6ñ¦Ï›•:=¨ÔéÍ_hÆþ6·7m¥6mÞM{üHã
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_157_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_157_3.dat Binary files differnew file mode 100755 index 00000000..167e6f84 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_157_3.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_161_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_161_3.dat new file mode 100755 index 00000000..72a26a4f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_161_3.dat @@ -0,0 +1,3 @@ +xÚíÜA +Â0Ð}N“ÜÿrnªF±ÑTká…,„€NûðÛ ÄZ—Ñ’f7J÷:³Æ’^`WáÙ”U×á»nXÚ¤¼Ç§M-½¾‡™#V¹¥+ß¡ËÛ+9z–ÜÒ–YÒl”)S¦L™òo•cŸ±¯Ê±O_Cå?ß©õ%Ê;*Kl‰M™2eÊ”)S¦L™2åYeý²]ÊÏÊ[bS¦L™2eÊ”)S¦LyVY¿lW„òGÊ[bS¦L™2eÊ”)S¦LÙÙ +ý²]g+$¶ßeÊ”)S¦L™2eÊ”Ð/Ûq¶BbKlÊ”)S¦L™2eÊqÊ™ÿ‚þ¨œ]à£{ÎA¿¼¦œÔ/×ô~¹¦ïŠœV9ï»\%ö÷‰[ãP¯#'
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_165_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_165_3.dat new file mode 100755 index 00000000..870af8f4 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_165_3.dat @@ -0,0 +1,2 @@ +xÚíÝA +Â0Ð}N3¹ÿåÜT‰#ØZþÀY¥uÈÃ:ÆÌ›¯1–ϱE®Uæ¹TÙT|SüþÒ·Ô ñ¿qž)òm-sG²øì ¾ ýBæÈ.C‘ĉ'Nœ8qâÄŸâáÿÕ÷âwv7û[Æç'„tgÄÿ!.Õ¥:qâĉ'Nœ8qâÄÄõãv`ˆ_+.Õ¥:qâĉ'Nœ8qâÄÄõãv`ˆ—êR8qâĉ'Nœ8qâΤèÇíÀ8“"Õ¥:qâĉ'Nœ8qâÄIÑÛq&EªKuâĉ'Nœ8qâ½Ä+¾È9:¹}kFT?^úñêпG¼Åo<0Õ«Cªg/æ_
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_169_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_169_3.dat new file mode 100755 index 00000000..94310952 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_169_3.dat @@ -0,0 +1 @@ +xÚíÝA‚0Ð}O3Üÿrnˆ¢‰ñ·¾¦¨)}á'3ªæ1EÎÇh‹ßÑ]î4üP[
£_«ñÁ¥]Kvè‡<ÃË™fpý©#ýûª¶y_Êlù[œgëe£ôéÓ§OŸ>}úôéÓÿoý ªµ%uJV/µ°“{¿„þ—ô%¿ä§OŸ>}úôéÓ§OŸþ¨úê}Ýú§êK~ÉOŸ>}úôéÓ§OŸ>ýQõÕûº=ô/Ñ—ü’Ÿ>}úôéÓ§OŸ>}ú£ê«÷u{èï×—ü’Ÿ>}úôéÓ§OŸ>}ú£ê«÷u{蟪/ù%?}úôéÓ§OŸ>}ú¿×¯.N4ÿ»<Oúµõ±‘”z¿z©÷lôêz¿zéö¢ŸøîW/É_’ÿ´yp
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_173_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_173_3.dat new file mode 100755 index 00000000..74669862 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_173_3.dat @@ -0,0 +1 @@ +xÚíÝA‚0Ð}O3ÜÿrnPkŒTTèŒyM$&e/|ù"ÖµdÝÝjÝuþy[¥a»i‹KˆÑ{úòÌM S&z[B…UBÂRIÂCÞ!Ón5Æ\w«4ìB $@ $°[”ÿ°£3Ûëc²½E^%”xw á ÒA:@ $@ $@ $B‚fI³D ÒA:@ $@ $@ $p¶Í’f‰„Ã%Hé@ $@ $@ $@BR š%Í ¿” ¤ $@ $@ $@ )$h–4K$L ¤ $@ $@ $@³„ü_{”PgØÛ´yŽÍ>áƒVMm–¢R³•𥑸™Òá¸t(1ï…¢|
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_177_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_177_3.dat Binary files differnew file mode 100755 index 00000000..9586979a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_177_3.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_21_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_21_3.dat Binary files differnew file mode 100755 index 00000000..bcb4eec4 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_21_3.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_25_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_25_3.dat Binary files differnew file mode 100755 index 00000000..0ffc375f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_25_3.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_29_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_29_3.dat Binary files differnew file mode 100755 index 00000000..6150ac12 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_29_3.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_33_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_33_3.dat Binary files differnew file mode 100755 index 00000000..6053b5e3 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_33_3.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_37_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_37_3.dat Binary files differnew file mode 100755 index 00000000..5dea5b9c --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_37_3.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_41_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_41_3.dat Binary files differnew file mode 100755 index 00000000..ca9ddc2a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_41_3.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_45_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_45_3.dat new file mode 100755 index 00000000..3daad97f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_45_3.dat @@ -0,0 +1,2 @@ +xÚí”K +À D÷sšñþ—ëFJ(&™)ô³0d¡øÐ‡’³FÞg!Œ[8Ò=èÛ&ËiaÂÎD)Åd8&AËÕ¬¡a³áÀÛä1'™I–I”«×³ž7Ù9ù exÀ“ß¾È ÄÒIìû&Ö´¯Cçu´ÍJy‚
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_49_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_49_3.dat Binary files differnew file mode 100755 index 00000000..7f6508dd --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_49_3.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_53_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_53_3.dat new file mode 100755 index 00000000..8800beab --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_53_3.dat @@ -0,0 +1,2 @@ +xÚí–K +À0D÷s½ÿåº ¥‰Ñ!
-(.Bp&|ˆ"-t–&`Æq‘Q-"†ð9Ù_+)Be/H8¾ãD®¼%‘a~šÐ}spKFN˜úöœ¶Ö=,ÂÆ;‡;a^tª4÷–‰Ï\™ÞF™ÎáÂÀÊÎSNé§éâ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_57_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_57_3.dat Binary files differnew file mode 100755 index 00000000..4e1e5da3 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_57_3.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_61_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_61_3.dat new file mode 100755 index 00000000..bf1a3cc7 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_61_3.dat @@ -0,0 +1,2 @@ +xÚí–A +À0ï¾fýÿçz‘4-%š•Ò*Ãâd°Ðpž!ÝyZÜ«‰æ‚uäõ(ù¸~ë=¹&ÉÛ“´‹)œÌR2â"/ò"ÿ<9çŠFΊ=r§þbòšó"/ò“ƒîÌíçrw¹"2®¯¹ãçÈøùBòèŸ#3-Ø0-KÀW
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_65_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_65_3.dat new file mode 100755 index 00000000..85892089 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_65_3.dat @@ -0,0 +1,2 @@ +xÚí—Q +€ Dÿç4»÷¿\?R•Û…Å,û!¨Oв-òNv®Í1 :Âc¡ZœuïÚ"U‹MÕF ~jK¨€´…-la‹[^q^³Q\éœ=…o”-la‹ÿZpUÒÞB¬„äÿ±@IµÖKµJzÉ¢|1â÷¹
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_69_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_69_3.dat new file mode 100755 index 00000000..55318a87 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_69_3.dat @@ -0,0 +1,2 @@ +xÚí×Á +€0Ð{¿&ûÿŸó2¤'—®dô l†=,¹F½îéy;$§ì‡¤”WEË-R„Ѝ³:¢¡%T,O2½×g…"Ä"‹,²È¢/DÍyĈ˜¹ôɧ{þO䮳È"‹,:N„vÈEñWNÎ#(&,ö,‘ä•ºŠ®ë]æxØ…
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_73_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_73_3.dat new file mode 100755 index 00000000..15be77f6 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_73_3.dat @@ -0,0 +1,2 @@ +xÚí˜Q +€0Cÿ{šìþ—ógè;è¥JJ?dÔ¬K„=ÚRaÃsJhTJ6exÎkaºú¥§\$é‘n¯´IE,-/ÑÓXB¢è*Ñ…=ËâžíÚÒýweeé4™¢è¸tÿöÌ’tšLщî«t t”ß«œèà¯bž –gF…fž ÖáqºõoÖd¢Ðdn-ð?
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_77_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_77_3.dat new file mode 100755 index 00000000..ec782804 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_77_3.dat @@ -0,0 +1,2 @@ +xÚíØA +€0Àû¾&ùÿç¼BŠiÖRaK"té`I@³¶|¶º…fX—ö±ÔâyiöåólõE‘:Sza18G¾À‰if˜K*©¤?–:YÜC1쌞졘(ቷ—êöJ*©¤’¾’jÊl*Õí•TRI×KëÌîR^Ø™æœÞksûÂ)cÌ)cÌ)³JZø¦¶óí¥äãa·
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_81_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_81_3.dat new file mode 100755 index 00000000..47bc0f79 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_81_3.dat @@ -0,0 +1,2 @@ +xÚíØ1 +À Fá=§‰÷¿\‘,JÑGAúÄ¡‹ýhjÀÌ>ÚÙ#Ê3X™À’ø:çk÷Ô¹\ûFM
JÝhuÇú½3¸¨>ƒTZõŸÔ{ÇãPSgøP'¹ÉkÔV¸jÕªU«¶_Û¯ý¯U«Vº•»=P
Îo†O:WÒ‹ÞäÝjü[ŸWxÒÎmò
Ù5
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_85_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_85_3.dat Binary files differnew file mode 100755 index 00000000..02c4f8cd --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_85_3.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_89_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_89_3.dat new file mode 100755 index 00000000..2b4cb59f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_89_3.dat @@ -0,0 +1,2 @@ +xÚíÙ1€ …á½§)÷¿œ*.@àUÁü
ƒÓÓ|±¶‰î¹’âeŵ6Û¢‚‹äw5*÷ô)
oôi¨K®‘¢4nk>Áè1}d>œÐ@
4ÐXYC¾oœò Û¡1<º©A§ h F„ûÆt +ß
4Ð@µ5´—ž1ÁWrÒï>7û«û†Gíµ}}¤¡x7|ÇN‘gÆîÚN
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_93_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_93_3.dat new file mode 100755 index 00000000..b4cc8a97 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_93_3.dat @@ -0,0 +1,2 @@ +xÚíÙA +€0DÑ}N“ÞÿrnJɪQš~éB¨Œú06 û£n®aá¸<<¦×‡‡ôÿe6×M—R™ýCùÅPÈ¥LÀ±ôÌ“išØ9MŽ2È ƒLkÅ®½dD»v*ó"a·ÔX†jBdAdªdèôZöÀTßdAd®qYø0exö÷ö¼Ÿqe§çÊN¯«Œú©ª&WV“ææQvcô
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_3/mask_97_3.dat b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_97_3.dat Binary files differnew file mode 100755 index 00000000..7adc9eba --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_3/mask_97_3.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_101_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_101_4.dat new file mode 100755 index 00000000..1c97dc04 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_101_4.dat @@ -0,0 +1,2 @@ +xÚíšA€ Füÿs^1 b¤‹ÆáИª]ÃØÖ4šm+Æ8Ð+˜Ve¬Ä^HR]–\Íc‡®ŠÛ +·oõWN#¸îXáéØá+lðžHE³cp\à—^.Î9qñW9ø¼ïç"ò…:¸À.ŸæBßÿó÷ùBƒ\àæ0Ìa˜ÃPǨcp\ÞÍÅïO•’‹N¥qjpGÖô}“ô}¥Ê$.äË…Š§ÐŠ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_105_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_105_4.dat new file mode 100755 index 00000000..0211cdb3 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_105_4.dat @@ -0,0 +1,2 @@ +xÚíšK +€0D÷=Mrÿ˹‘øA…ÎTáe‘EFóL2#ŽÈÌé¹¢…_I!“‹•ßú£ç딤Ѻ““Î-Õ«km¿Oñá]sST6*ùª'8Á NŸâ$Þî'N¥Z‹Êí^œ<JvNôsNp‚œà„ßÃïá÷è'æœà'8Á ¿‡ßÃï1÷˜{p‚œàô?N¿µnœ¼JóüBÏ…ùü^Øüž[i'úiHI-¶¢m+W
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_109_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_109_4.dat Binary files differnew file mode 100755 index 00000000..2cc0c815 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_109_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_113_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_113_4.dat new file mode 100755 index 00000000..99bd73f6 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_113_4.dat @@ -0,0 +1,2 @@ +xÚíÚA +à н§Ñû_®‹Jb)t&„çBBÒøK_•‰¤Ö³µÖ6õC+5/q_Üghfâ×Çú©Éíÿž¿CbH+¾p›º¿¯§;WK?ÞüGútô ‰ñ¿*GŽ9r|‚c`ÕÑc¾¶Î9FKHÌq4«9räÈ‘#Ç…ŽêUÏ×ÍGë*GŽ9räÈÑ>¹}rûäÖUë*GŽ9r|ºcÌ[Á£cNâ¬Þ[_=Æ×«5¼^ÍJ¼£ù¸ 1*ôq›v
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_117_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_117_4.dat new file mode 100755 index 00000000..38672591 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_117_4.dat @@ -0,0 +1,2 @@ +xÚíÚË +à н_£ÿÿs]4Ø¥©Dg¤nò¸à‰2Jj}´ÖÚÒ¾·RsSWG¶R¿ŒÉ§†žÚ)5¬»ÞE†áämá#ܯ•ã¾Õðk_"ÃzŸ”š3Â\¹råÊ•+×r
L»k|ê/{„¹;Ž'×àÔ<WóÕ:Ì•+W®\¹rÝÜU=lŸ3ÏÕ|µsåÊ•+W®\û;÷wîo¾Z‡¹råÊ•+×ý\ƒþ¼sÍKýÃûzešS×”z83u#Wóubjdð
àÝ$Ÿ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_121_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_121_4.dat Binary files differnew file mode 100755 index 00000000..84957eb7 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_121_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_125_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_125_4.dat Binary files differnew file mode 100755 index 00000000..b98dc813 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_125_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_129_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_129_4.dat Binary files differnew file mode 100755 index 00000000..8ecfa250 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_129_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_133_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_133_4.dat new file mode 100755 index 00000000..69f83acb --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_133_4.dat @@ -0,0 +1,3 @@ +xÚíÛA +à н§Ñû_®›Òš…BþÂËB¨2à‹f™ó{µ +ÇíÆcf¥¨°gÈKqøã=óÙ)Ú®£‹»çWÀŠlK2Þ¿ÊË8:oþCRd¬\pÁ\pñ@ï+$EÅÿÏùQ.ìÎç\pÁ\è#úˆ>¢Ø/œ#\pÁ\pÁ…>¢è#öç\pÁ\p¡è#úˆ>âqŽpÁ\pÁÅ.º¾$¿ºÈIqü𽤠dô‘ÑG’R»°_4¦èò€˜
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_137_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_137_4.dat Binary files differnew file mode 100755 index 00000000..0c09c487 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_137_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_141_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_141_4.dat Binary files differnew file mode 100755 index 00000000..62b03f24 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_141_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_145_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_145_4.dat Binary files differnew file mode 100755 index 00000000..33fb2112 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_145_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_149_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_149_4.dat new file mode 100755 index 00000000..de99310f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_149_4.dat @@ -0,0 +1,2 @@ +xÚíÜÁ +!н_£ÿÿsmœÉ Œ„Á+XÓ‹9=ó.ªõ=Zkça”š]ÕÞ’>K¯jò´o龪ý|¯SªŠSWKùZmË׌ºƒÃjé¾€Ã\’йçЪ2ï W\qÅW\qÅÕñ®"~·ß×jvÚtòê®òªŠv¥_Ùíƒ\qÅW\qÅW\q%g3Èäú•}+®¸âŠ+®¸âŠ+®är9ƒœA¿²rÅW\qÅW\qÅ•œAÎ g3èWöAû W\qÅW\qÅW—«œÆ]åVµ~v{êDŸ™3ÔÈœ!¹ªƒ\éWÇô«¬Â^<ír/
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_153_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_153_4.dat new file mode 100755 index 00000000..e827dd16 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_153_4.dat @@ -0,0 +1,2 @@ +xÚíØ1ƒ0ÀÞ¯ñýÿsi,')p!äµ4.ÜГ;±½¿WUmßçj=¿²„²ê°Ê.NO¾þ>úûšçTµÚ[÷¾S7vÜÛœg¶q•„?æ÷ÞÒ +š{pe¹o“3Î8ãŒ3Î8ãŒ3Î8{ØYXzðá,°²Û_úO¤ÓYfeñÎô3sÓÜäŒ3Î8ãŒ3Î8ãŒ3¹†\C®!×ÐÏÌMs“3Î8ãŒ3Î8ãŒ3Îär
¹†\C?379ãŒ3Î8ãŒ3Î8ãŒ3¹†\C®!×07ÍMÎ8ãŒ3Î8ãŒ3ÎÎq¶þÚé,»²ËmMrs›k¤Wvœ3ýìÀ~–WÜ€WB
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_157_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_157_4.dat new file mode 100755 index 00000000..ad5fcf69 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_157_4.dat @@ -0,0 +1 @@ +xÚíÜ1Â0ÀÞ¯ñýÿs4˜"FPà=iRX¢XÉÛ0ç멪€u 4ftÑÖl}Òm¾øßýžéâž»Sû|²ËÑŽëSÌû¶ŽÄPÏ5<]öÉrÇwÜqÇwÜq÷£»È^QáéN6ÃûáéZ¸sß™³æ,wÜqÇwÜqÇ>«Ïê³ú¬ûΜ5g¹ãŽ;î¸ãŽ;îôY}VŸÕgÝwæ,wÜqÇwÜqÇwú¬>«Ïê³æ¬9ËwÜqÇwÜq§Ïê³ú¬>ë¾3gÍYî¸ãŽ;î¸ãî[ww?P3Üå§ÛþòÑÆ˜ÝggtŸí®¡;÷]ûû.3àw4A¯
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_161_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_161_4.dat new file mode 100755 index 00000000..7604c454 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_161_4.dat @@ -0,0 +1 @@ +xÚíÚA‚@Ðýœfæþ—s!AL€Ä_|,ˆ™4òl)iï¯mŒ²ßl׉0'¼÷ +E¸û…£÷–]N\ºêüxÅ#·‡§2Ÿç/ü¼_¬{—7gÛò9ì¼Ù·ÔÀÖ}óï2‡rÈ!‡•?}-³#<Zü×χëj"¬áP>T—Õe9äC9äC9üÀ¡~ùöý²÷6Sʇ겺Ì!‡rÈ!‡rÈ!‡æôËÞÛ˜sÕe9äC9äC94ç _öÞÆœƒ|¨.«ËrÈ!‡rÈ!‡šsÐ/›s0ç ªËê2‡rÈ!‡rÈá8ì§}D‚ÃÎìçw£ùýrï—«DXΡ|xÃ|˜ä!à2ð
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_165_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_165_4.dat new file mode 100755 index 00000000..d83d6316 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_165_4.dat @@ -0,0 +1,3 @@ +xÚíÚA +1Ð}OÓÞÿr.ª"Ñ*?úfÑÅÌP‚óLHIï—kŒµÎ«õZQ¦…8 +Gy÷êqkñ-ž¿ÿÎn5£Œ½Ú+?ÌÒñùù¾Ýâ¿ø|ÖŽ÷Òþà·kKn®E¢¬ñŹä’K.¹ä’K.?ï2º£˜.ó£|¼EJ§{å2<Ê:.åKu\ç’K.¹ä’K.¹ä’Ë—úñ¿êÇmu)_ªãê8—\rÉ%—\rÉ%—\šÛÐ;'2·!_ªãê8—\rÉ%—\rÉ%—\šÛÐ;'2·!_ªã\rÉ%—\rÉ%—\rinC?nnÃ9‘¹
ùRç’K.¹ä’K.¹äò;.û²óHqY'ÊݽéþN·F?ÞKôã•¢,êR¾üé|™è My*3
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_169_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_169_4.dat Binary files differnew file mode 100755 index 00000000..4aac95c1 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_169_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_173_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_173_4.dat new file mode 100755 index 00000000..9df4d865 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_173_4.dat @@ -0,0 +1,2 @@ +xÚíØK +1À}N“ÜÿrnÂaøÃ~ZY!ÎÐJÑt^ï×5Æ(¸/«õÌjk–z[pjµ§_Ø?~v:|jwúÕ–_mû£îXzòoõ–Ó6?nËÞŠ<ö°j³$pË-·ÜrË-·_p[z¾=ÞVíæ–Qí¦3ÝæTéV¿ÕoÍ ÜrË-·ÜrË-·ÜrË-·r09˜L¦ßšÌ ÜrË-·ÜrË-·ÜrË-·r09˜L¦ßšÌ ÜrË-·ÜrË-·ÜrË-·r09˜L¦ßšÌ ÜrË-·ÜrË-·ÜrË-·r09˜L¦ßšÌ ÜrË-·ÜrË-·Ür›ævæ6¯Ú÷ßý?•,eå`=*K¬6Ú~û§ý6£à´*×
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_177_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_177_4.dat new file mode 100755 index 00000000..6437d251 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_177_4.dat @@ -0,0 +1,2 @@ +xÚíØA +!À»¯Ñÿ.1B„éMyð”![c÷þ\cŒ²ûZçV\¹Üq“Š7ßâÝEó‡Oýzg~ÅkëøË¿jž;pg”Šu²Í[*7Š×½¥ºöÀŠóTpÌ1ÇsÌ1ÇsÌñqÇ!¯ÿ‹ã Šw/òr¯ÿå8«âXÇú±~l®à˜cŽ9æ˜cŽ9æ˜cŽ9–»ÉÝänr7¹›~l®0WpÌ1ÇsÌ1ÇsÌ1ÇËÝänr7¹›~l®0WpÌ1ÇsÌ1ÇsÌ1ÇËÝänr7¹›~¬›+8æ˜cŽ9æ˜cŽ9æ˜cŽånr7¹›ÜM+8æ˜cŽ9æ˜cŽ9æ˜ãtÇû§oEÇ™ŸÉN¥Xy¹[ËÝR+¾‰cýø¯ûqNÑ\,4J
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_21_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_21_4.dat Binary files differnew file mode 100755 index 00000000..e006b67e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_21_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_25_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_25_4.dat Binary files differnew file mode 100755 index 00000000..0c7c44bb --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_25_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_29_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_29_4.dat Binary files differnew file mode 100755 index 00000000..c28dc20e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_29_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_33_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_33_4.dat Binary files differnew file mode 100755 index 00000000..5834b6fb --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_33_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_37_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_37_4.dat Binary files differnew file mode 100755 index 00000000..4bf2e26e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_37_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_41_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_41_4.dat Binary files differnew file mode 100755 index 00000000..b75b7d05 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_41_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_45_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_45_4.dat Binary files differnew file mode 100755 index 00000000..1b921f30 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_45_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_49_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_49_4.dat Binary files differnew file mode 100755 index 00000000..e417f947 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_49_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_53_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_53_4.dat Binary files differnew file mode 100755 index 00000000..7e88826d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_53_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_57_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_57_4.dat Binary files differnew file mode 100755 index 00000000..84669c7d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_57_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_61_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_61_4.dat Binary files differnew file mode 100755 index 00000000..d127c3be --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_61_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_65_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_65_4.dat new file mode 100755 index 00000000..c24343d9 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_65_4.dat @@ -0,0 +1,2 @@ +xÚí—K€ D÷œ¦½ÿåtX£Ð™.4ÓE1^ÂãÓhv†»OæÍx„ùéW“ð0ñ°JHÅ»‡½ðz[ÿ^܈[vâȾ +yy‡ZÀkß=`„™êv·cíǃî…<ȃ<üÚCî/)zàܑׯסÈÃÇÎC²Œš""
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_69_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_69_4.dat new file mode 100755 index 00000000..a73b1144 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_69_4.dat @@ -0,0 +1 @@ +xÚí—;À0CwN÷¿\—ˆ~¤¨;C$3°$<)/mºß¥žeÎ¥T±‘2 —:¥]ÿ^VV¡%ƲzAïnÈpv…s"y‘y‘y)zöá¤dÄu6½àª½Gú¾È‹¼ÈËi^šÂ/<ÊtÓrÂrrÚ)9ͤlõrìó‚.¶‡Ö
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_73_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_73_4.dat new file mode 100755 index 00000000..72f89227 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_73_4.dat @@ -0,0 +1,3 @@ +xÚí˜A +€0ïyMòÿÏé¡´)1Ý-‚lE3š=Ô}¬ˆ(Öô°9ŸTÇdÎÒò–E/eO ZOžîãÛÅ»KíY;ÛúpS5Ð+‘ÄëNžäIžŠžà| +"©eìvúö+DÝ“æNs'Oò¤WŽkîäIž¾ð„ì†Ìž¸¤FúòrÜi9Î&óô«ÿ ‡]?ÃóA
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_77_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_77_4.dat new file mode 100755 index 00000000..993c4860 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_77_4.dat @@ -0,0 +1,2 @@ +xÚí˜Ñ +€0Eßý÷ÿ?×ËjFe8¯0êú uØN©0Õµ‰lB´†6‡:«h·<½õ–§¥CÜe\,¹†ìÔÜ—~ø &rd0
»Sz£7z+ô–êoº»ÓÜþëäÝŽVâuÊ:¥7z£·Wo¿šËÎSÖ)ë”Þèmoù3ë
O‹y;:*h¥Þ>û¿a€ïCe
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_81_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_81_4.dat new file mode 100755 index 00000000..dd652161 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_81_4.dat @@ -0,0 +1,3 @@ +xÚí˜A +€0ïyMòÿÏy‘XEm7"89ä 2’Ѹ ûQ1ݳÌëˆ+¸x‰x;ÅÓt35DIY÷¶1x\:už³ýº•}î¦e/ ê§Æ#ñø¢Th< +‰ùùU¤BzÔË<²×ì5ñˆG<’×ä5{G<⢿®5Äñ<饫>¯]ž×UÄr¿xuÐ
”
Üû
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_85_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_85_4.dat Binary files differnew file mode 100755 index 00000000..c8d5123e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_85_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_89_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_89_4.dat new file mode 100755 index 00000000..5b9bd7ec --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_89_4.dat @@ -0,0 +1,2 @@ +xÚíÙ1 +€0…á½§Iî9«Æ% ÍŠ‡ VÞðÚ€f×r÷ÅÖ0}òzì=¼#9ùìéûÎäÒ•:§þû~›sØó¹¨1BÕg&ë4pÆgœq.p.»í°&g·ÿÂT05Érgú™sgœqÆçrgæ¯Ïuô3çÎ8ãŒóëkÿ.Egm²»b*ÐÍ&›7ÔÉÍÎ?íçúð
¯:Þü
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_93_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_93_4.dat new file mode 100755 index 00000000..be7f5e52 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_93_4.dat @@ -0,0 +1,2 @@ +xÚíÙK +À „á½§Iî¹n$}P‚ÅŒBù]¸‘Né§È@ÍÎáî%sŒfkÒ«¢}CzòoA}aʽ2½|¤îé~ØÀªD&žl=¥êÀÜç¦Yœ®•ÁwÜqÇ}³» q\ÜEéY³˜já®K_âÎyçžÁwÜqÇwz$=’É=ƒ;î¸ãŽû_Ý‹ÿ€>ÜõéŸ+ÑpÓÓöH“öÈéÜ9ï¢DÞi
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_4/mask_97_4.dat b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_97_4.dat Binary files differnew file mode 100755 index 00000000..5d848caa --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_4/mask_97_4.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_101_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_101_5.dat new file mode 100755 index 00000000..c21869e8 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_101_5.dat @@ -0,0 +1,2 @@ +xÚíšÑ +€ Eßýšíÿ®+£%=ÜM¢3Cbvó¸
Ѭ›«ÍNkû¹´gqõkqq{©%ôO¿ÒòüoçžùÈû,i–¨ÅKÖeœeçÁ3[›|‰îiž¶÷‡¾õ˜ã©V¥¥h]`Æ`Æ0¦Ê•±¸zË´TÔ0Gu×ì/qŒ8F®„1ƒ±3¶:WÊò>õ˜´#ŽÇÈ•0c0öQÆ8»øE=F#Ž‘+aÆþÄXÖÍž+cV©%9W>ßíQž]Tk©¹“Y-gL¸÷óîq¬DË艋Ï
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_105_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_105_5.dat Binary files differnew file mode 100755 index 00000000..bc8798c6 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_105_5.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_109_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_109_5.dat Binary files differnew file mode 100755 index 00000000..25a39440 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_109_5.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_113_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_113_5.dat new file mode 100755 index 00000000..25f42b8b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_113_5.dat @@ -0,0 +1,9 @@ +xÚí›Á +ƒ0DïùšÝÿÿ¹^Ò6¥I63[è[EDqÌc˜ÅÑ+•ÏjÃy¤ì81•û\c +·‹7Úc«åªÆõû?uý}DK™4¦}Çï,¥kkûgì--ž3[÷½´U…Æ‚y„UX…UXýSV ³:Ï«ëûÕî,÷è|æýкSúô¾Š¯â«° +«° +«° +«VÉ«¢¼*ÑXÒ[á«ø*¾ +«° +«° +«°zŠUò*ßN÷Vø*¾J€UX…UX…ÕSóèXiýÊjTi4f¹õZkU^Ôè_‡ìê~ŠU‘çxþ}Õ®ñZ/r
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_117_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_117_5.dat new file mode 100755 index 00000000..f236940d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_117_5.dat @@ -0,0 +1 @@ +xÚí›Á„ Dï|Mçÿn/*{ÁM¦ÕÍ+„˜pÐI_&mˆ-äŽèѾžCæõäòÏ32u?o-kgB7wc=¯™‘ªU%yíoÝùRæhÓ¯șDo:Ö¶y¤R£JkQ^a†a†a†ÿáqOgÚiJ¯ýç;úœáqOgÚ)ÓŠãÃø0Ã0Ã0Ãð[¦v×ö>ç=>ŒãÃ0Ã0Ã0ÃOf˜z˜3Ž»=>ŒãÃ0Ã0Ã0ü.†3ÿ÷ïZ£$¯×·8\õp”ÔÃùw4½Çá:ã¨ÖZÀp¤:q¹ÖÄù§X 7
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_121_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_121_5.dat Binary files differnew file mode 100755 index 00000000..9bb5c415 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_121_5.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_125_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_125_5.dat new file mode 100755 index 00000000..2161c50a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_125_5.dat @@ -0,0 +1,2 @@ +xÚí›A +Ä E÷ž&¹ÿåfc;S´¶$?‹‹”ŠØQ4ëáYaßhûyJ}9ºg=×Ú=©liŸ´.;nh_ÍÔýïwzýží.÷qôƒCW”¶üÏȧyº×
uëÞë’PkÏ;ÌÃ<ÌÃ<ÌÃ<ÌÃ|*óqëùÎü,‡
m“kWæqçNÊl—%ª
ŸÇçñy˜‡y˜‡y˜‡y˜‡ù×Ì×^Ïæ2ä°…sX|ŸÇçaæaæaæaæ3Ï™9lH‹Ïãóø<ÌÃ<ÌÃ<ÌÃ<Ì¿›÷웿Ì[í¢õüên`ì™TíÚûqж8ó¡^—vôy©öŽ«
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_129_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_129_5.dat Binary files differnew file mode 100755 index 00000000..f0c1d650 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_129_5.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_133_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_133_5.dat new file mode 100755 index 00000000..46be8b09 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_133_5.dat @@ -0,0 +1,2 @@ +xÚíÜA +Ã0DѽOcÝÿr]4%1mCTåÉÆ¯„Ð÷xΜ[DvÌ=Æá{Fúú–E¤Ïµˆäq½?Ý¿Ü9ûkeÑù}±'2^ý•cœçš4G”žÛ:¶3=J£K-šôF0‚Œ`#ÿÍHÒþˆwð'#ß<{Ú~—Z4é:BG舻F0‚Œ`„áGø~„ŽÐ:‚Œ`#Á?Âð#t„ŽÐw-Œ`#Á?Âð#ü¡#t#ÁF0r·…ïÙ}Qõš}e¤üÝòR‹Ú;ø<ùçCžéV‹}Ñ—‘ìó¢ËÙY-Hž
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_137_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_137_5.dat new file mode 100755 index 00000000..064e7f2f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_137_5.dat @@ -0,0 +1,3 @@ +xÚíÜÑ +ƒ0…áû>Móþ/·ÇkÅ1mÂøªÈXº³ü=‰–ö¾µÈhýÕÚîså×áH"ãk ÇM„:îúê–3qÒOü¿W}ÿ9’Ô–¬I”‰“ýHž1ÙG;þ-Ï©sìîÚ¶ù?Ò[%M +Å v°ƒìü#;£zg^ÝÔ3Õd}Î6‹“Å9ÛÆÎ¨Þ™×@7õ”Ò„ïð¾#gÃv°ƒì`;׳£Þ©\ï$ÔÅåŸð¾ÃwälØÁv°ƒì`çvÔ;ÞÖ}7Êwøß‘³a;ØÁv°ƒkAµ”ïð¾#gÃv°ƒì`ç=N2wúxg§WÓ¤@n?Ûëc}½SQ“:ûZd?+¨ÏÎú9vÆÎzß)£ÉæÝáP
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_141_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_141_5.dat Binary files differnew file mode 100755 index 00000000..60c1a8e8 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_141_5.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_145_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_145_5.dat Binary files differnew file mode 100755 index 00000000..9303c07f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_145_5.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_149_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_149_5.dat new file mode 100755 index 00000000..4256cefd --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_149_5.dat @@ -0,0 +1,3 @@ +xÚíÜ[ +Â0Ðÿ®&³ÿÍùcµ }-s+'Їœ^;AÇxŽêã=¦ÅëQÏ_gUÝﵪæãxÆÞGûõTí½ëȺV¹ïúœU̪UE®«å¬æõ_IÇ´;ÿŽÇT1×öÅóôÌ Š©µ +]W2È ƒ2Èà¶|oß5ØuóꆬuI¶:(WKU躒ƒrPÊAdAdA;
vÞ®íÑoíÝ_zNOæÏ{2rPÊA9È ƒ2È ƒ2Èà9íÑß}^O挞Œ”ƒrÐwQdAdAýn½žŒßMÈA9(dAdA¯^W󄲎äZ…Ý.+•³GŸ^«ÌÿëKêÉÜÑ`×µ}Ë`_FÖê÷k
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_153_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_153_5.dat new file mode 100755 index 00000000..deea09d7 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_153_5.dat @@ -0,0 +1,2 @@ +xÚíÜÑ +‚@Ðw¿fæÿ®—¬¨Ð¬`ïÖÙD"I¶ÁÃeê<:aÔu,7ï«öOfÖÛ³šuÀëPÍ6Žî~ñís¾zÕÁÏûãsÞÏ,jÄÕ¬c¯³ëÌVöZvߨmé°¸ì—s^uÜH®YðuÆ&›l²É&›l²É&›_±9¤¯±ÚÜê
;]³Ä^Ðjs«O;ì˜Ü”›rSn²É&›l²É&›l²9½Íy×›ƒÖèzAÖ’›rSnÊM6Ùd“M6Ùd“M6§µéÞƒ»÷@/è±$7å¦Ü”›l²É&›l²É&›lzŽzAžC‘›rSnÊM6Ùd“M6Ùd“ÍOlîþ7á°šUìu¶NªãÖ›»F¯Úcú·óÌêÍP³P›—œSÔ,l;HûO§
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_157_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_157_5.dat new file mode 100755 index 00000000..176e2a69 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_157_5.dat @@ -0,0 +1 @@ +xÚíØ1ƒ0DÑÞ§ñÞÿriœ˜( r*¥· „\™Õ~>Cï£*¥ú¬v¹ïsýØ]Åœ_{W!Çzﶬ/ÏÝÞõ÷ÝÅÕ)½Ë™»¹Áöä¤vÿ›ÏV‘®×6ÜV‘•Þ»ð¹Ã,f1‹YÌb³˜ÊÌn^o¯¿>¿þí\Oï]øÜñ,Ïò,Ïb³˜Å,f1‹YyVž•gåYžåYžåYÌb³˜Å,f1+Ïʳò¬<˳<˳¾1‹YÌb³˜Å¬<+Ïʳò,Ïò,Ïb³˜Å,f1‹YyVž•gåYžåYžåYÌb³˜Å,fÿïÝukys77—Èì}ïvçŠÉæmbž=¥wÙsw³)ï»tWÖ†Ó:®
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_161_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_161_5.dat new file mode 100755 index 00000000..70d5fb00 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_161_5.dat @@ -0,0 +1,2 @@ +xÚíÜI +ƒ@н§éºÿå²ÑDp@
èo|m ˜¢ßrHk㨤Ñ~c˜½o³^ÝÃJz×°‚–#5l»¿î¦S_92Ûî©“[¯}¾ÜÃÈZÊŸ‡ó=œìTê2ìÿÆ€×P‘™2[cVì衆ÌC–Yf™å'-‡ÛìXÎ9>üv§Ë~u»“½sK5ì`Êe¹,—å2Ë,³Ì2Ë,³Ì2Ëç-ë—ßÐ//¯/o_q¾}KÇç¾ä²\–Ër™e–Yf™e–Yf™å³–õËoè—Ý+òÿ¹/¹,—å²\f™e–Yf™e–YfÙ³úe÷Šx¶B.Ëe¹Ì2Ë,³Ì2Ë,³Ì²g+ôËîñl…\–ËrÙ16Ë,³Ì2Ë,³üì<œv.×r륆Á½Þ¼‚™ýrO5ÌÿŸöÔs_ýZÎÉ”uËI¹_ÃìÞ*Ÿ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_165_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_165_5.dat Binary files differnew file mode 100755 index 00000000..94af813d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_165_5.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_169_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_169_5.dat new file mode 100755 index 00000000..921a7707 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_169_5.dat @@ -0,0 +1 @@ +xÚíÛÑj„0Ð÷|Íäÿ®/¶niµ¢K¹£'ŠÈÊ.rö:ÁT-m&¶ZÛx9¯yüq§3rÿµOgàv¼OO_zׯ§u`]ÿüÈ·¾ßitëØ§™ãt½Ùñil¦oc¿‚ö1ã3j9Ž%_g|ëÔ§Æ)ûì³Ï>ûì³Ï>ûϰ¿=×teÌ&õþ_ã4¤Þÿ²¿=×t¥UŸÊ}¹/÷å>ûì³Ï>ûì³Ï>ûO´¯Þr½5/u“¹>¹/÷å¾ÜgŸ}öÙgŸ}öÙgÿ®öÕûO®÷½Ûsv®OîË}¹/÷ÙgŸ}öÙgŸ}öÙ·–G½o-w{¬å‘ûr_î{ægŸ}öÙgŸ}öÙg_½¯Þ÷nµ<r_îË}Ïüì³Ï>ûì³Ïþ=ì×n]˜4Nk§Öβ_Êðÿ÷«M½¿ž¦×ûû´Ï8ím?ñÿ´SF…·<·
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_173_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_173_5.dat new file mode 100755 index 00000000..f9a67413 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_173_5.dat @@ -0,0 +1,4 @@ +xÚíÛ[ +ƒ0Ðÿ¬&³ÿÍõG1Ðø¢gäD)[°Cz¼±÷eDÖѷц÷=R¾NÏ6²îóÚFÒíJmÞÏJq÷Pœ}ëxÞöséîÑ_¾õ}¶éGÚF©y;žíú¿‹ +[;ý]™öé¯eÃk[®ÅQbT«m±yË&0 L`ÂÌ„Y?á¨ÇðرÝÚæ¼wØ›· ïfý„£ÃcÇÊÕVNä9Á½˜À&0 L`˜×„Zý„¤}0=F=F9ANä÷L`˜À&0 L`Bz¬Y²fIñß=F9ANä&0 L`˜À&0 ž‹´fIÑs‘r‚œ 'È L`˜À&0 L`<iÍ’£ç"å9AN˜À&0 L`˜ðVÖaB¯XÛ"ý„±²ù×,Um…>Øö± +=Æw˜óZ¶gBΜPª¶”!8
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_177_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_177_5.dat new file mode 100755 index 00000000..b07c636b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_177_5.dat @@ -0,0 +1,11 @@ +xÚíÝÑŠÂ0Ð÷~ÍÌÿÿܾ؈šŠÎuO,"%
ñô:$Xui=¹ÕѶ«×ÕƒŸïô¸g÷Ƹ?Îq.úÕSo~æz׉¹õWï:ó™·=Žh1cÜqóøèñþ]ì”Ƕ¼îiÇÖ!÷¼ßçírß8Æó˜¬`+XÁ +V°‚ õеÓê»j›Ïjž;—8ÆóX®+ä +¹ÂoV°‚¬`+XÁ +V°â?[1µ^ñh-Ö³5Z;§¶©¶ùrmS®+ä +¹ÂoV°‚¬`+XÁ +V°â;¬°ËZ,µÍYµM¹B®+ä +V°‚¬`+XÁ +V°‚ö™Z‹eŸ©Ú¦}¦r…\!WȬ`+XÁ +V°‚¬`+ì3µKmÓ>S¹B®+ä +V°‚¬`+XÁ +V°¢cþò㊛Ç{g;¦^Qq5¡ZU»ÆÕÝ®ûŸQÛLã0+*&YDqÈñéô*6
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_21_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_21_5.dat Binary files differnew file mode 100755 index 00000000..04f97ea6 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_21_5.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_25_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_25_5.dat new file mode 100755 index 00000000..c20b59b1 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_25_5.dat @@ -0,0 +1,2 @@ +xÚ‘a +@!ƒÿï4îþ—޳ʢš™
?,"ÅÔÌ"j½¦¡Ž?n<¶OÕÛäa
¬w,l}rG‹M;ϦÏ9[ží¤¢_ú±x|ŸÊÖ=´l4lK¨ýv½
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_29_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_29_5.dat new file mode 100755 index 00000000..217ec1b8 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_29_5.dat @@ -0,0 +1,2 @@ +xÚÕ’] +À ƒß=M¿û_n0W .«Ž=-ÁŸ4¡mÄy×B€ìÓ+²‰·ÜR×á“çôˆ‹†$¼Æƒ.=s/,+îB÷7žó³q®zÄ~§q>=GéÙêŽZùyÎ:—ÜDRŸ«
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_33_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_33_5.dat Binary files differnew file mode 100755 index 00000000..726d7fd7 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_33_5.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_37_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_37_5.dat Binary files differnew file mode 100755 index 00000000..6d32ca6f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_37_5.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_41_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_41_5.dat new file mode 100755 index 00000000..e07c6172 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_41_5.dat @@ -0,0 +1,2 @@ +xÚíTA +À »÷5Íÿ?7˜«ÈXMtxÒ´x©¤ …¸?@‚˜›7@ò¾~"éN$õ‡SÖɰÄ{ø+C³¨ÛA'Êör\PŒpè<Þ÷-¼ ͺ:S3s¹Ô‰ùÛ»ËÞ©Îz#žóqw™ó› >þ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_45_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_45_5.dat new file mode 100755 index 00000000..5168a17f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_45_5.dat @@ -0,0 +1 @@ +xÚíUA€ »ï5íÿ?ç„U:N&Z"†”:;4P1=ƒbNvSGÆM1¶ÜË›½n<ëv`q³¤{ìîßMg§ã¶4þå=Gó-T¹‹º?›“Ôß='kíÈu™Ûž“õ>‘ëß'¯®çþÜ(éκŽö Jô{EßѵsÊ]È ,§sqð
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_49_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_49_5.dat Binary files differnew file mode 100755 index 00000000..9f3f3cd7 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_49_5.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_53_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_53_5.dat new file mode 100755 index 00000000..449807ba --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_53_5.dat @@ -0,0 +1 @@ +xÚíVAÀ »óúÿÏíâ"ËzYf5ƘJC ª
˜A;ÄÜáé¾Âlû\˜,†ëdòR.¯\(åe_Ýú ³æôеaNi5†ª\żކÿÐÐë‡aLPòò(¯ÐàÒ;×ý±2שÃãå¯jÈN6O
u…+é¯Ñl{y•«6odúá^
ãÚàçC[‡%®„
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_57_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_57_5.dat new file mode 100755 index 00000000..c7dd81f3 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_57_5.dat @@ -0,0 +1,2 @@ +xÚíVA +À »÷5Éÿ?·‹NÝlªƒZ‹HAbB‰Z0aÓìÎMÆÒÃd`1Ýz”'"<Õ1™æ9nvͨ.ãô)bÝ»µ~¤;˜<KÛÒö‡Ú¾x_×Ö÷ÛEu3ã·][ÿ/[T«oËJÛϵU羉4Ïçévå HûPnÖäë|»òÛ˜Im!wîL1/8,gã
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_61_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_61_5.dat new file mode 100755 index 00000000..dee749fb --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_61_5.dat @@ -0,0 +1 @@ +xÚí—Q€ Cÿwšíþ—óCÁÕ–ÏB‰¾´.Ý›•wÙpí¯¯»žKv€g;ÌÞû|3:ÅÆ}÷Ø“c0þɴ jÖj7(©lÑwe^™WæJæÉ¾…ÐSïÌwßÐt_e‹¾«Î«·Tæ•ùŠMœÏßOçsæô9ubs=Õ?þ‰ò~~Šù>Ÿ9ú¾ÕZ#tõBÁ~
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_65_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_65_5.dat Binary files differnew file mode 100755 index 00000000..ecd93806 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_65_5.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_69_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_69_5.dat Binary files differnew file mode 100755 index 00000000..ead4edc1 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_69_5.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_73_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_73_5.dat Binary files differnew file mode 100755 index 00000000..00001176 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_73_5.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_77_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_77_5.dat new file mode 100755 index 00000000..1652cdc2 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_77_5.dat @@ -0,0 +1 @@ +xÚí˜Q€ Cÿwšõþ—ó#&C`T£±ÃÃ6¼”ƹ—B¶ü(«Þ©9Ü
Ù'Ö†äÑÖìƒÚ¢ÓÀÀzk·å"hõÜêÝv.`»øâücXöB5[ñ(ÅÖF>71Ó/3ò4ÎØÎªz÷^'ÑÔÝ[˜Fyglg•®M>•OÅTLÅ4Ï”{÷&3ëWy*ŸÊ§b*¦ï`ºÚ<3õ;´‘îÞVo0÷/s—6nÿ‘§Ï0Íya½ß[û”ªmácE
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_81_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_81_5.dat new file mode 100755 index 00000000..71215e95 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_81_5.dat @@ -0,0 +1,3 @@ +xÚí˜Á +À Cï~Móÿ?·Ãtz°U4½ŒÔ"£ }tšÕÀMX2|.îÉ‹¸Ë™F\œžð¸õÑÂ~mõ‘4 ÷±¿ØXu +Ù”, ÍÌw—:— E†Æ„>еXÿõ̯=_Ö]g>±>óÂÆzæ×ž‡/ë)5ךk±k±kùµüZs¸X‹µX¿¬Y{Ü®Ñè}ô¶áç~mt¿æîšìÃÏý:S#™µÑ&;U#)ñÚä
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_85_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_85_5.dat Binary files differnew file mode 100755 index 00000000..09cf0e28 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_85_5.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_89_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_89_5.dat new file mode 100755 index 00000000..5fff5306 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_89_5.dat @@ -0,0 +1,2 @@ +xÚí™á +à „ÿû4Éû¿Üþ¸5eÆÙå®npQ¤´ Gú™cÖÍf‡µðl^^‡;;bŽ5;`¬k¶©¶U͹߮¾ßj`ÍNósÜùùO=Ú\[a6‡žÁ°¶~ÞnLÍD?‹
±!6ÄÆ°ñuüÈF%w¿*Ȭ²ÌkÎù¦f¢Ÿ77”SĆØbƒÃ†êXodw_ùØÂ—mµ¨â†â†ØbClüÈNÛ™
ck&äîY¯VoÜ¡™×·B×¢»Ø¨œAl6Æ
šæJjÜx
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_93_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_93_5.dat new file mode 100755 index 00000000..ec4240bd --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_93_5.dat @@ -0,0 +1,2 @@ +xÚí™K +Ã0D÷>æþ—ë&©»ð¯µ&
áÉÆfP^”ÁŠ8BY5ÊÇs(imœ®¼ÙÒ®¤±®=f—µ÷3¨/wÛ§§‡E»¬y¯§ŸïYŽQ¦wf‘á[}¯å¨
²„[»9ï0303ÏefÇÏÌ™Ùñ3'3=<òÆK{níæ¼Sg¨3ü›`f`f`æ¿Ìdø™ÞÞè®oiü£¦ÎPgø7ÁÌÀÌ܇GµjkÞG]í}?V/æë
kÒ×Þ÷ÀWi72–Js™vÃ|*"f^
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_5/mask_97_5.dat b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_97_5.dat new file mode 100755 index 00000000..509d1174 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_5/mask_97_5.dat @@ -0,0 +1 @@ +xÚíšA„ E÷œ¦½ÿåf!3bf5í¯Æ¼Bˆ‘ùé£|#f=<3l6<›§Ž_+xjÿ©ÁÛ)
“ÙÝ©Ãy°“ïÿ¯ ‰J
¹yXiïÜ»ª5¤IzsÙžîcëuÄeQ¡¡ °K°K°K·d)â—>,ͼwx®DƒÖ·.,;ãÂsÔ%êg,Á,Á,=€¥ëýRÐóὓ¼7u‰ºÄK°K°ôT–D÷†<(n lY’þŸÞhÐùVÛ¹s÷Þ•ôy¸Œ¥´ýP±§Eñ<¸öq
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_101_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_101_6.dat new file mode 100755 index 00000000..13f97a0f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_101_6.dat @@ -0,0 +1,2 @@ +xÚíšk +„0„ÿç4Éý/·Ën_.‰Ê2#ˆS‹H”6±_“PâÞZ ›Ïf˳_¡îé,ï7ØžÛà+·%×åWÇÂîd„–âÏnå}–“_&«Ok;Œ×úP¾Çfµ>³t‚yY×¾]Öns
°;s·æs‚Ú,º!Lk¬ÁÔ…cbLŒ‰12cÔX9Ëó1 ÔÀZç#XÐò±En#;ÏsvŒT~L~L±RŒ‰11vsÆ.‹•”³‹1‚ò±¿ò1ù1ù1ÅJ1&ÆÄØÓÙŃó1ù1ù1ÅJ1&ƞī²gËØKLƪjl¬¬k{g¼Úž5óKò1/ëÇù¯~,aŒ»ù‰õc««ö$“Þ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_105_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_105_6.dat new file mode 100755 index 00000000..a58fec74 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_105_6.dat @@ -0,0 +1,3 @@ +xÚíšQ +ƒ@Dÿsšäþ—k¡®ÙBÒZ»#o)Sd}ÌGÜ·Šå¹lú쯯Ö^‹žúú³®)G]Sñ4ÝS×ëšÞÞñ¸ó?½#BºZö:Õã+{þñÅ×s§ÁH¨KþNóiIŽÂ!¦«mþòe1 +RWe9!ÿ¡`ö`ï¶ìUy¯Ë€§uë:檞»ëÖåšU=w×-ûo•ÔºwB·}cMÞKßÃ÷è¹°{°{ä=òyßÃ÷è¹°{°{ä=òyßÃ÷è¹°{°wSö¤caoìi'Ýföºé½õyO=C•yO=C•y¯›ß[Ÿ÷Äó{SÞ»œ=—;Ÿ|v4}ϯ2‰¨¸0§
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_109_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_109_6.dat new file mode 100755 index 00000000..be7b4749 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_109_6.dat @@ -0,0 +1 @@ +xÚíšAÂ0ï~ýÿÏDBHÓC½HãV2‡Æ0Ùn¥uoªò£løì¯¯²¯Ë»=Û¢s9[ˆŽõl'«ºÝ?™íë7ÏíR"¯&“§Û2™ÕßÏ:7QqX_•n»
§û]£$ÓÕš–EIYôÁ*¦³ÆL”üq0 “0 “0ù{LJýäз(s\ɳwœô¾‰¦X÷-Ó7^öÑIt„I˜„I˜„Iü$~?‰N¢“0 “0 “0‰ŸÄOâ'ÑIt’g7LÂ$Lþ-“›„¡€Iuzrfr
M÷“^ê'}“éÍ÷“•ùÉ(ÉO~RŸé]1Y²ãLÄžuÒå9ó¡QÍÓ•
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_113_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_113_6.dat new file mode 100755 index 00000000..397f5274 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_113_6.dat @@ -0,0 +1,3 @@ +xÚí›Ñ Eßû5ôÿN£°¤Õ‰ëõÁÃÈbF6¨on´¶¯,m›>·gSÅ9ìñÞRWã½ðˆcŒÇ•9Œ&»%‰1ùöÏ_÷Þcx= GR^°šw-z?Ãêdzõvî=î,¹ê°}Ô¥?ǹژ³:Å9œmÓ=—ó=@U”¶ä²‰„UX…UXýVe ³ûÕÜÅ~Õby4WúÕéiæ¿:Ýe=ó‰ó¡<SZmAWÑUtVaVaVaõZVñ«5~µüÿÕþ´åVè*ºŠ®Â*¬Â*¬Â*¬^Å*~•wk¹ºŠ®¢«° +«° +«°ZϪb¥õ‘ÕayT¬f«ëüj¾ÖºÊ¯jÖZÏ~µÉß´t_@Un¥Ù°ªÑ¾€YWsV‹ê
¶[|H
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_117_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_117_6.dat new file mode 100755 index 00000000..99108bef --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_117_6.dat @@ -0,0 +1 @@ +xÚí›a Fÿ÷4íý/§‰#EœŒv3¾nŸŒŽ·/m ª›Y´©›T¿õÑwm<õ~?ölûjÁGÛ×îøšC×qSÿ/¯¾6f£tÝŸ¥oï¿Ó”f^ûl'ó7`ÁŸZø²ÌCÊ Â?×úŸ¥heª®²i¤¥šXq4Ó[Ù€´Ô‰…a†a†aþ†û9]P‹ÌÅñ³YžÍìéM<6îçaË[äð¸gÞ:Œ£Ã0Ã0Ã0×g˜x8:ÎYã¨z;7§C‡Ñat†a†a†aøº³Æ±,§C‡Ñat†a†a†‹á´íþOçU6Ô«8¢âáÌ=ðgî÷xx\ǵƑXÇQåt§2¬©JœZ‹ä:¬gˆÓ
C+\
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_121_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_121_6.dat Binary files differnew file mode 100755 index 00000000..f3c32994 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_121_6.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_125_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_125_6.dat new file mode 100755 index 00000000..ff64d44f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_125_6.dat @@ -0,0 +1 @@ +xÚí›A„0E÷œ¦Üÿr3™±¶&H
°yØb+öùSlhí0Ͳ6L¦ëöwEŸÍÑ¿žœfÇ®I‡»÷¤vî;»íó&vwž,oýÅ]ué¶dþ]|½k棘øÞ9ÔŠCú“¦}îs“1»9Jw9Ë¡½Zb¢=ðŠèå€PK&æaæaæaæa>€ù’õüɼ—Æú$<>¯OÑôvòÊù'ÅûËåCçÑytæaæaæaæa~›ùòõ|êžÔÙ'9li‹Î£óè<ÌÃ<ÌÃ<ÌÃ<ÌÃüóìI‘æç°è<:ÎÃ<ÌÃ<ÌÃ<ÌÃüóÙ•WæÇ²¸‚y¿:.z=¿ª
ŒÝ“ʯ
œsØVšÃ¶E=ll›_k0_#võ°³Î¯˜mÝ÷›j
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_129_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_129_6.dat Binary files differnew file mode 100755 index 00000000..b4695c3f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_129_6.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_133_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_133_6.dat Binary files differnew file mode 100755 index 00000000..40911dc5 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_133_6.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_137_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_137_6.dat new file mode 100755 index 00000000..43ccb68c --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_137_6.dat @@ -0,0 +1,2 @@ +xÚíœÑƒ Eßû5ðÿ?·e…¥ÕÍÍ^²4fHåp[1-e)UQÊV¬û]žU™W÷Nî5ù§o“*8|›Œ÷Û©ìW¹£š6žbk?Ý÷ÿ{‹f¬´„ì|>¾sÖª‡ìür6öö¸“6‡«ú°6ÉrÒŸ¶=ûüv¸Ú²þWy±Ú¢¶Š-¸Ê' +ìÀìÀΟ²ãÅ;qtQE>U–϶öf‘¯•ê³ìxñN]TcÑ(sâ®7tÝAwðÙ`v`v`v`çkvˆwfŽwtû;]«ùÞ ;躃Ï;°;°;°;çÙ!Þ™;Þaoôå]ºƒî ;øl°;°;°;°Ã· |Ê· 躃îà³ÁìÀìÀìŒì(3}Œìl.š?«…"Þ‰r}äÇ;Ú\}¼S¦Ù-AžœüwÚ<9;úEVœ'§×ˆôó¡ð
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_141_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_141_6.dat new file mode 100755 index 00000000..0340409a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_141_6.dat @@ -0,0 +1,10 @@ +xÚíœa Fÿ÷4íý/§‰c Òêâ¤]òزȅ¥ª[²¬¤=Éð[E럓ÖÜósî¹m,éšÛfÒn/ß|kj\ä¿òj§?g½ÝþòÈ[qþû(NO˜Zc5ûŠ©SGGP[oM×Vá’Ö¸´éf¼¥÷vÖL<<eÓ ++‘Äša*XG6¬ÄÀ)˜‚)˜‚)˜‚©cLåúS½6±tj¨Mö]o÷ÀßW8¿D~ñO·šD£`•?µ×†N¡Sèk?˜‚)˜‚)˜‚)˜*ÉþÔUü)ïÌ×?þSIÁ= +t +B§XûÁLÁLÁLÁT5¦ð§®âOqæîQ Sè:ÅÚ¦` +¦` +¦` +¦ø†Š3_¾¡E§Ð)t +¦` +¦` +¦`êâL¥‡¿yb*?2ÐÈT5i½?U!ÆK÷§*ÄxéþT7iý™o¸IÃEI¦´„R•ˆEÖuJ+MÆ7ºÄ'
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_145_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_145_6.dat Binary files differnew file mode 100755 index 00000000..6c142151 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_145_6.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_149_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_149_6.dat new file mode 100755 index 00000000..69e98835 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_149_6.dat @@ -0,0 +1,2 @@ +xÚíœÑ +ƒ0Eßó5ÍÿÿÜÆfµƒ47ÍÝ8UD“6Ëñ’ÓÚ2¼z´mØpßž¦ªk8«»¥öŒ}åÅGì«ÉôÓ•ÅëN}?}æÄW“:ƒÈÚguàWA´ËŒ7~Û;)ƒÇâãØ{̪ǿ+ÖWPþºOÛ¢¤öÍþrµEƒ\j˜wG)yË–àw©À‚A„A„A„AM%òÁá™æ2ùà`µµ2«’d”³l–¯àÚ|p}¦eqP’¢ƒè :ˆÂ    Âàej想=úlïþT5™©É ƒè :ˆÂ    ÂੲGÿë{ôÔdö×dÐAtDaaaaaï&Ø£§&Ãwè :ˆÂ    Ö1¨ÒÙð•Á-MRb0éÖW˜f½
ëöèuzŽ5™&Y“iIѺšŒNÑ€A—»PÑQS«ÎþL}4
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_153_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_153_6.dat Binary files differnew file mode 100755 index 00000000..3ab6130e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_153_6.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_157_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_157_6.dat new file mode 100755 index 00000000..b45c0cee --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_157_6.dat @@ -0,0 +1 @@ +xÚíÝA‚@DÑ}Ÿfæþ—ÓDÐN%¾Ón çü,ÚÖ–ÕSVÛVí>·ÇW³Ã³»—CöaïzÈ6îÝÉUü¼~Ò»·{`ný•ЏõÙÉÝ»dvVýy~µrÒ·Z¯"çq·Ûk{>g$ÅáXK¶õÈU}m\b÷ja¤GÞx˜Å,f1‹YÌbö¿™òÙ]½z¬‘Ìž½ƒš^¯.ë5Ó[?Ö嬜•³r³˜Å,f1‹YÌòY>Ëgù¬œ•³rVÎb³˜Å,f1‹Y>Ëgù,Ÿ•³rVÎúmŒYÌb³˜Å,fù,Ÿå³|VÎÊY9‹YÌb³˜Å,fù,Ÿå³|VÎÊY9+g1‹YÌb³˜ýŠÙ‹ ½Ì¦M7>2{9zºÏ¶hŸm3Éçûlò|ã9ßxóÙ¼™ä#f#Ÿx•6 ÿ˜³-îvë%…N'
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_161_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_161_6.dat Binary files differnew file mode 100755 index 00000000..ecec68b1 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_161_6.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_165_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_165_6.dat Binary files differnew file mode 100755 index 00000000..d641dfa3 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_165_6.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_169_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_169_6.dat new file mode 100755 index 00000000..ae689723 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_169_6.dat @@ -0,0 +1 @@ +xÚíÝáJÃ0Ðÿ÷i’÷9[ÛJÓɘók{²1Äb˜!gßnhHkSë‰-V?·¯KI¯»ïôó×ÏÝ1íý1Ýû§Æ£ÝŽ/½¨·Á˜ŽæÉ`þê/zû)*º=°Ÿ3Úì¿g6õöý^k6ÖÓ5CæÇéêYË<ÉK¨ÍkMùÚã[õy@ÓGµ&_=~¢²Ï>ûì³Ï>ûì³ûãµ¾ +õþõ'{«„Õ§_õVËŠÚx-âJýËø<ӛܗûr_î³Ï>ûì³Ï>ûì³iûêý;×ûù÷öœrOîË}¹/÷ÙgŸ}öÙgŸ}öÙ¿¦}õþ½ë}÷ö¼xOîË}¹/÷ÙgŸ}öÙgŸ}öÙ·—G½o/{{ìå‘ûr_îûÎÏ>ûì³Ï>ûì³o/zß^÷öØË#÷å¾Ü÷Ÿ}öÙgŸ}öÙ¿†ý£d²ì'ŸÊ³µ|ÔQR½ßNSï·Ã3¹’êý³œËÓãÏåYÖú²ÏäÚ³ÿ‰ZÉ'Çms¿EŸÇ·j›5ø
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_173_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_173_6.dat new file mode 100755 index 00000000..95fa97c7 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_173_6.dat @@ -0,0 +1 @@ +xÚíÝaŠÂ0Ðÿ9MrÿËí¢M SÜ*:a_-²5hh_¿)©uÛZÖ[éþ®÷¦lïÓoûÛ’sŸmKúšíø›NÇ{òÿãHÌ?xÏ`l‡#fï9>’ÚŸ[¯õeé·Ð„ÏŽß“¡ ï?þ^ìóöm÷ó®ð*û/KËm¿—ãhËy%ÞËv-nKl¥í»Âè–í¤kK¸L`˜À&&Ìê g5†¯µ•(·gºwxôY¢ÜžîÞáa¬žpVcøZ[‰Æ#O=¡ëSNä9Á½˜À&0 L`˜Ç„¥ê ¹ç'tjŒjŒ]QNä9Á½˜À&0 L`˜Ás–ÌYRcü@QNä9 L`˜À&0 L`‚ç"ÍYRcô\¤œ 'È r˜À&0 L`˜ÀÏE𳤯è¹H9ANä&0 L`˜À&ü²¯7špÜ6¯`Â|õ²¬õ„hm¸œs–ò¯
××ëR5Ƭ™³Æ˜½È‰ k\ÌX/²Ï ‘ )÷g9È
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_177_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_177_6.dat new file mode 100755 index 00000000..e9f0476f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_177_6.dat @@ -0,0 +1,14 @@ +xÚíÝÑn„ …á{žÞÿåÚ¤*4vÛ´u¦û¡1‰£²ø{<LGi‘Kí¥¿ëGUÌíäŠß÷Ç]çmÜ/ó6žÜÛjÿõ?mZ¤müzÌy/úξgÍjÏÿ\ÌÕ“—¢<`Å_¶ã>f{Š¿Ð¿³_ñù,¶,K9o 4ŽÇµôù7lËñÞniJiggiår<¤-MGÆ +¬À +¬À +¬À +¬xuVÄ÷+zÌÒRøCÌr9‡+Gqç6ÞQWžñb"ø×Qe×ï"ùWLº‚® +è +ß XXXXX/ÁŠ|~Åj,ÖnŒÖmu¼MÞæÚÛ¤+è +º‚®ð
‚XXXXXñ¯Ya,–±X¼Í;¼Mº‚® +è +ß XXXXX晋ež)oÓ<Sº‚® +è +¬À +¬À +¬À +¬À +¬À +¬0ÏÔX,Þ¦y¦t]AWÐXXXXX;V¤Hù‰92mެØe3éWdÉQØÇbeÉQؽÍ]>Ó˜Þf’|¦ƒ·™–5²H“û¸ëŠší¥÷7/D
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_21_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_21_6.dat new file mode 100755 index 00000000..6bd505b4 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_21_6.dat @@ -0,0 +1 @@ +xÚQÀ Cÿ9M{ÿËi]èÒýX‰1¤À Å-½C!»É³Dìû7
WìÙœ§Ø&rDñ)~Î]<MÎ ·3(>{ƒïA¡«ÿíŽa²¼Sý
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_25_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_25_6.dat new file mode 100755 index 00000000..d45083aa --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_25_6.dat @@ -0,0 +1 @@ +xÚQAÀ »÷5öÿŸÛ2)êe+Ä(XmÊZtÆ*(õÚ¹«;ÃçtJã<峂†_Ú¤‡3°oŠÜ½Ú´"Ì¢a²zh}Ñ&qv€µSGÊÖ™,ó-÷‡™J›Í4}³™¦oS[âü}w
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_29_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_29_6.dat new file mode 100755 index 00000000..0408e224 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_29_6.dat @@ -0,0 +1,3 @@ +xÚÕRA +À0ºûšäÿŸÛXcÔõ²ÓL(4E”ÔˆB +8CÖܾ޳nÃM©+lǪÃÕ†Ožé1]&•Ú¥4UëD-6-$:6ÊdZá?yæÛlôÝf£?í˜
åŽ8?žß²±<Ûlôž}ž¹g›göí *
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_33_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_33_6.dat Binary files differnew file mode 100755 index 00000000..8de4ba5c --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_33_6.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_37_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_37_6.dat new file mode 100755 index 00000000..b37ff0ab --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_37_6.dat @@ -0,0 +1 @@ +xÚí”AÀ &ýÿçÚT `½ôèj<PÜ0¡¢jB#ÿ&š´âù,Yo´îê…fU¯Ùjó*UõÕú•ÑYÀ–¬[ƒ•oY5Œ~Š5T7bnb, …]ÆË˜ûñü‚³¦‘ƒ-‹¹Ñ÷ñÃÌqF:s¢tæDcèØhÙ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_41_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_41_6.dat Binary files differnew file mode 100755 index 00000000..c1535f78 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_41_6.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_45_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_45_6.dat Binary files differnew file mode 100755 index 00000000..a7da7ee0 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_45_6.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_49_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_49_6.dat new file mode 100755 index 00000000..64ded709 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_49_6.dat @@ -0,0 +1,2 @@ +xÚíVQÅ ûïiàþ—{K&·ÂêÇûYX¤³ØÙ0ï̦!=ÛýŠtÇån&ÅðfpŒWL +±`¤/¯<Ï“KVrU¢\1öbGpáê@ÄìÓ‘&fNëŒ/+ƽqÙà ¢`ðàòAWÿçêSƒÉßRGpõþ_5|Æ|zÀ×xν:¸¢ªåJ©‚+WE©m4hÛ4Ú3t܃.×Á©Ag`\ÉÒù¬÷ʤþ*ÙÅFÈô
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_53_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_53_6.dat Binary files differnew file mode 100755 index 00000000..9139e325 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_53_6.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_57_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_57_6.dat new file mode 100755 index 00000000..61e7e242 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_57_6.dat @@ -0,0 +1,2 @@ +xÚíWÑà |çkàÿ®K¦ÂÜQ¹-Ë^@cª"Wr^U‡YÅÔM³>_Ý`åÃî8¦Ž¹€GÁ1“œ`–B”Ý`;»ù+ØÂì}åÄÌØ&s£ÒçŒ]<§J…QF=mb3 UŒF'ÚØ6¶Æ–á„E âü€¹5õJa÷wZØÖù60¢¬ +ß[¹·ë¶9¡±ý9¶”ì{Á–SšÛ\Ýž8Õ`k&Áœos}{â[Rß¾ý[¥+—þwðºÕO‹èd^jW
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_61_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_61_6.dat new file mode 100755 index 00000000..f2d3f10d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_61_6.dat @@ -0,0 +1,2 @@ +xÚí—KÀ D÷s¸ÿåÚ¤µE~Ý‚ihÌÄ'™ +ÑŽÍÀòNÏ'oVWßéà£jspèÚCzÓþìÑο©¥ÃaÌÏg!«…#WD%~Ü˃Y?±JÛfŒÚåR€E¸¢ŽÁKoæÍ¼™Ç˜§ü|Ƀˎ*ÌOÿP7 Šž‡åÛn¾ë¼½¥™7ósã†`ž½ïÌÍÖÀõsúåçdôD¾Ÿÿ¹Ÿsé~>ý<ßiÌK'Žl'¸×9¥ûÐ%.ÆîÄ7
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_65_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_65_6.dat new file mode 100755 index 00000000..550fc8fe --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_65_6.dat @@ -0,0 +1 @@ +xÚíWQÅ ûïiäþ—{KžNLk?˜e$ŒQ´ikÝìÆÚ4¸çö1{˜á‰ð×`+®!úÚ®ºM
Á?áû “’°È1b°8†ž£·¦.ŒÞ^ƒwÌÞò¹ìèsn²ÁFjè5¹…EaQX|‹=wä¼@°2v<òŋŞ»‰–|4w»êê\ÔUXÇBQz+óÊT±ÈTǹÖcøBÓzž/4ç8Õë,‘»5Ý`¡¥¨»ý¹È± üOV$
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_69_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_69_6.dat new file mode 100755 index 00000000..a3e4fa0f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_69_6.dat @@ -0,0 +1 @@ +xÚí˜KÄ @÷œî¹™dThÃðí¦ hLSSEžq eY@<ã¯+Þ*£|ß窮%‹®‹>¥¡¨®‹¹zÃ*7ë–Åe´¤öÆ6ãQ¶½©S`šÞ.²¯>»sEË'¨%@[‘Ž6°@PË0Ãh
£aôFÕxtpl²2§Âóý‡Q-g1N†feo^ßøÑüë†Ñ0F¯dT>N_ÕOö’‘wË‹G3ìùªu†åœÁ»gˆå{‘3<Ê[žÔºb?Â'6ï^ºŠ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_73_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_73_6.dat Binary files differnew file mode 100755 index 00000000..ab71b70a --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_73_6.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_77_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_77_6.dat new file mode 100755 index 00000000..ad5a660e --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_77_6.dat @@ -0,0 +1 @@ +xÚí˜Ñà EßùøÿŸÛ²i‹ÙÅVäaË.š¦±F©§pST›YÖô4q÷ú~´z…«=Ÿä:öÍ’
û6îøÍüôm8‘¾:#0PضiDy:2Å '§ùZs±Š&}Ïôçæ»œ\r‘0\¥Åš•˜Xw¬Â;iP¬äàÈ”Lœ)ÒS¬±£eÔ•Ü{hˆDu9÷L‘žb½•©JÞÒS÷ŒSÆ)™’)™ÞgZš{÷þeÜøŸé)ã”qJ¦dúLw+ƒ#Ó3-V0ÅÕ·lîjƒ¹™ýÚ ×S-ÕS
ê½9=ݯ÷¦5ÁPPïõq1Mõ?ågÓ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_81_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_81_6.dat new file mode 100755 index 00000000..28a6d075 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_81_6.dat @@ -0,0 +1,3 @@ +xÚí™Q„0Dÿ9
Üÿr»ÉÚR³LÕvök‡šÆ`´¤¯0 º;æi6ÜûçÑÚ\|ñí_¿êccÔ1«¢h¾ýuŒ§½ioÞó#2ƒ¬}x*.YûtÃ&¸± +Ö°¶ÀÖq/K‚;3ÌväeÐ̢Ȋ҂AÛH±ë?`]é5ÒðK¿¡Êü´†w¿!}{Zû߲žW +Œ”yâ·¾Àž^§_y¼k±k±–^K¯•תáb-ÖbÝYSÚ¸'ÖœNóÈuÃ×õšÕ#M½fõHS¯Q?|]¯IýðA¯ÆÚi™Mû÷‘yíì¤yuWà
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_85_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_85_6.dat Binary files differnew file mode 100755 index 00000000..d5403e49 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_85_6.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_89_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_89_6.dat Binary files differnew file mode 100755 index 00000000..eeeb5d19 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_89_6.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_93_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_93_6.dat Binary files differnew file mode 100755 index 00000000..6ff38db6 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_93_6.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_6/mask_97_6.dat b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_97_6.dat new file mode 100755 index 00000000..3a2072ef --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_6/mask_97_6.dat @@ -0,0 +1,2 @@ +xÚíšaƒ0…ÿs¸ÿå¶dÅêÒÒFx[²=4ÆH–ùŠoŠj3«4í&§s}¹*ŽÃž—÷aV¸cˆ&3žêÜ5‰arWç^aþ«÷LClÉÒzžqì,ÝÌÑ1†xî
µ‰SQ»N»ô»]·¢/GiuÄ`&æ ¢–wƒ%‚,‘%²D–ÈYú"K½t¸¤+åHE'|R2Ï(v1¨öv–¢ÿqiëëŸqd‰,‘%²D–~€¥è%Ø»Jjï}íͺĺÄgY"Kd‰,ý+Kñö +–]W–të+sF/)ü]¥.zNòÚÝ'`°>®½1='#–`+bƒ¥l]Z±”¶¬ýð
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_101_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_101_7.dat new file mode 100755 index 00000000..1f6bc512 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_101_7.dat @@ -0,0 +1 @@ +xÚíšQà Cÿ}rÿËm“Ê`éÚÊfªjòTé#±¥ÔäÞ54'tûí…îf¼aÈЇo°–$±c<ÙËêãÝÓÕñHÝ Ìh8„lÕÛûþÁëo=½Ä„ŽÅ§àHvº©;‹z€¤!ƒ|/‹FŽÐ3cfÌŒ‰S×Ê…±±ã‚¼êñ8©KŽ)4©9 ”Qç1ç1×J3fÆÌØÅ›S+ÅwÖcçõ˜ó˜ó˜k¥3cf캌ùîâ¾zÌyÌy̵Ҍ™±1¦{¨²bL÷¶çÍX™S+óï.to{>ô˜îmO§ÇJ2²û3cÄ<6ãXn0F´
)
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_105_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_105_7.dat new file mode 100755 index 00000000..6b0cacfe --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_105_7.dat @@ -0,0 +1,2 @@ +xÚíšA +Ä0E÷žFï¹a MÒ™˜N¾Px)¸pQ£¼è_è~ž|ñ(b¿F’„šç$.Ÿa¿o“ÝòWÿGNÚPæUÇ–·‰Mþ%{‡ëoôHQôÙUñl¯Ö´¿¬L^>+Éçm“#{°{eo&Y2sÛoM•)gncO9s›ßZг´3wÇo™„Ú+Âè{ô=f.ìÁìÁz½‡Þ£ïÑ÷˜¹°{°{è=ôz¾GßcæÂìÁÞsÙkCQÏžpÓíÊ^¶€&Ñ{^£÷¼NïÊ·UïÕìï
ìe5°ç}¯æE‰wGûßn+o
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_109_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_109_7.dat new file mode 100755 index 00000000..9875cbe8 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_109_7.dat @@ -0,0 +1,2 @@ +xÚíšA +…0D÷9MrÿË}µÚ*˜_x-d˜ú:Î"îÇŠ¢Ý-kÅ"Šâ¨š¢œ•dŒ{›¿Õ»ü¤·ñã¯ó³ÞÊגɬ|<b2+ßUá¶ýÊîÚ%Z}‰.Úq%Gi²¾¶h
QA9˜„I˜„I˜üG&‹Éž·<ßø oÍ:Ï,ubÞ2}ãmÞ„_
D'ÑI˜„I˜„I˜ÄOâ'ñ“è$: “0 “0 “øIü$~D'ùwÃ$LÂ䇙TÌOvLVOO^˜<ÓøÉòùÉÞOúbµÄOºÎOºÖOºÊ›™Þt\3™¬“®<ÊG¼Í«
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_113_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_113_7.dat new file mode 100755 index 00000000..b6e21598 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_113_7.dat @@ -0,0 +1,11 @@ +xÚíšQƒ Dÿ÷4pÿ˵M´PeQ‘6éÃd?Ô¸°>'ƒÒÚràQ5+ sûŒ¡)Ûc•Õê7úÕ-ÑÕó»1nní–ºqÿÉ”–ÔÍJtgô¬^îʉwÿ̘Ň-ùƒ?ÿ*š&Mmý@ee5éø^Ñ +c¢”° +«° +«ÀªÒ,¬¶ýªïbï\1¿×3ýjõ4óØT¿ZžfÅ¢·»oé¯ «è*º +«° +«° +«°:›Uüêt¿*ü¿ú½¹ºŠ®¢«° +«° +«° +«“Xů²0<·BWÑUtVaVaVƒYÕl +Þ°ªÙkýf5éýª_Ö ¿ªÙkýáW5{«µ€ä–5pnõC¬éªZ:¬Å bv
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_117_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_117_7.dat new file mode 100755 index 00000000..cde78c10 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_117_7.dat @@ -0,0 +1,2 @@ +xÚí›Yà Dÿç4pÿËU•²ÈêIª>"ù%6æed‘Ò8rðSÍNs޵U¯ánëk5¦Xeøjѻު®u—Ûî¦ô¿ö±VÛXgøê,l`øøîuÝÊÀî÷£!‚h‰XZù\VãlM±ìë|[ͬÝÂ0Ã0Ã0ÿÃí–#hF'ëøc]²±ž¾¦iéí>ìúŽûHÐat†a†a†a~<ÃÔÆzØyÆqkO‡£Ãè0Ã0Ã0ÃðææŒãªžF‡Ña†a†aþ)†§2Ç˰éfÃ’áþ%ƒÀz8ùëátO=¼žâ€3ÿ=Ž‚áÖî3œÜ:ìÿcw‘ +ûV$¢
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_121_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_121_7.dat new file mode 100755 index 00000000..d5d577f7 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_121_7.dat @@ -0,0 +1,2 @@ +xÚí›[ +Ä Eÿ³šdÿ›ú°ŒÆi;^az,ø#6Æž^rÝׂ§h¶ŽôµÈêÐ&Ýì¨çÜ^amýËÀ•YŸ9×_ÒØºô5C·r–6ë®ðt^ù^ÅWlûÅÐEä˜ôز~É¿|èM®èmýÁåÛmSò}÷¶Ó(
Û°
Û°
Û°}ŽmQ½]ŒZ§Vüq½]¼ÓvËѲ"™M¹1fÙúGÔÛÛ,q½½ÍB·Ñmt¶a¶a¶a¶ÂöÄz»eF²3»cxI¹—D·Ñmt¶a¶a¶a¶ÿŸmÎnð’ý$ºn£Û°
Û°
Û°
Ûa[}ï¦`[yëæÀö–°¾Þ–Þ»)Ïn<¹æ4ÜKú/éó¼¤O¢»s§l¨nçlÔmŸµÝ/GëÙ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_125_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_125_7.dat Binary files differnew file mode 100755 index 00000000..f9ec0887 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_125_7.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_129_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_129_7.dat Binary files differnew file mode 100755 index 00000000..9bf51d52 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_129_7.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_133_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_133_7.dat Binary files differnew file mode 100755 index 00000000..b643ffed --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_133_7.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_137_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_137_7.dat new file mode 100755 index 00000000..11d212bf --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_137_7.dat @@ -0,0 +1,5 @@ +xÚíœÝ +à Fïó4úþ/·
úc§‹«_Ç‚+¸{üSÚK<E±Óœ%um‰Ä”¶OÄÃcåPÕNsûÑuõÝß+ŸšÒʺQS,E)Öµ÷^ÿf¼Õaç—_ÃHkoKrÇ6»DÓZQ›öï‹Úö F><ŸÚÎo[l +ìÀìÀΟ²Ó +©ý0û¡7Ö‰µVÆl;b¶–ëüè™7æõfM¾S´†î ;è1ìÀìÀìÀìL³C¾<ßQíïœ[+@wÐt‡˜
v`v`v`v&Ù!ß ›ï°7ÚX+@wÐt‡˜
v`v`v`v8ÊYP΂¢;èºCÌ;°;°;°saG{™Ä;Ú»>vRœ|ÇžåùŽö®K¾£½ë£ØMÎðHÖ +‚³#Ð(S›ËÎòúqÌçd
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_141_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_141_7.dat new file mode 100755 index 00000000..98dffab0 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_141_7.dat @@ -0,0 +1 @@ +xÚíœA„ E÷=
Üÿr“IëŒTY@¿ÉÄÅ0!µøø|1”²—št¹bG0µæÔ—Ѥ…s¡2Z/îç¿oa‚ìøÜ\ÿq¬ãzÜÛOnÒ‹MºÏñÞn™šñt„áX"Kÿ—m‘eM}çÚòCpµíÄP™L^¾µŒ„S0S0S0S£L ø)Ç”‚ŸÚ˜êYß¾%žÔbÑý,õS©l?ÕzC§Ð)tеLÁLÁLÁLI2…ŸzŸŠRX¶h¾£@§Ð)tеLÁLÁLÁLI1…Ÿz‘ŸbÏ·óŽB§Ð)Ö~0S0S0S0Å7´ø)ö|ù†B§Ð)˜‚)˜‚)˜‚©÷3Õ–«L%Ÿtfª¬wM±Ÿ*:~ªhù©»¡Zºç«sn’cª÷$1UTtJgÆ8‹ÌÕOYºE
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_145_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_145_7.dat new file mode 100755 index 00000000..4aa2bac1 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_145_7.dat @@ -0,0 +1,2 @@ +xÚíœÑ +à Eßó5úÿ?W6Zµ ¦-›^é±àƒ2‰q§×ÄbG‰Ÿ¢X6(ƉuÍ"“LbÑbÝGuû÷ŽÎ¼®··Gkø¨:HwA[¿jšÔm¯øH¢ø¬Ýžï3ïŬýîßâOðkQ{l·|ê»èT›†EmÇJfÙLÊ?ŸÚ2"&Á¬Á¬Á¬Á¬ÁÚ¬)Äk©ÝRf¢—µÙcï“Ñ̉ÓåFŠÑ,§“z©¦=躆®±‡„5Xƒ5Xƒ5Xƒµ7±F¼¶\¼¦p–ÚUs#躆®±‡„5Xƒ5Xƒ5Xƒµ°F¼¶d¼ÆYö•ܺ†®¡kì!a
Ö`
Ö`
Ö`o‰×8ËæÛct
]CרC¬Á¬Á¬
gMå‚5…[´N¬%éÄk÷h•ñZp®?›¯IÜ£•ϲƒ^n$ˆÑæÞY7A×ÖÆëZP[¶
fȓ0
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_149_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_149_7.dat new file mode 100755 index 00000000..809f0055 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_149_7.dat @@ -0,0 +1 @@ +xÚíÜÑnà …á{?
¼ÿËu“Ú†…´2»ÕG$nˆˆcççÈF¡µKëÉ×Ðb3ª÷ôþÖªt³îûªPcçÌ¥Ó7[¢?9:ñÕý['éÁë9'¾*Ó–àGæÜa°½hÖ_¿¹/«zÁ+Î6XB‡>ê˜2ôqYÀJ…0Êù黚BfaƒÄ 1ˆÁªÈ‡9c7Gë ùOl©ô,Å^¥ßÅ꽓òÁ3ƒÕòA:Hé Ä 1ˆAbƒÄàÿ0X4œ%Ù«½û#ÇÔd>¢&Cé ¤ƒÄ 1ˆAbƒ<šA{ôo¿G¯&óLM†ÒA:H1ˆAbƒÄ 1è¿ {ôj2þ› ƒtbƒÄ 1ˆA&1Xç`¼_Ö9ÛðÊ`«™®B˜¶G_çlÃ5™:g5™¶ajMæÍLÔÁŠËèƒIý <|r.
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_153_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_153_7.dat new file mode 100755 index 00000000..c1ab2766 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_153_7.dat @@ -0,0 +1,2 @@ +xÚíÜQ‚0„á÷=
½ÿ匉B5ˆ +ю惤/ͲåïØIÙiº^-àî®Zkm|»YDhë9›ÎêÃ\{’KÇÆ¶·{Ÿy—³Ç3ßv†¶Ì»œE]u`RÚÇrý"›ß}ÛY½ëeÂ2{ÛVV8][×.n:+2_ç¶ÊÂBÃ&6±‰Mlb›ØÄ&6ß`3i¿9Y³³åΌ諡yÙúU}ÔŸ82f-vÚ–Õ6 nÒMºI7±‰Mlb›ØÄ&6±ùólæî7ãÎð‚þÕ¢›t“nÒMlb›ØÄ&6±‰Mlþ2›ÎüÍÙ^Ð/ˆnÒMºI7±‰Mlb›ØÄ&6±é;gxA¾C¡›t“núO‹Mlb›ØÄ&6±y€Íy•ÇfP¥Õ[6Ÿ4ºßÌ«Ùk`v^Ð+Ó9ÄʬOÛ±ùð}Ïæ”¨›™+ZXíè®=:iWE
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_157_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_157_7.dat new file mode 100755 index 00000000..2db27f68 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_157_7.dat @@ -0,0 +1,2 @@ +xÚíÜM +ƒ0Eáù[M²ÿÍ•‚ü£s¡_„ž‰ÇÛã }F¹†Qks½‡Ì{Ý%¶WqÛ÷×îø.ž¬Ýþϯ]Ü8evV½ßbvV}è®_µÜEÌ»n3W^KÃ\Ÿ`äÖV캽çZl³˜Å,f1‹YÌþ;³af±Ôkž·Þ¨×úiàè“ÁÄzÍôÖËz?urVÎÊY9‹YÌb³˜Å,fù,Ÿå³|VÎÊY9+g1‹YÌb³˜Å,Ÿå³|–ÏÊY9+gý7Æ,f1‹YÌb³|–ÏòY>+g嬜Å,f1‹YÌb³|–ÏòY>+g嬜•³˜Å,f1‹YÌþÈlâùƳi§o˜ý.\¦ÏÆo<úl;9º:Âg[®Ï¶lŸmÁÔ^œI>=gÏ™œ³-yk_TAµ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_161_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_161_7.dat new file mode 100755 index 00000000..35ba8ff4 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_161_7.dat @@ -0,0 +1 @@ +xÚíÜÑŽÂ …áûyšòþ/皨E)Æ®›î1~4áœý93,˵´ «+µØZT=Zeâ¶C»¹ú.<sþô¹ŸvÙÞÏ>~ißµ§¯ÊÞÖ&>Œ,–ÿÔï´6eù˜·í•Ö~,láW]ì
®»º2Íê꺱Ý\Ñþ;×µ2j"–±Œe,ËrXŒÝµVÓ(ícìµµZÓ–1‘™§8Þ©í§‰±/,'ÇØt™.ÓeºŒe,cËXÆ2–±Œåß±,_þŠ|ytõüç£ï|ÎÜ]¦Ët™.cËXÆ2–±Œe,cyËòå/É—ysî‹.ÓeºL—±Œe,cËXÆ2–±lo…|ÙZ{+è2]¦Ëbl,cËXÆ2–±Œe{+äËÖŠØ[A—é2]¦ËXÆ2–±Œe,c9åÌCÐXÎ<ëþÆò’Ÿ/owsP¾œyÖý]¾œyÖ}·VdÙìæ°¹¯d9J—Ó‡ì)Ë1õ jI2ë
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_165_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_165_7.dat new file mode 100755 index 00000000..e27fb8ed --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_165_7.dat @@ -0,0 +1 @@ +xÚíÝQŽ‚0Ðÿ·š²ÿÍM&A,NĘ̈!—x0é‡&XZׇZ»-KØ«[êÞÉeÉj‡½Œëæx,ƒ§¼ú.ï·g}ë`;ÿþþ+k{ËÑ~1ŸŽÿ}á+k{ËØ¥ž™¾SGùWãgî}‡Ý¬@Û?_µö8í¾o+·k][·hô”Wü8~·u·ÜMÆgœqÆg|j<õ·ú¶¶Ú*ßq=<¯”Ïü¤æÛ“RwÆ“ëñÕøøÌüÄÌéŸÈq9.Çå8ãŒ3Î8ãŒ3Î8ãIÆÕãUGÿ?~µsnr\ŽËq9Î8ãŒ3Î8ãŒ3Îx€qõøÇÕã®yç979.Çå¸gœqÆgœqÆgÜ=)êq×À¸'EŽËq9.ÇgœqÆgœqÆwOŠzÜ50îI‘ãr\ŽûÎ8ãŒ3Î8ãŒ_ÆøVvd}²ÇÞøü¡aõx˯ÇÛ5êñã)¹&ÿ9)ñÙÞf¼¥çxþ3øYH]ûS^*/
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_169_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_169_7.dat Binary files differnew file mode 100755 index 00000000..ef1a181f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_169_7.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_173_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_173_7.dat new file mode 100755 index 00000000..3b513712 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_173_7.dat @@ -0,0 +1 @@ +xÚíÝÝjÃ0Ð{=Mòþ/·
RbÖH¬û«´rj\Ç=ý,ºm·coúZŽ8;»ïmÏ÷½mÛÝë±0b÷ƒ_ßšt$~Wu5ÛÇšNnÜ'?dÕf2¶í(oÁïßGÚ,Løþù÷õ6_{»zÅÑ÷Æ?
Ë9úwq9Ç
àS!ÆŒëÛ9Îoë€î2 L`˜˜pµŒ¬––O»Enï¶v8L˜²v8L¸º UáY×"ûd}ê K›r‚œ 'È ÖL`˜À&0 L`B'fÕ:ïO8ÛTcTc\kŒr‚œ 'È ÖL`˜À&0 L`BìY²gIñ§kŒr‚œ 'È ÖL`˜À&0 L`‚ç"íYRcô\¤œ 'È r˜À&0 L`˜ÀÏEÚ³¤Æè¹H9ANä&0 L`˜À&ü'úÿ!Ø;êÅq+¶Yõ„l*´Ü³´þ=Ø€ã9¶jŒ[2ÚÖÿ€ Ms¤Ÿ‡Ô„–çë*Ø
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_177_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_177_7.dat Binary files differnew file mode 100755 index 00000000..068477c9 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_177_7.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_21_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_21_7.dat new file mode 100755 index 00000000..4f9f1386 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_21_7.dat @@ -0,0 +1,4 @@ +xÚQ +À0Bÿ=Þÿr]š˜-?]Rl‡ä݃2•¦¹nc +ð[޹öÝnùAÃ".–j+êi +~‹x3<úaXÚ{H†ÖC1xÉÀ)â„á¤üSå
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_25_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_25_7.dat new file mode 100755 index 00000000..cefe1b97 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_25_7.dat @@ -0,0 +1 @@ +xÚQAÀ »÷5ôÿŸ[F@©àaƒ„ÄÔÖZ̲…
>;’ÐÀQ±ÊIÖÀQH8Rÿ¼ñÒˆGÉ"èz,ù½&‡ñþ;±Ð'oñ»97”%P8%÷6oǽ;]ÞúNWnÝ[äf7¹ÌÕvÔ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_29_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_29_7.dat new file mode 100755 index 00000000..e3d7391b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_29_7.dat @@ -0,0 +1,2 @@ +xÚÕR9À ÛýšøÿŸ«ŠÈQpX˜$læÅ²f!I2pgSãªêªMZj·ºóÌÃÂ<K.êØ· +ÂÇ—Ñê^DŽ/è_yž/ßgcxVÙ0ŸBf#ŽÖÿ]BÇϽʳhí9Þ¹ËóÀ›^Ÿä
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_33_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_33_7.dat new file mode 100755 index 00000000..1763f428 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_33_7.dat @@ -0,0 +1 @@ +xÚíSAÀ@¼Ïkøÿçš6¨¨á²Ç’8Èì07-n† ZbE0¶"™£°t]…£µ3Ztqúó>"útŠðï¡e0¾¿#Ô›`_Ë1-±ÞóŽé´câÝÍ÷òha÷~/¡Eh™4¦Åæ"ä˜~Ï
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_37_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_37_7.dat Binary files differnew file mode 100755 index 00000000..87d9a1a9 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_37_7.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_41_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_41_7.dat new file mode 100755 index 00000000..8acec04f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_41_7.dat @@ -0,0 +1 @@ +xÚíTAÀ »÷5ôÿŸ[fDYæ(O’^¬ÅbR3/~t/L"¹Ä7SQQ5…jÔ\S—šiíñÎb£ß#Õ†ÈÂ×+Ç£¤w#æzõx?Þã½§A-ëšw•u曑Y7$b.%AËÇ;•w«Rµ÷ñoxG}?ƒ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_45_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_45_7.dat Binary files differnew file mode 100755 index 00000000..dbba31d0 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_45_7.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_49_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_49_7.dat new file mode 100755 index 00000000..be5dce8b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_49_7.dat @@ -0,0 +1 @@ +xÚíVË€0»÷kàÿÎhœC-X.žÆ<ì¨h6Ì‹3À½ð,B
Ô”ó,ç@’Ï€d5$K¸"T|p•%9"¼ypæ,ïâæ¡=<ÆÉm¡•÷˜Ô‰!‹«?¸¦÷DÀW"˜²(®¸Aó-Ö\-
J\©{0p¥lÁWW]ƒÒŒ4ºž?4h=
Z_ƒÖd+ýg(æ*ã*+ë¶µ(ÏÇæ
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_53_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_53_7.dat new file mode 100755 index 00000000..7028ef6d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_53_7.dat @@ -0,0 +1 @@ +xÚí–KÀ D÷s¼ÿåš&´Råkºiª&,H)øtD×jÁ=¨µÐÎQa˜ž«P"d¹s1ZyC®|heyüCvéaH°›7ø‰+ùO…Åu¥QÎsZtb…°Íð7µvãúÀŠh™VµÌµ~èùà·1#ÿ~‡[Ëf˜†ùÙæfHkZ¶JtµœŸmZÎÏ6½—ÉsÕúዃw¸rÍ&C×ß¶f
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_57_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_57_7.dat new file mode 100755 index 00000000..ee3107a3 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_57_7.dat @@ -0,0 +1 @@ +xÚí—A€0ïûšöÿŸ3&•ÒH)Kb¼Ð&ˆqÅÖžÕ[-ÌÀÞÏÖŒ…Ú9Ér¡Ys×.«ÎiÝÓ¾`´ìòØú~—ëGé{cœiƒÕ‚»\Y<˜.©|·Åì2´ØÛŸÙ2š ŠQQ[[÷^N·ë€88½líÏÃÁ[}[šPl?g+o*Ï–˜4W¶û1 ü Õ33˜Ò[¿\Gosóbkèm”mËômÿÊ^¬¢hë
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_61_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_61_7.dat new file mode 100755 index 00000000..76f8d727 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_61_7.dat @@ -0,0 +1,2 @@ +xÚí—K +À D÷sšäþ—+-µ*5ÿm²¤cŸaT¢1Ø9—YÌ쌧êL9Âç³¶¬²çísù//i‡‡Ê\„éb®ö©æÂÄ«âÞë-"^²DŒH-iÝ;bnA¢¼™7ófîet¶7Ù·yÌ£E:r”<4ß6ó(üu÷y{K3oæ"óÌý|a½oÌ?ᜟ‡ï竟“ò´pù9åýœj~NêÆ›Èìs¹ÑçTYú¡Âu
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_65_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_65_7.dat new file mode 100755 index 00000000..d8b92062 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_65_7.dat @@ -0,0 +1 @@ +xÚí—A€ ïûøÿçŒI‘*í6^L!é…°¶¸i)mTÇT] VW\ï9e@—4Ku^Â#N%:ö,ÖÙmYÈÂJNã„è9¯“Šà¶©ˆvAè2úþÑÏœ”HÉ"YüšÅÜö+?`ýBXDüBX̽{¿‹Á.³Ëw‘ÿ¨d‘,~΂k’,¸^ïbQâ~±.Ãà\¯wó®×ë^«Ï÷îOY˜ÞEôJmXâÑÏ"%
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_69_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_69_7.dat Binary files differnew file mode 100755 index 00000000..c2db0204 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_69_7.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_73_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_73_7.dat Binary files differnew file mode 100755 index 00000000..f414e4a5 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_73_7.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_77_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_77_7.dat Binary files differnew file mode 100755 index 00000000..3e52bfd3 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_77_7.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_81_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_81_7.dat new file mode 100755 index 00000000..78e08dfc --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_81_7.dat @@ -0,0 +1 @@ +xÚí™ÁÄ Dïó5ðÿ?×lÒ"ÍŠÝ•¹u4áÀa„>ukv
oÌ40Ý÷ãT±%9¯‘Ô6²ü÷ªU5‹*sI{`¹ªÿ™_²>S?ç}(:yâTïlï{G&E\”Ö6¨õ}"A’¤X‹õXϬ«²³Ç<Âmwx°fÜá‘G”<k£ræU•eεεX‹µX‹µüZ~s;\¬ÅZ¬3ë¸|y¬ /ÍwÖÕÃp˯ëׯ÷ëUÛ[~Í}O¬«oÓ`mÌsÍÝ‘¤)~,Ý
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_85_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_85_7.dat Binary files differnew file mode 100755 index 00000000..a53824ae --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_85_7.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_89_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_89_7.dat Binary files differnew file mode 100755 index 00000000..32934a44 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_89_7.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_93_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_93_7.dat Binary files differnew file mode 100755 index 00000000..1955f6b7 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_93_7.dat diff --git a/vendor/aferrandini/phpqrcode/cache/mask_7/mask_97_7.dat b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_97_7.dat new file mode 100755 index 00000000..b277368b --- /dev/null +++ b/vendor/aferrandini/phpqrcode/cache/mask_7/mask_97_7.dat @@ -0,0 +1,2 @@ +xÚíšáƒ0„ÿßÓÀû¿Ü²D[ÌÚâV.K¶«Iÿ`…â'ž³sxáº÷Âyäå”$ùxãxÃ"Îéª×=O^&pêb¶ÊÈYʬž$êKŸÝ£àÁÉŽ8KŸ…ËÎeÃŒó¡¥ ÔøŸ3:$bI,‰%±$–ÄÒYb襶 +M)¯TôŽ
%q®¬Èô綦EÿœX}jlØT—T—ôŽKbI,‰%±ô,ñõã_e[%íýŽöV]R]Ò;N,‰%±$–þ’%VŸ@`‰Ñ%pa©m€§—(}ñ_¥%í%ÚÛ¸ÚÛøÚÛÈ4¥='u)ai¿.;M¾ù>
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/composer.json b/vendor/aferrandini/phpqrcode/composer.json new file mode 100644 index 00000000..ff793567 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/composer.json @@ -0,0 +1,21 @@ +{ + "name": "aferrandini/phpqrcode", + "description": "PHPQRCode porting and changed for PHP 5.3 compatibility", + "keywords": ["php", "qrcode", "barcode"], + "homepage": "https://github.com/aferrandini/PHPQRCode", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Ariel Ferrandini", + "email": "arielferrandini@gmail.com", + "homepage": "http://www.ferrandini.com/" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-0": { "PHPQRCode": "lib/" } + } +} diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode.php new file mode 100644 index 00000000..e96c5e3d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode.php @@ -0,0 +1,42 @@ +<?php +/** + * PHPQRCode.php + * + * Created by arielferrandini + */ +$QR_BASEDIR = dirname(__FILE__).DIRECTORY_SEPARATOR; + +// Required libs + +/* + * PHP QR Code encoder + * + * Config file, feel free to modify + */ + +define('QR_CACHEABLE', true); // use cache - more disk reads but less CPU power, masks and format templates are stored there +define('QR_CACHE_DIR', dirname(__FILE__).DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR); // used when QR_CACHEABLE === true +define('QR_LOG_DIR', dirname(__FILE__).DIRECTORY_SEPARATOR); // default error logs dir + +define('QR_FIND_BEST_MASK', true); // if true, estimates best mask (spec. default, but extremally slow; set to false to significant performance boost but (propably) worst quality code +define('QR_FIND_FROM_RANDOM', false); // if false, checks all masks available, otherwise value tells count of masks need to be checked, mask id are got randomly +define('QR_DEFAULT_MASK', 2); // when QR_FIND_BEST_MASK === false + +define('QR_PNG_MAXIMUM_SIZE', 1024); + + +// Supported output formats + +define('QR_FORMAT_TEXT', 0); +define('QR_FORMAT_PNG', 1); + +/** PHPQRCode root directory */ +if (!defined('PHPQRCODE_ROOT')) { + define('PHPQRCODE_ROOT', dirname(__FILE__) . '/'); + require(PHPQRCODE_ROOT . 'PHPQRCode/Autoloader.php'); +} + +class PHPQRCode +{ + +} diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/Autoloader.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/Autoloader.php new file mode 100755 index 00000000..6fe2bb2f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/Autoloader.php @@ -0,0 +1,48 @@ +<?php +/** + * Autoloader + * + * Copyright (c) 2006 - 2011 PHPExcel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category PHPQRCode + * @package PHPQRCode + */ + +namespace PHPQRCode; + +class Autoloader +{ + public static function register() + { + spl_autoload_register(array(new self, 'autoload')); + } + + public static function autoload($class) + { + if ((class_exists($class)) || (strpos($class, 'PHPQRCode') !== 0)) { + return false; + } + + $file = dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR + . str_replace(array('\\', "\0"), array('/', ''), $class).'.php'; + + if (is_file($file)) { + require $file; + } + } + +}
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/Constants.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/Constants.php new file mode 100644 index 00000000..49fc6434 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/Constants.php @@ -0,0 +1,58 @@ +<?php +/** + * Constants.php + * + * Created by arielferrandini + */ + +namespace PHPQRCode; + +class Constants +{ + const QR_CACHEABLE = false; + const QR_CACHE_DIR = ''; //dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR; + const QR_LOG_DIR = '/tmp/qrcode_log/'; + + const QR_FIND_BEST_MASK = true; + const QR_FIND_FROM_RANDOM = false; + const QR_DEFAULT_MASK = 2; + + const QR_PNG_MAXIMUM_SIZE = 1024; + + // Encoding modes + const QR_MODE_NUL = -1; + const QR_MODE_NUM = 0; + const QR_MODE_AN = 1; + const QR_MODE_8 = 2; + const QR_MODE_KANJI = 3; + const QR_MODE_STRUCTURE = 4; + + // Levels of error correction. + const QR_ECLEVEL_L = 0; + const QR_ECLEVEL_M = 1; + const QR_ECLEVEL_Q = 2; + const QR_ECLEVEL_H = 3; + + // Supported output formats + const QR_FORMAT_TEXT = 0; + const QR_FORMAT_PNG = 1; + + const QR_IMAGE = true; + + const STRUCTURE_HEADER_BITS = 20; + const MAX_STRUCTURED_SYMBOLS = 16; + + // Maks + const N1 = 3; + const N2 = 3; + const N3 = 40; + const N4 = 10; + + const QRSPEC_VERSION_MAX = 40; + const QRSPEC_WIDTH_MAX = 177; + + const QRCAP_WIDTH = 0; + const QRCAP_WORDS = 1; + const QRCAP_REMINDER = 2; + const QRCAP_EC = 3; +} diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/FrameFiller.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/FrameFiller.php new file mode 100644 index 00000000..02e94e3f --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/FrameFiller.php @@ -0,0 +1,96 @@ +<?php +/** + * FrameFiller.php + * + * Created by arielferrandini + */ + +namespace PHPQRCode; + +class FrameFiller { + + public $width; + public $frame; + public $x; + public $y; + public $dir; + public $bit; + + //---------------------------------------------------------------------- + public function __construct($width, &$frame) + { + $this->width = $width; + $this->frame = $frame; + $this->x = $width - 1; + $this->y = $width - 1; + $this->dir = -1; + $this->bit = -1; + } + + //---------------------------------------------------------------------- + public function setFrameAt($at, $val) + { + $this->frame[$at['y']][$at['x']] = chr($val); + } + + //---------------------------------------------------------------------- + public function getFrameAt($at) + { + return ord($this->frame[$at['y']][$at['x']]); + } + + //---------------------------------------------------------------------- + public function next() + { + do { + + if($this->bit == -1) { + $this->bit = 0; + return array('x'=>$this->x, 'y'=>$this->y); + } + + $x = $this->x; + $y = $this->y; + $w = $this->width; + + if($this->bit == 0) { + $x--; + $this->bit++; + } else { + $x++; + $y += $this->dir; + $this->bit--; + } + + if($this->dir < 0) { + if($y < 0) { + $y = 0; + $x -= 2; + $this->dir = 1; + if($x == 6) { + $x--; + $y = 9; + } + } + } else { + if($y == $w) { + $y = $w - 1; + $x -= 2; + $this->dir = -1; + if($x == 6) { + $x--; + $y -= 8; + } + } + } + if($x < 0 || $y < 0) return null; + + $this->x = $x; + $this->y = $y; + + } while(ord($this->frame[$y][$x]) & 0x80); + + return array('x'=>$x, 'y'=>$y); + } + +} ;
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRbitstream.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRbitstream.php new file mode 100755 index 00000000..93606f13 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRbitstream.php @@ -0,0 +1,182 @@ +<?php
+/*
+ * PHP QR Code encoder
+ *
+ * Bitstream class
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net>
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia <deltalab at poczta dot fm>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+namespace PHPQRCode;
+
+class QRbitstream {
+
+ public $data = array();
+
+ //----------------------------------------------------------------------
+ public function size()
+ {
+ return count($this->data);
+ }
+
+ //----------------------------------------------------------------------
+ public function allocate($setLength)
+ {
+ $this->data = array_fill(0, $setLength, 0);
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public static function newFromNum($bits, $num)
+ {
+ $bstream = new QRbitstream();
+ $bstream->allocate($bits);
+
+ $mask = 1 << ($bits - 1);
+ for($i=0; $i<$bits; $i++) {
+ if($num & $mask) {
+ $bstream->data[$i] = 1;
+ } else {
+ $bstream->data[$i] = 0;
+ }
+ $mask = $mask >> 1;
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public static function newFromBytes($size, $data)
+ {
+ $bstream = new QRbitstream();
+ $bstream->allocate($size * 8);
+ $p=0;
+
+ for($i=0; $i<$size; $i++) {
+ $mask = 0x80;
+ for($j=0; $j<8; $j++) {
+ if($data[$i] & $mask) {
+ $bstream->data[$p] = 1;
+ } else {
+ $bstream->data[$p] = 0;
+ }
+ $p++;
+ $mask = $mask >> 1;
+ }
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function append(QRbitstream $arg)
+ {
+ if (is_null($arg)) {
+ return -1;
+ }
+
+ if($arg->size() == 0) {
+ return 0;
+ }
+
+ if($this->size() == 0) {
+ $this->data = $arg->data;
+ return 0;
+ }
+
+ $this->data = array_values(array_merge($this->data, $arg->data));
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendNum($bits, $num)
+ {
+ if ($bits == 0)
+ return 0;
+
+ $b = QRbitstream::newFromNum($bits, $num);
+
+ if(is_null($b))
+ return -1;
+
+ $ret = $this->append($b);
+ unset($b);
+
+ return $ret;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendBytes($size, $data)
+ {
+ if ($size == 0)
+ return 0;
+
+ $b = QRbitstream::newFromBytes($size, $data);
+
+ if(is_null($b))
+ return -1;
+
+ $ret = $this->append($b);
+ unset($b);
+
+ return $ret;
+ }
+
+ //----------------------------------------------------------------------
+ public function toByte()
+ {
+
+ $size = $this->size();
+
+ if($size == 0) {
+ return array();
+ }
+
+ $data = array_fill(0, (int)(($size + 7) / 8), 0);
+ $bytes = (int)($size / 8);
+
+ $p = 0;
+
+ for($i=0; $i<$bytes; $i++) {
+ $v = 0;
+ for($j=0; $j<8; $j++) {
+ $v = $v << 1;
+ $v |= $this->data[$p];
+ $p++;
+ }
+ $data[$i] = $v;
+ }
+
+ if($size & 7) {
+ $v = 0;
+ for($j=0; $j<($size & 7); $j++) {
+ $v = $v << 1;
+ $v |= $this->data[$p];
+ $p++;
+ }
+ $data[$bytes] = $v;
+ }
+
+ return $data;
+ }
+
+}
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRcode.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRcode.php new file mode 100644 index 00000000..08b60243 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRcode.php @@ -0,0 +1,158 @@ +<?php +/** + * QRcode.php + * + * Created by arielferrandini + */ + +namespace PHPQRCode; + +use Exception; + +class QRcode { + + public $version; + public $width; + public $data; + + //---------------------------------------------------------------------- + public function encodeMask(QRinput $input, $mask) + { + if($input->getVersion() < 0 || $input->getVersion() > Constants::QRSPEC_VERSION_MAX) { + throw new Exception('wrong version'); + } + if($input->getErrorCorrectionLevel() > Constants::QR_ECLEVEL_H) { + throw new Exception('wrong level'); + } + + $raw = new QRrawcode($input); + + QRtools::markTime('after_raw'); + + $version = $raw->version; + $width = QRspec::getWidth($version); + $frame = QRspec::newFrame($version); + + $filler = new FrameFiller($width, $frame); + if(is_null($filler)) { + return NULL; + } + + // inteleaved data and ecc codes + for($i=0; $i<$raw->dataLength + $raw->eccLength; $i++) { + $code = $raw->getCode(); + $bit = 0x80; + for($j=0; $j<8; $j++) { + $addr = $filler->next(); + $filler->setFrameAt($addr, 0x02 | (($bit & $code) != 0)); + $bit = $bit >> 1; + } + } + + QRtools::markTime('after_filler'); + + unset($raw); + + // remainder bits + $j = QRspec::getRemainder($version); + for($i=0; $i<$j; $i++) { + $addr = $filler->next(); + $filler->setFrameAt($addr, 0x02); + } + + $frame = $filler->frame; + unset($filler); + + + // masking + $maskObj = new QRmask(); + if($mask < 0) { + + if (Constants::QR_FIND_BEST_MASK) { + $masked = $maskObj->mask($width, $frame, $input->getErrorCorrectionLevel()); + } else { + $masked = $maskObj->makeMask($width, $frame, (intval(Constants::QR_DEFAULT_MASK) % 8), $input->getErrorCorrectionLevel()); + } + } else { + $masked = $maskObj->makeMask($width, $frame, $mask, $input->getErrorCorrectionLevel()); + } + + if($masked == NULL) { + return NULL; + } + + QRtools::markTime('after_mask'); + + $this->version = $version; + $this->width = $width; + $this->data = $masked; + + return $this; + } + + //---------------------------------------------------------------------- + public function encodeInput(QRinput $input) + { + return $this->encodeMask($input, -1); + } + + //---------------------------------------------------------------------- + public function encodeString8bit($string, $version, $level) + { + if(string == NULL) { + throw new Exception('empty string!'); + return NULL; + } + + $input = new QRinput($version, $level); + if($input == NULL) return NULL; + + $ret = $input->append($input, Constants::QR_MODE_8, strlen($string), str_split($string)); + if($ret < 0) { + unset($input); + return NULL; + } + return $this->encodeInput($input); + } + + //---------------------------------------------------------------------- + public function encodeString($string, $version, $level, $hint, $casesensitive) + { + + if($hint != Constants::QR_MODE_8 && $hint != Constants::QR_MODE_KANJI) { + throw new Exception('bad hint'); + return NULL; + } + + $input = new QRinput($version, $level); + if($input == NULL) return NULL; + + $ret = QRsplit::splitStringToQRinput($string, $input, $hint, $casesensitive); + if($ret < 0) { + return NULL; + } + + return $this->encodeInput($input); + } + + //---------------------------------------------------------------------- + public static function png($text, $outfile = false, $level = Constants::QR_ECLEVEL_L, $size = 3, $margin = 4, $saveandprint=false) + { + $enc = QRencode::factory($level, $size, $margin); + return $enc->encodePNG($text, $outfile, $saveandprint=false); + } + + //---------------------------------------------------------------------- + public static function text($text, $outfile = false, $level = Constants::QR_ECLEVEL_L, $size = 3, $margin = 4) + { + $enc = QRencode::factory($level, $size, $margin); + return $enc->encode($text, $outfile); + } + + //---------------------------------------------------------------------- + public static function raw($text, $outfile = false, $level = Constants::QR_ECLEVEL_L, $size = 3, $margin = 4) + { + $enc = QRencode::factory($level, $size, $margin); + return $enc->encodeRAW($text, $outfile); + } +} diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRencode.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRencode.php new file mode 100755 index 00000000..d05ab6b7 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRencode.php @@ -0,0 +1,137 @@ +<?php
+/*
+ * PHP QR Code encoder
+ *
+ * Main encoder classes.
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net>
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia <deltalab at poczta dot fm>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+namespace PHPQRCode;
+
+use Exception;
+
+class QRencode {
+
+ public $casesensitive = true;
+ public $eightbit = false;
+
+ public $version = 0;
+ public $size = 3;
+ public $margin = 4;
+
+ public $structured = 0; // not supported yet
+
+ public $level = Constants::QR_ECLEVEL_L;
+ public $hint = Constants::QR_MODE_8;
+
+ //----------------------------------------------------------------------
+ public static function factory($level = Constants::QR_ECLEVEL_L, $size = 3, $margin = 4)
+ {
+ $enc = new QRencode();
+ $enc->size = $size;
+ $enc->margin = $margin;
+
+ switch ($level.'') {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ $enc->level = $level;
+ break;
+ case 'l':
+ case 'L':
+ $enc->level = Constants::QR_ECLEVEL_L;
+ break;
+ case 'm':
+ case 'M':
+ $enc->level = Constants::QR_ECLEVEL_M;
+ break;
+ case 'q':
+ case 'Q':
+ $enc->level = Constants::QR_ECLEVEL_Q;
+ break;
+ case 'h':
+ case 'H':
+ $enc->level = Constants::QR_ECLEVEL_H;
+ break;
+ }
+
+ return $enc;
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeRAW($intext, $outfile = false)
+ {
+ $code = new QRcode();
+
+ if($this->eightbit) {
+ $code->encodeString8bit($intext, $this->version, $this->level);
+ } else {
+ $code->encodeString($intext, $this->version, $this->level, $this->hint, $this->casesensitive);
+ }
+
+ return $code->data;
+ }
+
+ //----------------------------------------------------------------------
+ public function encode($intext, $outfile = false)
+ {
+ $code = new QRcode();
+
+ if($this->eightbit) {
+ $code->encodeString8bit($intext, $this->version, $this->level);
+ } else {
+ $code->encodeString($intext, $this->version, $this->level, $this->hint, $this->casesensitive);
+ }
+
+ QRtools::markTime('after_encode');
+
+ if ($outfile!== false) {
+ file_put_contents($outfile, join("\n", QRtools::binarize($code->data)));
+ } else {
+ return QRtools::binarize($code->data);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodePNG($intext, $outfile = false,$saveandprint=false)
+ {
+ try {
+ ob_start();
+ $tab = $this->encode($intext);
+ $err = ob_get_contents();
+ ob_end_clean();
+
+ if ($err != '')
+ QRtools::log($outfile, "ERROR: " . $err);
+
+ $maxSize = (int)(Constants::QR_PNG_MAXIMUM_SIZE / (count($tab)+2*$this->margin));
+
+ QRimage::png($tab, $outfile, min(max(1, $this->size), $maxSize), $this->margin,$saveandprint);
+ } catch (Exception $e) {
+ echo $e->getMessage();
+ die();
+
+ QRtools::log($outfile, $e->getMessage());
+ }
+ }
+}
diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRimage.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRimage.php new file mode 100755 index 00000000..430a16f8 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRimage.php @@ -0,0 +1,95 @@ +<?php
+/*
+ * PHP QR Code encoder
+ *
+ * Image output of code using GD2
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia <deltalab at poczta dot fm>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+namespace PHPQRCode;
+
+class QRimage {
+
+ //----------------------------------------------------------------------
+ public static function png($frame, $filename = false, $pixelPerPoint = 4, $outerFrame = 4,$saveandprint=FALSE)
+ {
+ $image = self::image($frame, $pixelPerPoint, $outerFrame);
+
+ if ($filename === false) {
+ Header("Content-type: image/png");
+ ImagePng($image);
+ } else {
+ if($saveandprint===TRUE){
+ ImagePng($image, $filename);
+ header("Content-type: image/png");
+ ImagePng($image);
+ }else{
+ ImagePng($image, $filename);
+ }
+ }
+
+ ImageDestroy($image);
+ }
+
+ //----------------------------------------------------------------------
+ public static function jpg($frame, $filename = false, $pixelPerPoint = 8, $outerFrame = 4, $q = 85)
+ {
+ $image = self::image($frame, $pixelPerPoint, $outerFrame);
+
+ if ($filename === false) {
+ Header("Content-type: image/jpeg");
+ ImageJpeg($image, null, $q);
+ } else {
+ ImageJpeg($image, $filename, $q);
+ }
+
+ ImageDestroy($image);
+ }
+
+ //----------------------------------------------------------------------
+ private static function image($frame, $pixelPerPoint = 4, $outerFrame = 4)
+ {
+ $h = count($frame);
+ $w = strlen($frame[0]);
+
+ $imgW = $w + 2*$outerFrame;
+ $imgH = $h + 2*$outerFrame;
+
+ $base_image =ImageCreate($imgW, $imgH);
+
+ $col[0] = ImageColorAllocate($base_image,255,255,255);
+ $col[1] = ImageColorAllocate($base_image,0,0,0);
+
+ imagefill($base_image, 0, 0, $col[0]);
+
+ for($y=0; $y<$h; $y++) {
+ for($x=0; $x<$w; $x++) {
+ if ($frame[$y][$x] == '1') {
+ ImageSetPixel($base_image,$x+$outerFrame,$y+$outerFrame,$col[1]);
+ }
+ }
+ }
+
+ $target_image =ImageCreate($imgW * $pixelPerPoint, $imgH * $pixelPerPoint);
+ ImageCopyResized($target_image, $base_image, 0, 0, 0, 0, $imgW * $pixelPerPoint, $imgH * $pixelPerPoint, $imgW, $imgH);
+ ImageDestroy($base_image);
+
+ return $target_image;
+ }
+}
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRinput.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRinput.php new file mode 100755 index 00000000..8bdd21e7 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRinput.php @@ -0,0 +1,486 @@ +<?php
+/*
+ * PHP QR Code encoder
+ *
+ * Input encoding class
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net>
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia <deltalab at poczta dot fm>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+namespace PHPQRCode;
+
+use Exception;
+
+class QRinput {
+
+ public $items;
+
+ private $version;
+ private $level;
+
+ //----------------------------------------------------------------------
+ public function __construct($version = 0, $level = Constants::QR_ECLEVEL_L)
+ {
+ if ($version < 0 || $version > Constants::QRSPEC_VERSION_MAX || $level > Constants::QR_ECLEVEL_H) {
+ throw new Exception('Invalid version no');
+ return NULL;
+ }
+
+ $this->version = $version;
+ $this->level = $level;
+ }
+
+ //----------------------------------------------------------------------
+ public function getVersion()
+ {
+ return $this->version;
+ }
+
+ //----------------------------------------------------------------------
+ public function setVersion($version)
+ {
+ if($version < 0 || $version > Constants::QRSPEC_VERSION_MAX) {
+ throw new Exception('Invalid version no');
+ return -1;
+ }
+
+ $this->version = $version;
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function getErrorCorrectionLevel()
+ {
+ return $this->level;
+ }
+
+ //----------------------------------------------------------------------
+ public function setErrorCorrectionLevel($level)
+ {
+ if($level > Constants::QR_ECLEVEL_H) {
+ throw new Exception('Invalid ECLEVEL');
+ return -1;
+ }
+
+ $this->level = $level;
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendEntry(QRinputItem $entry)
+ {
+ $this->items[] = $entry;
+ }
+
+ //----------------------------------------------------------------------
+ public function append($mode, $size, $data)
+ {
+ try {
+ $entry = new QRinputItem($mode, $size, $data);
+ $this->items[] = $entry;
+ return 0;
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+
+ public function insertStructuredAppendHeader($size, $index, $parity)
+ {
+ if( $size > Constants::MAX_STRUCTURED_SYMBOLS ) {
+ throw new Exception('insertStructuredAppendHeader wrong size');
+ }
+
+ if( $index <= 0 || $index > Constants::MAX_STRUCTURED_SYMBOLS ) {
+ throw new Exception('insertStructuredAppendHeader wrong index');
+ }
+
+ $buf = array($size, $index, $parity);
+
+ try {
+ $entry = new QRinputItem(Constants::QR_MODE_STRUCTURE, 3, buf);
+ array_unshift($this->items, $entry);
+ return 0;
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function calcParity()
+ {
+ $parity = 0;
+
+ foreach($this->items as $item) {
+ if($item->mode != Constants::QR_MODE_STRUCTURE) {
+ for($i=$item->size-1; $i>=0; $i--) {
+ $parity ^= $item->data[$i];
+ }
+ }
+ }
+
+ return $parity;
+ }
+
+ //----------------------------------------------------------------------
+ public static function checkModeNum($size, $data)
+ {
+ for($i=0; $i<$size; $i++) {
+ if((ord($data[$i]) < ord('0')) || (ord($data[$i]) > ord('9'))){
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ //----------------------------------------------------------------------
+ public static function estimateBitsModeNum($size)
+ {
+ $w = (int)$size / 3;
+ $bits = $w * 10;
+
+ switch($size - $w * 3) {
+ case 1:
+ $bits += 4;
+ break;
+ case 2:
+ $bits += 7;
+ break;
+ default:
+ break;
+ }
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public static $anTable = array(
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ );
+
+ //----------------------------------------------------------------------
+ public static function lookAnTable($c)
+ {
+ return (($c > 127)?-1:self::$anTable[$c]);
+ }
+
+ //----------------------------------------------------------------------
+ public static function checkModeAn($size, $data)
+ {
+ for($i=0; $i<$size; $i++) {
+ if (self::lookAnTable(ord($data[$i])) == -1) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ //----------------------------------------------------------------------
+ public static function estimateBitsModeAn($size)
+ {
+ $w = (int)($size / 2);
+ $bits = $w * 11;
+
+ if($size & 1) {
+ $bits += 6;
+ }
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public static function estimateBitsMode8($size)
+ {
+ return $size * 8;
+ }
+
+ //----------------------------------------------------------------------
+ public function estimateBitsModeKanji($size)
+ {
+ return (int)(($size / 2) * 13);
+ }
+
+ //----------------------------------------------------------------------
+ public static function checkModeKanji($size, $data)
+ {
+ if($size & 1)
+ return false;
+
+ for($i=0; $i<$size; $i+=2) {
+ $val = (ord($data[$i]) << 8) | ord($data[$i+1]);
+ if( $val < 0x8140
+ || ($val > 0x9ffc && $val < 0xe040)
+ || $val > 0xebbf) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /***********************************************************************
+ * Validation
+ **********************************************************************/
+
+ public static function check($mode, $size, $data)
+ {
+ if($size <= 0)
+ return false;
+
+ switch($mode) {
+ case Constants::QR_MODE_NUM: return self::checkModeNum($size, $data); break;
+ case Constants::QR_MODE_AN: return self::checkModeAn($size, $data); break;
+ case Constants::QR_MODE_KANJI: return self::checkModeKanji($size, $data); break;
+ case Constants::QR_MODE_8: return true; break;
+ case Constants::QR_MODE_STRUCTURE: return true; break;
+
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+
+ //----------------------------------------------------------------------
+ public function estimateBitStreamSize($version)
+ {
+ $bits = 0;
+
+ foreach($this->items as $item) {
+ $bits += $item->estimateBitStreamSizeOfEntry($version);
+ }
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public function estimateVersion()
+ {
+ $version = 0;
+ $prev = 0;
+ do {
+ $prev = $version;
+ $bits = $this->estimateBitStreamSize($prev);
+ $version = QRspec::getMinimumVersion((int)(($bits + 7) / 8), $this->level);
+ if ($version < 0) {
+ return -1;
+ }
+ } while ($version > $prev);
+
+ return $version;
+ }
+
+ //----------------------------------------------------------------------
+ public static function lengthOfCode($mode, $version, $bits)
+ {
+ $payload = $bits - 4 - QRspec::lengthIndicator($mode, $version);
+ switch($mode) {
+ case Constants::QR_MODE_NUM:
+ $chunks = (int)($payload / 10);
+ $remain = $payload - $chunks * 10;
+ $size = $chunks * 3;
+ if($remain >= 7) {
+ $size += 2;
+ } else if($remain >= 4) {
+ $size += 1;
+ }
+ break;
+ case Constants::QR_MODE_AN:
+ $chunks = (int)($payload / 11);
+ $remain = $payload - $chunks * 11;
+ $size = $chunks * 2;
+ if($remain >= 6)
+ $size++;
+ break;
+ case Constants::QR_MODE_8:
+ $size = (int)($payload / 8);
+ break;
+ case Constants::QR_MODE_KANJI:
+ $size = (int)(($payload / 13) * 2);
+ break;
+ case Constants::QR_MODE_STRUCTURE:
+ $size = (int)($payload / 8);
+ break;
+ default:
+ $size = 0;
+ break;
+ }
+
+ $maxsize = QRspec::maximumWords($mode, $version);
+ if($size < 0) $size = 0;
+ if($size > $maxsize) $size = $maxsize;
+
+ return $size;
+ }
+
+ //----------------------------------------------------------------------
+ public function createBitStream()
+ {
+ $total = 0;
+
+ foreach($this->items as $item) {
+ $bits = $item->encodeBitStream($this->version);
+
+ if($bits < 0)
+ return -1;
+
+ $total += $bits;
+ }
+
+ return $total;
+ }
+
+ //----------------------------------------------------------------------
+ public function convertData()
+ {
+ $ver = $this->estimateVersion();
+ if($ver > $this->getVersion()) {
+ $this->setVersion($ver);
+ }
+
+ for(;;) {
+ $bits = $this->createBitStream();
+
+ if($bits < 0)
+ return -1;
+
+ $ver = QRspec::getMinimumVersion((int)(($bits + 7) / 8), $this->level);
+ if($ver < 0) {
+ throw new Exception('WRONG VERSION');
+ return -1;
+ } else if($ver > $this->getVersion()) {
+ $this->setVersion($ver);
+ } else {
+ break;
+ }
+ }
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendPaddingBit(&$bstream)
+ {
+ $bits = $bstream->size();
+ $maxwords = QRspec::getDataLength($this->version, $this->level);
+ $maxbits = $maxwords * 8;
+
+ if ($maxbits == $bits) {
+ return 0;
+ }
+
+ if ($maxbits - $bits < 5) {
+ return $bstream->appendNum($maxbits - $bits, 0);
+ }
+
+ $bits += 4;
+ $words = (int)(($bits + 7) / 8);
+
+ $padding = new QRbitstream();
+ $ret = $padding->appendNum($words * 8 - $bits + 4, 0);
+
+ if($ret < 0)
+ return $ret;
+
+ $padlen = $maxwords - $words;
+
+ if($padlen > 0) {
+
+ $padbuf = array();
+ for($i=0; $i<$padlen; $i++) {
+ $padbuf[$i] = ($i&1)?0x11:0xec;
+ }
+
+ $ret = $padding->appendBytes($padlen, $padbuf);
+
+ if($ret < 0)
+ return $ret;
+
+ }
+
+ $ret = $bstream->append($padding);
+
+ return $ret;
+ }
+
+ //----------------------------------------------------------------------
+ public function mergeBitStream()
+ {
+ if($this->convertData() < 0) {
+ return null;
+ }
+
+ $bstream = new QRbitstream();
+
+ foreach($this->items as $item) {
+ $ret = $bstream->append($item->bstream);
+ if($ret < 0) {
+ return null;
+ }
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function getBitStream()
+ {
+
+ $bstream = $this->mergeBitStream();
+
+ if($bstream == null) {
+ return null;
+ }
+
+ $ret = $this->appendPaddingBit($bstream);
+ if($ret < 0) {
+ return null;
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function getByteStream()
+ {
+ $bstream = $this->getBitStream();
+ if($bstream == null) {
+ return null;
+ }
+
+ return $bstream->toByte();
+ }
+}
+
+
diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRinputItem.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRinputItem.php new file mode 100644 index 00000000..1e5eb18d --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRinputItem.php @@ -0,0 +1,246 @@ +<?php + +namespace PHPQRCode; + +use Exception; + +class QRinputItem { + + public $mode; + public $size; + public $data; + public $bstream; + + public function __construct($mode, $size, $data, $bstream = null) + { + $setData = array_slice($data, 0, $size); + + if (count($setData) < $size) { + $setData = array_merge($setData, array_fill(0,$size-count($setData),0)); + } + + if(!QRinput::check($mode, $size, $setData)) { + throw new Exception('Error m:'.$mode.',s:'.$size.',d:'.join(',',$setData)); + return null; + } + + $this->mode = $mode; + $this->size = $size; + $this->data = $setData; + $this->bstream = $bstream; + } + + //---------------------------------------------------------------------- + public function encodeModeNum($version) + { + try { + + $words = (int)($this->size / 3); + $bs = new QRbitstream(); + + $val = 0x1; + $bs->appendNum(4, $val); + $bs->appendNum(QRspec::lengthIndicator(Constants::QR_MODE_NUM, $version), $this->size); + + for($i=0; $i<$words; $i++) { + $val = (ord($this->data[$i*3 ]) - ord('0')) * 100; + $val += (ord($this->data[$i*3+1]) - ord('0')) * 10; + $val += (ord($this->data[$i*3+2]) - ord('0')); + $bs->appendNum(10, $val); + } + + if($this->size - $words * 3 == 1) { + $val = ord($this->data[$words*3]) - ord('0'); + $bs->appendNum(4, $val); + } else if($this->size - $words * 3 == 2) { + $val = (ord($this->data[$words*3 ]) - ord('0')) * 10; + $val += (ord($this->data[$words*3+1]) - ord('0')); + $bs->appendNum(7, $val); + } + + $this->bstream = $bs; + return 0; + + } catch (Exception $e) { + return -1; + } + } + + //---------------------------------------------------------------------- + public function encodeModeAn($version) + { + try { + $words = (int)($this->size / 2); + $bs = new QRbitstream(); + + $bs->appendNum(4, 0x02); + $bs->appendNum(QRspec::lengthIndicator(Constants::QR_MODE_AN, $version), $this->size); + + for($i=0; $i<$words; $i++) { + $val = (int)QRinput::lookAnTable(ord($this->data[$i*2 ])) * 45; + $val += (int)QRinput::lookAnTable(ord($this->data[$i*2+1])); + + $bs->appendNum(11, $val); + } + + if($this->size & 1) { + $val = QRinput::lookAnTable(ord($this->data[$words * 2])); + $bs->appendNum(6, $val); + } + + $this->bstream = $bs; + return 0; + + } catch (Exception $e) { + return -1; + } + } + + //---------------------------------------------------------------------- + public function encodeMode8($version) + { + try { + $bs = new QRbitstream(); + + $bs->appendNum(4, 0x4); + $bs->appendNum(QRspec::lengthIndicator(Constants::QR_MODE_8, $version), $this->size); + + for($i=0; $i<$this->size; $i++) { + $bs->appendNum(8, ord($this->data[$i])); + } + + $this->bstream = $bs; + return 0; + + } catch (Exception $e) { + return -1; + } + } + + //---------------------------------------------------------------------- + public function encodeModeKanji($version) + { + try { + + $bs = new QRbitstream(); + + $bs->appendNum(4, 0x8); + $bs->appendNum(QRspec::lengthIndicator(Constants::QR_MODE_KANJI, $version), (int)($this->size / 2)); + + for($i=0; $i<$this->size; $i+=2) { + $val = (ord($this->data[$i]) << 8) | ord($this->data[$i+1]); + if($val <= 0x9ffc) { + $val -= 0x8140; + } else { + $val -= 0xc140; + } + + $h = ($val >> 8) * 0xc0; + $val = ($val & 0xff) + $h; + + $bs->appendNum(13, $val); + } + + $this->bstream = $bs; + return 0; + + } catch (Exception $e) { + return -1; + } + } + + //---------------------------------------------------------------------- + public function encodeModeStructure() + { + try { + $bs = new QRbitstream(); + + $bs->appendNum(4, 0x03); + $bs->appendNum(4, ord($this->data[1]) - 1); + $bs->appendNum(4, ord($this->data[0]) - 1); + $bs->appendNum(8, ord($this->data[2])); + + $this->bstream = $bs; + return 0; + + } catch (Exception $e) { + return -1; + } + } + + //---------------------------------------------------------------------- + public function estimateBitStreamSizeOfEntry($version) + { + $bits = 0; + + if($version == 0) + $version = 1; + + switch($this->mode) { + case Constants::QR_MODE_NUM: $bits = QRinput::estimateBitsModeNum($this->size); break; + case Constants::QR_MODE_AN: $bits = QRinput::estimateBitsModeAn($this->size); break; + case Constants::QR_MODE_8: $bits = QRinput::estimateBitsMode8($this->size); break; + case Constants::QR_MODE_KANJI: $bits = QRinput::estimateBitsModeKanji($this->size);break; + case Constants::QR_MODE_STRUCTURE: return Constants::STRUCTURE_HEADER_BITS; + default: + return 0; + } + + $l = QRspec::lengthIndicator($this->mode, $version); + $m = 1 << $l; + $num = (int)(($this->size + $m - 1) / $m); + + $bits += $num * (4 + $l); + + return $bits; + } + + //---------------------------------------------------------------------- + public function encodeBitStream($version) + { + try { + + unset($this->bstream); + $words = QRspec::maximumWords($this->mode, $version); + + if($this->size > $words) { + + $st1 = new QRinputItem($this->mode, $words, $this->data); + $st2 = new QRinputItem($this->mode, $this->size - $words, array_slice($this->data, $words)); + + $st1->encodeBitStream($version); + $st2->encodeBitStream($version); + + $this->bstream = new QRbitstream(); + $this->bstream->append($st1->bstream); + $this->bstream->append($st2->bstream); + + unset($st1); + unset($st2); + + } else { + + $ret = 0; + + switch($this->mode) { + case Constants::QR_MODE_NUM: $ret = $this->encodeModeNum($version); break; + case Constants::QR_MODE_AN: $ret = $this->encodeModeAn($version); break; + case Constants::QR_MODE_8: $ret = $this->encodeMode8($version); break; + case Constants::QR_MODE_KANJI: $ret = $this->encodeModeKanji($version);break; + case Constants::QR_MODE_STRUCTURE: $ret = $this->encodeModeStructure(); break; + + default: + break; + } + + if($ret < 0) + return -1; + } + + return $this->bstream->size(); + + } catch (Exception $e) { + return -1; + } + } +} diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRmask.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRmask.php new file mode 100755 index 00000000..2be76f47 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRmask.php @@ -0,0 +1,325 @@ +<?php
+/*
+ * PHP QR Code encoder
+ *
+ * Masking
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net>
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia <deltalab at poczta dot fm>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+namespace PHPQRCode;
+
+class QRmask {
+
+ public $runLength = array();
+
+ //----------------------------------------------------------------------
+ public function __construct()
+ {
+ $this->runLength = array_fill(0, Constants::QRSPEC_WIDTH_MAX + 1, 0);
+ }
+
+ //----------------------------------------------------------------------
+ public function writeFormatInformation($width, &$frame, $mask, $level)
+ {
+ $blacks = 0;
+ $format = QRspec::getFormatInfo($mask, $level);
+
+ for($i=0; $i<8; $i++) {
+ if($format & 1) {
+ $blacks += 2;
+ $v = 0x85;
+ } else {
+ $v = 0x84;
+ }
+
+ $frame[8][$width - 1 - $i] = chr($v);
+ if($i < 6) {
+ $frame[$i][8] = chr($v);
+ } else {
+ $frame[$i + 1][8] = chr($v);
+ }
+ $format = $format >> 1;
+ }
+
+ for($i=0; $i<7; $i++) {
+ if($format & 1) {
+ $blacks += 2;
+ $v = 0x85;
+ } else {
+ $v = 0x84;
+ }
+
+ $frame[$width - 7 + $i][8] = chr($v);
+ if($i == 0) {
+ $frame[8][7] = chr($v);
+ } else {
+ $frame[8][6 - $i] = chr($v);
+ }
+
+ $format = $format >> 1;
+ }
+
+ return $blacks;
+ }
+
+ //----------------------------------------------------------------------
+ public function mask0($x, $y) { return ($x+$y)&1; }
+ public function mask1($x, $y) { return ($y&1); }
+ public function mask2($x, $y) { return ($x%3); }
+ public function mask3($x, $y) { return ($x+$y)%3; }
+ public function mask4($x, $y) { return (((int)($y/2))+((int)($x/3)))&1; }
+ public function mask5($x, $y) { return (($x*$y)&1)+($x*$y)%3; }
+ public function mask6($x, $y) { return ((($x*$y)&1)+($x*$y)%3)&1; }
+ public function mask7($x, $y) { return ((($x*$y)%3)+(($x+$y)&1))&1; }
+
+ //----------------------------------------------------------------------
+ private function generateMaskNo($maskNo, $width, $frame)
+ {
+ $bitMask = array_fill(0, $width, array_fill(0, $width, 0));
+
+ for($y=0; $y<$width; $y++) {
+ for($x=0; $x<$width; $x++) {
+ if(ord($frame[$y][$x]) & 0x80) {
+ $bitMask[$y][$x] = 0;
+ } else {
+ $maskFunc = call_user_func(array($this, 'mask'.$maskNo), $x, $y);
+ $bitMask[$y][$x] = ($maskFunc == 0)?1:0;
+ }
+
+ }
+ }
+
+ return $bitMask;
+ }
+
+ //----------------------------------------------------------------------
+ public static function serial($bitFrame)
+ {
+ $codeArr = array();
+
+ foreach ($bitFrame as $line)
+ $codeArr[] = join('', $line);
+
+ return gzcompress(join("\n", $codeArr), 9);
+ }
+
+ //----------------------------------------------------------------------
+ public static function unserial($code)
+ {
+ $codeArr = array();
+
+ $codeLines = explode("\n", gzuncompress($code));
+ foreach ($codeLines as $line)
+ $codeArr[] = str_split($line);
+
+ return $codeArr;
+ }
+
+ //----------------------------------------------------------------------
+ public function makeMaskNo($maskNo, $width, $s, &$d, $maskGenOnly = false)
+ {
+ $b = 0;
+ $bitMask = array();
+
+ $fileName = Constants::QR_CACHE_DIR.'mask_'.$maskNo.DIRECTORY_SEPARATOR.'mask_'.$width.'_'.$maskNo.'.dat';
+
+ if (Constants::QR_CACHEABLE) {
+ if (file_exists($fileName)) {
+ $bitMask = self::unserial(file_get_contents($fileName));
+ } else {
+ $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d);
+ if (!file_exists(Constants::QR_CACHE_DIR.'mask_'.$maskNo))
+ mkdir(Constants::QR_CACHE_DIR.'mask_'.$maskNo);
+ file_put_contents($fileName, self::serial($bitMask));
+ }
+ } else {
+ $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d);
+ }
+
+ if ($maskGenOnly)
+ return;
+
+ $d = $s;
+
+ for($y=0; $y<$width; $y++) {
+ for($x=0; $x<$width; $x++) {
+ if($bitMask[$y][$x] == 1) {
+ $d[$y][$x] = chr(ord($s[$y][$x]) ^ (int)$bitMask[$y][$x]);
+ }
+ $b += (int)(ord($d[$y][$x]) & 1);
+ }
+ }
+
+ return $b;
+ }
+
+ //----------------------------------------------------------------------
+ public function makeMask($width, $frame, $maskNo, $level)
+ {
+ $masked = array_fill(0, $width, str_repeat("\0", $width));
+ $this->makeMaskNo($maskNo, $width, $frame, $masked);
+ $this->writeFormatInformation($width, $masked, $maskNo, $level);
+
+ return $masked;
+ }
+
+ //----------------------------------------------------------------------
+ public function calcN1N3($length)
+ {
+ $demerit = 0;
+
+ for($i=0; $i<$length; $i++) {
+
+ if($this->runLength[$i] >= 5) {
+ $demerit += (Constants::N1 + ($this->runLength[$i] - 5));
+ }
+ if($i & 1) {
+ if(($i >= 3) && ($i < ($length-2)) && ($this->runLength[$i] % 3 == 0)) {
+ $fact = (int)($this->runLength[$i] / 3);
+ if(($this->runLength[$i-2] == $fact) &&
+ ($this->runLength[$i-1] == $fact) &&
+ ($this->runLength[$i+1] == $fact) &&
+ ($this->runLength[$i+2] == $fact)) {
+ if(($this->runLength[$i-3] < 0) || ($this->runLength[$i-3] >= (4 * $fact))) {
+ $demerit += Constants::N3;
+ } else if((($i+3) >= $length) || ($this->runLength[$i+3] >= (4 * $fact))) {
+ $demerit += Constants::N3;
+ }
+ }
+ }
+ }
+ }
+ return $demerit;
+ }
+
+ //----------------------------------------------------------------------
+ public function evaluateSymbol($width, $frame)
+ {
+ $head = 0;
+ $demerit = 0;
+
+ for($y=0; $y<$width; $y++) {
+ $head = 0;
+ $this->runLength[0] = 1;
+
+ $frameY = $frame[$y];
+
+ if ($y>0)
+ $frameYM = $frame[$y-1];
+
+ for($x=0; $x<$width; $x++) {
+ if(($x > 0) && ($y > 0)) {
+ $b22 = ord($frameY[$x]) & ord($frameY[$x-1]) & ord($frameYM[$x]) & ord($frameYM[$x-1]);
+ $w22 = ord($frameY[$x]) | ord($frameY[$x-1]) | ord($frameYM[$x]) | ord($frameYM[$x-1]);
+
+ if(($b22 | ($w22 ^ 1))&1) {
+ $demerit += Constants::N2;
+ }
+ }
+ if(($x == 0) && (ord($frameY[$x]) & 1)) {
+ $this->runLength[0] = -1;
+ $head = 1;
+ $this->runLength[$head] = 1;
+ } else if($x > 0) {
+ if((ord($frameY[$x]) ^ ord($frameY[$x-1])) & 1) {
+ $head++;
+ $this->runLength[$head] = 1;
+ } else {
+ $this->runLength[$head]++;
+ }
+ }
+ }
+
+ $demerit += $this->calcN1N3($head+1);
+ }
+
+ for($x=0; $x<$width; $x++) {
+ $head = 0;
+ $this->runLength[0] = 1;
+
+ for($y=0; $y<$width; $y++) {
+ if($y == 0 && (ord($frame[$y][$x]) & 1)) {
+ $this->runLength[0] = -1;
+ $head = 1;
+ $this->runLength[$head] = 1;
+ } else if($y > 0) {
+ if((ord($frame[$y][$x]) ^ ord($frame[$y-1][$x])) & 1) {
+ $head++;
+ $this->runLength[$head] = 1;
+ } else {
+ $this->runLength[$head]++;
+ }
+ }
+ }
+
+ $demerit += $this->calcN1N3($head+1);
+ }
+
+ return $demerit;
+ }
+
+
+ //----------------------------------------------------------------------
+ public function mask($width, $frame, $level)
+ {
+ $minDemerit = PHP_INT_MAX;
+ $bestMaskNum = 0;
+ $bestMask = array();
+
+ $checked_masks = array(0,1,2,3,4,5,6,7);
+
+ if (Constants::QR_FIND_FROM_RANDOM !== false) {
+
+ $howManuOut = 8-(Constants::QR_FIND_FROM_RANDOM % 9);
+ for ($i = 0; $i < $howManuOut; $i++) {
+ $remPos = rand (0, count($checked_masks)-1);
+ unset($checked_masks[$remPos]);
+ $checked_masks = array_values($checked_masks);
+ }
+
+ }
+
+ $bestMask = $frame;
+
+ foreach($checked_masks as $i) {
+ $mask = array_fill(0, $width, str_repeat("\0", $width));
+
+ $demerit = 0;
+ $blacks = 0;
+ $blacks = $this->makeMaskNo($i, $width, $frame, $mask);
+ $blacks += $this->writeFormatInformation($width, $mask, $i, $level);
+ $blacks = (int)(100 * $blacks / ($width * $width));
+ $demerit = (int)((int)(abs($blacks - 50) / 5) * Constants::N4);
+ $demerit += $this->evaluateSymbol($width, $mask);
+
+ if($demerit < $minDemerit) {
+ $minDemerit = $demerit;
+ $bestMask = $mask;
+ $bestMaskNum = $i;
+ }
+ }
+
+ return $bestMask;
+ }
+
+ //----------------------------------------------------------------------
+}
diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrawcode.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrawcode.php new file mode 100644 index 00000000..25eae7c8 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrawcode.php @@ -0,0 +1,117 @@ +<?php +/** + * QRrawcode.php + * + * Created by arielferrandini + */ + +namespace PHPQRCode; + +use Exception; + +class QRrawcode { + public $version; + public $datacode = array(); + public $ecccode = array(); + public $blocks; + public $rsblocks = array(); //of RSblock + public $count; + public $dataLength; + public $eccLength; + public $b1; + + //---------------------------------------------------------------------- + public function __construct(QRinput $input) + { + $spec = array(0,0,0,0,0); + + $this->datacode = $input->getByteStream(); + if(is_null($this->datacode)) { + throw new Exception('null input string'); + } + + QRspec::getEccSpec($input->getVersion(), $input->getErrorCorrectionLevel(), $spec); + + $this->version = $input->getVersion(); + $this->b1 = QRspec::rsBlockNum1($spec); + $this->dataLength = QRspec::rsDataLength($spec); + $this->eccLength = QRspec::rsEccLength($spec); + $this->ecccode = array_fill(0, $this->eccLength, 0); + $this->blocks = QRspec::rsBlockNum($spec); + + $ret = $this->init($spec); + if($ret < 0) { + throw new Exception('block alloc error'); + return null; + } + + $this->count = 0; + } + + //---------------------------------------------------------------------- + public function init(array $spec) + { + $dl = QRspec::rsDataCodes1($spec); + $el = QRspec::rsEccCodes1($spec); + $rs = QRrs::init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el); + + + $blockNo = 0; + $dataPos = 0; + $eccPos = 0; + for($i=0; $i<QRspec::rsBlockNum1($spec); $i++) { + $ecc = array_slice($this->ecccode,$eccPos); + $this->rsblocks[$blockNo] = new QRrsblock($dl, array_slice($this->datacode, $dataPos), $el, $ecc, $rs); + $this->ecccode = array_merge(array_slice($this->ecccode,0, $eccPos), $ecc); + + $dataPos += $dl; + $eccPos += $el; + $blockNo++; + } + + if(QRspec::rsBlockNum2($spec) == 0) + return 0; + + $dl = QRspec::rsDataCodes2($spec); + $el = QRspec::rsEccCodes2($spec); + $rs = QRrs::init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el); + + if($rs == NULL) return -1; + + for($i=0; $i<QRspec::rsBlockNum2($spec); $i++) { + $ecc = array_slice($this->ecccode,$eccPos); + $this->rsblocks[$blockNo] = new QRrsblock($dl, array_slice($this->datacode, $dataPos), $el, $ecc, $rs); + $this->ecccode = array_merge(array_slice($this->ecccode,0, $eccPos), $ecc); + + $dataPos += $dl; + $eccPos += $el; + $blockNo++; + } + + return 0; + } + + //---------------------------------------------------------------------- + public function getCode() + { + $ret = null; + + if($this->count < $this->dataLength) { + $row = $this->count % $this->blocks; + $col = $this->count / $this->blocks; + if($col >= $this->rsblocks[0]->dataLength) { + $row += $this->b1; + } + $ret = $this->rsblocks[$row]->data[$col]; + } else if($this->count < $this->dataLength + $this->eccLength) { + $row = ($this->count - $this->dataLength) % $this->blocks; + $col = ($this->count - $this->dataLength) / $this->blocks; + $ret = $this->rsblocks[$row]->ecc[$col]; + } else { + return 0; + } + $this->count++; + + return $ret; + } +} diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrs.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrs.php new file mode 100755 index 00000000..66f0d5e7 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrs.php @@ -0,0 +1,56 @@ +<?php
+/*
+ * PHP QR Code encoder
+ *
+ * Reed-Solomon error correction support
+ *
+ * Copyright (C) 2002, 2003, 2004, 2006 Phil Karn, KA9Q
+ * (libfec is released under the GNU Lesser General Public License.)
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net>
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia <deltalab at poczta dot fm>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+namespace PHPQRCode;
+
+class QRrs {
+
+ public static $items = array();
+
+ //----------------------------------------------------------------------
+ public static function init_rs($symsize, $gfpoly, $fcr, $prim, $nroots, $pad)
+ {
+ foreach(self::$items as $rs) {
+ if($rs->pad != $pad) continue;
+ if($rs->nroots != $nroots) continue;
+ if($rs->mm != $symsize) continue;
+ if($rs->gfpoly != $gfpoly) continue;
+ if($rs->fcr != $fcr) continue;
+ if($rs->prim != $prim) continue;
+
+ return $rs;
+ }
+
+ $rs = QRrsItem::init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad);
+ array_unshift(self::$items, $rs);
+
+ return $rs;
+ }
+}
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrsItem.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrsItem.php new file mode 100644 index 00000000..ce63a8c3 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrsItem.php @@ -0,0 +1,162 @@ +<?php +/** + * QRrsItem.php + * + * Created by arielferrandini + */ + +namespace PHPQRCode; + +class QRrsItem { + + public $mm; // Bits per symbol + public $nn; // Symbols per block (= (1<<mm)-1) + public $alpha_to = array(); // log lookup table + public $index_of = array(); // Antilog lookup table + public $genpoly = array(); // Generator polynomial + public $nroots; // Number of generator roots = number of parity symbols + public $fcr; // First consecutive root, index form + public $prim; // Primitive element, index form + public $iprim; // prim-th root of 1, index form + public $pad; // Padding bytes in shortened block + public $gfpoly; + + //---------------------------------------------------------------------- + public function modnn($x) + { + while ($x >= $this->nn) { + $x -= $this->nn; + $x = ($x >> $this->mm) + ($x & $this->nn); + } + + return $x; + } + + //---------------------------------------------------------------------- + public static function init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad) + { + // Common code for intializing a Reed-Solomon control block (char or int symbols) + // Copyright 2004 Phil Karn, KA9Q + // May be used under the terms of the GNU Lesser General Public License (LGPL) + + $rs = null; + + // Check parameter ranges + if($symsize < 0 || $symsize > 8) return $rs; + if($fcr < 0 || $fcr >= (1<<$symsize)) return $rs; + if($prim <= 0 || $prim >= (1<<$symsize)) return $rs; + if($nroots < 0 || $nroots >= (1<<$symsize)) return $rs; // Can't have more roots than symbol values! + if($pad < 0 || $pad >= ((1<<$symsize) -1 - $nroots)) return $rs; // Too much padding + + $rs = new QRrsItem(); + $rs->mm = $symsize; + $rs->nn = (1<<$symsize)-1; + $rs->pad = $pad; + + $rs->alpha_to = array_fill(0, $rs->nn+1, 0); + $rs->index_of = array_fill(0, $rs->nn+1, 0); + + // PHP style macro replacement ;) + $NN =& $rs->nn; + $A0 =& $NN; + + // Generate Galois field lookup tables + $rs->index_of[0] = $A0; // log(zero) = -inf + $rs->alpha_to[$A0] = 0; // alpha**-inf = 0 + $sr = 1; + + for($i=0; $i<$rs->nn; $i++) { + $rs->index_of[$sr] = $i; + $rs->alpha_to[$i] = $sr; + $sr <<= 1; + if($sr & (1<<$symsize)) { + $sr ^= $gfpoly; + } + $sr &= $rs->nn; + } + + if($sr != 1){ + // field generator polynomial is not primitive! + $rs = NULL; + return $rs; + } + + /* Form RS code generator polynomial from its roots */ + $rs->genpoly = array_fill(0, $nroots+1, 0); + + $rs->fcr = $fcr; + $rs->prim = $prim; + $rs->nroots = $nroots; + $rs->gfpoly = $gfpoly; + + /* Find prim-th root of 1, used in decoding */ + for($iprim=1;($iprim % $prim) != 0;$iprim += $rs->nn) + ; // intentional empty-body loop! + + $rs->iprim = (int)($iprim / $prim); + $rs->genpoly[0] = 1; + + for ($i = 0,$root=$fcr*$prim; $i < $nroots; $i++, $root += $prim) { + $rs->genpoly[$i+1] = 1; + + // Multiply rs->genpoly[] by @**(root + x) + for ($j = $i; $j > 0; $j--) { + if ($rs->genpoly[$j] != 0) { + $rs->genpoly[$j] = $rs->genpoly[$j-1] ^ $rs->alpha_to[$rs->modnn($rs->index_of[$rs->genpoly[$j]] + $root)]; + } else { + $rs->genpoly[$j] = $rs->genpoly[$j-1]; + } + } + // rs->genpoly[0] can never be zero + $rs->genpoly[0] = $rs->alpha_to[$rs->modnn($rs->index_of[$rs->genpoly[0]] + $root)]; + } + + // convert rs->genpoly[] to index form for quicker encoding + for ($i = 0; $i <= $nroots; $i++) + $rs->genpoly[$i] = $rs->index_of[$rs->genpoly[$i]]; + + return $rs; + } + + //---------------------------------------------------------------------- + public function encode_rs_char($data, &$parity) + { + $MM =& $this->mm; + $NN =& $this->nn; + $ALPHA_TO =& $this->alpha_to; + $INDEX_OF =& $this->index_of; + $GENPOLY =& $this->genpoly; + $NROOTS =& $this->nroots; + $FCR =& $this->fcr; + $PRIM =& $this->prim; + $IPRIM =& $this->iprim; + $PAD =& $this->pad; + $A0 =& $NN; + + $parity = array_fill(0, $NROOTS, 0); + + for($i=0; $i< ($NN-$NROOTS-$PAD); $i++) { + + $feedback = $INDEX_OF[$data[$i] ^ $parity[0]]; + if($feedback != $A0) { + // feedback term is non-zero + + // This line is unnecessary when GENPOLY[NROOTS] is unity, as it must + // always be for the polynomials constructed by init_rs() + $feedback = $this->modnn($NN - $GENPOLY[$NROOTS] + $feedback); + + for($j=1;$j<$NROOTS;$j++) { + $parity[$j] ^= $ALPHA_TO[$this->modnn($feedback + $GENPOLY[$NROOTS-$j])]; + } + } + + // Shift + array_shift($parity); + if($feedback != $A0) { + array_push($parity, $ALPHA_TO[$this->modnn($feedback + $GENPOLY[0])]); + } else { + array_push($parity, 0); + } + } + } +}
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrsblock.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrsblock.php new file mode 100644 index 00000000..c1d01f22 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRrsblock.php @@ -0,0 +1,25 @@ +<?php +/** + * QRrsblock.php + * + * Created by arielferrandini + */ + +namespace PHPQRCode; + +class QRrsblock { + public $dataLength; + public $data = array(); + public $eccLength; + public $ecc = array(); + + public function __construct($dl, $data, $el, &$ecc, QRrsItem $rs) + { + $rs->encode_rs_char($data, $ecc); + + $this->dataLength = $dl; + $this->data = $data; + $this->eccLength = $el; + $this->ecc = $ecc; + } +};
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRspec.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRspec.php new file mode 100755 index 00000000..d6843260 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRspec.php @@ -0,0 +1,586 @@ +<?php
+/*
+ * PHP QR Code encoder
+ *
+ * QR Code specifications
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net>
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia <deltalab at poczta dot fm>
+ *
+ * The following data / specifications are taken from
+ * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004)
+ * or
+ * "Automatic identification and data capture techniques --
+ * QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+namespace PHPQRCode;
+
+class QRspec {
+
+ public static $capacity = array(
+ array( 0, 0, 0, array( 0, 0, 0, 0)),
+ array( 21, 26, 0, array( 7, 10, 13, 17)), // 1
+ array( 25, 44, 7, array( 10, 16, 22, 28)),
+ array( 29, 70, 7, array( 15, 26, 36, 44)),
+ array( 33, 100, 7, array( 20, 36, 52, 64)),
+ array( 37, 134, 7, array( 26, 48, 72, 88)), // 5
+ array( 41, 172, 7, array( 36, 64, 96, 112)),
+ array( 45, 196, 0, array( 40, 72, 108, 130)),
+ array( 49, 242, 0, array( 48, 88, 132, 156)),
+ array( 53, 292, 0, array( 60, 110, 160, 192)),
+ array( 57, 346, 0, array( 72, 130, 192, 224)), //10
+ array( 61, 404, 0, array( 80, 150, 224, 264)),
+ array( 65, 466, 0, array( 96, 176, 260, 308)),
+ array( 69, 532, 0, array( 104, 198, 288, 352)),
+ array( 73, 581, 3, array( 120, 216, 320, 384)),
+ array( 77, 655, 3, array( 132, 240, 360, 432)), //15
+ array( 81, 733, 3, array( 144, 280, 408, 480)),
+ array( 85, 815, 3, array( 168, 308, 448, 532)),
+ array( 89, 901, 3, array( 180, 338, 504, 588)),
+ array( 93, 991, 3, array( 196, 364, 546, 650)),
+ array( 97, 1085, 3, array( 224, 416, 600, 700)), //20
+ array(101, 1156, 4, array( 224, 442, 644, 750)),
+ array(105, 1258, 4, array( 252, 476, 690, 816)),
+ array(109, 1364, 4, array( 270, 504, 750, 900)),
+ array(113, 1474, 4, array( 300, 560, 810, 960)),
+ array(117, 1588, 4, array( 312, 588, 870, 1050)), //25
+ array(121, 1706, 4, array( 336, 644, 952, 1110)),
+ array(125, 1828, 4, array( 360, 700, 1020, 1200)),
+ array(129, 1921, 3, array( 390, 728, 1050, 1260)),
+ array(133, 2051, 3, array( 420, 784, 1140, 1350)),
+ array(137, 2185, 3, array( 450, 812, 1200, 1440)), //30
+ array(141, 2323, 3, array( 480, 868, 1290, 1530)),
+ array(145, 2465, 3, array( 510, 924, 1350, 1620)),
+ array(149, 2611, 3, array( 540, 980, 1440, 1710)),
+ array(153, 2761, 3, array( 570, 1036, 1530, 1800)),
+ array(157, 2876, 0, array( 570, 1064, 1590, 1890)), //35
+ array(161, 3034, 0, array( 600, 1120, 1680, 1980)),
+ array(165, 3196, 0, array( 630, 1204, 1770, 2100)),
+ array(169, 3362, 0, array( 660, 1260, 1860, 2220)),
+ array(173, 3532, 0, array( 720, 1316, 1950, 2310)),
+ array(177, 3706, 0, array( 750, 1372, 2040, 2430)) //40
+ );
+
+ //----------------------------------------------------------------------
+ public static function getDataLength($version, $level)
+ {
+ return self::$capacity[$version][Constants::QRCAP_WORDS] - self::$capacity[$version][Constants::QRCAP_EC][$level];
+ }
+
+ //----------------------------------------------------------------------
+ public static function getECCLength($version, $level)
+ {
+ return self::$capacity[$version][Constants::QRCAP_EC][$level];
+ }
+
+ //----------------------------------------------------------------------
+ public static function getWidth($version)
+ {
+ return self::$capacity[$version][Constants::QRCAP_WIDTH];
+ }
+
+ //----------------------------------------------------------------------
+ public static function getRemainder($version)
+ {
+ return self::$capacity[$version][Constants::QRCAP_REMINDER];
+ }
+
+ //----------------------------------------------------------------------
+ public static function getMinimumVersion($size, $level)
+ {
+
+ for($i=1; $i<= Constants::QRSPEC_VERSION_MAX; $i++) {
+ $words = self::$capacity[$i][Constants::QRCAP_WORDS] - self::$capacity[$i][Constants::QRCAP_EC][$level];
+ if($words >= $size)
+ return $i;
+ }
+
+ return -1;
+ }
+
+ //######################################################################
+
+ public static $lengthTableBits = array(
+ array(10, 12, 14),
+ array( 9, 11, 13),
+ array( 8, 16, 16),
+ array( 8, 10, 12)
+ );
+
+ //----------------------------------------------------------------------
+ public static function lengthIndicator($mode, $version)
+ {
+ if ($mode == Constants::QR_MODE_STRUCTURE)
+ return 0;
+
+ if ($version <= 9) {
+ $l = 0;
+ } else if ($version <= 26) {
+ $l = 1;
+ } else {
+ $l = 2;
+ }
+
+ return self::$lengthTableBits[$mode][$l];
+ }
+
+ //----------------------------------------------------------------------
+ public static function maximumWords($mode, $version)
+ {
+ if($mode == Constants::QR_MODE_STRUCTURE)
+ return 3;
+
+ if($version <= 9) {
+ $l = 0;
+ } else if($version <= 26) {
+ $l = 1;
+ } else {
+ $l = 2;
+ }
+
+ $bits = self::$lengthTableBits[$mode][$l];
+ $words = (1 << $bits) - 1;
+
+ if($mode == Constants::QR_MODE_KANJI) {
+ $words *= 2; // the number of bytes is required
+ }
+
+ return $words;
+ }
+
+ // Error correction code -----------------------------------------------
+ // Table of the error correction code (Reed-Solomon block)
+ // See Table 12-16 (pp.30-36), JIS X0510:2004.
+
+ public static $eccTable = array(
+ array(array( 0, 0), array( 0, 0), array( 0, 0), array( 0, 0)),
+ array(array( 1, 0), array( 1, 0), array( 1, 0), array( 1, 0)), // 1
+ array(array( 1, 0), array( 1, 0), array( 1, 0), array( 1, 0)),
+ array(array( 1, 0), array( 1, 0), array( 2, 0), array( 2, 0)),
+ array(array( 1, 0), array( 2, 0), array( 2, 0), array( 4, 0)),
+ array(array( 1, 0), array( 2, 0), array( 2, 2), array( 2, 2)), // 5
+ array(array( 2, 0), array( 4, 0), array( 4, 0), array( 4, 0)),
+ array(array( 2, 0), array( 4, 0), array( 2, 4), array( 4, 1)),
+ array(array( 2, 0), array( 2, 2), array( 4, 2), array( 4, 2)),
+ array(array( 2, 0), array( 3, 2), array( 4, 4), array( 4, 4)),
+ array(array( 2, 2), array( 4, 1), array( 6, 2), array( 6, 2)), //10
+ array(array( 4, 0), array( 1, 4), array( 4, 4), array( 3, 8)),
+ array(array( 2, 2), array( 6, 2), array( 4, 6), array( 7, 4)),
+ array(array( 4, 0), array( 8, 1), array( 8, 4), array(12, 4)),
+ array(array( 3, 1), array( 4, 5), array(11, 5), array(11, 5)),
+ array(array( 5, 1), array( 5, 5), array( 5, 7), array(11, 7)), //15
+ array(array( 5, 1), array( 7, 3), array(15, 2), array( 3, 13)),
+ array(array( 1, 5), array(10, 1), array( 1, 15), array( 2, 17)),
+ array(array( 5, 1), array( 9, 4), array(17, 1), array( 2, 19)),
+ array(array( 3, 4), array( 3, 11), array(17, 4), array( 9, 16)),
+ array(array( 3, 5), array( 3, 13), array(15, 5), array(15, 10)), //20
+ array(array( 4, 4), array(17, 0), array(17, 6), array(19, 6)),
+ array(array( 2, 7), array(17, 0), array( 7, 16), array(34, 0)),
+ array(array( 4, 5), array( 4, 14), array(11, 14), array(16, 14)),
+ array(array( 6, 4), array( 6, 14), array(11, 16), array(30, 2)),
+ array(array( 8, 4), array( 8, 13), array( 7, 22), array(22, 13)), //25
+ array(array(10, 2), array(19, 4), array(28, 6), array(33, 4)),
+ array(array( 8, 4), array(22, 3), array( 8, 26), array(12, 28)),
+ array(array( 3, 10), array( 3, 23), array( 4, 31), array(11, 31)),
+ array(array( 7, 7), array(21, 7), array( 1, 37), array(19, 26)),
+ array(array( 5, 10), array(19, 10), array(15, 25), array(23, 25)), //30
+ array(array(13, 3), array( 2, 29), array(42, 1), array(23, 28)),
+ array(array(17, 0), array(10, 23), array(10, 35), array(19, 35)),
+ array(array(17, 1), array(14, 21), array(29, 19), array(11, 46)),
+ array(array(13, 6), array(14, 23), array(44, 7), array(59, 1)),
+ array(array(12, 7), array(12, 26), array(39, 14), array(22, 41)), //35
+ array(array( 6, 14), array( 6, 34), array(46, 10), array( 2, 64)),
+ array(array(17, 4), array(29, 14), array(49, 10), array(24, 46)),
+ array(array( 4, 18), array(13, 32), array(48, 14), array(42, 32)),
+ array(array(20, 4), array(40, 7), array(43, 22), array(10, 67)),
+ array(array(19, 6), array(18, 31), array(34, 34), array(20, 61)),//40
+ );
+
+ //----------------------------------------------------------------------
+ // CACHEABLE!!!
+
+ public static function getEccSpec($version, $level, array &$spec)
+ {
+ if (count($spec) < 5) {
+ $spec = array(0,0,0,0,0);
+ }
+
+ $b1 = self::$eccTable[$version][$level][0];
+ $b2 = self::$eccTable[$version][$level][1];
+ $data = self::getDataLength($version, $level);
+ $ecc = self::getECCLength($version, $level);
+
+ if($b2 == 0) {
+ $spec[0] = $b1;
+ $spec[1] = (int)($data / $b1);
+ $spec[2] = (int)($ecc / $b1);
+ $spec[3] = 0;
+ $spec[4] = 0;
+ } else {
+ $spec[0] = $b1;
+ $spec[1] = (int)($data / ($b1 + $b2));
+ $spec[2] = (int)($ecc / ($b1 + $b2));
+ $spec[3] = $b2;
+ $spec[4] = $spec[1] + 1;
+ }
+ }
+
+ // Alignment pattern ---------------------------------------------------
+
+ // Positions of alignment patterns.
+ // This array includes only the second and the third position of the
+ // alignment patterns. Rest of them can be calculated from the distance
+ // between them.
+
+ // See Table 1 in Appendix E (pp.71) of JIS X0510:2004.
+
+ public static $alignmentPattern = array(
+ array( 0, 0),
+ array( 0, 0), array(18, 0), array(22, 0), array(26, 0), array(30, 0), // 1- 5
+ array(34, 0), array(22, 38), array(24, 42), array(26, 46), array(28, 50), // 6-10
+ array(30, 54), array(32, 58), array(34, 62), array(26, 46), array(26, 48), //11-15
+ array(26, 50), array(30, 54), array(30, 56), array(30, 58), array(34, 62), //16-20
+ array(28, 50), array(26, 50), array(30, 54), array(28, 54), array(32, 58), //21-25
+ array(30, 58), array(34, 62), array(26, 50), array(30, 54), array(26, 52), //26-30
+ array(30, 56), array(34, 60), array(30, 58), array(34, 62), array(30, 54), //31-35
+ array(24, 50), array(28, 54), array(32, 58), array(26, 54), array(30, 58), //35-40
+ );
+
+
+ /** --------------------------------------------------------------------
+ * Put an alignment marker.
+ * @param frame
+ * @param width
+ * @param ox,oy center coordinate of the pattern
+ */
+ public static function putAlignmentMarker(array &$frame, $ox, $oy)
+ {
+ $finder = array(
+ "\xa1\xa1\xa1\xa1\xa1",
+ "\xa1\xa0\xa0\xa0\xa1",
+ "\xa1\xa0\xa1\xa0\xa1",
+ "\xa1\xa0\xa0\xa0\xa1",
+ "\xa1\xa1\xa1\xa1\xa1"
+ );
+
+ $yStart = $oy-2;
+ $xStart = $ox-2;
+
+ for($y=0; $y<5; $y++) {
+ QRstr::set($frame, $xStart, $yStart+$y, $finder[$y]);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function putAlignmentPattern($version, &$frame, $width)
+ {
+ if($version < 2)
+ return;
+
+ $d = self::$alignmentPattern[$version][1] - self::$alignmentPattern[$version][0];
+ if($d < 0) {
+ $w = 2;
+ } else {
+ $w = (int)(($width - self::$alignmentPattern[$version][0]) / $d + 2);
+ }
+
+ if($w * $w - 3 == 1) {
+ $x = self::$alignmentPattern[$version][0];
+ $y = self::$alignmentPattern[$version][0];
+ self::putAlignmentMarker($frame, $x, $y);
+ return;
+ }
+
+ $cx = self::$alignmentPattern[$version][0];
+ for($x=1; $x<$w - 1; $x++) {
+ self::putAlignmentMarker($frame, 6, $cx);
+ self::putAlignmentMarker($frame, $cx, 6);
+ $cx += $d;
+ }
+
+ $cy = self::$alignmentPattern[$version][0];
+ for($y=0; $y<$w-1; $y++) {
+ $cx = self::$alignmentPattern[$version][0];
+ for($x=0; $x<$w-1; $x++) {
+ self::putAlignmentMarker($frame, $cx, $cy);
+ $cx += $d;
+ }
+ $cy += $d;
+ }
+ }
+
+ // Version information pattern -----------------------------------------
+
+ // Version information pattern (BCH coded).
+ // See Table 1 in Appendix D (pp.68) of JIS X0510:2004.
+
+ // size: [Constants::QRSPEC_VERSION_MAX - 6]
+
+ public static $versionPattern = array(
+ 0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d,
+ 0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9,
+ 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75,
+ 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64,
+ 0x27541, 0x28c69
+ );
+
+ //----------------------------------------------------------------------
+ public static function getVersionPattern($version)
+ {
+ if($version < 7 || $version > Constants::QRSPEC_VERSION_MAX)
+ return 0;
+
+ return self::$versionPattern[$version -7];
+ }
+
+ // Format information --------------------------------------------------
+ // See calcFormatInfo in tests/test_qrspec.c (orginal qrencode c lib)
+
+ public static $formatInfo = array(
+ array(0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976),
+ array(0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0),
+ array(0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed),
+ array(0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b)
+ );
+
+ public static function getFormatInfo($mask, $level)
+ {
+ if($mask < 0 || $mask > 7)
+ return 0;
+
+ if($level < 0 || $level > 3)
+ return 0;
+
+ return self::$formatInfo[$level][$mask];
+ }
+
+ // Frame ---------------------------------------------------------------
+ // Cache of initial frames.
+
+ public static $frames = array();
+
+ /** --------------------------------------------------------------------
+ * Put a finder pattern.
+ * @param frame
+ * @param width
+ * @param ox,oy upper-left coordinate of the pattern
+ */
+ public static function putFinderPattern(&$frame, $ox, $oy)
+ {
+ $finder = array(
+ "\xc1\xc1\xc1\xc1\xc1\xc1\xc1",
+ "\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
+ "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
+ "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
+ "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
+ "\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
+ "\xc1\xc1\xc1\xc1\xc1\xc1\xc1"
+ );
+
+ for($y=0; $y<7; $y++) {
+ QRstr::set($frame, $ox, $oy+$y, $finder[$y]);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function createFrame($version)
+ {
+ $width = self::$capacity[$version][Constants::QRCAP_WIDTH];
+ $frameLine = str_repeat ("\0", $width);
+ $frame = array_fill(0, $width, $frameLine);
+
+ // Finder pattern
+ self::putFinderPattern($frame, 0, 0);
+ self::putFinderPattern($frame, $width - 7, 0);
+ self::putFinderPattern($frame, 0, $width - 7);
+
+ // Separator
+ $yOffset = $width - 7;
+
+ for($y=0; $y<7; $y++) {
+ $frame[$y][7] = "\xc0";
+ $frame[$y][$width - 8] = "\xc0";
+ $frame[$yOffset][7] = "\xc0";
+ $yOffset++;
+ }
+
+ $setPattern = str_repeat("\xc0", 8);
+
+ QRstr::set($frame, 0, 7, $setPattern);
+ QRstr::set($frame, $width-8, 7, $setPattern);
+ QRstr::set($frame, 0, $width - 8, $setPattern);
+
+ // Format info
+ $setPattern = str_repeat("\x84", 9);
+ QRstr::set($frame, 0, 8, $setPattern);
+ QRstr::set($frame, $width - 8, 8, $setPattern, 8);
+
+ $yOffset = $width - 8;
+
+ for($y=0; $y<8; $y++,$yOffset++) {
+ $frame[$y][8] = "\x84";
+ $frame[$yOffset][8] = "\x84";
+ }
+
+ // Timing pattern
+
+ for($i=1; $i<$width-15; $i++) {
+ $frame[6][7+$i] = chr(0x90 | ($i & 1));
+ $frame[7+$i][6] = chr(0x90 | ($i & 1));
+ }
+
+ // Alignment pattern
+ self::putAlignmentPattern($version, $frame, $width);
+
+ // Version information
+ if($version >= 7) {
+ $vinf = self::getVersionPattern($version);
+
+ $v = $vinf;
+
+ for($x=0; $x<6; $x++) {
+ for($y=0; $y<3; $y++) {
+ $frame[($width - 11)+$y][$x] = chr(0x88 | ($v & 1));
+ $v = $v >> 1;
+ }
+ }
+
+ $v = $vinf;
+ for($y=0; $y<6; $y++) {
+ for($x=0; $x<3; $x++) {
+ $frame[$y][$x+($width - 11)] = chr(0x88 | ($v & 1));
+ $v = $v >> 1;
+ }
+ }
+ }
+
+ // and a little bit...
+ $frame[$width - 8][8] = "\x81";
+
+ return $frame;
+ }
+
+ //----------------------------------------------------------------------
+ public static function debug($frame, $binary_mode = false)
+ {
+ if ($binary_mode) {
+
+ foreach ($frame as &$frameLine) {
+ $frameLine = join('<span class="m"> </span>', explode('0', $frameLine));
+ $frameLine = join('██', explode('1', $frameLine));
+ }
+
+ ?>
+ <style>
+ .m { background-color: white; }
+ </style>
+ <?php
+ echo '<pre><tt><br/ ><br/ ><br/ > ';
+ echo join("<br/ > ", $frame);
+ echo '</tt></pre><br/ ><br/ ><br/ ><br/ ><br/ ><br/ >';
+
+ } else {
+
+ foreach ($frame as &$frameLine) {
+ $frameLine = join('<span class="m"> </span>', explode("\xc0", $frameLine));
+ $frameLine = join('<span class="m">▒</span>', explode("\xc1", $frameLine));
+ $frameLine = join('<span class="p"> </span>', explode("\xa0", $frameLine));
+ $frameLine = join('<span class="p">▒</span>', explode("\xa1", $frameLine));
+ $frameLine = join('<span class="s">◇</span>', explode("\x84", $frameLine)); //format 0
+ $frameLine = join('<span class="s">◆</span>', explode("\x85", $frameLine)); //format 1
+ $frameLine = join('<span class="x">☢</span>', explode("\x81", $frameLine)); //special bit
+ $frameLine = join('<span class="c"> </span>', explode("\x90", $frameLine)); //clock 0
+ $frameLine = join('<span class="c">◷</span>', explode("\x91", $frameLine)); //clock 1
+ $frameLine = join('<span class="f"> </span>', explode("\x88", $frameLine)); //version
+ $frameLine = join('<span class="f">▒</span>', explode("\x89", $frameLine)); //version
+ $frameLine = join('♦', explode("\x01", $frameLine));
+ $frameLine = join('⋅', explode("\0", $frameLine));
+ }
+
+ ?>
+ <style>
+ .p { background-color: yellow; }
+ .m { background-color: #00FF00; }
+ .s { background-color: #FF0000; }
+ .c { background-color: aqua; }
+ .x { background-color: pink; }
+ .f { background-color: gold; }
+ </style>
+ <?php
+ echo "<pre><tt>";
+ echo join("<br/ >", $frame);
+ echo "</tt></pre>";
+
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function serial($frame)
+ {
+ return gzcompress(join("\n", $frame), 9);
+ }
+
+ //----------------------------------------------------------------------
+ public static function unserial($code)
+ {
+ return explode("\n", gzuncompress($code));
+ }
+
+ //----------------------------------------------------------------------
+ public static function newFrame($version)
+ {
+ if($version < 1 || $version > Constants::QRSPEC_VERSION_MAX)
+ return null;
+
+ if(!isset(self::$frames[$version])) {
+
+ $fileName = Constants::QR_CACHE_DIR.'frame_'.$version.'.dat';
+
+ if (Constants::QR_CACHEABLE) {
+ if (file_exists($fileName)) {
+ self::$frames[$version] = self::unserial(file_get_contents($fileName));
+ } else {
+ self::$frames[$version] = self::createFrame($version);
+ file_put_contents($fileName, self::serial(self::$frames[$version]));
+ }
+ } else {
+ self::$frames[$version] = self::createFrame($version);
+ }
+ }
+
+ if(is_null(self::$frames[$version]))
+ return null;
+
+ return self::$frames[$version];
+ }
+
+ //----------------------------------------------------------------------
+ public static function rsBlockNum($spec) { return $spec[0] + $spec[3]; }
+ public static function rsBlockNum1($spec) { return $spec[0]; }
+ public static function rsDataCodes1($spec) { return $spec[1]; }
+ public static function rsEccCodes1($spec) { return $spec[2]; }
+ public static function rsBlockNum2($spec) { return $spec[3]; }
+ public static function rsDataCodes2($spec) { return $spec[4]; }
+ public static function rsEccCodes2($spec) { return $spec[2]; }
+ public static function rsDataLength($spec) { return ($spec[0] * $spec[1]) + ($spec[3] * $spec[4]); }
+ public static function rsEccLength($spec) { return ($spec[0] + $spec[3]) * $spec[2]; }
+
+}
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRsplit.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRsplit.php new file mode 100755 index 00000000..805140a9 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRsplit.php @@ -0,0 +1,316 @@ +<?php
+/*
+ * PHP QR Code encoder
+ *
+ * Input splitting classes
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net>
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia <deltalab at poczta dot fm>
+ *
+ * The following data / specifications are taken from
+ * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004)
+ * or
+ * "Automatic identification and data capture techniques --
+ * QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+namespace PHPQRCode;
+
+use Exception;
+
+class QRsplit {
+
+ public $dataStr = '';
+ public $input;
+ public $modeHint;
+
+ //----------------------------------------------------------------------
+ public function __construct($dataStr, $input, $modeHint)
+ {
+ $this->dataStr = $dataStr;
+ $this->input = $input;
+ $this->modeHint = $modeHint;
+ }
+
+ //----------------------------------------------------------------------
+ public static function isdigitat($str, $pos)
+ {
+ if ($pos >= strlen($str))
+ return false;
+
+ return ((ord($str[$pos]) >= ord('0'))&&(ord($str[$pos]) <= ord('9')));
+ }
+
+ //----------------------------------------------------------------------
+ public static function isalnumat($str, $pos)
+ {
+ if ($pos >= strlen($str))
+ return false;
+
+ return (QRinput::lookAnTable(ord($str[$pos])) >= 0);
+ }
+
+ //----------------------------------------------------------------------
+ public function identifyMode($pos)
+ {
+ if ($pos >= strlen($this->dataStr))
+ return Constants::QR_MODE_NUL;
+
+ $c = $this->dataStr[$pos];
+
+ if(self::isdigitat($this->dataStr, $pos)) {
+ return Constants::QR_MODE_NUM;
+ } else if(self::isalnumat($this->dataStr, $pos)) {
+ return Constants::QR_MODE_AN;
+ } else if($this->modeHint == Constants::QR_MODE_KANJI) {
+
+ if ($pos+1 < strlen($this->dataStr))
+ {
+ $d = $this->dataStr[$pos+1];
+ $word = (ord($c) << 8) | ord($d);
+ if(($word >= 0x8140 && $word <= 0x9ffc) || ($word >= 0xe040 && $word <= 0xebbf)) {
+ return Constants::QR_MODE_KANJI;
+ }
+ }
+ }
+
+ return Constants::QR_MODE_8;
+ }
+
+ //----------------------------------------------------------------------
+ public function eatNum()
+ {
+ $ln = QRspec::lengthIndicator(Constants::QR_MODE_NUM, $this->input->getVersion());
+
+ $p = 0;
+ while(self::isdigitat($this->dataStr, $p)) {
+ $p++;
+ }
+
+ $run = $p;
+ $mode = $this->identifyMode($p);
+
+ if($mode == Constants::QR_MODE_8) {
+ $dif = QRinput::estimateBitsModeNum($run) + 4 + $ln
+ + QRinput::estimateBitsMode8(1) // + 4 + l8
+ - QRinput::estimateBitsMode8($run + 1); // - 4 - l8
+ if($dif > 0) {
+ return $this->eat8();
+ }
+ }
+ if($mode == Constants::QR_MODE_AN) {
+ $dif = QRinput::estimateBitsModeNum($run) + 4 + $ln
+ + QRinput::estimateBitsModeAn(1) // + 4 + la
+ - QRinput::estimateBitsModeAn($run + 1);// - 4 - la
+ if($dif > 0) {
+ return $this->eatAn();
+ }
+ }
+
+ $ret = $this->input->append(Constants::QR_MODE_NUM, $run, str_split($this->dataStr));
+ if($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ //----------------------------------------------------------------------
+ public function eatAn()
+ {
+ $la = QRspec::lengthIndicator(Constants::QR_MODE_AN, $this->input->getVersion());
+ $ln = QRspec::lengthIndicator(Constants::QR_MODE_NUM, $this->input->getVersion());
+
+ $p = 0;
+
+ while(self::isalnumat($this->dataStr, $p)) {
+ if(self::isdigitat($this->dataStr, $p)) {
+ $q = $p;
+ while(self::isdigitat($this->dataStr, $q)) {
+ $q++;
+ }
+
+ $dif = QRinput::estimateBitsModeAn($p) // + 4 + la
+ + QRinput::estimateBitsModeNum($q - $p) + 4 + $ln
+ - QRinput::estimateBitsModeAn($q); // - 4 - la
+
+ if($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } else {
+ $p++;
+ }
+ }
+
+ $run = $p;
+
+ if(!self::isalnumat($this->dataStr, $p)) {
+ $dif = QRinput::estimateBitsModeAn($run) + 4 + $la
+ + QRinput::estimateBitsMode8(1) // + 4 + l8
+ - QRinput::estimateBitsMode8($run + 1); // - 4 - l8
+ if($dif > 0) {
+ return $this->eat8();
+ }
+ }
+
+ $ret = $this->input->append(Constants::QR_MODE_AN, $run, str_split($this->dataStr));
+ if($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ //----------------------------------------------------------------------
+ public function eatKanji()
+ {
+ $p = 0;
+
+ while($this->identifyMode($p) == Constants::QR_MODE_KANJI) {
+ $p += 2;
+ }
+
+ $ret = $this->input->append(Constants::QR_MODE_KANJI, $p, str_split($this->dataStr));
+ if($ret < 0)
+ return -1;
+
+ return $ret;
+ }
+
+ //----------------------------------------------------------------------
+ public function eat8()
+ {
+ $la = QRspec::lengthIndicator(Constants::QR_MODE_AN, $this->input->getVersion());
+ $ln = QRspec::lengthIndicator(Constants::QR_MODE_NUM, $this->input->getVersion());
+
+ $p = 1;
+ $dataStrLen = strlen($this->dataStr);
+
+ while($p < $dataStrLen) {
+
+ $mode = $this->identifyMode($p);
+ if($mode == Constants::QR_MODE_KANJI) {
+ break;
+ }
+ if($mode == Constants::QR_MODE_NUM) {
+ $q = $p;
+ while(self::isdigitat($this->dataStr, $q)) {
+ $q++;
+ }
+ $dif = QRinput::estimateBitsMode8($p) // + 4 + l8
+ + QRinput::estimateBitsModeNum($q - $p) + 4 + $ln
+ - QRinput::estimateBitsMode8($q); // - 4 - l8
+ if($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } else if($mode == Constants::QR_MODE_AN) {
+ $q = $p;
+ while(self::isalnumat($this->dataStr, $q)) {
+ $q++;
+ }
+ $dif = QRinput::estimateBitsMode8($p) // + 4 + l8
+ + QRinput::estimateBitsModeAn($q - $p) + 4 + $la
+ - QRinput::estimateBitsMode8($q); // - 4 - l8
+ if($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } else {
+ $p++;
+ }
+ }
+
+ $run = $p;
+ $ret = $this->input->append(Constants::QR_MODE_8, $run, str_split($this->dataStr));
+
+ if($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ //----------------------------------------------------------------------
+ public function splitString()
+ {
+ while (strlen($this->dataStr) > 0)
+ {
+ if($this->dataStr == '')
+ return 0;
+
+ $mode = $this->identifyMode(0);
+
+ switch ($mode) {
+ case Constants::QR_MODE_NUM: $length = $this->eatNum(); break;
+ case Constants::QR_MODE_AN: $length = $this->eatAn(); break;
+ case Constants::QR_MODE_KANJI:
+ if ($hint == Constants::QR_MODE_KANJI)
+ $length = $this->eatKanji();
+ else $length = $this->eat8();
+ break;
+ default: $length = $this->eat8(); break;
+
+ }
+
+ if($length == 0) return 0;
+ if($length < 0) return -1;
+
+ $this->dataStr = substr($this->dataStr, $length);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function toUpper()
+ {
+ $stringLen = strlen($this->dataStr);
+ $p = 0;
+
+ while ($p<$stringLen) {
+ $mode = self::identifyMode(substr($this->dataStr, $p), $this->modeHint);
+ if($mode == Constants::QR_MODE_KANJI) {
+ $p += 2;
+ } else {
+ if (ord($this->dataStr[$p]) >= ord('a') && ord($this->dataStr[$p]) <= ord('z')) {
+ $this->dataStr[$p] = chr(ord($this->dataStr[$p]) - 32);
+ }
+ $p++;
+ }
+ }
+
+ return $this->dataStr;
+ }
+
+ //----------------------------------------------------------------------
+ public static function splitStringToQRinput($string, QRinput $input, $modeHint, $casesensitive = true)
+ {
+ if(is_null($string) || $string == '\0' || $string == '') {
+ throw new Exception('empty string!!!');
+ }
+
+ $split = new QRsplit($string, $input, $modeHint);
+
+ if(!$casesensitive)
+ $split->toUpper();
+
+ return $split->splitString();
+ }
+}
diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRstr.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRstr.php new file mode 100644 index 00000000..64c4bd5c --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRstr.php @@ -0,0 +1,35 @@ +<?php + +/* + * PHP QR Code encoder + * + * Common constants + * + * Based on libqrencode C library distributed under LGPL 2.1 + * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net> + * + * PHP QR Code is distributed under LGPL 3 + * Copyright (C) 2010 Dominik Dzienia <deltalab at poczta dot fm> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace PHPQRCode; + +class QRstr { + public static function set(&$srctab, $x, $y, $repl, $replLen = false) { + $srctab[$y] = substr_replace($srctab[$y], ($replLen !== false)?substr($repl,0,$replLen):$repl, $x, ($replLen !== false)?$replLen:strlen($repl)); + } +}
\ No newline at end of file diff --git a/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRtools.php b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRtools.php new file mode 100755 index 00000000..7c75a6e2 --- /dev/null +++ b/vendor/aferrandini/phpqrcode/lib/PHPQRCode/QRtools.php @@ -0,0 +1,171 @@ +<?php
+/*
+ * PHP QR Code encoder
+ *
+ * Toolset, handy and debug utilites.
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia <deltalab at poczta dot fm>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+namespace PHPQRCode;
+
+class QRtools {
+
+ //----------------------------------------------------------------------
+ public static function binarize($frame)
+ {
+ $len = count($frame);
+ foreach ($frame as &$frameLine) {
+
+ for($i=0; $i<$len; $i++) {
+ $frameLine[$i] = (ord($frameLine[$i])&1)?'1':'0';
+ }
+ }
+
+ return $frame;
+ }
+
+ //----------------------------------------------------------------------
+ public static function tcpdfBarcodeArray($code, $mode = 'QR,L', $tcPdfVersion = '4.5.037')
+ {
+ $barcode_array = array();
+
+ if (!is_array($mode))
+ $mode = explode(',', $mode);
+
+ $eccLevel = 'L';
+
+ if (count($mode) > 1) {
+ $eccLevel = $mode[1];
+ }
+
+ $qrTab = QRcode::text($code, false, $eccLevel);
+ $size = count($qrTab);
+
+ $barcode_array['num_rows'] = $size;
+ $barcode_array['num_cols'] = $size;
+ $barcode_array['bcode'] = array();
+
+ foreach ($qrTab as $line) {
+ $arrAdd = array();
+ foreach(str_split($line) as $char)
+ $arrAdd[] = ($char=='1')?1:0;
+ $barcode_array['bcode'][] = $arrAdd;
+ }
+
+ return $barcode_array;
+ }
+
+ //----------------------------------------------------------------------
+ public static function clearCache()
+ {
+ self::$frames = array();
+ }
+
+ //----------------------------------------------------------------------
+ public static function buildCache()
+ {
+ QRtools::markTime('before_build_cache');
+
+ $mask = new QRmask();
+ for ($a=1; $a <= Constants::QRSPEC_VERSION_MAX; $a++) {
+ $frame = QRspec::newFrame($a);
+ if (Constants::QR_IMAGE) {
+ $fileName = Constants::QR_CACHE_DIR.'frame_'.$a.'.png';
+ QRimage::png(self::binarize($frame), $fileName, 1, 0);
+ }
+
+ $width = count($frame);
+ $bitMask = array_fill(0, $width, array_fill(0, $width, 0));
+ for ($maskNo=0; $maskNo<8; $maskNo++)
+ $mask->makeMaskNo($maskNo, $width, $frame, $bitMask, true);
+ }
+
+ QRtools::markTime('after_build_cache');
+ }
+
+ //----------------------------------------------------------------------
+ public static function log($outfile, $err)
+ {
+ if (Constants::QR_LOG_DIR !== false) {
+ if ($err != '') {
+ if ($outfile !== false) {
+ file_put_contents(Constants::QR_LOG_DIR.basename($outfile).'-errors.txt', date('Y-m-d H:i:s').': '.$err, FILE_APPEND);
+ } else {
+ file_put_contents(Constants::QR_LOG_DIR.'errors.txt', date('Y-m-d H:i:s').': '.$err, FILE_APPEND);
+ }
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function dumpMask($frame)
+ {
+ $width = count($frame);
+ for($y=0;$y<$width;$y++) {
+ for($x=0;$x<$width;$x++) {
+ echo ord($frame[$y][$x]).',';
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function markTime($markerId)
+ {
+ list($usec, $sec) = explode(" ", microtime());
+ $time = ((float)$usec + (float)$sec);
+
+ if (!isset($GLOBALS['qr_time_bench']))
+ $GLOBALS['qr_time_bench'] = array();
+
+ $GLOBALS['qr_time_bench'][$markerId] = $time;
+ }
+
+ //----------------------------------------------------------------------
+ public static function timeBenchmark()
+ {
+ self::markTime('finish');
+
+ $lastTime = 0;
+ $startTime = 0;
+ $p = 0;
+
+ echo '<table cellpadding="3" cellspacing="1">
+ <thead><tr style="border-bottom:1px solid silver"><td colspan="2" style="text-align:center">BENCHMARK</td></tr></thead>
+ <tbody>';
+
+ foreach($GLOBALS['qr_time_bench'] as $markerId=>$thisTime) {
+ if ($p > 0) {
+ echo '<tr><th style="text-align:right">till '.$markerId.': </th><td>'.number_format($thisTime-$lastTime, 6).'s</td></tr>';
+ } else {
+ $startTime = $thisTime;
+ }
+
+ $p++;
+ $lastTime = $thisTime;
+ }
+
+ echo '</tbody><tfoot>
+ <tr style="border-top:2px solid black"><th style="text-align:right">TOTAL: </th><td>'.number_format($lastTime-$startTime, 6).'s</td></tr>
+ </tfoot>
+ </table>';
+ }
+
+}
+
+QRtools::markTime('start');
diff --git a/vendor/aferrandini/phpqrcode/readme.md b/vendor/aferrandini/phpqrcode/readme.md new file mode 100755 index 00000000..e8f2f5ab --- /dev/null +++ b/vendor/aferrandini/phpqrcode/readme.md @@ -0,0 +1,37 @@ +# PHP QRCode Library + +To install this library please follow the next steps: + +## Install the library using `composer`: + +Add the required module to your `composer.json` file: + + { + "require": { + ... + "aferrandini/phpqrcode": "1.0.1" + ... + } + } + +Then run the command `composer update`. + + +## Usage + +Sample code: + + \PHPQRCode\QRcode::png("Test", "/tmp/qrcode.png", 'L', 4, 2); + +This code will generate a PNG file on '/tmp/qrcode.png' with a QRCode that contains the word 'Test'. + +## Acknowledgements + +This library is an import of PHP QR Code by Dominik Dzienia that you can find at http://phpqrcode.sourceforge.net + +Based on C libqrencode library (ver. 3.1.1), Copyright (C) 2006-2010 by Kentaro Fukuchi +http://megaui.net/fukuchi/works/qrencode/index.en.html + +QR Code is registered trademarks of DENSO WAVE INCORPORATED in JAPAN and other countries. + +Reed-Solomon code encoder is written by Phil Karn, KA9Q. Copyright (C) 2002, 2003, 2004, 2006 Phil Karn, KA9Q diff --git a/vendor/autoload.php b/vendor/autoload.php new file mode 100644 index 00000000..099ee6b9 --- /dev/null +++ b/vendor/autoload.php @@ -0,0 +1,7 @@ +<?php + +// autoload.php @generated by Composer + +require_once __DIR__ . '/composer/autoload_real.php'; + +return ComposerAutoloaderInit6edea6294a88689e3f5c56484bb70c9b::getLoader(); diff --git a/vendor/bin/picofeed b/vendor/bin/picofeed new file mode 120000 index 00000000..7b63b542 --- /dev/null +++ b/vendor/bin/picofeed @@ -0,0 +1 @@ +../miniflux/picofeed/picofeed
\ No newline at end of file diff --git a/vendor/christian-riesen/base32/.gitignore b/vendor/christian-riesen/base32/.gitignore new file mode 100644 index 00000000..23f1f99d --- /dev/null +++ b/vendor/christian-riesen/base32/.gitignore @@ -0,0 +1,3 @@ +composer.lock +build/ +vendor/ diff --git a/vendor/christian-riesen/base32/.scrutinizer.yml b/vendor/christian-riesen/base32/.scrutinizer.yml new file mode 100644 index 00000000..cf8b9954 --- /dev/null +++ b/vendor/christian-riesen/base32/.scrutinizer.yml @@ -0,0 +1,14 @@ +before_commands: + - "composer install --prefer-dist" + +tools: + php_mess_detector: true + php_code_sniffer: true + php_analyzer: true + sensiolabs_security_checker: true + php_code_coverage: true + php_cpd: true + php_pdepend: + excluded_dirs: [vendor/, tests/*] +filter: + excluded_paths: [vendor/] diff --git a/vendor/christian-riesen/base32/.travis.yml b/vendor/christian-riesen/base32/.travis.yml new file mode 100644 index 00000000..0b0505e6 --- /dev/null +++ b/vendor/christian-riesen/base32/.travis.yml @@ -0,0 +1,19 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +before_script: + - composer install + +before_script: + - curl -s http://getcomposer.org/installer | php + - php composer.phar update --dev --no-interaction + - mkdir -p build/logs + +script: + - php vendor/bin/phpunit --coverage-text diff --git a/vendor/christian-riesen/base32/LICENSE b/vendor/christian-riesen/base32/LICENSE new file mode 100644 index 00000000..624fceb1 --- /dev/null +++ b/vendor/christian-riesen/base32/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2013-2014 Christian Riesen + +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. diff --git a/vendor/christian-riesen/base32/README.md b/vendor/christian-riesen/base32/README.md new file mode 100644 index 00000000..00d693d6 --- /dev/null +++ b/vendor/christian-riesen/base32/README.md @@ -0,0 +1,60 @@ +base32 +====== + +Base32 Encoder/Decoder for PHP according to RFC 4648 + +[](http://travis-ci.org/ChristianRiesen/base32) +[](http://hhvm.h4cc.de/package/christian-riesen/base32) + +[](https://packagist.org/packages/christian-riesen/base32) [](https://packagist.org/packages/christian-riesen/base32) [](https://packagist.org/packages/christian-riesen/base32) [](https://packagist.org/packages/christian-riesen/base32) + +Do you like this? Flattr it: + +[](http://flattr.com/thing/720563/ChristianRiesenbase32-on-GitHub) + +Usage +----- + + <?php + + // Include class or user autoloader + use Base32\Base32; + + $string = 'fooba'; + + $encoded = Base32::encode($string); + // $encoded contains now 'MZXW6YTB' + + $decoded = Base32::decode($encoded); + // $decoded is again 'fooba' + + +About +===== + +Use +--- + +Initially created to work with the [one time password project](https://github.com/ChristianRiesen/otp), yet it can stand alone just as well as [Jordi Boggiano](http://seld.be/) kindly pointed out. It's the only Base32 implementation I could make work that passes the test vectors (and contains unit tests). + +Goal +---- +Have a RFC compliant Base32 encoder and decoder. The implementation could be improved, but for now, it does the job and has unit tests. Ideally, the class can be enhanced while the unit tests keep passing. + +Requirements +------------ + +PHP 5.3.x+ + +If you want to run the tests, PHPUnit 3.6 or up is required. + +Author +------ + +Christian Riesen <chris.riesen@gmail.com> http://christianriesen.com + +Acknowledgements +---------------- + +Base32 is mostly based on the work of https://github.com/NTICompass/PHP-Base32 + diff --git a/vendor/christian-riesen/base32/build.xml b/vendor/christian-riesen/base32/build.xml new file mode 100644 index 00000000..c06a49f9 --- /dev/null +++ b/vendor/christian-riesen/base32/build.xml @@ -0,0 +1,130 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project name="christianriesen-base32" default="build"> + <target name="build" depends="prepare,lint,phploc,pdepend,phpmd-ci,phpcs-ci,phpcpd,phpunit,phpcb"/> + + <target name="build-parallel" depends="prepare,lint,tools-parallel,phpunit,phpcb"/> + + <target name="tools-parallel" description="Run tools in parallel"> + <parallel threadCount="2"> + <sequential> + <antcall target="pdepend"/> + <antcall target="phpmd-ci"/> + </sequential> + <antcall target="phpcpd"/> + <antcall target="phpcs-ci"/> + <antcall target="phploc"/> + </parallel> + </target> + + <target name="clean" description="Cleanup build artifacts"> + <delete dir="${basedir}/build/code-browser"/> + <delete dir="${basedir}/build/coverage"/> + <delete dir="${basedir}/build/logs"/> + <delete dir="${basedir}/build/pdepend"/> + <exec executable="bash"> + <arg value="-c" /> + <arg value="curl -s http://getcomposer.org/installer | php" /> + </exec> + <exec executable="php"> + <arg value="composer.phar" /> + <arg value="install" /> + </exec> + </target> + + <target name="prepare" depends="clean" description="Prepare for build"> + <mkdir dir="${basedir}/build/code-browser"/> + <mkdir dir="${basedir}/build/coverage"/> + <mkdir dir="${basedir}/build/logs"/> + <mkdir dir="${basedir}/build/pdepend"/> + </target> + + <target name="lint" description="Perform syntax check of sourcecode files"> + <apply executable="php" failonerror="true"> + <arg value="-l"/> + + <fileset dir="${basedir}/src"> + <include name="**/*.php"/> + <modified/> + </fileset> + + <fileset dir="${basedir}/tests"> + <include name="**/*.php"/> + <modified/> + </fileset> + </apply> + </target> + + <target name="phploc" description="Measure project size using PHPLOC"> + <exec executable="phploc"> + <arg value="--log-csv"/> + <arg value="${basedir}/build/logs/phploc.csv"/> + <arg path="${basedir}/src"/> + </exec> + </target> + + <target name="pdepend" description="Calculate software metrics using PHP_Depend"> + <exec executable="pdepend"> + <arg value="--jdepend-xml=${basedir}/build/logs/jdepend.xml"/> + <arg value="--jdepend-chart=${basedir}/build/pdepend/dependencies.svg"/> + <arg value="--overview-pyramid=${basedir}/build/pdepend/overview-pyramid.svg"/> + <arg path="${basedir}/src"/> + </exec> + </target> + + <target name="phpmd" description="Perform project mess detection using PHPMD and print human readable output. Intended for usage on the command line before committing."> + <exec executable="phpmd"> + <arg path="${basedir}/src"/> + <arg value="text"/> + <arg value="${basedir}/build/phpmd.xml"/> + </exec> + </target> + + <target name="phpmd-ci" description="Perform project mess detection using PHPMD creating a log file for the continuous integration server"> + <exec executable="phpmd"> + <arg path="${basedir}/src"/> + <arg value="xml"/> + <arg value="${basedir}/build/phpmd.xml"/> + <arg value="--reportfile"/> + <arg value="${basedir}/build/logs/pmd.xml"/> + </exec> + </target> + + <target name="phpcs" description="Find coding standard violations using PHP_CodeSniffer and print human readable output. Intended for usage on the command line before committing."> + <exec executable="phpcs"> + <arg value="--standard=${basedir}/build/phpcs.xml"/> + <arg path="${basedir}/src"/> + </exec> + </target> + + <target name="phpcs-ci" description="Find coding standard violations using PHP_CodeSniffer creating a log file for the continuous integration server"> + <exec executable="phpcs" output="/dev/null"> + <arg value="--report=checkstyle"/> + <arg value="--report-file=${basedir}/build/logs/checkstyle.xml"/> + <arg value="--standard=${basedir}/build/phpcs.xml"/> + <arg path="${basedir}/src"/> + </exec> + </target> + + <target name="phpcpd" description="Find duplicate code using PHPCPD"> + <exec executable="phpcpd"> + <arg value="--log-pmd"/> + <arg value="${basedir}/build/logs/pmd-cpd.xml"/> + <arg path="${basedir}/src"/> + </exec> + </target> + + <target name="phpunit" description="Run unit tests with PHPUnit"> + <exec executable="phpunit" failonerror="true"/> + </target> + + <target name="phpcb" description="Aggregate tool output with PHP_CodeBrowser"> + <exec executable="phpcb"> + <arg value="--log"/> + <arg path="${basedir}/build/logs"/> + <arg value="--source"/> + <arg path="${basedir}/src"/> + <arg value="--output"/> + <arg path="${basedir}/build/code-browser"/> + </exec> + </target> +</project> diff --git a/vendor/christian-riesen/base32/composer.json b/vendor/christian-riesen/base32/composer.json new file mode 100644 index 00000000..0c61fbf7 --- /dev/null +++ b/vendor/christian-riesen/base32/composer.json @@ -0,0 +1,33 @@ +{ + "name": "christian-riesen/base32", + "type": "library", + "description": "Base32 encoder/decoder according to RFC 4648", + "keywords": ["base32","encode","decode","rfc4648"], + "homepage": "https://github.com/ChristianRiesen/base32", + "license": "MIT", + "authors": [ + { + "name": "Christian Riesen", + "email": "chris.riesen@gmail.com", + "homepage": "http://christianriesen.com", + "role": "Developer" + } + ], + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "satooshi/php-coveralls": "0.*" + }, + "autoload": { + "psr-4": { + "Base32\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + } +} diff --git a/vendor/christian-riesen/base32/phpunit.xml.dist b/vendor/christian-riesen/base32/phpunit.xml.dist new file mode 100644 index 00000000..3e2def6f --- /dev/null +++ b/vendor/christian-riesen/base32/phpunit.xml.dist @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<phpunit + backupGlobals="false" + backupStaticAttributes="false" + convertErrorsToExceptions="true" + convertNoticesToExceptions="true" + convertWarningsToExceptions="true" + processIsolation="false" + stopOnFailure="false" + syntaxCheck="false" + bootstrap="tests/bootstrap.php" + colors="true"> + <testsuites> + <testsuite name="Base32 Test Suite"> + <directory suffix="Test.php">tests/</directory> + </testsuite> + </testsuites> + + <filter> + <whitelist> + <directory suffix=".php">src/</directory> + </whitelist> + </filter> + + <logging> + <log type="coverage-html" target="build/coverage" title="Base32" + charset="UTF-8" yui="true" highlight="true" + lowUpperBound="35" highLowerBound="70"/> + <log type="coverage-clover" target="build/logs/clover.xml"/> + <log type="junit" target="build/logs/junit.xml" logIncompleteSkipped="false"/> + </logging> +</phpunit> diff --git a/vendor/christian-riesen/base32/src/Base32.php b/vendor/christian-riesen/base32/src/Base32.php new file mode 100644 index 00000000..bf790182 --- /dev/null +++ b/vendor/christian-riesen/base32/src/Base32.php @@ -0,0 +1,146 @@ +<?php +namespace Base32; + +/** + * Base32 encoder and decoder + * + * Last update: 2012-06-20 + * + * RFC 4648 compliant + * @link http://www.ietf.org/rfc/rfc4648.txt + * + * Some groundwork based on this class + * https://github.com/NTICompass/PHP-Base32 + * + * @author Christian Riesen <chris.riesen@gmail.com> + * @link http://christianriesen.com + * @license MIT License see LICENSE file + */ +class Base32 +{ + /** + * Alphabet for encoding and decoding base32 + * + * @var array + */ + private static $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567='; + + /** + * Creates an array from a binary string into a given chunk size + * + * @param string $binaryString String to chunk + * @param integer $bits Number of bits per chunk + * @return array + */ + private static function chunk($binaryString, $bits) + { + $binaryString = chunk_split($binaryString, $bits, ' '); + + if (substr($binaryString, (strlen($binaryString)) - 1) == ' ') { + $binaryString = substr($binaryString, 0, strlen($binaryString)-1); + } + + return explode(' ', $binaryString); + } + + /** + * Encodes into base32 + * + * @param string $string Clear text string + * @return string Base32 encoded string + */ + public static function encode($string) + { + if (strlen($string) == 0) { + // Gives an empty string + + return ''; + } + + // Convert string to binary + $binaryString = ''; + + foreach (str_split($string) as $s) { + // Return each character as an 8-bit binary string + $binaryString .= sprintf('%08b', ord($s)); + } + + // Break into 5-bit chunks, then break that into an array + $binaryArray = self::chunk($binaryString, 5); + + // Pad array to be divisible by 8 + while (count($binaryArray) % 8 !== 0) { + $binaryArray[] = null; + } + + $base32String = ''; + + // Encode in base32 + foreach ($binaryArray as $bin) { + $char = 32; + + if (!is_null($bin)) { + // Pad the binary strings + $bin = str_pad($bin, 5, 0, STR_PAD_RIGHT); + $char = bindec($bin); + } + + // Base32 character + $base32String .= self::$alphabet[$char]; + } + + return $base32String; + } + + /** + * Decodes base32 + * + * @param string $base32String Base32 encoded string + * @return string Clear text string + */ + public static function decode($base32String) + { + // Only work in upper cases + $base32String = strtoupper($base32String); + + // Remove anything that is not base32 alphabet + $pattern = '/[^A-Z2-7]/'; + + $base32String = preg_replace($pattern, '', $base32String); + + if (strlen($base32String) == 0) { + // Gives an empty string + return ''; + } + + $base32Array = str_split($base32String); + + $string = ''; + + foreach ($base32Array as $str) { + $char = strpos(self::$alphabet, $str); + + // Ignore the padding character + if ($char !== 32) { + $string .= sprintf('%05b', $char); + } + } + + while (strlen($string) %8 !== 0) { + $string = substr($string, 0, strlen($string)-1); + } + + $binaryArray = self::chunk($string, 8); + + $realString = ''; + + foreach ($binaryArray as $bin) { + // Pad each value to 8 bits + $bin = str_pad($bin, 8, 0, STR_PAD_RIGHT); + // Convert binary strings to ASCII + $realString .= chr(bindec($bin)); + } + + return $realString; + } +} diff --git a/vendor/christian-riesen/base32/tests/Base32Test.php b/vendor/christian-riesen/base32/tests/Base32Test.php new file mode 100644 index 00000000..3e5924ce --- /dev/null +++ b/vendor/christian-riesen/base32/tests/Base32Test.php @@ -0,0 +1,51 @@ +<?php + +namespace Base32; + +use Base32\Base32; + +/** + * Base32 test case. + */ +class Base32Test extends \PHPUnit_Framework_TestCase +{ + /** + * Tests Base32->decode() + * + * Testing test vectors according to RFC 4648 + * http://www.ietf.org/rfc/rfc4648.txt + */ + public function testDecode() + { + // RFC test vectors say that empty string returns empty string + $this->assertEquals('', Base32::decode('')); + + // these strings are taken from the RFC + $this->assertEquals('f', Base32::decode('MY======')); + $this->assertEquals('fo', Base32::decode('MZXQ====')); + $this->assertEquals('foo', Base32::decode('MZXW6===')); + $this->assertEquals('foob', Base32::decode('MZXW6YQ=')); + $this->assertEquals('fooba', Base32::decode('MZXW6YTB')); + $this->assertEquals('foobar', Base32::decode('MZXW6YTBOI======')); + + // Decoding a string made up entirely of invalid characters + $this->assertEquals('', Base32::decode('8908908908908908')); + } + + /** + * Encoder tests, reverse of the decodes + */ + public function testEncode() + { + // RFC test vectors say that empty string returns empty string + $this->assertEquals('', Base32::encode('')); + + // these strings are taken from the RFC + $this->assertEquals('MY======', Base32::encode('f')); + $this->assertEquals('MZXQ====', Base32::encode('fo')); + $this->assertEquals('MZXW6===', Base32::encode('foo')); + $this->assertEquals('MZXW6YQ=', Base32::encode('foob')); + $this->assertEquals('MZXW6YTB', Base32::encode('fooba')); + $this->assertEquals('MZXW6YTBOI======', Base32::encode('foobar')); + } +} diff --git a/vendor/christian-riesen/base32/tests/bootstrap.php b/vendor/christian-riesen/base32/tests/bootstrap.php new file mode 100644 index 00000000..12bea5b1 --- /dev/null +++ b/vendor/christian-riesen/base32/tests/bootstrap.php @@ -0,0 +1,5 @@ +<?php + +$loader = require __DIR__ . '/../vendor/autoload.php'; +$loader->add("Base32", __DIR__); +$loader->register(); diff --git a/vendor/christian-riesen/otp/.gitignore b/vendor/christian-riesen/otp/.gitignore new file mode 100644 index 00000000..bab62331 --- /dev/null +++ b/vendor/christian-riesen/otp/.gitignore @@ -0,0 +1,5 @@ +.svn +/.buildpath +/.project +/.settings + diff --git a/vendor/christian-riesen/otp/.travis.yml b/vendor/christian-riesen/otp/.travis.yml new file mode 100644 index 00000000..b863299e --- /dev/null +++ b/vendor/christian-riesen/otp/.travis.yml @@ -0,0 +1,11 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + +script: phpunit + diff --git a/vendor/christian-riesen/otp/LICENSE b/vendor/christian-riesen/otp/LICENSE new file mode 100644 index 00000000..70894746 --- /dev/null +++ b/vendor/christian-riesen/otp/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) Christian Riesen http://christianriesen.com + +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. + diff --git a/vendor/christian-riesen/otp/README.md b/vendor/christian-riesen/otp/README.md new file mode 100644 index 00000000..c979dbbc --- /dev/null +++ b/vendor/christian-riesen/otp/README.md @@ -0,0 +1,105 @@ +One Time Passwords +================== + +[](http://travis-ci.org/ChristianRiesen/otp) + +Did you like this? Flattr it: + +[](https://flattr.com/thing/719284/ChristianRiesenotp-on-GitHub) + +Installation +------------ + +Use [composer](http://getcomposer.org/) and require the library in your `composer.json` + + { + "require": { + "christian-riesen/otp": "1.*", + } + } + +Usage +----- + +```php +<?php + +use Otp\Otp; +use Otp\GoogleAuthenticator; + +// Seperate class, see https://github.com/ChristianRiesen/base32 +use Base32\Base32; + +// Get a Pseudo Secret +// Defaults to 16 characters +$secret = GoogleAuthenticator::generateRandom(); + +// Url for the QR code +// Using totp method +$url = GoogleAuthenticator::getQrCodeUrl('totp', 'Label like user@host.com', $secret); + +// Save the secret with the users account +// Display QR Code to the user + +// Now how to check +$otp = new Otp(); + +// $key is a 6 digit number, coming from the User +// Assuming this is present and sanitized +// Allows for a 1 code time drift by default +// Third parameter can alter that behavior +if ($otp->checkTotp(Base32::decode($secret), $key)) { + // Correct key + // IMPORTANT! Note this key as being used + // so nobody could launch a replay attack. + // Cache that for the next minutes and you + // should be good. +} else { + // Wrong key +} + +// Just to create a key for display (testing) +$key = $otp->totp($secret); + +``` + +Sample script in `example` folder. Requires sessions to work (for secret storage). + +Class Otp +--------- + +Implements hotp according to [RFC4226](https://tools.ietf.org/html/rfc4226) and totp according to [RFC6238](https://tools.ietf.org/html/rfc6238) (only sha1 algorithm). Once you have a secret, you can use it directly in this class to create the passwords themselves (mainly for debugging use) or use the check functions to safely check the validity of the keys. The `checkTotp` function also includes a helper to battle timedrift. + +Class GoogleAuthenticator +------------------------- + +Static function class to generate a correct url for the QR code, so you can easy scan it with your device. Google Authenticator is avaiaible as application for iPhone and Android. This removes the burden to create such an app from the developers of websites by using this set of classes. + +There are also older open source versions of the Google Authenticator app for both [iPhone](https://github.com/google/google-authenticator) and [Android](https://github.com/google/google-authenticator-android) + +This helper class uses the random_int function from PHP7, or the polyfill method from [paragonie/random_compat](https://packagist.org/packages/paragonie/random_compat) if present and falls back on other (less "secure") random generators. + +About +===== + +Requirements +------------ + +PHP 5.3.x+ + +Uses [Base32 class](https://github.com/ChristianRiesen/base32). + +If you want to run the tests, PHPUnit 3.6 or up is required. + +Author +------ + +Christian Riesen <chris.riesen@gmail.com> http://christianriesen.com + +Acknowledgements +---------------- + +The classes have been inspired by many different places that were talking about otp and Google Authenticator. Thank you all for your help. + +Project setup ideas blantently taken from https://github.com/Seldaek/monolog + diff --git a/vendor/christian-riesen/otp/composer.json b/vendor/christian-riesen/otp/composer.json new file mode 100644 index 00000000..94f16c67 --- /dev/null +++ b/vendor/christian-riesen/otp/composer.json @@ -0,0 +1,28 @@ +{ + "name": "christian-riesen/otp", + "type": "library", + "description": "One Time Passwords, hotp and totp according to RFC4226 and RFC6238", + "keywords": ["otp","hotp","totp","googleauthenticator","rfc4226","rfc6238"], + "homepage": "https://github.com/ChristianRiesen/otp", + "license": "MIT", + "authors": [ + { + "name": "Christian Riesen", + "email": "chris.riesen@gmail.com", + "homepage": "http://christianriesen.com", + "role": "Developer" + } + ], + "require": { + "php": ">=5.3.0", + "christian-riesen/base32": ">=1.0" + }, + "suggest": { + "paragonie/random_compat": "Optional polyfill for a more secure random generator for pre PHP7 versions" + }, + "autoload": { + "psr-0": { + "Otp": "src" + } + } +} diff --git a/vendor/christian-riesen/otp/example/index.php b/vendor/christian-riesen/otp/example/index.php new file mode 100644 index 00000000..4da362c5 --- /dev/null +++ b/vendor/christian-riesen/otp/example/index.php @@ -0,0 +1,100 @@ +<?php + +session_start(); // using it as storage temporary + +require_once __DIR__ . '/../vendor/autoload.php'; + +use Otp\Otp; +use Otp\GoogleAuthenticator; +use Base32\Base32; + +// Getting a secret, either by generating or from storage +// DON'T use sessions as storage for this in production!!! +$secret = 0; + +if (isset($_SESSION['otpsecret'])) { + $secret = $_SESSION['otpsecret']; +} + +if (strlen($secret) != 16) { + $secret = GoogleAuthenticator::generateRandom(); + $_SESSION['otpsecret'] = $secret; +} + +// The secret is now an easy stored Base32 string. +// To use it in totp though we need to decode it into the original +$otp = new Otp(); + +$currentTotp = $otp->totp(Base32::decode($secret)); + +$qrCode = GoogleAuthenticator::getQrCodeUrl('totp', 'otpsample@cr', $secret); +$keyUri = GoogleAuthenticator::getKeyUri('totp', 'otpsample@cr', $secret); + +?><html> +<head> +<title>One Time Passwords Example</title> +</head> +<body> + +<h1>One Time Passwords Example</h1> + +Secret is <?php echo $secret; ?>. This is saved with the users credentials. +<br /> +<br /> +<hr /> + +QR Code for totp:<br /> +<img src="<?php echo $qrCode; ?>" /> +<br /> +This QR Code contains the Key URI: <?php echo $keyUri; ?> +<br /> +<hr /> + +Current totp would be <?php echo $currentTotp; ?><br /> +<br /> +<hr /> + +Because of timedrift, you could technically enter a code before or after it +would actually be used. This form uses the checkTotp function. To test this, +open this page, wait until the key changes once or twice (not more) on your +Google Authenticator, then hit submit. Even though the key is "wrong" because of +small time differences, you can still use it. +<form action="" method="post"> +<input type="text" name="otpkey" value="<?php echo $currentTotp; ?>" /><br /> +<input type="submit"> +</form> + +<br /> +Output:<br /> +<br /> + + +<?php + +if (isset($_POST['otpkey'])) { + // Sanatizing, this should take care of it + $key = preg_replace('/[^0-9]/', '', $_POST['otpkey']); + + // Standard is 6 for keys, but can be changed with setDigits on $otp + if (strlen($key) == 6) { + // Remember that the secret is a base32 string that needs decoding + // to use it here! + if ($otp->checkTotp(Base32::decode($secret), $key)) { + echo 'Key correct!'; + // Add here something that makes note of this key and will not allow + // the use of it, for this user for the next 2 minutes. This way you + // prevent a replay attack. Otherwise your OTP is missing one of the + // key features it can bring in security to your application! + } else { + echo 'Wrong key!'; + } + + } else { + echo 'Key not the correct size'; + } +} + +?> + +</body> +</html> diff --git a/vendor/christian-riesen/otp/phpunit.xml.dist b/vendor/christian-riesen/otp/phpunit.xml.dist new file mode 100644 index 00000000..7ffc6c63 --- /dev/null +++ b/vendor/christian-riesen/otp/phpunit.xml.dist @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<phpunit colors="true"> + <testsuites> + <testsuite name="Otp Test Suite"> + <directory>tests/Otp/</directory> + </testsuite> + </testsuites> + + <filter> + <whitelist> + <directory suffix=".php">src/Otp/</directory> + </whitelist> + </filter> +</phpunit> diff --git a/vendor/christian-riesen/otp/src/Otp/GoogleAuthenticator.php b/vendor/christian-riesen/otp/src/Otp/GoogleAuthenticator.php new file mode 100644 index 00000000..23e67ff0 --- /dev/null +++ b/vendor/christian-riesen/otp/src/Otp/GoogleAuthenticator.php @@ -0,0 +1,189 @@ +<?php +namespace Otp; + +/** + * Google Authenticator + * + * Last update: 2014-08-19 + * + * Can be easy used with Google Authenticator + * @link https://code.google.com/p/google-authenticator/ + * + * @author Christian Riesen <chris.riesen@gmail.com> + * @link http://christianriesen.com + * @license MIT License see LICENSE file + */ + +class GoogleAuthenticator +{ + protected static $allowedTypes = array('hotp', 'totp'); + + protected static $height = 200; + protected static $width = 200; + + /** + * Returns the Key URI + * + * Format of encoded url is here: + * https://code.google.com/p/google-authenticator/wiki/KeyUriFormat + * Should be done in a better fashion + * + * @param string $type totp or hotp + * @param string $label Label to display this as to the user + * @param string $secret Base32 encoded secret + * @param integer $counter Required by hotp, otherwise ignored + * @param array $options Optional fields that will be set if present + * + * @return string Key URI + */ + public static function getKeyUri($type, $label, $secret, $counter = null, $options = array()) + { + // two types only.. + if (!in_array($type, self::$allowedTypes)) { + throw new \InvalidArgumentException('Type has to be of allowed types list'); + } + + // Label can't be empty + $label = trim($label); + + if (strlen($label) < 1) { + throw new \InvalidArgumentException('Label has to be one or more printable characters'); + } + + if (substr_count($label, ':') > 2) { + throw new \InvalidArgumentException('Account name contains illegal colon characters'); + } + + // Secret needs to be here + if (strlen($secret) < 1) { + throw new \InvalidArgumentException('No secret present'); + } + + // check for counter on hotp + if ($type == 'hotp' && is_null($counter)) { + throw new \InvalidArgumentException('Counter required for hotp'); + } + + // This is the base, these are at least required + $otpauth = 'otpauth://' . $type . '/' . str_replace(array(':', ' '), array('%3A', '%20'), $label) . '?secret=' . rawurlencode($secret); + + if ($type == 'hotp' && !is_null($counter)) { + $otpauth .= '&counter=' . intval($counter); + } + + // Now check the options array + + // algorithm (currently ignored by Authenticator) + // Defaults to SHA1 + if (array_key_exists('algorithm', $options)) { + $otpauth .= '&algorithm=' . rawurlencode($options['algorithm']); + } + + // digits (currently ignored by Authenticator) + // Defaults to 6 + if (array_key_exists('digits', $options) && intval($options['digits']) !== 6 && intval($options['digits']) !== 8) { + throw new \InvalidArgumentException('Digits can only have the values 6 or 8, ' . $options['digits'] . ' given'); + } elseif (array_key_exists('digits', $options)) { + $otpauth .= '&digits=' . intval($options['digits']); + } + + // period, only for totp (currently ignored by Authenticator) + // Defaults to 30 + if ($type == 'totp' && array_key_exists('period', $options)) { + $otpauth .= '&period=' . rawurlencode($options['period']); + } + + // issuer + // Defaults to none + if (array_key_exists('issuer', $options)) { + $otpauth .= '&issuer=' . rawurlencode($options['issuer']); + } + + return $otpauth; + } + + + /** + * Returns the QR code url + * + * Format of encoded url is here: + * https://code.google.com/p/google-authenticator/wiki/KeyUriFormat + * Should be done in a better fashion + * + * @param string $type totp or hotp + * @param string $label Label to display this as to the user + * @param string $secret Base32 encoded secret + * @param integer $counter Required by hotp, otherwise ignored + * @param array $options Optional fields that will be set if present + * + * @return string URL to the QR code + */ + public static function getQrCodeUrl($type, $label, $secret, $counter = null, $options = array()) + { + // Width and height can be overwritten + $width = self::$width; + + if (array_key_exists('width', $options) && is_numeric($options['width'])) { + $width = $options['width']; + } + + $height = self::$height; + + if (array_key_exists('height', $options) && is_numeric($options['height'])) { + $height = $options['height']; + } + + $otpauth = self::getKeyUri($type, $label, $secret, $counter, $options); + + $url = 'https://chart.googleapis.com/chart?chs=' . $width . 'x' + . $height . '&cht=qr&chld=M|0&chl=' . urlencode($otpauth); + + return $url; + } + + /** + * Creates a pseudo random Base32 string + * + * This could decode into anything. It's located here as a small helper + * where code that might need base32 usually also needs something like this. + * + * @param integer $length Exact length of output string + * @return string Base32 encoded random + */ + public static function generateRandom($length = 16) + { + $keys = array_merge(range('A','Z'), range(2,7)); // No padding char + + $string = ''; + + for ($i = 0; $i < $length; $i++) { + $string .= $keys[self::getRand()]; + } + + return $string; + } + + /** + * Get random number + * + * @return int Random number between 0 and 31 (including) + */ + private static function getRand() + { + if (function_exists('random_int')) { + // Uses either the PHP7 internal function or the polyfill if present + return random_int(0, 31); + } elseif (function_exists('openssl_random_pseudo_bytes')) { + $bytes = openssl_random_pseudo_bytes(2); + $number = hexdec(bin2hex($bytes)); + + if ($number > 31) { + $number = $number % 32; + } + + return $number; + } else { + return mt_rand(0, 31); + } + } +} diff --git a/vendor/christian-riesen/otp/src/Otp/Otp.php b/vendor/christian-riesen/otp/src/Otp/Otp.php new file mode 100644 index 00000000..7d954870 --- /dev/null +++ b/vendor/christian-riesen/otp/src/Otp/Otp.php @@ -0,0 +1,310 @@ +<?php +namespace Otp; + +/** + * One Time Passwords + * + * Last update: 2012-06-16 + * + * Implements HOTP and TOTP + * + * HMAC-Based One-time Password(HOTP) algorithm specified in RFC 4226 + * @link https://tools.ietf.org/html/rfc4226 + * + * Time-based One-time Password (TOTP) algorithm specified in RFC 6238 + * @link https://tools.ietf.org/html/rfc6238 + * + * As a note: This code is NOT 2038 proof! The min concern is the function + * getBinaryCounter that uses the pack function which can't handle 64bit yet. + * + * Can be easy used with Google Authenticator + * @link https://code.google.com/p/google-authenticator/ + * + * @author Christian Riesen <chris.riesen@gmail.com> + * @link http://christianriesen.com + * @license MIT License see LICENSE file + */ + +class Otp implements OtpInterface +{ + /** + * The digits the code can have + * + * Either 6 or 8. + * Authenticator does only support 6. + * + * @var integer + */ + protected $digits = 6; + + /** + * Time in seconds one counter period is long + * + * @var integer + */ + protected $period = 30; + + /** + * Possible algorithms + * + * @var array + */ + protected $allowedAlgorithms = array('sha1', 'sha256', 'sha512'); + + /** + * Currently used algorithm + * + * @var string + */ + protected $algorithm = 'sha1'; + + /* (non-PHPdoc) + * @see Otp.OtpInterface::hotp() + */ + public function hotp($secret, $counter) + { + if (!is_numeric($counter)) { + throw new \InvalidArgumentException('Counter must be integer'); + } + + $hash = hash_hmac( + $this->algorithm, + $this->getBinaryCounter($counter), + $secret, + true + ); + + return str_pad($this->truncate($hash), $this->digits, '0', STR_PAD_LEFT); + } + + /* (non-PHPdoc) + * @see Otp.OtpInterface::totp() + */ + public function totp($secret, $timecounter = null) + { + if (is_null($timecounter)) { + $timecounter = $this->getTimecounter(); + } + + return $this->hotp($secret, $timecounter); + } + + /* (non-PHPdoc) + * @see Otp.OtpInterface::checkHotp() + */ + public function checkHotp($secret, $counter, $key) + { + return $this->safeCompare($this->hotp($secret, $counter), $key); + } + + /* (non-PHPdoc) + * @see Otp.OtpInterface::checkTotp() + */ + public function checkTotp($secret, $key, $timedrift = 1) + { + if (!is_numeric($timedrift) || $timedrift < 0) { + throw new \InvalidArgumentException('Invalid timedrift supplied'); + } + // Counter comes from time now + // Also we check the current timestamp as well as previous and future ones + // according to $timerange + $timecounter = $this->getTimecounter(); + + $start = $timecounter - ($timedrift); + $end = $timecounter + ($timedrift); + + // We first try the current, as it is the most likely to work + if ($this->safeCompare($this->totp($secret, $timecounter), $key)) { + return true; + } elseif ($timedrift == 0) { + // When timedrift is 0, this is the end of the checks + return false; + } + + // Well, that didn't work, so try the others + for ($t = $start; $t <= $end; $t = $t + 1) { + if ($t == $timecounter) { + // Already tried that one + continue; + } + + if ($this->safeCompare($this->totp($secret, $t), $key)) { + return true; + } + } + + // if none worked, then return false + return false; + } + + /** + * Changing the used algorithm for hashing + * + * Can only be one of the algorithms in the allowedAlgorithms property. + * + * @param string $algorithm + * @throws \InvalidArgumentException + * @return \Otp\Otp + */ + + /* + * This has been disabled since it does not bring the expected results + * according to the RFC test vectors for sha256 or sha512. + * Until that is fixed, the algorithm simply stays at sha1. + * Google Authenticator does not support sha256 and sha512 at the moment. + * + + public function setAlgorithm($algorithm) + { + if (!in_array($algorithm, $this->allowedAlgorithms)) { + throw new \InvalidArgumentException('Not an allowed algorithm: ' . $algorithm); + } + + $this->algorithm = $algorithm; + + return $this; + } + // */ + + /** + * Get the algorithms name (lowercase) + * + * @return string + */ + public function getAlgorithm() + { + return $this->algorithm; + } + + /** + * Setting period lenght for totp + * + * @param integer $period + * @throws \InvalidArgumentException + * @return \Otp\Otp + */ + public function setPeriod($period) + { + if (!is_int($period)) { + throw new \InvalidArgumentException('Period must be an integer'); + } + + $this->period = $period; + + return $this; + } + + /** + * Returns the set period value + * + * @return integer + */ + public function getPeriod() + { + return $this->period; + } + + /** + * Setting number of otp digits + * + * @param integer $digits Number of digits for the otp (6 or 8) + * @throws \InvalidArgumentException + * @return \Otp\Otp + */ + public function setDigits($digits) + { + if (!in_array($digits, array(6, 8))) { + throw new \InvalidArgumentException('Digits must be 6 or 8'); + } + + $this->digits = $digits; + + return $this; + } + + /** + * Returns number of digits in the otp + * + * @return integer + */ + public function getDigits() + { + return $this->digits; + } + + /** + * Generates a binary counter for hashing + * + * Warning: Not 2038 safe. Maybe until then pack supports 64bit. + * + * @param integer $counter Counter in integer form + * @return string Binary string + */ + protected function getBinaryCounter($counter) + { + return pack('N*', 0) . pack('N*', $counter); + } + + /** + * Generating time counter + * + * This is the time divided by 30 by default. + * + * @return integer Time counter + */ + protected function getTimecounter() + { + return floor(time() / $this->period); + } + + /** + * Creates the basic number for otp from hash + * + * This number is left padded with zeros to the required length by the + * calling function. + * + * @param string $hash hmac hash + * @return number + */ + protected function truncate($hash) + { + $offset = ord($hash[19]) & 0xf; + + return ( + ((ord($hash[$offset+0]) & 0x7f) << 24 ) | + ((ord($hash[$offset+1]) & 0xff) << 16 ) | + ((ord($hash[$offset+2]) & 0xff) << 8 ) | + (ord($hash[$offset+3]) & 0xff) + ) % pow(10, $this->digits); + } + + /** + * Safely compares two inputs + * + * Assumed inputs are numbers and strings. + * Compares them in a time linear manner. No matter how much you guess + * correct of the partial content, it does not change the time it takes to + * run the entire comparison. + * + * @param mixed $a + * @param mixed $b + * @return boolean + */ + protected function safeCompare($a, $b) + { + $sha1a = sha1($a); + $sha1b = sha1($b); + + // Now the compare is always the same length. Even considering minute + // time differences in sha1 creation, all you know is that a longer + // input takes longer to hash, not how long the actual compared value is + $result = 0; + + for ($i = 0; $i < 40; $i++) { + $result |= ord($sha1a[$i]) ^ ord($sha1b[$i]); + } + + return $result == 0; + } +} + diff --git a/vendor/christian-riesen/otp/src/Otp/OtpInterface.php b/vendor/christian-riesen/otp/src/Otp/OtpInterface.php new file mode 100644 index 00000000..7ff34f2f --- /dev/null +++ b/vendor/christian-riesen/otp/src/Otp/OtpInterface.php @@ -0,0 +1,65 @@ +<?php +namespace Otp; + +/** + * Interface for HOTP and TOTP + * + * Last update: 2012-06-16 + * + * HMAC-Based One-time Password(HOTP) algorithm specified in RFC 4226 + * @link https://tools.ietf.org/html/rfc4226 + * + * Time-based One-time Password (TOTP) algorithm specified in RFC 6238 + * @link https://tools.ietf.org/html/rfc6238 + * + * @author Christian Riesen <chris.riesen@gmail.com> + * @link http://christianriesen.com + * @license MIT License see LICENSE file + */ + +interface OtpInterface +{ + /** + * Returns OTP using the HOTP algorithm + * + * @param string $secret + * @param integer $counter + * @return string One Time Password + */ + function hotp($secret, $counter); + + /** + * Returns OTP using the TOTP algorithm + * + * @param string $secret + * @param integer $timecounter Optional: Uses current time if null + * @return string One Time Password + */ + function totp($secret, $timecounter = null); + + /** + * Checks Hotp against a key + * + * This is a helper function, but is here to ensure the Totp can be checked + * in the same manner. + * + * @param string $secret + * @param integer $counter + * @param string $key + * + * @return boolean If key is correct + */ + function checkHotp($secret, $counter, $key); + + /** + * Checks Totp agains a key + * + * + * @param string $secret + * @param integer $key + * @param integer $timedrift + * + * @return boolean If key is correct + */ + function checkTotp($secret, $key, $timedrift = 1); +} diff --git a/vendor/christian-riesen/otp/tests/Otp/GoogleAuthenticatorTest.php b/vendor/christian-riesen/otp/tests/Otp/GoogleAuthenticatorTest.php new file mode 100644 index 00000000..219bdaa8 --- /dev/null +++ b/vendor/christian-riesen/otp/tests/Otp/GoogleAuthenticatorTest.php @@ -0,0 +1,88 @@ +<?php + +require_once __DIR__ . '/../../src/Otp/GoogleAuthenticator.php'; + +use Otp\GoogleAuthenticator; + +/** + * GoogleAuthenticator test case. + */ +class GoogleAuthenticatorTest extends \PHPUnit_Framework_TestCase +{ + /** + * Tests getQrCodeUrl + */ + public function testGetQrCodeUrl() + { + $secret = 'MEP3EYVA6XNFNVNM'; // testing secret + + // Standard totp case + $this->assertEquals( + 'https://chart.googleapis.com/chart?chs=200x200&cht=qr&chld=M|0&chl=otpauth%3A%2F%2Ftotp%2Fuser%40host.com%3Fsecret%3DMEP3EYVA6XNFNVNM', + GoogleAuthenticator::getQrCodeUrl('totp', 'user@host.com', $secret) + ); + + // hotp (include a counter) + $this->assertEquals( + 'https://chart.googleapis.com/chart?chs=200x200&cht=qr&chld=M|0&chl=otpauth%3A%2F%2Fhotp%2Fuser%40host.com%3Fsecret%3DMEP3EYVA6XNFNVNM%26counter%3D1234', + GoogleAuthenticator::getQrCodeUrl('hotp', 'user@host.com', $secret, 1234) + ); + + // totp, this time with a parameter for chaning the size of the QR + $this->assertEquals( + 'https://chart.googleapis.com/chart?chs=300x300&cht=qr&chld=M|0&chl=otpauth%3A%2F%2Ftotp%2Fuser%40host.com%3Fsecret%3DMEP3EYVA6XNFNVNM', + GoogleAuthenticator::getQrCodeUrl('totp', 'user@host.com', $secret, null, array('height' => 300, 'width' => 300)) + ); + + } + + /** + * Tests getKeyUri + */ + public function testGetKeyUri() + { + $secret = 'MEP3EYVA6XNFNVNM'; // testing secret + + // Standard totp case + $this->assertEquals( + 'otpauth://totp/user@host.com?secret=MEP3EYVA6XNFNVNM', + GoogleAuthenticator::getKeyUri('totp', 'user@host.com', $secret) + ); + + // hotp (include a counter) + $this->assertEquals( + 'otpauth://hotp/user@host.com?secret=MEP3EYVA6XNFNVNM&counter=1234', + GoogleAuthenticator::getKeyUri('hotp', 'user@host.com', $secret, 1234) + ); + + // totp/hotp with an issuer in the label + $this->assertEquals( + 'otpauth://hotp/issuer%3Auser@host.com?secret=MEP3EYVA6XNFNVNM&counter=1234', + GoogleAuthenticator::getKeyUri('hotp', 'issuer:user@host.com', $secret, 1234) + ); + + // totp/hotp with an issuer and spaces in the label + $this->assertEquals( + 'otpauth://hotp/an%20issuer%3A%20user@host.com?secret=MEP3EYVA6XNFNVNM&counter=1234', + GoogleAuthenticator::getKeyUri('hotp', 'an issuer: user@host.com', $secret, 1234) + ); + + // totp/hotp with an issuer as option + $this->assertEquals( + 'otpauth://hotp/an%20issuer%3Auser@host.com?secret=MEP3EYVA6XNFNVNM&counter=1234&issuer=an%20issuer', + GoogleAuthenticator::getKeyUri('hotp', 'an issuer:user@host.com', $secret, 1234, array('issuer' => 'an issuer')) + ); + } + + /** + * Tests generateRandom + */ + public function testGenerateRandom() + { + // contains numbers 2-7 and letters A-Z in large letters, 16 chars long + $this->assertRegExp('/[2-7A-Z]{16}/', GoogleAuthenticator::generateRandom()); + + // Can be told to make a longer secret + $this->assertRegExp('/[2-7A-Z]{18}/', GoogleAuthenticator::generateRandom(18)); + } +} diff --git a/vendor/christian-riesen/otp/tests/Otp/OtpTest.php b/vendor/christian-riesen/otp/tests/Otp/OtpTest.php new file mode 100644 index 00000000..ccfdddab --- /dev/null +++ b/vendor/christian-riesen/otp/tests/Otp/OtpTest.php @@ -0,0 +1,124 @@ +<?php + +require_once __DIR__ . '/../../src/Otp/OtpInterface.php'; +require_once __DIR__ . '/../../src/Otp/Otp.php'; + +use Otp\Otp; + +/** + * Otp test case. + */ +class OtpTest extends \PHPUnit_Framework_TestCase +{ + /** + * + * @var Otp + */ + private $Otp; + + private $secret = "12345678901234567890"; + + /** + * Prepares the environment before running a test. + */ + protected function setUp() + { + parent::setUp(); + + $this->Otp = new Otp(); + + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->Otp = null; + + parent::tearDown(); + } + + /** + * Tests Otp->hotp() + * + * Using test vectors from RFC + * https://tools.ietf.org/html/rfc4226 + */ + public function testHotpRfc() + { + $secret = $this->secret; + + $this->assertEquals('755224', $this->Otp->hotp($secret, 0)); + $this->assertEquals('287082', $this->Otp->hotp($secret, 1)); + $this->assertEquals('359152', $this->Otp->hotp($secret, 2)); + $this->assertEquals('969429', $this->Otp->hotp($secret, 3)); + $this->assertEquals('338314', $this->Otp->hotp($secret, 4)); + $this->assertEquals('254676', $this->Otp->hotp($secret, 5)); + $this->assertEquals('287922', $this->Otp->hotp($secret, 6)); + $this->assertEquals('162583', $this->Otp->hotp($secret, 7)); + $this->assertEquals('399871', $this->Otp->hotp($secret, 8)); + $this->assertEquals('520489', $this->Otp->hotp($secret, 9)); + } + + /** + * Tests TOTP general construction + * + * Still uses the hotp function, but since totp is a bit more special, has + * its own tests + * Using test vectors from RFC + * https://tools.ietf.org/html/rfc6238 + */ + public function testTotpRfc() + { + $secret = $this->secret; + + // Test vectors are in 8 digits + $this->Otp->setDigits(8); + + // The time presented in the test vector has to be first divided through 30 + // to count as the key + + // SHA 1 grouping + $this->assertEquals('94287082', $this->Otp->hotp($secret, floor(59/30)), 'sha1 with time 59'); + $this->assertEquals('07081804', $this->Otp->hotp($secret, floor(1111111109/30)), 'sha1 with time 1111111109'); + $this->assertEquals('14050471', $this->Otp->hotp($secret, floor(1111111111/30)), 'sha1 with time 1111111111'); + $this->assertEquals('89005924', $this->Otp->hotp($secret, floor(1234567890/30)), 'sha1 with time 1234567890'); + $this->assertEquals('69279037', $this->Otp->hotp($secret, floor(2000000000/30)), 'sha1 with time 2000000000'); + $this->assertEquals('65353130', $this->Otp->hotp($secret, floor(20000000000/30)), 'sha1 with time 20000000000'); + + /* + The following tests do NOT pass. + Once the otp class can deal with these correctly, they can be used again. + They are here for completeness test vectors from the RFC. + + // SHA 256 grouping + $this->Otp->setAlgorithm('sha256'); + $this->assertEquals('46119246', $this->Otp->hotp($secret, floor(59/30)), 'sha256 with time 59'); + $this->assertEquals('07081804', $this->Otp->hotp($secret, floor(1111111109/30)), 'sha256 with time 1111111109'); + $this->assertEquals('14050471', $this->Otp->hotp($secret, floor(1111111111/30)), 'sha256 with time 1111111111'); + $this->assertEquals('89005924', $this->Otp->hotp($secret, floor(1234567890/30)), 'sha256 with time 1234567890'); + $this->assertEquals('69279037', $this->Otp->hotp($secret, floor(2000000000/30)), 'sha256 with time 2000000000'); + $this->assertEquals('65353130', $this->Otp->hotp($secret, floor(20000000000/30)), 'sha256 with time 20000000000'); + + // SHA 512 grouping + $this->Otp->setAlgorithm('sha512'); + $this->assertEquals('90693936', $this->Otp->hotp($secret, floor(59/30)), 'sha512 with time 59'); + $this->assertEquals('25091201', $this->Otp->hotp($secret, floor(1111111109/30)), 'sha512 with time 1111111109'); + $this->assertEquals('99943326', $this->Otp->hotp($secret, floor(1111111111/30)), 'sha512 with time 1111111111'); + $this->assertEquals('93441116', $this->Otp->hotp($secret, floor(1234567890/30)), 'sha512 with time 1234567890'); + $this->assertEquals('38618901', $this->Otp->hotp($secret, floor(2000000000/30)), 'sha512 with time 2000000000'); + $this->assertEquals('47863826', $this->Otp->hotp($secret, floor(20000000000/30)), 'sha512 with time 20000000000'); + */ + } + + /** + * @expectedException InvalidArgumentException + * @expectedExceptionMessage Counter must be integer + */ + public function testHotpInvalidCounter() + { + $this->Otp->hotp($this->secret, 'a'); + } + +} diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php new file mode 100644 index 00000000..2c72175e --- /dev/null +++ b/vendor/composer/ClassLoader.php @@ -0,0 +1,445 @@ +<?php + +/* + * This file is part of Composer. + * + * (c) Nils Adermann <naderman@naderman.de> + * Jordi Boggiano <j.boggiano@seld.be> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier <fabien@symfony.com> + * @author Jordi Boggiano <j.boggiano@seld.be> + * @see http://www.php-fig.org/psr/psr-0/ + * @see http://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + // PSR-4 + private $prefixLengthsPsr4 = array(); + private $prefixDirsPsr4 = array(); + private $fallbackDirsPsr4 = array(); + + // PSR-0 + private $prefixesPsr0 = array(); + private $fallbackDirsPsr0 = array(); + + private $useIncludePath = false; + private $classMap = array(); + private $classMapAuthoritative = false; + private $missingClasses = array(); + private $apcuPrefix; + + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', $this->prefixesPsr0); + } + + return array(); + } + + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 base directories + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return bool|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath.'\\'; + if (isset($this->prefixDirsPsr4[$search])) { + foreach ($this->prefixDirsPsr4[$search] as $dir) { + $length = $this->prefixLengthsPsr4[$first][$search]; + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile($file) +{ + include $file; +} diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE new file mode 100644 index 00000000..f27399a0 --- /dev/null +++ b/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +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. + diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php new file mode 100644 index 00000000..70275ac0 --- /dev/null +++ b/vendor/composer/autoload_classmap.php @@ -0,0 +1,907 @@ +<?php + +// autoload_classmap.php @generated by Composer + +$vendorDir = dirname(dirname(__FILE__)); +$baseDir = dirname($vendorDir); + +return array( + 'Base32\\Base32' => $vendorDir . '/christian-riesen/base32/src/Base32.php', + 'Eluceo\\iCal\\Component' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Component.php', + 'Eluceo\\iCal\\Component\\Alarm' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Component/Alarm.php', + 'Eluceo\\iCal\\Component\\Calendar' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Component/Calendar.php', + 'Eluceo\\iCal\\Component\\Event' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Component/Event.php', + 'Eluceo\\iCal\\Component\\Timezone' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Component/Timezone.php', + 'Eluceo\\iCal\\Component\\TimezoneRule' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Component/TimezoneRule.php', + 'Eluceo\\iCal\\ParameterBag' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/ParameterBag.php', + 'Eluceo\\iCal\\Property' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property.php', + 'Eluceo\\iCal\\PropertyBag' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/PropertyBag.php', + 'Eluceo\\iCal\\Property\\ArrayValue' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property/ArrayValue.php', + 'Eluceo\\iCal\\Property\\DateTimeProperty' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property/DateTimeProperty.php', + 'Eluceo\\iCal\\Property\\DateTimesProperty' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property/DateTimesProperty.php', + 'Eluceo\\iCal\\Property\\Event\\Attendees' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property/Event/Attendees.php', + 'Eluceo\\iCal\\Property\\Event\\Description' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property/Event/Description.php', + 'Eluceo\\iCal\\Property\\Event\\Organizer' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property/Event/Organizer.php', + 'Eluceo\\iCal\\Property\\Event\\RecurrenceId' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceId.php', + 'Eluceo\\iCal\\Property\\Event\\RecurrenceRule' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceRule.php', + 'Eluceo\\iCal\\Property\\StringValue' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property/StringValue.php', + 'Eluceo\\iCal\\Property\\ValueInterface' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Property/ValueInterface.php', + 'Eluceo\\iCal\\Util\\ComponentUtil' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Util/ComponentUtil.php', + 'Eluceo\\iCal\\Util\\DateUtil' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Util/DateUtil.php', + 'Eluceo\\iCal\\Util\\PropertyValueUtil' => $vendorDir . '/eluceo/ical/src/Eluceo/iCal/Util/PropertyValueUtil.php', + 'Gregwar\\Captcha\\CaptchaBuilder' => $vendorDir . '/gregwar/captcha/CaptchaBuilder.php', + 'Gregwar\\Captcha\\CaptchaBuilderInterface' => $vendorDir . '/gregwar/captcha/CaptchaBuilderInterface.php', + 'Gregwar\\Captcha\\ImageFileHandler' => $vendorDir . '/gregwar/captcha/ImageFileHandler.php', + 'Gregwar\\Captcha\\PhraseBuilder' => $vendorDir . '/gregwar/captcha/PhraseBuilder.php', + 'Gregwar\\Captcha\\PhraseBuilderInterface' => $vendorDir . '/gregwar/captcha/PhraseBuilderInterface.php', + 'JsonRPC\\Client' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Client.php', + 'JsonRPC\\Exception\\AccessDeniedException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Exception/AccessDeniedException.php', + 'JsonRPC\\Exception\\AuthenticationFailureException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Exception/AuthenticationFailureException.php', + 'JsonRPC\\Exception\\ConnectionFailureException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Exception/ConnectionFailureException.php', + 'JsonRPC\\Exception\\InvalidJsonFormatException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonFormatException.php', + 'JsonRPC\\Exception\\InvalidJsonRpcFormatException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonRpcFormatException.php', + 'JsonRPC\\Exception\\ResponseEncodingFailureException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Exception/ResponseEncodingFailureException.php', + 'JsonRPC\\Exception\\ResponseException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Exception/ResponseException.php', + 'JsonRPC\\Exception\\ServerErrorException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Exception/ServerErrorException.php', + 'JsonRPC\\HttpClient' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/HttpClient.php', + 'JsonRPC\\MiddlewareHandler' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/MiddlewareHandler.php', + 'JsonRPC\\MiddlewareInterface' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/MiddlewareInterface.php', + 'JsonRPC\\ProcedureHandler' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/ProcedureHandler.php', + 'JsonRPC\\Request\\BatchRequestParser' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Request/BatchRequestParser.php', + 'JsonRPC\\Request\\RequestBuilder' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Request/RequestBuilder.php', + 'JsonRPC\\Request\\RequestParser' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Request/RequestParser.php', + 'JsonRPC\\Response\\ResponseBuilder' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Response/ResponseBuilder.php', + 'JsonRPC\\Response\\ResponseParser' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Response/ResponseParser.php', + 'JsonRPC\\Server' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php', + 'JsonRPC\\Validator\\HostValidator' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Validator/HostValidator.php', + 'JsonRPC\\Validator\\JsonEncodingValidator' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Validator/JsonEncodingValidator.php', + 'JsonRPC\\Validator\\JsonFormatValidator' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Validator/JsonFormatValidator.php', + 'JsonRPC\\Validator\\RpcFormatValidator' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Validator/RpcFormatValidator.php', + 'JsonRPC\\Validator\\UserValidator' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Validator/UserValidator.php', + 'Kanboard\\Action\\Base' => $baseDir . '/app/Action/Base.php', + 'Kanboard\\Action\\CommentCreation' => $baseDir . '/app/Action/CommentCreation.php', + 'Kanboard\\Action\\CommentCreationMoveTaskColumn' => $baseDir . '/app/Action/CommentCreationMoveTaskColumn.php', + 'Kanboard\\Action\\TaskAssignCategoryColor' => $baseDir . '/app/Action/TaskAssignCategoryColor.php', + 'Kanboard\\Action\\TaskAssignCategoryLabel' => $baseDir . '/app/Action/TaskAssignCategoryLabel.php', + 'Kanboard\\Action\\TaskAssignCategoryLink' => $baseDir . '/app/Action/TaskAssignCategoryLink.php', + 'Kanboard\\Action\\TaskAssignColorCategory' => $baseDir . '/app/Action/TaskAssignColorCategory.php', + 'Kanboard\\Action\\TaskAssignColorColumn' => $baseDir . '/app/Action/TaskAssignColorColumn.php', + 'Kanboard\\Action\\TaskAssignColorLink' => $baseDir . '/app/Action/TaskAssignColorLink.php', + 'Kanboard\\Action\\TaskAssignColorOnDueDate' => $baseDir . '/app/Action/TaskAssignColorOnDueDate.php', + 'Kanboard\\Action\\TaskAssignColorPriority' => $baseDir . '/app/Action/TaskAssignColorPriority.php', + 'Kanboard\\Action\\TaskAssignColorSwimlane' => $baseDir . '/app/Action/TaskAssignColorSwimlane.php', + 'Kanboard\\Action\\TaskAssignColorUser' => $baseDir . '/app/Action/TaskAssignColorUser.php', + 'Kanboard\\Action\\TaskAssignCreator' => $baseDir . '/app/Action/TaskAssignCreator.php', + 'Kanboard\\Action\\TaskAssignCurrentUser' => $baseDir . '/app/Action/TaskAssignCurrentUser.php', + 'Kanboard\\Action\\TaskAssignCurrentUserColumn' => $baseDir . '/app/Action/TaskAssignCurrentUserColumn.php', + 'Kanboard\\Action\\TaskAssignDueDateOnCreation' => $baseDir . '/app/Action/TaskAssignDueDateOnCreation.php', + 'Kanboard\\Action\\TaskAssignPrioritySwimlane' => $baseDir . '/app/Action/TaskAssignPrioritySwimlane.php', + 'Kanboard\\Action\\TaskAssignSpecificUser' => $baseDir . '/app/Action/TaskAssignSpecificUser.php', + 'Kanboard\\Action\\TaskAssignUser' => $baseDir . '/app/Action/TaskAssignUser.php', + 'Kanboard\\Action\\TaskClose' => $baseDir . '/app/Action/TaskClose.php', + 'Kanboard\\Action\\TaskCloseColumn' => $baseDir . '/app/Action/TaskCloseColumn.php', + 'Kanboard\\Action\\TaskCloseNoActivity' => $baseDir . '/app/Action/TaskCloseNoActivity.php', + 'Kanboard\\Action\\TaskCloseNoActivityColumn' => $baseDir . '/app/Action/TaskCloseNoActivityColumn.php', + 'Kanboard\\Action\\TaskCloseNotMovedColumn' => $baseDir . '/app/Action/TaskCloseNotMovedColumn.php', + 'Kanboard\\Action\\TaskCreation' => $baseDir . '/app/Action/TaskCreation.php', + 'Kanboard\\Action\\TaskDuplicateAnotherProject' => $baseDir . '/app/Action/TaskDuplicateAnotherProject.php', + 'Kanboard\\Action\\TaskEmail' => $baseDir . '/app/Action/TaskEmail.php', + 'Kanboard\\Action\\TaskEmailNoActivity' => $baseDir . '/app/Action/TaskEmailNoActivity.php', + 'Kanboard\\Action\\TaskMoveAnotherProject' => $baseDir . '/app/Action/TaskMoveAnotherProject.php', + 'Kanboard\\Action\\TaskMoveColumnAssigned' => $baseDir . '/app/Action/TaskMoveColumnAssigned.php', + 'Kanboard\\Action\\TaskMoveColumnCategoryChange' => $baseDir . '/app/Action/TaskMoveColumnCategoryChange.php', + 'Kanboard\\Action\\TaskMoveColumnClosed' => $baseDir . '/app/Action/TaskMoveColumnClosed.php', + 'Kanboard\\Action\\TaskMoveColumnNotMovedPeriod' => $baseDir . '/app/Action/TaskMoveColumnNotMovedPeriod.php', + 'Kanboard\\Action\\TaskMoveColumnUnAssigned' => $baseDir . '/app/Action/TaskMoveColumnUnAssigned.php', + 'Kanboard\\Action\\TaskOpen' => $baseDir . '/app/Action/TaskOpen.php', + 'Kanboard\\Action\\TaskUpdateStartDate' => $baseDir . '/app/Action/TaskUpdateStartDate.php', + 'Kanboard\\Analytic\\AverageLeadCycleTimeAnalytic' => $baseDir . '/app/Analytic/AverageLeadCycleTimeAnalytic.php', + 'Kanboard\\Analytic\\AverageTimeSpentColumnAnalytic' => $baseDir . '/app/Analytic/AverageTimeSpentColumnAnalytic.php', + 'Kanboard\\Analytic\\EstimatedTimeComparisonAnalytic' => $baseDir . '/app/Analytic/EstimatedTimeComparisonAnalytic.php', + 'Kanboard\\Analytic\\TaskDistributionAnalytic' => $baseDir . '/app/Analytic/TaskDistributionAnalytic.php', + 'Kanboard\\Analytic\\UserDistributionAnalytic' => $baseDir . '/app/Analytic/UserDistributionAnalytic.php', + 'Kanboard\\Api\\Authorization\\ActionAuthorization' => $baseDir . '/app/Api/Authorization/ActionAuthorization.php', + 'Kanboard\\Api\\Authorization\\CategoryAuthorization' => $baseDir . '/app/Api/Authorization/CategoryAuthorization.php', + 'Kanboard\\Api\\Authorization\\ColumnAuthorization' => $baseDir . '/app/Api/Authorization/ColumnAuthorization.php', + 'Kanboard\\Api\\Authorization\\CommentAuthorization' => $baseDir . '/app/Api/Authorization/CommentAuthorization.php', + 'Kanboard\\Api\\Authorization\\ProcedureAuthorization' => $baseDir . '/app/Api/Authorization/ProcedureAuthorization.php', + 'Kanboard\\Api\\Authorization\\ProjectAuthorization' => $baseDir . '/app/Api/Authorization/ProjectAuthorization.php', + 'Kanboard\\Api\\Authorization\\SubtaskAuthorization' => $baseDir . '/app/Api/Authorization/SubtaskAuthorization.php', + 'Kanboard\\Api\\Authorization\\TagAuthorization' => $baseDir . '/app/Api/Authorization/TagAuthorization.php', + 'Kanboard\\Api\\Authorization\\TaskAuthorization' => $baseDir . '/app/Api/Authorization/TaskAuthorization.php', + 'Kanboard\\Api\\Authorization\\TaskFileAuthorization' => $baseDir . '/app/Api/Authorization/TaskFileAuthorization.php', + 'Kanboard\\Api\\Authorization\\TaskLinkAuthorization' => $baseDir . '/app/Api/Authorization/TaskLinkAuthorization.php', + 'Kanboard\\Api\\Authorization\\UserAuthorization' => $baseDir . '/app/Api/Authorization/UserAuthorization.php', + 'Kanboard\\Api\\Middleware\\AuthenticationMiddleware' => $baseDir . '/app/Api/Middleware/AuthenticationMiddleware.php', + 'Kanboard\\Api\\Procedure\\ActionProcedure' => $baseDir . '/app/Api/Procedure/ActionProcedure.php', + 'Kanboard\\Api\\Procedure\\AppProcedure' => $baseDir . '/app/Api/Procedure/AppProcedure.php', + 'Kanboard\\Api\\Procedure\\BaseProcedure' => $baseDir . '/app/Api/Procedure/BaseProcedure.php', + 'Kanboard\\Api\\Procedure\\BoardProcedure' => $baseDir . '/app/Api/Procedure/BoardProcedure.php', + 'Kanboard\\Api\\Procedure\\CategoryProcedure' => $baseDir . '/app/Api/Procedure/CategoryProcedure.php', + 'Kanboard\\Api\\Procedure\\ColumnProcedure' => $baseDir . '/app/Api/Procedure/ColumnProcedure.php', + 'Kanboard\\Api\\Procedure\\CommentProcedure' => $baseDir . '/app/Api/Procedure/CommentProcedure.php', + 'Kanboard\\Api\\Procedure\\GroupMemberProcedure' => $baseDir . '/app/Api/Procedure/GroupMemberProcedure.php', + 'Kanboard\\Api\\Procedure\\GroupProcedure' => $baseDir . '/app/Api/Procedure/GroupProcedure.php', + 'Kanboard\\Api\\Procedure\\LinkProcedure' => $baseDir . '/app/Api/Procedure/LinkProcedure.php', + 'Kanboard\\Api\\Procedure\\MeProcedure' => $baseDir . '/app/Api/Procedure/MeProcedure.php', + 'Kanboard\\Api\\Procedure\\ProjectFileProcedure' => $baseDir . '/app/Api/Procedure/ProjectFileProcedure.php', + 'Kanboard\\Api\\Procedure\\ProjectPermissionProcedure' => $baseDir . '/app/Api/Procedure/ProjectPermissionProcedure.php', + 'Kanboard\\Api\\Procedure\\ProjectProcedure' => $baseDir . '/app/Api/Procedure/ProjectProcedure.php', + 'Kanboard\\Api\\Procedure\\SubtaskProcedure' => $baseDir . '/app/Api/Procedure/SubtaskProcedure.php', + 'Kanboard\\Api\\Procedure\\SubtaskTimeTrackingProcedure' => $baseDir . '/app/Api/Procedure/SubtaskTimeTrackingProcedure.php', + 'Kanboard\\Api\\Procedure\\SwimlaneProcedure' => $baseDir . '/app/Api/Procedure/SwimlaneProcedure.php', + 'Kanboard\\Api\\Procedure\\TagProcedure' => $baseDir . '/app/Api/Procedure/TagProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskExternalLinkProcedure' => $baseDir . '/app/Api/Procedure/TaskExternalLinkProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskFileProcedure' => $baseDir . '/app/Api/Procedure/TaskFileProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskLinkProcedure' => $baseDir . '/app/Api/Procedure/TaskLinkProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskMetadataProcedure' => $baseDir . '/app/Api/Procedure/TaskMetadataProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskProcedure' => $baseDir . '/app/Api/Procedure/TaskProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskTagProcedure' => $baseDir . '/app/Api/Procedure/TaskTagProcedure.php', + 'Kanboard\\Api\\Procedure\\UserProcedure' => $baseDir . '/app/Api/Procedure/UserProcedure.php', + 'Kanboard\\Auth\\ApiAccessTokenAuth' => $baseDir . '/app/Auth/ApiAccessTokenAuth.php', + 'Kanboard\\Auth\\DatabaseAuth' => $baseDir . '/app/Auth/DatabaseAuth.php', + 'Kanboard\\Auth\\LdapAuth' => $baseDir . '/app/Auth/LdapAuth.php', + 'Kanboard\\Auth\\RememberMeAuth' => $baseDir . '/app/Auth/RememberMeAuth.php', + 'Kanboard\\Auth\\ReverseProxyAuth' => $baseDir . '/app/Auth/ReverseProxyAuth.php', + 'Kanboard\\Auth\\TotpAuth' => $baseDir . '/app/Auth/TotpAuth.php', + 'Kanboard\\Console\\BaseCommand' => $baseDir . '/app/Console/BaseCommand.php', + 'Kanboard\\Console\\CronjobCommand' => $baseDir . '/app/Console/CronjobCommand.php', + 'Kanboard\\Console\\DatabaseMigrationCommand' => $baseDir . '/app/Console/DatabaseMigrationCommand.php', + 'Kanboard\\Console\\DatabaseVersionCommand' => $baseDir . '/app/Console/DatabaseVersionCommand.php', + 'Kanboard\\Console\\JobCommand' => $baseDir . '/app/Console/JobCommand.php', + 'Kanboard\\Console\\LocaleComparatorCommand' => $baseDir . '/app/Console/LocaleComparatorCommand.php', + 'Kanboard\\Console\\LocaleSyncCommand' => $baseDir . '/app/Console/LocaleSyncCommand.php', + 'Kanboard\\Console\\PluginInstallCommand' => $baseDir . '/app/Console/PluginInstallCommand.php', + 'Kanboard\\Console\\PluginUninstallCommand' => $baseDir . '/app/Console/PluginUninstallCommand.php', + 'Kanboard\\Console\\PluginUpgradeCommand' => $baseDir . '/app/Console/PluginUpgradeCommand.php', + 'Kanboard\\Console\\ProjectDailyColumnStatsExportCommand' => $baseDir . '/app/Console/ProjectDailyColumnStatsExportCommand.php', + 'Kanboard\\Console\\ProjectDailyStatsCalculationCommand' => $baseDir . '/app/Console/ProjectDailyStatsCalculationCommand.php', + 'Kanboard\\Console\\ResetPasswordCommand' => $baseDir . '/app/Console/ResetPasswordCommand.php', + 'Kanboard\\Console\\ResetTwoFactorCommand' => $baseDir . '/app/Console/ResetTwoFactorCommand.php', + 'Kanboard\\Console\\SubtaskExportCommand' => $baseDir . '/app/Console/SubtaskExportCommand.php', + 'Kanboard\\Console\\TaskExportCommand' => $baseDir . '/app/Console/TaskExportCommand.php', + 'Kanboard\\Console\\TaskOverdueNotificationCommand' => $baseDir . '/app/Console/TaskOverdueNotificationCommand.php', + 'Kanboard\\Console\\TaskTriggerCommand' => $baseDir . '/app/Console/TaskTriggerCommand.php', + 'Kanboard\\Console\\TransitionExportCommand' => $baseDir . '/app/Console/TransitionExportCommand.php', + 'Kanboard\\Console\\WorkerCommand' => $baseDir . '/app/Console/WorkerCommand.php', + 'Kanboard\\Controller\\ActionController' => $baseDir . '/app/Controller/ActionController.php', + 'Kanboard\\Controller\\ActionCreationController' => $baseDir . '/app/Controller/ActionCreationController.php', + 'Kanboard\\Controller\\ActivityController' => $baseDir . '/app/Controller/ActivityController.php', + 'Kanboard\\Controller\\AnalyticController' => $baseDir . '/app/Controller/AnalyticController.php', + 'Kanboard\\Controller\\AppController' => $baseDir . '/app/Controller/AppController.php', + 'Kanboard\\Controller\\AuthController' => $baseDir . '/app/Controller/AuthController.php', + 'Kanboard\\Controller\\AvatarFileController' => $baseDir . '/app/Controller/AvatarFileController.php', + 'Kanboard\\Controller\\BaseController' => $baseDir . '/app/Controller/BaseController.php', + 'Kanboard\\Controller\\BoardAjaxController' => $baseDir . '/app/Controller/BoardAjaxController.php', + 'Kanboard\\Controller\\BoardPopoverController' => $baseDir . '/app/Controller/BoardPopoverController.php', + 'Kanboard\\Controller\\BoardTooltipController' => $baseDir . '/app/Controller/BoardTooltipController.php', + 'Kanboard\\Controller\\BoardViewController' => $baseDir . '/app/Controller/BoardViewController.php', + 'Kanboard\\Controller\\CaptchaController' => $baseDir . '/app/Controller/CaptchaController.php', + 'Kanboard\\Controller\\CategoryController' => $baseDir . '/app/Controller/CategoryController.php', + 'Kanboard\\Controller\\ColumnController' => $baseDir . '/app/Controller/ColumnController.php', + 'Kanboard\\Controller\\ColumnMoveRestrictionController' => $baseDir . '/app/Controller/ColumnMoveRestrictionController.php', + 'Kanboard\\Controller\\ColumnRestrictionController' => $baseDir . '/app/Controller/ColumnRestrictionController.php', + 'Kanboard\\Controller\\CommentController' => $baseDir . '/app/Controller/CommentController.php', + 'Kanboard\\Controller\\CommentListController' => $baseDir . '/app/Controller/CommentListController.php', + 'Kanboard\\Controller\\CommentMailController' => $baseDir . '/app/Controller/CommentMailController.php', + 'Kanboard\\Controller\\ConfigController' => $baseDir . '/app/Controller/ConfigController.php', + 'Kanboard\\Controller\\CurrencyController' => $baseDir . '/app/Controller/CurrencyController.php', + 'Kanboard\\Controller\\CustomFilterController' => $baseDir . '/app/Controller/CustomFilterController.php', + 'Kanboard\\Controller\\DashboardController' => $baseDir . '/app/Controller/DashboardController.php', + 'Kanboard\\Controller\\DocumentationController' => $baseDir . '/app/Controller/DocumentationController.php', + 'Kanboard\\Controller\\ExportController' => $baseDir . '/app/Controller/ExportController.php', + 'Kanboard\\Controller\\ExternalTaskCreationController' => $baseDir . '/app/Controller/ExternalTaskCreationController.php', + 'Kanboard\\Controller\\ExternalTaskViewController' => $baseDir . '/app/Controller/ExternalTaskViewController.php', + 'Kanboard\\Controller\\FeedController' => $baseDir . '/app/Controller/FeedController.php', + 'Kanboard\\Controller\\FileViewerController' => $baseDir . '/app/Controller/FileViewerController.php', + 'Kanboard\\Controller\\GroupAjaxController' => $baseDir . '/app/Controller/GroupAjaxController.php', + 'Kanboard\\Controller\\GroupCreationController' => $baseDir . '/app/Controller/GroupCreationController.php', + 'Kanboard\\Controller\\GroupListController' => $baseDir . '/app/Controller/GroupListController.php', + 'Kanboard\\Controller\\GroupModificationController' => $baseDir . '/app/Controller/GroupModificationController.php', + 'Kanboard\\Controller\\ICalendarController' => $baseDir . '/app/Controller/ICalendarController.php', + 'Kanboard\\Controller\\LinkController' => $baseDir . '/app/Controller/LinkController.php', + 'Kanboard\\Controller\\OAuthController' => $baseDir . '/app/Controller/OAuthController.php', + 'Kanboard\\Controller\\PasswordResetController' => $baseDir . '/app/Controller/PasswordResetController.php', + 'Kanboard\\Controller\\PluginController' => $baseDir . '/app/Controller/PluginController.php', + 'Kanboard\\Controller\\PredefinedTaskDescriptionController' => $baseDir . '/app/Controller/PredefinedTaskDescriptionController.php', + 'Kanboard\\Controller\\ProjectActionDuplicationController' => $baseDir . '/app/Controller/ProjectActionDuplicationController.php', + 'Kanboard\\Controller\\ProjectCreationController' => $baseDir . '/app/Controller/ProjectCreationController.php', + 'Kanboard\\Controller\\ProjectEditController' => $baseDir . '/app/Controller/ProjectEditController.php', + 'Kanboard\\Controller\\ProjectFileController' => $baseDir . '/app/Controller/ProjectFileController.php', + 'Kanboard\\Controller\\ProjectListController' => $baseDir . '/app/Controller/ProjectListController.php', + 'Kanboard\\Controller\\ProjectOverviewController' => $baseDir . '/app/Controller/ProjectOverviewController.php', + 'Kanboard\\Controller\\ProjectPermissionController' => $baseDir . '/app/Controller/ProjectPermissionController.php', + 'Kanboard\\Controller\\ProjectPredefinedContentController' => $baseDir . '/app/Controller/ProjectPredefinedContentController.php', + 'Kanboard\\Controller\\ProjectRoleController' => $baseDir . '/app/Controller/ProjectRoleController.php', + 'Kanboard\\Controller\\ProjectRoleRestrictionController' => $baseDir . '/app/Controller/ProjectRoleRestrictionController.php', + 'Kanboard\\Controller\\ProjectStatusController' => $baseDir . '/app/Controller/ProjectStatusController.php', + 'Kanboard\\Controller\\ProjectTagController' => $baseDir . '/app/Controller/ProjectTagController.php', + 'Kanboard\\Controller\\ProjectUserOverviewController' => $baseDir . '/app/Controller/ProjectUserOverviewController.php', + 'Kanboard\\Controller\\ProjectViewController' => $baseDir . '/app/Controller/ProjectViewController.php', + 'Kanboard\\Controller\\SearchController' => $baseDir . '/app/Controller/SearchController.php', + 'Kanboard\\Controller\\SubtaskController' => $baseDir . '/app/Controller/SubtaskController.php', + 'Kanboard\\Controller\\SubtaskConverterController' => $baseDir . '/app/Controller/SubtaskConverterController.php', + 'Kanboard\\Controller\\SubtaskRestrictionController' => $baseDir . '/app/Controller/SubtaskRestrictionController.php', + 'Kanboard\\Controller\\SubtaskStatusController' => $baseDir . '/app/Controller/SubtaskStatusController.php', + 'Kanboard\\Controller\\SwimlaneController' => $baseDir . '/app/Controller/SwimlaneController.php', + 'Kanboard\\Controller\\TagController' => $baseDir . '/app/Controller/TagController.php', + 'Kanboard\\Controller\\TaskAjaxController' => $baseDir . '/app/Controller/TaskAjaxController.php', + 'Kanboard\\Controller\\TaskBulkController' => $baseDir . '/app/Controller/TaskBulkController.php', + 'Kanboard\\Controller\\TaskCreationController' => $baseDir . '/app/Controller/TaskCreationController.php', + 'Kanboard\\Controller\\TaskDuplicationController' => $baseDir . '/app/Controller/TaskDuplicationController.php', + 'Kanboard\\Controller\\TaskExternalLinkController' => $baseDir . '/app/Controller/TaskExternalLinkController.php', + 'Kanboard\\Controller\\TaskFileController' => $baseDir . '/app/Controller/TaskFileController.php', + 'Kanboard\\Controller\\TaskImportController' => $baseDir . '/app/Controller/TaskImportController.php', + 'Kanboard\\Controller\\TaskInternalLinkController' => $baseDir . '/app/Controller/TaskInternalLinkController.php', + 'Kanboard\\Controller\\TaskListController' => $baseDir . '/app/Controller/TaskListController.php', + 'Kanboard\\Controller\\TaskMailController' => $baseDir . '/app/Controller/TaskMailController.php', + 'Kanboard\\Controller\\TaskModificationController' => $baseDir . '/app/Controller/TaskModificationController.php', + 'Kanboard\\Controller\\TaskMovePositionController' => $baseDir . '/app/Controller/TaskMovePositionController.php', + 'Kanboard\\Controller\\TaskPopoverController' => $baseDir . '/app/Controller/TaskPopoverController.php', + 'Kanboard\\Controller\\TaskRecurrenceController' => $baseDir . '/app/Controller/TaskRecurrenceController.php', + 'Kanboard\\Controller\\TaskStatusController' => $baseDir . '/app/Controller/TaskStatusController.php', + 'Kanboard\\Controller\\TaskSuppressionController' => $baseDir . '/app/Controller/TaskSuppressionController.php', + 'Kanboard\\Controller\\TaskViewController' => $baseDir . '/app/Controller/TaskViewController.php', + 'Kanboard\\Controller\\TwoFactorController' => $baseDir . '/app/Controller/TwoFactorController.php', + 'Kanboard\\Controller\\UserAjaxController' => $baseDir . '/app/Controller/UserAjaxController.php', + 'Kanboard\\Controller\\UserApiAccessController' => $baseDir . '/app/Controller/UserApiAccessController.php', + 'Kanboard\\Controller\\UserCreationController' => $baseDir . '/app/Controller/UserCreationController.php', + 'Kanboard\\Controller\\UserCredentialController' => $baseDir . '/app/Controller/UserCredentialController.php', + 'Kanboard\\Controller\\UserImportController' => $baseDir . '/app/Controller/UserImportController.php', + 'Kanboard\\Controller\\UserInviteController' => $baseDir . '/app/Controller/UserInviteController.php', + 'Kanboard\\Controller\\UserListController' => $baseDir . '/app/Controller/UserListController.php', + 'Kanboard\\Controller\\UserModificationController' => $baseDir . '/app/Controller/UserModificationController.php', + 'Kanboard\\Controller\\UserStatusController' => $baseDir . '/app/Controller/UserStatusController.php', + 'Kanboard\\Controller\\UserViewController' => $baseDir . '/app/Controller/UserViewController.php', + 'Kanboard\\Controller\\WebNotificationController' => $baseDir . '/app/Controller/WebNotificationController.php', + 'Kanboard\\Core\\Action\\ActionManager' => $baseDir . '/app/Core/Action/ActionManager.php', + 'Kanboard\\Core\\Base' => $baseDir . '/app/Core/Base.php', + 'Kanboard\\Core\\Cache\\BaseCache' => $baseDir . '/app/Core/Cache/BaseCache.php', + 'Kanboard\\Core\\Cache\\CacheInterface' => $baseDir . '/app/Core/Cache/CacheInterface.php', + 'Kanboard\\Core\\Cache\\FileCache' => $baseDir . '/app/Core/Cache/FileCache.php', + 'Kanboard\\Core\\Cache\\MemoryCache' => $baseDir . '/app/Core/Cache/MemoryCache.php', + 'Kanboard\\Core\\Controller\\AccessForbiddenException' => $baseDir . '/app/Core/Controller/AccessForbiddenException.php', + 'Kanboard\\Core\\Controller\\BaseException' => $baseDir . '/app/Core/Controller/BaseException.php', + 'Kanboard\\Core\\Controller\\BaseMiddleware' => $baseDir . '/app/Core/Controller/BaseMiddleware.php', + 'Kanboard\\Core\\Controller\\PageNotFoundException' => $baseDir . '/app/Core/Controller/PageNotFoundException.php', + 'Kanboard\\Core\\Controller\\Runner' => $baseDir . '/app/Core/Controller/Runner.php', + 'Kanboard\\Core\\Csv' => $baseDir . '/app/Core/Csv.php', + 'Kanboard\\Core\\DateParser' => $baseDir . '/app/Core/DateParser.php', + 'Kanboard\\Core\\Event\\EventManager' => $baseDir . '/app/Core/Event/EventManager.php', + 'Kanboard\\Core\\ExternalLink\\ExternalLinkInterface' => $baseDir . '/app/Core/ExternalLink/ExternalLinkInterface.php', + 'Kanboard\\Core\\ExternalLink\\ExternalLinkManager' => $baseDir . '/app/Core/ExternalLink/ExternalLinkManager.php', + 'Kanboard\\Core\\ExternalLink\\ExternalLinkProviderInterface' => $baseDir . '/app/Core/ExternalLink/ExternalLinkProviderInterface.php', + 'Kanboard\\Core\\ExternalLink\\ExternalLinkProviderNotFound' => $baseDir . '/app/Core/ExternalLink/ExternalLinkProviderNotFound.php', + 'Kanboard\\Core\\ExternalTask\\AccessForbiddenException' => $baseDir . '/app/Core/ExternalTask/AccessForbiddenException.php', + 'Kanboard\\Core\\ExternalTask\\ExternalTaskException' => $baseDir . '/app/Core/ExternalTask/ExternalTaskException.php', + 'Kanboard\\Core\\ExternalTask\\ExternalTaskInterface' => $baseDir . '/app/Core/ExternalTask/ExternalTaskInterface.php', + 'Kanboard\\Core\\ExternalTask\\ExternalTaskManager' => $baseDir . '/app/Core/ExternalTask/ExternalTaskManager.php', + 'Kanboard\\Core\\ExternalTask\\ExternalTaskProviderInterface' => $baseDir . '/app/Core/ExternalTask/ExternalTaskProviderInterface.php', + 'Kanboard\\Core\\ExternalTask\\NotFoundException' => $baseDir . '/app/Core/ExternalTask/NotFoundException.php', + 'Kanboard\\Core\\ExternalTask\\ProviderNotFoundException' => $baseDir . '/app/Core/ExternalTask/ProviderNotFoundException.php', + 'Kanboard\\Core\\Filter\\CriteriaInterface' => $baseDir . '/app/Core/Filter/CriteriaInterface.php', + 'Kanboard\\Core\\Filter\\FilterInterface' => $baseDir . '/app/Core/Filter/FilterInterface.php', + 'Kanboard\\Core\\Filter\\FormatterInterface' => $baseDir . '/app/Core/Filter/FormatterInterface.php', + 'Kanboard\\Core\\Filter\\Lexer' => $baseDir . '/app/Core/Filter/Lexer.php', + 'Kanboard\\Core\\Filter\\LexerBuilder' => $baseDir . '/app/Core/Filter/LexerBuilder.php', + 'Kanboard\\Core\\Filter\\OrCriteria' => $baseDir . '/app/Core/Filter/OrCriteria.php', + 'Kanboard\\Core\\Filter\\QueryBuilder' => $baseDir . '/app/Core/Filter/QueryBuilder.php', + 'Kanboard\\Core\\Group\\GroupBackendProviderInterface' => $baseDir . '/app/Core/Group/GroupBackendProviderInterface.php', + 'Kanboard\\Core\\Group\\GroupManager' => $baseDir . '/app/Core/Group/GroupManager.php', + 'Kanboard\\Core\\Group\\GroupProviderInterface' => $baseDir . '/app/Core/Group/GroupProviderInterface.php', + 'Kanboard\\Core\\Helper' => $baseDir . '/app/Core/Helper.php', + 'Kanboard\\Core\\Http\\Client' => $baseDir . '/app/Core/Http/Client.php', + 'Kanboard\\Core\\Http\\OAuth2' => $baseDir . '/app/Core/Http/OAuth2.php', + 'Kanboard\\Core\\Http\\RememberMeCookie' => $baseDir . '/app/Core/Http/RememberMeCookie.php', + 'Kanboard\\Core\\Http\\Request' => $baseDir . '/app/Core/Http/Request.php', + 'Kanboard\\Core\\Http\\Response' => $baseDir . '/app/Core/Http/Response.php', + 'Kanboard\\Core\\Http\\Route' => $baseDir . '/app/Core/Http/Route.php', + 'Kanboard\\Core\\Http\\Router' => $baseDir . '/app/Core/Http/Router.php', + 'Kanboard\\Core\\Ldap\\Client' => $baseDir . '/app/Core/Ldap/Client.php', + 'Kanboard\\Core\\Ldap\\ClientException' => $baseDir . '/app/Core/Ldap/ClientException.php', + 'Kanboard\\Core\\Ldap\\ConnectionException' => $baseDir . '/app/Core/Ldap/ConnectionException.php', + 'Kanboard\\Core\\Ldap\\Entries' => $baseDir . '/app/Core/Ldap/Entries.php', + 'Kanboard\\Core\\Ldap\\Entry' => $baseDir . '/app/Core/Ldap/Entry.php', + 'Kanboard\\Core\\Ldap\\Group' => $baseDir . '/app/Core/Ldap/Group.php', + 'Kanboard\\Core\\Ldap\\Query' => $baseDir . '/app/Core/Ldap/Query.php', + 'Kanboard\\Core\\Ldap\\User' => $baseDir . '/app/Core/Ldap/User.php', + 'Kanboard\\Core\\Mail\\Client' => $baseDir . '/app/Core/Mail/Client.php', + 'Kanboard\\Core\\Mail\\ClientInterface' => $baseDir . '/app/Core/Mail/ClientInterface.php', + 'Kanboard\\Core\\Mail\\Transport\\Mail' => $baseDir . '/app/Core/Mail/Transport/Mail.php', + 'Kanboard\\Core\\Mail\\Transport\\Sendmail' => $baseDir . '/app/Core/Mail/Transport/Sendmail.php', + 'Kanboard\\Core\\Mail\\Transport\\Smtp' => $baseDir . '/app/Core/Mail/Transport/Smtp.php', + 'Kanboard\\Core\\Markdown' => $baseDir . '/app/Core/Markdown.php', + 'Kanboard\\Core\\Notification\\NotificationInterface' => $baseDir . '/app/Core/Notification/NotificationInterface.php', + 'Kanboard\\Core\\ObjectStorage\\FileStorage' => $baseDir . '/app/Core/ObjectStorage/FileStorage.php', + 'Kanboard\\Core\\ObjectStorage\\ObjectStorageException' => $baseDir . '/app/Core/ObjectStorage/ObjectStorageException.php', + 'Kanboard\\Core\\ObjectStorage\\ObjectStorageInterface' => $baseDir . '/app/Core/ObjectStorage/ObjectStorageInterface.php', + 'Kanboard\\Core\\Paginator' => $baseDir . '/app/Core/Paginator.php', + 'Kanboard\\Core\\Plugin\\Base' => $baseDir . '/app/Core/Plugin/Base.php', + 'Kanboard\\Core\\Plugin\\Directory' => $baseDir . '/app/Core/Plugin/Directory.php', + 'Kanboard\\Core\\Plugin\\Hook' => $baseDir . '/app/Core/Plugin/Hook.php', + 'Kanboard\\Core\\Plugin\\Installer' => $baseDir . '/app/Core/Plugin/Installer.php', + 'Kanboard\\Core\\Plugin\\Loader' => $baseDir . '/app/Core/Plugin/Loader.php', + 'Kanboard\\Core\\Plugin\\PluginException' => $baseDir . '/app/Core/Plugin/PluginException.php', + 'Kanboard\\Core\\Plugin\\PluginInstallerException' => $baseDir . '/app/Core/Plugin/PluginInstallerException.php', + 'Kanboard\\Core\\Plugin\\SchemaHandler' => $baseDir . '/app/Core/Plugin/SchemaHandler.php', + 'Kanboard\\Core\\Plugin\\Version' => $baseDir . '/app/Core/Plugin/Version.php', + 'Kanboard\\Core\\Queue\\JobHandler' => $baseDir . '/app/Core/Queue/JobHandler.php', + 'Kanboard\\Core\\Queue\\QueueManager' => $baseDir . '/app/Core/Queue/QueueManager.php', + 'Kanboard\\Core\\Security\\AccessMap' => $baseDir . '/app/Core/Security/AccessMap.php', + 'Kanboard\\Core\\Security\\AuthenticationManager' => $baseDir . '/app/Core/Security/AuthenticationManager.php', + 'Kanboard\\Core\\Security\\AuthenticationProviderInterface' => $baseDir . '/app/Core/Security/AuthenticationProviderInterface.php', + 'Kanboard\\Core\\Security\\Authorization' => $baseDir . '/app/Core/Security/Authorization.php', + 'Kanboard\\Core\\Security\\OAuthAuthenticationProviderInterface' => $baseDir . '/app/Core/Security/OAuthAuthenticationProviderInterface.php', + 'Kanboard\\Core\\Security\\PasswordAuthenticationProviderInterface' => $baseDir . '/app/Core/Security/PasswordAuthenticationProviderInterface.php', + 'Kanboard\\Core\\Security\\PostAuthenticationProviderInterface' => $baseDir . '/app/Core/Security/PostAuthenticationProviderInterface.php', + 'Kanboard\\Core\\Security\\PreAuthenticationProviderInterface' => $baseDir . '/app/Core/Security/PreAuthenticationProviderInterface.php', + 'Kanboard\\Core\\Security\\Role' => $baseDir . '/app/Core/Security/Role.php', + 'Kanboard\\Core\\Security\\SessionCheckProviderInterface' => $baseDir . '/app/Core/Security/SessionCheckProviderInterface.php', + 'Kanboard\\Core\\Security\\Token' => $baseDir . '/app/Core/Security/Token.php', + 'Kanboard\\Core\\Session\\FlashMessage' => $baseDir . '/app/Core/Session/FlashMessage.php', + 'Kanboard\\Core\\Session\\SessionManager' => $baseDir . '/app/Core/Session/SessionManager.php', + 'Kanboard\\Core\\Session\\SessionStorage' => $baseDir . '/app/Core/Session/SessionStorage.php', + 'Kanboard\\Core\\Template' => $baseDir . '/app/Core/Template.php', + 'Kanboard\\Core\\Thumbnail' => $baseDir . '/app/Core/Thumbnail.php', + 'Kanboard\\Core\\Tool' => $baseDir . '/app/Core/Tool.php', + 'Kanboard\\Core\\Translator' => $baseDir . '/app/Core/Translator.php', + 'Kanboard\\Core\\User\\Avatar\\AvatarManager' => $baseDir . '/app/Core/User/Avatar/AvatarManager.php', + 'Kanboard\\Core\\User\\Avatar\\AvatarProviderInterface' => $baseDir . '/app/Core/User/Avatar/AvatarProviderInterface.php', + 'Kanboard\\Core\\User\\GroupSync' => $baseDir . '/app/Core/User/GroupSync.php', + 'Kanboard\\Core\\User\\UserBackendProviderInterface' => $baseDir . '/app/Core/User/UserBackendProviderInterface.php', + 'Kanboard\\Core\\User\\UserManager' => $baseDir . '/app/Core/User/UserManager.php', + 'Kanboard\\Core\\User\\UserProfile' => $baseDir . '/app/Core/User/UserProfile.php', + 'Kanboard\\Core\\User\\UserProperty' => $baseDir . '/app/Core/User/UserProperty.php', + 'Kanboard\\Core\\User\\UserProviderInterface' => $baseDir . '/app/Core/User/UserProviderInterface.php', + 'Kanboard\\Core\\User\\UserSession' => $baseDir . '/app/Core/User/UserSession.php', + 'Kanboard\\Core\\User\\UserSync' => $baseDir . '/app/Core/User/UserSync.php', + 'Kanboard\\Decorator\\ColumnMoveRestrictionCacheDecorator' => $baseDir . '/app/Decorator/ColumnMoveRestrictionCacheDecorator.php', + 'Kanboard\\Decorator\\ColumnRestrictionCacheDecorator' => $baseDir . '/app/Decorator/ColumnRestrictionCacheDecorator.php', + 'Kanboard\\Decorator\\MetadataCacheDecorator' => $baseDir . '/app/Decorator/MetadataCacheDecorator.php', + 'Kanboard\\Decorator\\ProjectRoleRestrictionCacheDecorator' => $baseDir . '/app/Decorator/ProjectRoleRestrictionCacheDecorator.php', + 'Kanboard\\Decorator\\UserCacheDecorator' => $baseDir . '/app/Decorator/UserCacheDecorator.php', + 'Kanboard\\EventBuilder\\BaseEventBuilder' => $baseDir . '/app/EventBuilder/BaseEventBuilder.php', + 'Kanboard\\EventBuilder\\CommentEventBuilder' => $baseDir . '/app/EventBuilder/CommentEventBuilder.php', + 'Kanboard\\EventBuilder\\EventIteratorBuilder' => $baseDir . '/app/EventBuilder/EventIteratorBuilder.php', + 'Kanboard\\EventBuilder\\ProjectFileEventBuilder' => $baseDir . '/app/EventBuilder/ProjectFileEventBuilder.php', + 'Kanboard\\EventBuilder\\SubtaskEventBuilder' => $baseDir . '/app/EventBuilder/SubtaskEventBuilder.php', + 'Kanboard\\EventBuilder\\TaskEventBuilder' => $baseDir . '/app/EventBuilder/TaskEventBuilder.php', + 'Kanboard\\EventBuilder\\TaskFileEventBuilder' => $baseDir . '/app/EventBuilder/TaskFileEventBuilder.php', + 'Kanboard\\EventBuilder\\TaskLinkEventBuilder' => $baseDir . '/app/EventBuilder/TaskLinkEventBuilder.php', + 'Kanboard\\Event\\AuthFailureEvent' => $baseDir . '/app/Event/AuthFailureEvent.php', + 'Kanboard\\Event\\AuthSuccessEvent' => $baseDir . '/app/Event/AuthSuccessEvent.php', + 'Kanboard\\Event\\CommentEvent' => $baseDir . '/app/Event/CommentEvent.php', + 'Kanboard\\Event\\GenericEvent' => $baseDir . '/app/Event/GenericEvent.php', + 'Kanboard\\Event\\ProjectFileEvent' => $baseDir . '/app/Event/ProjectFileEvent.php', + 'Kanboard\\Event\\SubtaskEvent' => $baseDir . '/app/Event/SubtaskEvent.php', + 'Kanboard\\Event\\TaskEvent' => $baseDir . '/app/Event/TaskEvent.php', + 'Kanboard\\Event\\TaskFileEvent' => $baseDir . '/app/Event/TaskFileEvent.php', + 'Kanboard\\Event\\TaskLinkEvent' => $baseDir . '/app/Event/TaskLinkEvent.php', + 'Kanboard\\Event\\TaskListEvent' => $baseDir . '/app/Event/TaskListEvent.php', + 'Kanboard\\Event\\UserProfileSyncEvent' => $baseDir . '/app/Event/UserProfileSyncEvent.php', + 'Kanboard\\Export\\SubtaskExport' => $baseDir . '/app/Export/SubtaskExport.php', + 'Kanboard\\Export\\TaskExport' => $baseDir . '/app/Export/TaskExport.php', + 'Kanboard\\Export\\TransitionExport' => $baseDir . '/app/Export/TransitionExport.php', + 'Kanboard\\ExternalLink\\AttachmentLink' => $baseDir . '/app/ExternalLink/AttachmentLink.php', + 'Kanboard\\ExternalLink\\AttachmentLinkProvider' => $baseDir . '/app/ExternalLink/AttachmentLinkProvider.php', + 'Kanboard\\ExternalLink\\BaseLink' => $baseDir . '/app/ExternalLink/BaseLink.php', + 'Kanboard\\ExternalLink\\BaseLinkProvider' => $baseDir . '/app/ExternalLink/BaseLinkProvider.php', + 'Kanboard\\ExternalLink\\FileLink' => $baseDir . '/app/ExternalLink/FileLink.php', + 'Kanboard\\ExternalLink\\FileLinkProvider' => $baseDir . '/app/ExternalLink/FileLinkProvider.php', + 'Kanboard\\ExternalLink\\WebLink' => $baseDir . '/app/ExternalLink/WebLink.php', + 'Kanboard\\ExternalLink\\WebLinkProvider' => $baseDir . '/app/ExternalLink/WebLinkProvider.php', + 'Kanboard\\Filter\\BaseComparisonFilter' => $baseDir . '/app/Filter/BaseComparisonFilter.php', + 'Kanboard\\Filter\\BaseDateFilter' => $baseDir . '/app/Filter/BaseDateFilter.php', + 'Kanboard\\Filter\\BaseFilter' => $baseDir . '/app/Filter/BaseFilter.php', + 'Kanboard\\Filter\\ProjectActivityCreationDateFilter' => $baseDir . '/app/Filter/ProjectActivityCreationDateFilter.php', + 'Kanboard\\Filter\\ProjectActivityCreatorFilter' => $baseDir . '/app/Filter/ProjectActivityCreatorFilter.php', + 'Kanboard\\Filter\\ProjectActivityProjectIdFilter' => $baseDir . '/app/Filter/ProjectActivityProjectIdFilter.php', + 'Kanboard\\Filter\\ProjectActivityProjectIdsFilter' => $baseDir . '/app/Filter/ProjectActivityProjectIdsFilter.php', + 'Kanboard\\Filter\\ProjectActivityProjectNameFilter' => $baseDir . '/app/Filter/ProjectActivityProjectNameFilter.php', + 'Kanboard\\Filter\\ProjectActivityTaskIdFilter' => $baseDir . '/app/Filter/ProjectActivityTaskIdFilter.php', + 'Kanboard\\Filter\\ProjectActivityTaskStatusFilter' => $baseDir . '/app/Filter/ProjectActivityTaskStatusFilter.php', + 'Kanboard\\Filter\\ProjectActivityTaskTitleFilter' => $baseDir . '/app/Filter/ProjectActivityTaskTitleFilter.php', + 'Kanboard\\Filter\\ProjectGroupRoleProjectFilter' => $baseDir . '/app/Filter/ProjectGroupRoleProjectFilter.php', + 'Kanboard\\Filter\\ProjectGroupRoleUsernameFilter' => $baseDir . '/app/Filter/ProjectGroupRoleUsernameFilter.php', + 'Kanboard\\Filter\\ProjectIdsFilter' => $baseDir . '/app/Filter/ProjectIdsFilter.php', + 'Kanboard\\Filter\\ProjectStatusFilter' => $baseDir . '/app/Filter/ProjectStatusFilter.php', + 'Kanboard\\Filter\\ProjectTypeFilter' => $baseDir . '/app/Filter/ProjectTypeFilter.php', + 'Kanboard\\Filter\\ProjectUserRoleProjectFilter' => $baseDir . '/app/Filter/ProjectUserRoleProjectFilter.php', + 'Kanboard\\Filter\\ProjectUserRoleUsernameFilter' => $baseDir . '/app/Filter/ProjectUserRoleUsernameFilter.php', + 'Kanboard\\Filter\\TaskAssigneeFilter' => $baseDir . '/app/Filter/TaskAssigneeFilter.php', + 'Kanboard\\Filter\\TaskCategoryFilter' => $baseDir . '/app/Filter/TaskCategoryFilter.php', + 'Kanboard\\Filter\\TaskColorFilter' => $baseDir . '/app/Filter/TaskColorFilter.php', + 'Kanboard\\Filter\\TaskColumnFilter' => $baseDir . '/app/Filter/TaskColumnFilter.php', + 'Kanboard\\Filter\\TaskCommentFilter' => $baseDir . '/app/Filter/TaskCommentFilter.php', + 'Kanboard\\Filter\\TaskCompletionDateFilter' => $baseDir . '/app/Filter/TaskCompletionDateFilter.php', + 'Kanboard\\Filter\\TaskCreationDateFilter' => $baseDir . '/app/Filter/TaskCreationDateFilter.php', + 'Kanboard\\Filter\\TaskCreatorFilter' => $baseDir . '/app/Filter/TaskCreatorFilter.php', + 'Kanboard\\Filter\\TaskDescriptionFilter' => $baseDir . '/app/Filter/TaskDescriptionFilter.php', + 'Kanboard\\Filter\\TaskDueDateFilter' => $baseDir . '/app/Filter/TaskDueDateFilter.php', + 'Kanboard\\Filter\\TaskDueDateRangeFilter' => $baseDir . '/app/Filter/TaskDueDateRangeFilter.php', + 'Kanboard\\Filter\\TaskIdExclusionFilter' => $baseDir . '/app/Filter/TaskIdExclusionFilter.php', + 'Kanboard\\Filter\\TaskIdFilter' => $baseDir . '/app/Filter/TaskIdFilter.php', + 'Kanboard\\Filter\\TaskLinkFilter' => $baseDir . '/app/Filter/TaskLinkFilter.php', + 'Kanboard\\Filter\\TaskModificationDateFilter' => $baseDir . '/app/Filter/TaskModificationDateFilter.php', + 'Kanboard\\Filter\\TaskMovedDateFilter' => $baseDir . '/app/Filter/TaskMovedDateFilter.php', + 'Kanboard\\Filter\\TaskPriorityFilter' => $baseDir . '/app/Filter/TaskPriorityFilter.php', + 'Kanboard\\Filter\\TaskProjectFilter' => $baseDir . '/app/Filter/TaskProjectFilter.php', + 'Kanboard\\Filter\\TaskProjectsFilter' => $baseDir . '/app/Filter/TaskProjectsFilter.php', + 'Kanboard\\Filter\\TaskReferenceFilter' => $baseDir . '/app/Filter/TaskReferenceFilter.php', + 'Kanboard\\Filter\\TaskScoreFilter' => $baseDir . '/app/Filter/TaskScoreFilter.php', + 'Kanboard\\Filter\\TaskStartDateFilter' => $baseDir . '/app/Filter/TaskStartDateFilter.php', + 'Kanboard\\Filter\\TaskStartsWithIdFilter' => $baseDir . '/app/Filter/TaskStartsWithIdFilter.php', + 'Kanboard\\Filter\\TaskStatusFilter' => $baseDir . '/app/Filter/TaskStatusFilter.php', + 'Kanboard\\Filter\\TaskSubtaskAssigneeFilter' => $baseDir . '/app/Filter/TaskSubtaskAssigneeFilter.php', + 'Kanboard\\Filter\\TaskSwimlaneFilter' => $baseDir . '/app/Filter/TaskSwimlaneFilter.php', + 'Kanboard\\Filter\\TaskTagFilter' => $baseDir . '/app/Filter/TaskTagFilter.php', + 'Kanboard\\Filter\\TaskTitleFilter' => $baseDir . '/app/Filter/TaskTitleFilter.php', + 'Kanboard\\Filter\\UserNameFilter' => $baseDir . '/app/Filter/UserNameFilter.php', + 'Kanboard\\Formatter\\BaseFormatter' => $baseDir . '/app/Formatter/BaseFormatter.php', + 'Kanboard\\Formatter\\BoardColumnFormatter' => $baseDir . '/app/Formatter/BoardColumnFormatter.php', + 'Kanboard\\Formatter\\BoardFormatter' => $baseDir . '/app/Formatter/BoardFormatter.php', + 'Kanboard\\Formatter\\BoardSwimlaneFormatter' => $baseDir . '/app/Formatter/BoardSwimlaneFormatter.php', + 'Kanboard\\Formatter\\BoardTaskFormatter' => $baseDir . '/app/Formatter/BoardTaskFormatter.php', + 'Kanboard\\Formatter\\GroupAutoCompleteFormatter' => $baseDir . '/app/Formatter/GroupAutoCompleteFormatter.php', + 'Kanboard\\Formatter\\ProjectActivityEventFormatter' => $baseDir . '/app/Formatter/ProjectActivityEventFormatter.php', + 'Kanboard\\Formatter\\ProjectApiFormatter' => $baseDir . '/app/Formatter/ProjectApiFormatter.php', + 'Kanboard\\Formatter\\ProjectsApiFormatter' => $baseDir . '/app/Formatter/ProjectsApiFormatter.php', + 'Kanboard\\Formatter\\SubtaskListFormatter' => $baseDir . '/app/Formatter/SubtaskListFormatter.php', + 'Kanboard\\Formatter\\SubtaskTimeTrackingCalendarFormatter' => $baseDir . '/app/Formatter/SubtaskTimeTrackingCalendarFormatter.php', + 'Kanboard\\Formatter\\TaskApiFormatter' => $baseDir . '/app/Formatter/TaskApiFormatter.php', + 'Kanboard\\Formatter\\TaskAutoCompleteFormatter' => $baseDir . '/app/Formatter/TaskAutoCompleteFormatter.php', + 'Kanboard\\Formatter\\TaskICalFormatter' => $baseDir . '/app/Formatter/TaskICalFormatter.php', + 'Kanboard\\Formatter\\TaskListFormatter' => $baseDir . '/app/Formatter/TaskListFormatter.php', + 'Kanboard\\Formatter\\TaskListSubtaskAssigneeFormatter' => $baseDir . '/app/Formatter/TaskListSubtaskAssigneeFormatter.php', + 'Kanboard\\Formatter\\TaskListSubtaskFormatter' => $baseDir . '/app/Formatter/TaskListSubtaskFormatter.php', + 'Kanboard\\Formatter\\TaskSuggestMenuFormatter' => $baseDir . '/app/Formatter/TaskSuggestMenuFormatter.php', + 'Kanboard\\Formatter\\TasksApiFormatter' => $baseDir . '/app/Formatter/TasksApiFormatter.php', + 'Kanboard\\Formatter\\UserAutoCompleteFormatter' => $baseDir . '/app/Formatter/UserAutoCompleteFormatter.php', + 'Kanboard\\Formatter\\UserMentionFormatter' => $baseDir . '/app/Formatter/UserMentionFormatter.php', + 'Kanboard\\Group\\DatabaseBackendGroupProvider' => $baseDir . '/app/Group/DatabaseBackendGroupProvider.php', + 'Kanboard\\Group\\DatabaseGroupProvider' => $baseDir . '/app/Group/DatabaseGroupProvider.php', + 'Kanboard\\Group\\LdapBackendGroupProvider' => $baseDir . '/app/Group/LdapBackendGroupProvider.php', + 'Kanboard\\Group\\LdapGroupProvider' => $baseDir . '/app/Group/LdapGroupProvider.php', + 'Kanboard\\Helper\\AppHelper' => $baseDir . '/app/Helper/AppHelper.php', + 'Kanboard\\Helper\\AssetHelper' => $baseDir . '/app/Helper/AssetHelper.php', + 'Kanboard\\Helper\\AvatarHelper' => $baseDir . '/app/Helper/AvatarHelper.php', + 'Kanboard\\Helper\\BoardHelper' => $baseDir . '/app/Helper/BoardHelper.php', + 'Kanboard\\Helper\\CommentHelper' => $baseDir . '/app/Helper/CommentHelper.php', + 'Kanboard\\Helper\\DateHelper' => $baseDir . '/app/Helper/DateHelper.php', + 'Kanboard\\Helper\\FileHelper' => $baseDir . '/app/Helper/FileHelper.php', + 'Kanboard\\Helper\\FormHelper' => $baseDir . '/app/Helper/FormHelper.php', + 'Kanboard\\Helper\\HookHelper' => $baseDir . '/app/Helper/HookHelper.php', + 'Kanboard\\Helper\\LayoutHelper' => $baseDir . '/app/Helper/LayoutHelper.php', + 'Kanboard\\Helper\\MailHelper' => $baseDir . '/app/Helper/MailHelper.php', + 'Kanboard\\Helper\\ModalHelper' => $baseDir . '/app/Helper/ModalHelper.php', + 'Kanboard\\Helper\\ModelHelper' => $baseDir . '/app/Helper/ModelHelper.php', + 'Kanboard\\Helper\\ProjectActivityHelper' => $baseDir . '/app/Helper/ProjectActivityHelper.php', + 'Kanboard\\Helper\\ProjectHeaderHelper' => $baseDir . '/app/Helper/ProjectHeaderHelper.php', + 'Kanboard\\Helper\\ProjectRoleHelper' => $baseDir . '/app/Helper/ProjectRoleHelper.php', + 'Kanboard\\Helper\\SubtaskHelper' => $baseDir . '/app/Helper/SubtaskHelper.php', + 'Kanboard\\Helper\\TaskHelper' => $baseDir . '/app/Helper/TaskHelper.php', + 'Kanboard\\Helper\\TextHelper' => $baseDir . '/app/Helper/TextHelper.php', + 'Kanboard\\Helper\\UrlHelper' => $baseDir . '/app/Helper/UrlHelper.php', + 'Kanboard\\Helper\\UserHelper' => $baseDir . '/app/Helper/UserHelper.php', + 'Kanboard\\Import\\TaskImport' => $baseDir . '/app/Import/TaskImport.php', + 'Kanboard\\Import\\UserImport' => $baseDir . '/app/Import/UserImport.php', + 'Kanboard\\Job\\BaseJob' => $baseDir . '/app/Job/BaseJob.php', + 'Kanboard\\Job\\CommentEventJob' => $baseDir . '/app/Job/CommentEventJob.php', + 'Kanboard\\Job\\EmailJob' => $baseDir . '/app/Job/EmailJob.php', + 'Kanboard\\Job\\HttpAsyncJob' => $baseDir . '/app/Job/HttpAsyncJob.php', + 'Kanboard\\Job\\NotificationJob' => $baseDir . '/app/Job/NotificationJob.php', + 'Kanboard\\Job\\ProjectFileEventJob' => $baseDir . '/app/Job/ProjectFileEventJob.php', + 'Kanboard\\Job\\ProjectMetricJob' => $baseDir . '/app/Job/ProjectMetricJob.php', + 'Kanboard\\Job\\SubtaskEventJob' => $baseDir . '/app/Job/SubtaskEventJob.php', + 'Kanboard\\Job\\TaskEventJob' => $baseDir . '/app/Job/TaskEventJob.php', + 'Kanboard\\Job\\TaskFileEventJob' => $baseDir . '/app/Job/TaskFileEventJob.php', + 'Kanboard\\Job\\TaskLinkEventJob' => $baseDir . '/app/Job/TaskLinkEventJob.php', + 'Kanboard\\Job\\UserMentionJob' => $baseDir . '/app/Job/UserMentionJob.php', + 'Kanboard\\Middleware\\ApplicationAuthorizationMiddleware' => $baseDir . '/app/Middleware/ApplicationAuthorizationMiddleware.php', + 'Kanboard\\Middleware\\AuthenticationMiddleware' => $baseDir . '/app/Middleware/AuthenticationMiddleware.php', + 'Kanboard\\Middleware\\BootstrapMiddleware' => $baseDir . '/app/Middleware/BootstrapMiddleware.php', + 'Kanboard\\Middleware\\PostAuthenticationMiddleware' => $baseDir . '/app/Middleware/PostAuthenticationMiddleware.php', + 'Kanboard\\Middleware\\ProjectAuthorizationMiddleware' => $baseDir . '/app/Middleware/ProjectAuthorizationMiddleware.php', + 'Kanboard\\Model\\ActionModel' => $baseDir . '/app/Model/ActionModel.php', + 'Kanboard\\Model\\ActionParameterModel' => $baseDir . '/app/Model/ActionParameterModel.php', + 'Kanboard\\Model\\AvatarFileModel' => $baseDir . '/app/Model/AvatarFileModel.php', + 'Kanboard\\Model\\BoardModel' => $baseDir . '/app/Model/BoardModel.php', + 'Kanboard\\Model\\CategoryModel' => $baseDir . '/app/Model/CategoryModel.php', + 'Kanboard\\Model\\ColorModel' => $baseDir . '/app/Model/ColorModel.php', + 'Kanboard\\Model\\ColumnModel' => $baseDir . '/app/Model/ColumnModel.php', + 'Kanboard\\Model\\ColumnMoveRestrictionModel' => $baseDir . '/app/Model/ColumnMoveRestrictionModel.php', + 'Kanboard\\Model\\ColumnRestrictionModel' => $baseDir . '/app/Model/ColumnRestrictionModel.php', + 'Kanboard\\Model\\CommentModel' => $baseDir . '/app/Model/CommentModel.php', + 'Kanboard\\Model\\ConfigModel' => $baseDir . '/app/Model/ConfigModel.php', + 'Kanboard\\Model\\CurrencyModel' => $baseDir . '/app/Model/CurrencyModel.php', + 'Kanboard\\Model\\CustomFilterModel' => $baseDir . '/app/Model/CustomFilterModel.php', + 'Kanboard\\Model\\FileModel' => $baseDir . '/app/Model/FileModel.php', + 'Kanboard\\Model\\GroupMemberModel' => $baseDir . '/app/Model/GroupMemberModel.php', + 'Kanboard\\Model\\GroupModel' => $baseDir . '/app/Model/GroupModel.php', + 'Kanboard\\Model\\InviteModel' => $baseDir . '/app/Model/InviteModel.php', + 'Kanboard\\Model\\LanguageModel' => $baseDir . '/app/Model/LanguageModel.php', + 'Kanboard\\Model\\LastLoginModel' => $baseDir . '/app/Model/LastLoginModel.php', + 'Kanboard\\Model\\LinkModel' => $baseDir . '/app/Model/LinkModel.php', + 'Kanboard\\Model\\MetadataModel' => $baseDir . '/app/Model/MetadataModel.php', + 'Kanboard\\Model\\NotificationModel' => $baseDir . '/app/Model/NotificationModel.php', + 'Kanboard\\Model\\NotificationTypeModel' => $baseDir . '/app/Model/NotificationTypeModel.php', + 'Kanboard\\Model\\PasswordResetModel' => $baseDir . '/app/Model/PasswordResetModel.php', + 'Kanboard\\Model\\PredefinedTaskDescriptionModel' => $baseDir . '/app/Model/PredefinedTaskDescriptionModel.php', + 'Kanboard\\Model\\ProjectActivityModel' => $baseDir . '/app/Model/ProjectActivityModel.php', + 'Kanboard\\Model\\ProjectDailyColumnStatsModel' => $baseDir . '/app/Model/ProjectDailyColumnStatsModel.php', + 'Kanboard\\Model\\ProjectDailyStatsModel' => $baseDir . '/app/Model/ProjectDailyStatsModel.php', + 'Kanboard\\Model\\ProjectDuplicationModel' => $baseDir . '/app/Model/ProjectDuplicationModel.php', + 'Kanboard\\Model\\ProjectFileModel' => $baseDir . '/app/Model/ProjectFileModel.php', + 'Kanboard\\Model\\ProjectGroupRoleModel' => $baseDir . '/app/Model/ProjectGroupRoleModel.php', + 'Kanboard\\Model\\ProjectMetadataModel' => $baseDir . '/app/Model/ProjectMetadataModel.php', + 'Kanboard\\Model\\ProjectModel' => $baseDir . '/app/Model/ProjectModel.php', + 'Kanboard\\Model\\ProjectNotificationModel' => $baseDir . '/app/Model/ProjectNotificationModel.php', + 'Kanboard\\Model\\ProjectNotificationTypeModel' => $baseDir . '/app/Model/ProjectNotificationTypeModel.php', + 'Kanboard\\Model\\ProjectPermissionModel' => $baseDir . '/app/Model/ProjectPermissionModel.php', + 'Kanboard\\Model\\ProjectRoleModel' => $baseDir . '/app/Model/ProjectRoleModel.php', + 'Kanboard\\Model\\ProjectRoleRestrictionModel' => $baseDir . '/app/Model/ProjectRoleRestrictionModel.php', + 'Kanboard\\Model\\ProjectTaskDuplicationModel' => $baseDir . '/app/Model/ProjectTaskDuplicationModel.php', + 'Kanboard\\Model\\ProjectTaskPriorityModel' => $baseDir . '/app/Model/ProjectTaskPriorityModel.php', + 'Kanboard\\Model\\ProjectUserRoleModel' => $baseDir . '/app/Model/ProjectUserRoleModel.php', + 'Kanboard\\Model\\RememberMeSessionModel' => $baseDir . '/app/Model/RememberMeSessionModel.php', + 'Kanboard\\Model\\SettingModel' => $baseDir . '/app/Model/SettingModel.php', + 'Kanboard\\Model\\SubtaskModel' => $baseDir . '/app/Model/SubtaskModel.php', + 'Kanboard\\Model\\SubtaskPositionModel' => $baseDir . '/app/Model/SubtaskPositionModel.php', + 'Kanboard\\Model\\SubtaskStatusModel' => $baseDir . '/app/Model/SubtaskStatusModel.php', + 'Kanboard\\Model\\SubtaskTaskConversionModel' => $baseDir . '/app/Model/SubtaskTaskConversionModel.php', + 'Kanboard\\Model\\SubtaskTimeTrackingModel' => $baseDir . '/app/Model/SubtaskTimeTrackingModel.php', + 'Kanboard\\Model\\SwimlaneModel' => $baseDir . '/app/Model/SwimlaneModel.php', + 'Kanboard\\Model\\TagDuplicationModel' => $baseDir . '/app/Model/TagDuplicationModel.php', + 'Kanboard\\Model\\TagModel' => $baseDir . '/app/Model/TagModel.php', + 'Kanboard\\Model\\TaskAnalyticModel' => $baseDir . '/app/Model/TaskAnalyticModel.php', + 'Kanboard\\Model\\TaskCreationModel' => $baseDir . '/app/Model/TaskCreationModel.php', + 'Kanboard\\Model\\TaskDuplicationModel' => $baseDir . '/app/Model/TaskDuplicationModel.php', + 'Kanboard\\Model\\TaskExternalLinkModel' => $baseDir . '/app/Model/TaskExternalLinkModel.php', + 'Kanboard\\Model\\TaskFileModel' => $baseDir . '/app/Model/TaskFileModel.php', + 'Kanboard\\Model\\TaskFinderModel' => $baseDir . '/app/Model/TaskFinderModel.php', + 'Kanboard\\Model\\TaskLinkModel' => $baseDir . '/app/Model/TaskLinkModel.php', + 'Kanboard\\Model\\TaskMetadataModel' => $baseDir . '/app/Model/TaskMetadataModel.php', + 'Kanboard\\Model\\TaskModel' => $baseDir . '/app/Model/TaskModel.php', + 'Kanboard\\Model\\TaskModificationModel' => $baseDir . '/app/Model/TaskModificationModel.php', + 'Kanboard\\Model\\TaskPositionModel' => $baseDir . '/app/Model/TaskPositionModel.php', + 'Kanboard\\Model\\TaskProjectDuplicationModel' => $baseDir . '/app/Model/TaskProjectDuplicationModel.php', + 'Kanboard\\Model\\TaskProjectMoveModel' => $baseDir . '/app/Model/TaskProjectMoveModel.php', + 'Kanboard\\Model\\TaskRecurrenceModel' => $baseDir . '/app/Model/TaskRecurrenceModel.php', + 'Kanboard\\Model\\TaskStatusModel' => $baseDir . '/app/Model/TaskStatusModel.php', + 'Kanboard\\Model\\TaskTagModel' => $baseDir . '/app/Model/TaskTagModel.php', + 'Kanboard\\Model\\TimezoneModel' => $baseDir . '/app/Model/TimezoneModel.php', + 'Kanboard\\Model\\TransitionModel' => $baseDir . '/app/Model/TransitionModel.php', + 'Kanboard\\Model\\UserLockingModel' => $baseDir . '/app/Model/UserLockingModel.php', + 'Kanboard\\Model\\UserMetadataModel' => $baseDir . '/app/Model/UserMetadataModel.php', + 'Kanboard\\Model\\UserModel' => $baseDir . '/app/Model/UserModel.php', + 'Kanboard\\Model\\UserNotificationFilterModel' => $baseDir . '/app/Model/UserNotificationFilterModel.php', + 'Kanboard\\Model\\UserNotificationModel' => $baseDir . '/app/Model/UserNotificationModel.php', + 'Kanboard\\Model\\UserNotificationTypeModel' => $baseDir . '/app/Model/UserNotificationTypeModel.php', + 'Kanboard\\Model\\UserUnreadNotificationModel' => $baseDir . '/app/Model/UserUnreadNotificationModel.php', + 'Kanboard\\Notification\\ActivityStreamNotification' => $baseDir . '/app/Notification/ActivityStreamNotification.php', + 'Kanboard\\Notification\\MailNotification' => $baseDir . '/app/Notification/MailNotification.php', + 'Kanboard\\Notification\\WebNotification' => $baseDir . '/app/Notification/WebNotification.php', + 'Kanboard\\Notification\\WebhookNotification' => $baseDir . '/app/Notification/WebhookNotification.php', + 'Kanboard\\Pagination\\DashboardPagination' => $baseDir . '/app/Pagination/DashboardPagination.php', + 'Kanboard\\Pagination\\ProjectPagination' => $baseDir . '/app/Pagination/ProjectPagination.php', + 'Kanboard\\Pagination\\SubtaskPagination' => $baseDir . '/app/Pagination/SubtaskPagination.php', + 'Kanboard\\Pagination\\TaskPagination' => $baseDir . '/app/Pagination/TaskPagination.php', + 'Kanboard\\Pagination\\UserPagination' => $baseDir . '/app/Pagination/UserPagination.php', + 'Kanboard\\ServiceProvider\\ActionProvider' => $baseDir . '/app/ServiceProvider/ActionProvider.php', + 'Kanboard\\ServiceProvider\\ApiProvider' => $baseDir . '/app/ServiceProvider/ApiProvider.php', + 'Kanboard\\ServiceProvider\\AuthenticationProvider' => $baseDir . '/app/ServiceProvider/AuthenticationProvider.php', + 'Kanboard\\ServiceProvider\\AvatarProvider' => $baseDir . '/app/ServiceProvider/AvatarProvider.php', + 'Kanboard\\ServiceProvider\\CacheProvider' => $baseDir . '/app/ServiceProvider/CacheProvider.php', + 'Kanboard\\ServiceProvider\\ClassProvider' => $baseDir . '/app/ServiceProvider/ClassProvider.php', + 'Kanboard\\ServiceProvider\\CommandProvider' => $baseDir . '/app/ServiceProvider/CommandProvider.php', + 'Kanboard\\ServiceProvider\\DatabaseProvider' => $baseDir . '/app/ServiceProvider/DatabaseProvider.php', + 'Kanboard\\ServiceProvider\\EventDispatcherProvider' => $baseDir . '/app/ServiceProvider/EventDispatcherProvider.php', + 'Kanboard\\ServiceProvider\\ExternalLinkProvider' => $baseDir . '/app/ServiceProvider/ExternalLinkProvider.php', + 'Kanboard\\ServiceProvider\\ExternalTaskProvider' => $baseDir . '/app/ServiceProvider/ExternalTaskProvider.php', + 'Kanboard\\ServiceProvider\\FilterProvider' => $baseDir . '/app/ServiceProvider/FilterProvider.php', + 'Kanboard\\ServiceProvider\\FormatterProvider' => $baseDir . '/app/ServiceProvider/FormatterProvider.php', + 'Kanboard\\ServiceProvider\\GroupProvider' => $baseDir . '/app/ServiceProvider/GroupProvider.php', + 'Kanboard\\ServiceProvider\\HelperProvider' => $baseDir . '/app/ServiceProvider/HelperProvider.php', + 'Kanboard\\ServiceProvider\\JobProvider' => $baseDir . '/app/ServiceProvider/JobProvider.php', + 'Kanboard\\ServiceProvider\\LoggingProvider' => $baseDir . '/app/ServiceProvider/LoggingProvider.php', + 'Kanboard\\ServiceProvider\\MailProvider' => $baseDir . '/app/ServiceProvider/MailProvider.php', + 'Kanboard\\ServiceProvider\\NotificationProvider' => $baseDir . '/app/ServiceProvider/NotificationProvider.php', + 'Kanboard\\ServiceProvider\\ObjectStorageProvider' => $baseDir . '/app/ServiceProvider/ObjectStorageProvider.php', + 'Kanboard\\ServiceProvider\\PluginProvider' => $baseDir . '/app/ServiceProvider/PluginProvider.php', + 'Kanboard\\ServiceProvider\\QueueProvider' => $baseDir . '/app/ServiceProvider/QueueProvider.php', + 'Kanboard\\ServiceProvider\\RouteProvider' => $baseDir . '/app/ServiceProvider/RouteProvider.php', + 'Kanboard\\ServiceProvider\\SessionProvider' => $baseDir . '/app/ServiceProvider/SessionProvider.php', + 'Kanboard\\ServiceProvider\\UserProvider' => $baseDir . '/app/ServiceProvider/UserProvider.php', + 'Kanboard\\Subscriber\\AuthSubscriber' => $baseDir . '/app/Subscriber/AuthSubscriber.php', + 'Kanboard\\Subscriber\\BaseSubscriber' => $baseDir . '/app/Subscriber/BaseSubscriber.php', + 'Kanboard\\Subscriber\\BootstrapSubscriber' => $baseDir . '/app/Subscriber/BootstrapSubscriber.php', + 'Kanboard\\Subscriber\\LdapUserPhotoSubscriber' => $baseDir . '/app/Subscriber/LdapUserPhotoSubscriber.php', + 'Kanboard\\Subscriber\\NotificationSubscriber' => $baseDir . '/app/Subscriber/NotificationSubscriber.php', + 'Kanboard\\Subscriber\\ProjectDailySummarySubscriber' => $baseDir . '/app/Subscriber/ProjectDailySummarySubscriber.php', + 'Kanboard\\Subscriber\\ProjectModificationDateSubscriber' => $baseDir . '/app/Subscriber/ProjectModificationDateSubscriber.php', + 'Kanboard\\Subscriber\\RecurringTaskSubscriber' => $baseDir . '/app/Subscriber/RecurringTaskSubscriber.php', + 'Kanboard\\Subscriber\\TransitionSubscriber' => $baseDir . '/app/Subscriber/TransitionSubscriber.php', + 'Kanboard\\User\\Avatar\\AvatarFileProvider' => $baseDir . '/app/User/Avatar/AvatarFileProvider.php', + 'Kanboard\\User\\Avatar\\LetterAvatarProvider' => $baseDir . '/app/User/Avatar/LetterAvatarProvider.php', + 'Kanboard\\User\\DatabaseBackendUserProvider' => $baseDir . '/app/User/DatabaseBackendUserProvider.php', + 'Kanboard\\User\\DatabaseUserProvider' => $baseDir . '/app/User/DatabaseUserProvider.php', + 'Kanboard\\User\\LdapUserProvider' => $baseDir . '/app/User/LdapUserProvider.php', + 'Kanboard\\User\\OAuthUserProvider' => $baseDir . '/app/User/OAuthUserProvider.php', + 'Kanboard\\User\\ReverseProxyUserProvider' => $baseDir . '/app/User/ReverseProxyUserProvider.php', + 'Kanboard\\Validator\\ActionValidator' => $baseDir . '/app/Validator/ActionValidator.php', + 'Kanboard\\Validator\\AuthValidator' => $baseDir . '/app/Validator/AuthValidator.php', + 'Kanboard\\Validator\\BaseValidator' => $baseDir . '/app/Validator/BaseValidator.php', + 'Kanboard\\Validator\\CategoryValidator' => $baseDir . '/app/Validator/CategoryValidator.php', + 'Kanboard\\Validator\\ColumnMoveRestrictionValidator' => $baseDir . '/app/Validator/ColumnMoveRestrictionValidator.php', + 'Kanboard\\Validator\\ColumnRestrictionValidator' => $baseDir . '/app/Validator/ColumnRestrictionValidator.php', + 'Kanboard\\Validator\\ColumnValidator' => $baseDir . '/app/Validator/ColumnValidator.php', + 'Kanboard\\Validator\\CommentValidator' => $baseDir . '/app/Validator/CommentValidator.php', + 'Kanboard\\Validator\\CurrencyValidator' => $baseDir . '/app/Validator/CurrencyValidator.php', + 'Kanboard\\Validator\\CustomFilterValidator' => $baseDir . '/app/Validator/CustomFilterValidator.php', + 'Kanboard\\Validator\\ExternalLinkValidator' => $baseDir . '/app/Validator/ExternalLinkValidator.php', + 'Kanboard\\Validator\\GroupValidator' => $baseDir . '/app/Validator/GroupValidator.php', + 'Kanboard\\Validator\\LinkValidator' => $baseDir . '/app/Validator/LinkValidator.php', + 'Kanboard\\Validator\\PasswordResetValidator' => $baseDir . '/app/Validator/PasswordResetValidator.php', + 'Kanboard\\Validator\\PredefinedTaskDescriptionValidator' => $baseDir . '/app/Validator/PredefinedTaskDescriptionValidator.php', + 'Kanboard\\Validator\\ProjectRoleValidator' => $baseDir . '/app/Validator/ProjectRoleValidator.php', + 'Kanboard\\Validator\\ProjectValidator' => $baseDir . '/app/Validator/ProjectValidator.php', + 'Kanboard\\Validator\\SubtaskValidator' => $baseDir . '/app/Validator/SubtaskValidator.php', + 'Kanboard\\Validator\\SwimlaneValidator' => $baseDir . '/app/Validator/SwimlaneValidator.php', + 'Kanboard\\Validator\\TagValidator' => $baseDir . '/app/Validator/TagValidator.php', + 'Kanboard\\Validator\\TaskLinkValidator' => $baseDir . '/app/Validator/TaskLinkValidator.php', + 'Kanboard\\Validator\\TaskValidator' => $baseDir . '/app/Validator/TaskValidator.php', + 'Kanboard\\Validator\\UserValidator' => $baseDir . '/app/Validator/UserValidator.php', + 'Otp\\GoogleAuthenticator' => $vendorDir . '/christian-riesen/otp/src/Otp/GoogleAuthenticator.php', + 'Otp\\Otp' => $vendorDir . '/christian-riesen/otp/src/Otp/Otp.php', + 'Otp\\OtpInterface' => $vendorDir . '/christian-riesen/otp/src/Otp/OtpInterface.php', + 'PHPQRCode' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode.php', + 'PHPQRCode\\Autoloader' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/Autoloader.php', + 'PHPQRCode\\Constants' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/Constants.php', + 'PHPQRCode\\FrameFiller' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/FrameFiller.php', + 'PHPQRCode\\QRbitstream' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRbitstream.php', + 'PHPQRCode\\QRcode' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRcode.php', + 'PHPQRCode\\QRencode' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRencode.php', + 'PHPQRCode\\QRimage' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRimage.php', + 'PHPQRCode\\QRinput' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRinput.php', + 'PHPQRCode\\QRinputItem' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRinputItem.php', + 'PHPQRCode\\QRmask' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRmask.php', + 'PHPQRCode\\QRrawcode' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRrawcode.php', + 'PHPQRCode\\QRrs' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRrs.php', + 'PHPQRCode\\QRrsItem' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRrsItem.php', + 'PHPQRCode\\QRrsblock' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRrsblock.php', + 'PHPQRCode\\QRspec' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRspec.php', + 'PHPQRCode\\QRsplit' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRsplit.php', + 'PHPQRCode\\QRstr' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRstr.php', + 'PHPQRCode\\QRtools' => $vendorDir . '/aferrandini/phpqrcode/lib/PHPQRCode/QRtools.php', + 'Parsedown' => $vendorDir . '/erusev/parsedown/Parsedown.php', + 'ParsedownTest' => $vendorDir . '/erusev/parsedown/test/ParsedownTest.php', + 'PicoDb\\Builder\\BaseBuilder' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Builder/BaseBuilder.php', + 'PicoDb\\Builder\\ConditionBuilder' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Builder/ConditionBuilder.php', + 'PicoDb\\Builder\\InsertBuilder' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Builder/InsertBuilder.php', + 'PicoDb\\Builder\\OrConditionBuilder' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Builder/OrConditionBuilder.php', + 'PicoDb\\Builder\\UpdateBuilder' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Builder/UpdateBuilder.php', + 'PicoDb\\Database' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Database.php', + 'PicoDb\\DriverFactory' => $vendorDir . '/fguillot/picodb/lib/PicoDb/DriverFactory.php', + 'PicoDb\\Driver\\Base' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Base.php', + 'PicoDb\\Driver\\Mssql' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Mssql.php', + 'PicoDb\\Driver\\Mysql' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Mysql.php', + 'PicoDb\\Driver\\Postgres' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Postgres.php', + 'PicoDb\\Driver\\Sqlite' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php', + 'PicoDb\\Hashtable' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Hashtable.php', + 'PicoDb\\LargeObject' => $vendorDir . '/fguillot/picodb/lib/PicoDb/LargeObject.php', + 'PicoDb\\SQLException' => $vendorDir . '/fguillot/picodb/lib/PicoDb/SQLException.php', + 'PicoDb\\Schema' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Schema.php', + 'PicoDb\\StatementHandler' => $vendorDir . '/fguillot/picodb/lib/PicoDb/StatementHandler.php', + 'PicoDb\\Table' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Table.php', + 'PicoDb\\UrlParser' => $vendorDir . '/fguillot/picodb/lib/PicoDb/UrlParser.php', + 'PicoFeed\\Base' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Base.php', + 'PicoFeed\\Client\\Client' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/Client.php', + 'PicoFeed\\Client\\ClientException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/ClientException.php', + 'PicoFeed\\Client\\Curl' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/Curl.php', + 'PicoFeed\\Client\\ForbiddenException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/ForbiddenException.php', + 'PicoFeed\\Client\\HttpHeaders' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/HttpHeaders.php', + 'PicoFeed\\Client\\InvalidCertificateException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php', + 'PicoFeed\\Client\\InvalidUrlException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/InvalidUrlException.php', + 'PicoFeed\\Client\\MaxRedirectException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/MaxRedirectException.php', + 'PicoFeed\\Client\\MaxSizeException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/MaxSizeException.php', + 'PicoFeed\\Client\\Stream' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/Stream.php', + 'PicoFeed\\Client\\TimeoutException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/TimeoutException.php', + 'PicoFeed\\Client\\UnauthorizedException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/UnauthorizedException.php', + 'PicoFeed\\Client\\Url' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Client/Url.php', + 'PicoFeed\\Config\\Config' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Config/Config.php', + 'PicoFeed\\Encoding\\Encoding' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Encoding/Encoding.php', + 'PicoFeed\\Filter\\Attribute' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Filter/Attribute.php', + 'PicoFeed\\Filter\\Filter' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Filter/Filter.php', + 'PicoFeed\\Filter\\Html' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Filter/Html.php', + 'PicoFeed\\Filter\\Tag' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Filter/Tag.php', + 'PicoFeed\\Generator\\ContentGeneratorInterface' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Generator/ContentGeneratorInterface.php', + 'PicoFeed\\Generator\\FileContentGenerator' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Generator/FileContentGenerator.php', + 'PicoFeed\\Generator\\YoutubeContentGenerator' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Generator/YoutubeContentGenerator.php', + 'PicoFeed\\Logging\\Logger' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Logging/Logger.php', + 'PicoFeed\\Parser\\Atom' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/Atom.php', + 'PicoFeed\\Parser\\DateParser' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/DateParser.php', + 'PicoFeed\\Parser\\Feed' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/Feed.php', + 'PicoFeed\\Parser\\Item' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/Item.php', + 'PicoFeed\\Parser\\MalformedXmlException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php', + 'PicoFeed\\Parser\\Parser' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/Parser.php', + 'PicoFeed\\Parser\\ParserException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/ParserException.php', + 'PicoFeed\\Parser\\ParserInterface' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/ParserInterface.php', + 'PicoFeed\\Parser\\Rss10' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/Rss10.php', + 'PicoFeed\\Parser\\Rss20' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/Rss20.php', + 'PicoFeed\\Parser\\Rss91' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/Rss91.php', + 'PicoFeed\\Parser\\Rss92' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/Rss92.php', + 'PicoFeed\\Parser\\XmlEntityException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/XmlEntityException.php', + 'PicoFeed\\Parser\\XmlParser' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Parser/XmlParser.php', + 'PicoFeed\\PicoFeedException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/PicoFeedException.php', + 'PicoFeed\\Processor\\ContentFilterProcessor' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Processor/ContentFilterProcessor.php', + 'PicoFeed\\Processor\\ContentGeneratorProcessor' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Processor/ContentGeneratorProcessor.php', + 'PicoFeed\\Processor\\ItemPostProcessor' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Processor/ItemPostProcessor.php', + 'PicoFeed\\Processor\\ItemProcessorInterface' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Processor/ItemProcessorInterface.php', + 'PicoFeed\\Processor\\ScraperProcessor' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Processor/ScraperProcessor.php', + 'PicoFeed\\Reader\\Favicon' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Reader/Favicon.php', + 'PicoFeed\\Reader\\Reader' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Reader/Reader.php', + 'PicoFeed\\Reader\\ReaderException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Reader/ReaderException.php', + 'PicoFeed\\Reader\\SubscriptionNotFoundException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Reader/SubscriptionNotFoundException.php', + 'PicoFeed\\Reader\\UnsupportedFeedFormatException' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Reader/UnsupportedFeedFormatException.php', + 'PicoFeed\\Scraper\\CandidateParser' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Scraper/CandidateParser.php', + 'PicoFeed\\Scraper\\ParserInterface' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Scraper/ParserInterface.php', + 'PicoFeed\\Scraper\\RuleLoader' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Scraper/RuleLoader.php', + 'PicoFeed\\Scraper\\RuleParser' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Scraper/RuleParser.php', + 'PicoFeed\\Scraper\\Scraper' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Scraper/Scraper.php', + 'PicoFeed\\Serialization\\Subscription' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Serialization/Subscription.php', + 'PicoFeed\\Serialization\\SubscriptionList' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionList.php', + 'PicoFeed\\Serialization\\SubscriptionListBuilder' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListBuilder.php', + 'PicoFeed\\Serialization\\SubscriptionListParser' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListParser.php', + 'PicoFeed\\Serialization\\SubscriptionParser' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionParser.php', + 'PicoFeed\\Syndication\\AtomFeedBuilder' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Syndication/AtomFeedBuilder.php', + 'PicoFeed\\Syndication\\AtomHelper' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Syndication/AtomHelper.php', + 'PicoFeed\\Syndication\\AtomItemBuilder' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Syndication/AtomItemBuilder.php', + 'PicoFeed\\Syndication\\FeedBuilder' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Syndication/FeedBuilder.php', + 'PicoFeed\\Syndication\\ItemBuilder' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Syndication/ItemBuilder.php', + 'PicoFeed\\Syndication\\Rss20FeedBuilder' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20FeedBuilder.php', + 'PicoFeed\\Syndication\\Rss20Helper' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20Helper.php', + 'PicoFeed\\Syndication\\Rss20ItemBuilder' => $vendorDir . '/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20ItemBuilder.php', + 'Pimple\\Container' => $vendorDir . '/pimple/pimple/src/Pimple/Container.php', + 'Pimple\\ServiceProviderInterface' => $vendorDir . '/pimple/pimple/src/Pimple/ServiceProviderInterface.php', + 'Pimple\\Tests\\Fixtures\\Invokable' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Fixtures/Invokable.php', + 'Pimple\\Tests\\Fixtures\\NonInvokable' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php', + 'Pimple\\Tests\\Fixtures\\PimpleServiceProvider' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Fixtures/PimpleServiceProvider.php', + 'Pimple\\Tests\\Fixtures\\Service' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php', + 'Pimple\\Tests\\PimpleServiceProviderInterfaceTest' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php', + 'Pimple\\Tests\\PimpleTest' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/PimpleTest.php', + 'Psr\\Log\\AbstractLogger' => $vendorDir . '/psr/log/Psr/Log/AbstractLogger.php', + 'Psr\\Log\\InvalidArgumentException' => $vendorDir . '/psr/log/Psr/Log/InvalidArgumentException.php', + 'Psr\\Log\\LogLevel' => $vendorDir . '/psr/log/Psr/Log/LogLevel.php', + 'Psr\\Log\\LoggerAwareInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerAwareInterface.php', + 'Psr\\Log\\LoggerAwareTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerAwareTrait.php', + 'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerInterface.php', + 'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerTrait.php', + 'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/Psr/Log/NullLogger.php', + 'Psr\\Log\\Test\\DummyTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', + 'Psr\\Log\\Test\\LoggerInterfaceTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', + 'SimpleLogger\\Base' => $vendorDir . '/fguillot/simpleLogger/src/SimpleLogger/Base.php', + 'SimpleLogger\\File' => $vendorDir . '/fguillot/simpleLogger/src/SimpleLogger/File.php', + 'SimpleLogger\\Logger' => $vendorDir . '/fguillot/simpleLogger/src/SimpleLogger/Logger.php', + 'SimpleLogger\\Stderr' => $vendorDir . '/fguillot/simpleLogger/src/SimpleLogger/Stderr.php', + 'SimpleLogger\\Stdout' => $vendorDir . '/fguillot/simpleLogger/src/SimpleLogger/Stdout.php', + 'SimpleLogger\\Syslog' => $vendorDir . '/fguillot/simpleLogger/src/SimpleLogger/Syslog.php', + 'SimpleQueue\\Adapter\\AmqpQueueAdapter' => $vendorDir . '/fguillot/simple-queue/src/Adapter/AmqpQueueAdapter.php', + 'SimpleQueue\\Adapter\\AwsSqsQueueAdapter' => $vendorDir . '/fguillot/simple-queue/src/Adapter/AwsSqsQueueAdapter.php', + 'SimpleQueue\\Adapter\\BeanstalkQueueAdapter' => $vendorDir . '/fguillot/simple-queue/src/Adapter/BeanstalkQueueAdapter.php', + 'SimpleQueue\\Adapter\\DisqueQueueAdapter' => $vendorDir . '/fguillot/simple-queue/src/Adapter/DisqueQueueAdapter.php', + 'SimpleQueue\\Adapter\\MemoryQueueAdapter' => $vendorDir . '/fguillot/simple-queue/src/Adapter/MemoryQueueAdapter.php', + 'SimpleQueue\\Exception\\NotSupportedException' => $vendorDir . '/fguillot/simple-queue/src/Exception/NotSupportedException.php', + 'SimpleQueue\\Job' => $vendorDir . '/fguillot/simple-queue/src/Job.php', + 'SimpleQueue\\Queue' => $vendorDir . '/fguillot/simple-queue/src/Queue.php', + 'SimpleQueue\\QueueAdapterInterface' => $vendorDir . '/fguillot/simple-queue/src/QueueAdapterInterface.php', + 'SimpleValidator\\Validator' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validator.php', + 'SimpleValidator\\Validators\\Alpha' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Alpha.php', + 'SimpleValidator\\Validators\\AlphaNumeric' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/AlphaNumeric.php', + 'SimpleValidator\\Validators\\Base' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Base.php', + 'SimpleValidator\\Validators\\Date' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Date.php', + 'SimpleValidator\\Validators\\Email' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Email.php', + 'SimpleValidator\\Validators\\Equals' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Equals.php', + 'SimpleValidator\\Validators\\Exists' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Exists.php', + 'SimpleValidator\\Validators\\GreaterThan' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/GreaterThan.php', + 'SimpleValidator\\Validators\\InArray' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/InArray.php', + 'SimpleValidator\\Validators\\Integer' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Integer.php', + 'SimpleValidator\\Validators\\Ip' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Ip.php', + 'SimpleValidator\\Validators\\Length' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Length.php', + 'SimpleValidator\\Validators\\MaxLength' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/MaxLength.php', + 'SimpleValidator\\Validators\\MinLength' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/MinLength.php', + 'SimpleValidator\\Validators\\NotEmpty' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/NotEmpty.php', + 'SimpleValidator\\Validators\\NotEquals' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/NotEquals.php', + 'SimpleValidator\\Validators\\NotInArray' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/NotInArray.php', + 'SimpleValidator\\Validators\\Numeric' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Numeric.php', + 'SimpleValidator\\Validators\\Range' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Range.php', + 'SimpleValidator\\Validators\\Required' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Required.php', + 'SimpleValidator\\Validators\\Unique' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Unique.php', + 'Symfony\\Component\\Console\\Application' => $vendorDir . '/symfony/console/Application.php', + 'Symfony\\Component\\Console\\Command\\Command' => $vendorDir . '/symfony/console/Command/Command.php', + 'Symfony\\Component\\Console\\Command\\HelpCommand' => $vendorDir . '/symfony/console/Command/HelpCommand.php', + 'Symfony\\Component\\Console\\Command\\ListCommand' => $vendorDir . '/symfony/console/Command/ListCommand.php', + 'Symfony\\Component\\Console\\ConsoleEvents' => $vendorDir . '/symfony/console/ConsoleEvents.php', + 'Symfony\\Component\\Console\\Descriptor\\ApplicationDescription' => $vendorDir . '/symfony/console/Descriptor/ApplicationDescription.php', + 'Symfony\\Component\\Console\\Descriptor\\Descriptor' => $vendorDir . '/symfony/console/Descriptor/Descriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\DescriptorInterface' => $vendorDir . '/symfony/console/Descriptor/DescriptorInterface.php', + 'Symfony\\Component\\Console\\Descriptor\\JsonDescriptor' => $vendorDir . '/symfony/console/Descriptor/JsonDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\MarkdownDescriptor' => $vendorDir . '/symfony/console/Descriptor/MarkdownDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\TextDescriptor' => $vendorDir . '/symfony/console/Descriptor/TextDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\XmlDescriptor' => $vendorDir . '/symfony/console/Descriptor/XmlDescriptor.php', + 'Symfony\\Component\\Console\\Event\\ConsoleCommandEvent' => $vendorDir . '/symfony/console/Event/ConsoleCommandEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleEvent' => $vendorDir . '/symfony/console/Event/ConsoleEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleExceptionEvent' => $vendorDir . '/symfony/console/Event/ConsoleExceptionEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleTerminateEvent' => $vendorDir . '/symfony/console/Event/ConsoleTerminateEvent.php', + 'Symfony\\Component\\Console\\Exception\\CommandNotFoundException' => $vendorDir . '/symfony/console/Exception/CommandNotFoundException.php', + 'Symfony\\Component\\Console\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/console/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Console\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/console/Exception/InvalidArgumentException.php', + 'Symfony\\Component\\Console\\Exception\\InvalidOptionException' => $vendorDir . '/symfony/console/Exception/InvalidOptionException.php', + 'Symfony\\Component\\Console\\Exception\\LogicException' => $vendorDir . '/symfony/console/Exception/LogicException.php', + 'Symfony\\Component\\Console\\Exception\\RuntimeException' => $vendorDir . '/symfony/console/Exception/RuntimeException.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatter' => $vendorDir . '/symfony/console/Formatter/OutputFormatter.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterInterface' => $vendorDir . '/symfony/console/Formatter/OutputFormatterInterface.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyle' => $vendorDir . '/symfony/console/Formatter/OutputFormatterStyle.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleInterface' => $vendorDir . '/symfony/console/Formatter/OutputFormatterStyleInterface.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleStack' => $vendorDir . '/symfony/console/Formatter/OutputFormatterStyleStack.php', + 'Symfony\\Component\\Console\\Helper\\DebugFormatterHelper' => $vendorDir . '/symfony/console/Helper/DebugFormatterHelper.php', + 'Symfony\\Component\\Console\\Helper\\DescriptorHelper' => $vendorDir . '/symfony/console/Helper/DescriptorHelper.php', + 'Symfony\\Component\\Console\\Helper\\DialogHelper' => $vendorDir . '/symfony/console/Helper/DialogHelper.php', + 'Symfony\\Component\\Console\\Helper\\FormatterHelper' => $vendorDir . '/symfony/console/Helper/FormatterHelper.php', + 'Symfony\\Component\\Console\\Helper\\Helper' => $vendorDir . '/symfony/console/Helper/Helper.php', + 'Symfony\\Component\\Console\\Helper\\HelperInterface' => $vendorDir . '/symfony/console/Helper/HelperInterface.php', + 'Symfony\\Component\\Console\\Helper\\HelperSet' => $vendorDir . '/symfony/console/Helper/HelperSet.php', + 'Symfony\\Component\\Console\\Helper\\InputAwareHelper' => $vendorDir . '/symfony/console/Helper/InputAwareHelper.php', + 'Symfony\\Component\\Console\\Helper\\ProcessHelper' => $vendorDir . '/symfony/console/Helper/ProcessHelper.php', + 'Symfony\\Component\\Console\\Helper\\ProgressBar' => $vendorDir . '/symfony/console/Helper/ProgressBar.php', + 'Symfony\\Component\\Console\\Helper\\ProgressHelper' => $vendorDir . '/symfony/console/Helper/ProgressHelper.php', + 'Symfony\\Component\\Console\\Helper\\ProgressIndicator' => $vendorDir . '/symfony/console/Helper/ProgressIndicator.php', + 'Symfony\\Component\\Console\\Helper\\QuestionHelper' => $vendorDir . '/symfony/console/Helper/QuestionHelper.php', + 'Symfony\\Component\\Console\\Helper\\SymfonyQuestionHelper' => $vendorDir . '/symfony/console/Helper/SymfonyQuestionHelper.php', + 'Symfony\\Component\\Console\\Helper\\Table' => $vendorDir . '/symfony/console/Helper/Table.php', + 'Symfony\\Component\\Console\\Helper\\TableCell' => $vendorDir . '/symfony/console/Helper/TableCell.php', + 'Symfony\\Component\\Console\\Helper\\TableHelper' => $vendorDir . '/symfony/console/Helper/TableHelper.php', + 'Symfony\\Component\\Console\\Helper\\TableSeparator' => $vendorDir . '/symfony/console/Helper/TableSeparator.php', + 'Symfony\\Component\\Console\\Helper\\TableStyle' => $vendorDir . '/symfony/console/Helper/TableStyle.php', + 'Symfony\\Component\\Console\\Input\\ArgvInput' => $vendorDir . '/symfony/console/Input/ArgvInput.php', + 'Symfony\\Component\\Console\\Input\\ArrayInput' => $vendorDir . '/symfony/console/Input/ArrayInput.php', + 'Symfony\\Component\\Console\\Input\\Input' => $vendorDir . '/symfony/console/Input/Input.php', + 'Symfony\\Component\\Console\\Input\\InputArgument' => $vendorDir . '/symfony/console/Input/InputArgument.php', + 'Symfony\\Component\\Console\\Input\\InputAwareInterface' => $vendorDir . '/symfony/console/Input/InputAwareInterface.php', + 'Symfony\\Component\\Console\\Input\\InputDefinition' => $vendorDir . '/symfony/console/Input/InputDefinition.php', + 'Symfony\\Component\\Console\\Input\\InputInterface' => $vendorDir . '/symfony/console/Input/InputInterface.php', + 'Symfony\\Component\\Console\\Input\\InputOption' => $vendorDir . '/symfony/console/Input/InputOption.php', + 'Symfony\\Component\\Console\\Input\\StringInput' => $vendorDir . '/symfony/console/Input/StringInput.php', + 'Symfony\\Component\\Console\\Logger\\ConsoleLogger' => $vendorDir . '/symfony/console/Logger/ConsoleLogger.php', + 'Symfony\\Component\\Console\\Output\\BufferedOutput' => $vendorDir . '/symfony/console/Output/BufferedOutput.php', + 'Symfony\\Component\\Console\\Output\\ConsoleOutput' => $vendorDir . '/symfony/console/Output/ConsoleOutput.php', + 'Symfony\\Component\\Console\\Output\\ConsoleOutputInterface' => $vendorDir . '/symfony/console/Output/ConsoleOutputInterface.php', + 'Symfony\\Component\\Console\\Output\\NullOutput' => $vendorDir . '/symfony/console/Output/NullOutput.php', + 'Symfony\\Component\\Console\\Output\\Output' => $vendorDir . '/symfony/console/Output/Output.php', + 'Symfony\\Component\\Console\\Output\\OutputInterface' => $vendorDir . '/symfony/console/Output/OutputInterface.php', + 'Symfony\\Component\\Console\\Output\\StreamOutput' => $vendorDir . '/symfony/console/Output/StreamOutput.php', + 'Symfony\\Component\\Console\\Question\\ChoiceQuestion' => $vendorDir . '/symfony/console/Question/ChoiceQuestion.php', + 'Symfony\\Component\\Console\\Question\\ConfirmationQuestion' => $vendorDir . '/symfony/console/Question/ConfirmationQuestion.php', + 'Symfony\\Component\\Console\\Question\\Question' => $vendorDir . '/symfony/console/Question/Question.php', + 'Symfony\\Component\\Console\\Shell' => $vendorDir . '/symfony/console/Shell.php', + 'Symfony\\Component\\Console\\Style\\OutputStyle' => $vendorDir . '/symfony/console/Style/OutputStyle.php', + 'Symfony\\Component\\Console\\Style\\StyleInterface' => $vendorDir . '/symfony/console/Style/StyleInterface.php', + 'Symfony\\Component\\Console\\Style\\SymfonyStyle' => $vendorDir . '/symfony/console/Style/SymfonyStyle.php', + 'Symfony\\Component\\Console\\Tester\\ApplicationTester' => $vendorDir . '/symfony/console/Tester/ApplicationTester.php', + 'Symfony\\Component\\Console\\Tester\\CommandTester' => $vendorDir . '/symfony/console/Tester/CommandTester.php', + 'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/ContainerAwareEventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcherInterface' => $vendorDir . '/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener' => $vendorDir . '/symfony/event-dispatcher/Debug/WrappedListener.php', + 'Symfony\\Component\\EventDispatcher\\DependencyInjection\\RegisterListenersPass' => $vendorDir . '/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php', + 'Symfony\\Component\\EventDispatcher\\Event' => $vendorDir . '/symfony/event-dispatcher/Event.php', + 'Symfony\\Component\\EventDispatcher\\EventDispatcher' => $vendorDir . '/symfony/event-dispatcher/EventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\EventDispatcherInterface' => $vendorDir . '/symfony/event-dispatcher/EventDispatcherInterface.php', + 'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface' => $vendorDir . '/symfony/event-dispatcher/EventSubscriberInterface.php', + 'Symfony\\Component\\EventDispatcher\\GenericEvent' => $vendorDir . '/symfony/event-dispatcher/GenericEvent.php', + 'Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/ImmutableEventDispatcher.php', + 'Symfony\\Polyfill\\Mbstring\\Mbstring' => $vendorDir . '/symfony/polyfill-mbstring/Mbstring.php', + 'ZendXml\\Exception\\ExceptionInterface' => $vendorDir . '/zendframework/zendxml/library/ZendXml/Exception/ExceptionInterface.php', + 'ZendXml\\Exception\\InvalidArgumentException' => $vendorDir . '/zendframework/zendxml/library/ZendXml/Exception/InvalidArgumentException.php', + 'ZendXml\\Exception\\RuntimeException' => $vendorDir . '/zendframework/zendxml/library/ZendXml/Exception/RuntimeException.php', + 'ZendXml\\Security' => $vendorDir . '/zendframework/zendxml/library/ZendXml/Security.php', +); diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php new file mode 100644 index 00000000..e7fae22d --- /dev/null +++ b/vendor/composer/autoload_files.php @@ -0,0 +1,15 @@ +<?php + +// autoload_files.php @generated by Composer + +$vendorDir = dirname(dirname(__FILE__)); +$baseDir = dirname($vendorDir); + +return array( + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', + '5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php', + '8cd2fca4db21bffce1ad0612f7caeec4' => $vendorDir . '/ramsey/array_column/src/array_column.php', + '2c102faa651ef8ea5874edb585946bce' => $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php', + 'f51af1d1e172536bcdb5baf6f649449d' => $baseDir . '/app/functions.php', + 'da6e17f7b0fa11d4819751ff2afd0bac' => $baseDir . '/app/Library/password.php', +); diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php new file mode 100644 index 00000000..c2ba047d --- /dev/null +++ b/vendor/composer/autoload_namespaces.php @@ -0,0 +1,20 @@ +<?php + +// autoload_namespaces.php @generated by Composer + +$vendorDir = dirname(dirname(__FILE__)); +$baseDir = dirname($vendorDir); + +return array( + 'ZendXml\\' => array($vendorDir . '/zendframework/zendxml/library'), + 'SimpleValidator' => array($vendorDir . '/fguillot/simple-validator/src'), + 'SimpleLogger' => array($vendorDir . '/fguillot/simpleLogger/src'), + 'Pimple' => array($vendorDir . '/pimple/pimple/src'), + 'PicoFeed' => array($vendorDir . '/miniflux/picofeed/lib'), + 'PicoDb' => array($vendorDir . '/fguillot/picodb/lib'), + 'Parsedown' => array($vendorDir . '/erusev/parsedown'), + 'PHPQRCode' => array($vendorDir . '/aferrandini/phpqrcode/lib'), + 'Otp' => array($vendorDir . '/christian-riesen/otp/src'), + 'JsonRPC' => array($vendorDir . '/fguillot/json-rpc/src'), + 'Eluceo\\iCal' => array($vendorDir . '/eluceo/ical/src'), +); diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php new file mode 100644 index 00000000..7367aa46 --- /dev/null +++ b/vendor/composer/autoload_psr4.php @@ -0,0 +1,17 @@ +<?php + +// autoload_psr4.php @generated by Composer + +$vendorDir = dirname(dirname(__FILE__)); +$baseDir = dirname($vendorDir); + +return array( + 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), + 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), + 'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'), + 'SimpleQueue\\' => array($vendorDir . '/fguillot/simple-queue/src'), + 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), + 'Kanboard\\' => array($baseDir . '/app'), + 'Gregwar\\Captcha\\' => array($vendorDir . '/gregwar/captcha'), + 'Base32\\' => array($vendorDir . '/christian-riesen/base32/src'), +); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php new file mode 100644 index 00000000..2f490fe8 --- /dev/null +++ b/vendor/composer/autoload_real.php @@ -0,0 +1,70 @@ +<?php + +// autoload_real.php @generated by Composer + +class ComposerAutoloaderInit6edea6294a88689e3f5c56484bb70c9b +{ + private static $loader; + + public static function loadClassLoader($class) + { + if ('Composer\Autoload\ClassLoader' === $class) { + require __DIR__ . '/ClassLoader.php'; + } + } + + public static function getLoader() + { + if (null !== self::$loader) { + return self::$loader; + } + + spl_autoload_register(array('ComposerAutoloaderInit6edea6294a88689e3f5c56484bb70c9b', 'loadClassLoader'), true, true); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(); + spl_autoload_unregister(array('ComposerAutoloaderInit6edea6294a88689e3f5c56484bb70c9b', 'loadClassLoader')); + + $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); + if ($useStaticLoader) { + require_once __DIR__ . '/autoload_static.php'; + + call_user_func(\Composer\Autoload\ComposerStaticInit6edea6294a88689e3f5c56484bb70c9b::getInitializer($loader)); + } else { + $map = require __DIR__ . '/autoload_namespaces.php'; + foreach ($map as $namespace => $path) { + $loader->set($namespace, $path); + } + + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + } + + $loader->register(true); + + if ($useStaticLoader) { + $includeFiles = Composer\Autoload\ComposerStaticInit6edea6294a88689e3f5c56484bb70c9b::$files; + } else { + $includeFiles = require __DIR__ . '/autoload_files.php'; + } + foreach ($includeFiles as $fileIdentifier => $file) { + composerRequire6edea6294a88689e3f5c56484bb70c9b($fileIdentifier, $file); + } + + return $loader; + } +} + +function composerRequire6edea6294a88689e3f5c56484bb70c9b($fileIdentifier, $file) +{ + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + require $file; + + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + } +} diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php new file mode 100644 index 00000000..145c79bd --- /dev/null +++ b/vendor/composer/autoload_static.php @@ -0,0 +1,1055 @@ +<?php + +// autoload_static.php @generated by Composer + +namespace Composer\Autoload; + +class ComposerStaticInit6edea6294a88689e3f5c56484bb70c9b +{ + public static $files = array ( + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', + '5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php', + '8cd2fca4db21bffce1ad0612f7caeec4' => __DIR__ . '/..' . '/ramsey/array_column/src/array_column.php', + '2c102faa651ef8ea5874edb585946bce' => __DIR__ . '/..' . '/swiftmailer/swiftmailer/lib/swift_required.php', + 'f51af1d1e172536bcdb5baf6f649449d' => __DIR__ . '/../..' . '/app/functions.php', + 'da6e17f7b0fa11d4819751ff2afd0bac' => __DIR__ . '/../..' . '/app/Library/password.php', + ); + + public static $prefixLengthsPsr4 = array ( + 'S' => + array ( + 'Symfony\\Polyfill\\Mbstring\\' => 26, + 'Symfony\\Component\\EventDispatcher\\' => 34, + 'Symfony\\Component\\Console\\' => 26, + 'SimpleQueue\\' => 12, + ), + 'P' => + array ( + 'Psr\\Log\\' => 8, + ), + 'K' => + array ( + 'Kanboard\\' => 9, + ), + 'G' => + array ( + 'Gregwar\\Captcha\\' => 16, + ), + 'B' => + array ( + 'Base32\\' => 7, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'Symfony\\Polyfill\\Mbstring\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', + ), + 'Symfony\\Component\\EventDispatcher\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/event-dispatcher', + ), + 'Symfony\\Component\\Console\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/console', + ), + 'SimpleQueue\\' => + array ( + 0 => __DIR__ . '/..' . '/fguillot/simple-queue/src', + ), + 'Psr\\Log\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', + ), + 'Kanboard\\' => + array ( + 0 => __DIR__ . '/../..' . '/app', + ), + 'Gregwar\\Captcha\\' => + array ( + 0 => __DIR__ . '/..' . '/gregwar/captcha', + ), + 'Base32\\' => + array ( + 0 => __DIR__ . '/..' . '/christian-riesen/base32/src', + ), + ); + + public static $prefixesPsr0 = array ( + 'Z' => + array ( + 'ZendXml\\' => + array ( + 0 => __DIR__ . '/..' . '/zendframework/zendxml/library', + ), + ), + 'S' => + array ( + 'SimpleValidator' => + array ( + 0 => __DIR__ . '/..' . '/fguillot/simple-validator/src', + ), + 'SimpleLogger' => + array ( + 0 => __DIR__ . '/..' . '/fguillot/simpleLogger/src', + ), + ), + 'P' => + array ( + 'Pimple' => + array ( + 0 => __DIR__ . '/..' . '/pimple/pimple/src', + ), + 'PicoFeed' => + array ( + 0 => __DIR__ . '/..' . '/miniflux/picofeed/lib', + ), + 'PicoDb' => + array ( + 0 => __DIR__ . '/..' . '/fguillot/picodb/lib', + ), + 'Parsedown' => + array ( + 0 => __DIR__ . '/..' . '/erusev/parsedown', + ), + 'PHPQRCode' => + array ( + 0 => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib', + ), + ), + 'O' => + array ( + 'Otp' => + array ( + 0 => __DIR__ . '/..' . '/christian-riesen/otp/src', + ), + ), + 'J' => + array ( + 'JsonRPC' => + array ( + 0 => __DIR__ . '/..' . '/fguillot/json-rpc/src', + ), + ), + 'E' => + array ( + 'Eluceo\\iCal' => + array ( + 0 => __DIR__ . '/..' . '/eluceo/ical/src', + ), + ), + ); + + public static $classMap = array ( + 'Base32\\Base32' => __DIR__ . '/..' . '/christian-riesen/base32/src/Base32.php', + 'Eluceo\\iCal\\Component' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Component.php', + 'Eluceo\\iCal\\Component\\Alarm' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Component/Alarm.php', + 'Eluceo\\iCal\\Component\\Calendar' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Component/Calendar.php', + 'Eluceo\\iCal\\Component\\Event' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Component/Event.php', + 'Eluceo\\iCal\\Component\\Timezone' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Component/Timezone.php', + 'Eluceo\\iCal\\Component\\TimezoneRule' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Component/TimezoneRule.php', + 'Eluceo\\iCal\\ParameterBag' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/ParameterBag.php', + 'Eluceo\\iCal\\Property' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property.php', + 'Eluceo\\iCal\\PropertyBag' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/PropertyBag.php', + 'Eluceo\\iCal\\Property\\ArrayValue' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property/ArrayValue.php', + 'Eluceo\\iCal\\Property\\DateTimeProperty' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property/DateTimeProperty.php', + 'Eluceo\\iCal\\Property\\DateTimesProperty' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property/DateTimesProperty.php', + 'Eluceo\\iCal\\Property\\Event\\Attendees' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property/Event/Attendees.php', + 'Eluceo\\iCal\\Property\\Event\\Description' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property/Event/Description.php', + 'Eluceo\\iCal\\Property\\Event\\Organizer' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property/Event/Organizer.php', + 'Eluceo\\iCal\\Property\\Event\\RecurrenceId' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceId.php', + 'Eluceo\\iCal\\Property\\Event\\RecurrenceRule' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceRule.php', + 'Eluceo\\iCal\\Property\\StringValue' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property/StringValue.php', + 'Eluceo\\iCal\\Property\\ValueInterface' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Property/ValueInterface.php', + 'Eluceo\\iCal\\Util\\ComponentUtil' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Util/ComponentUtil.php', + 'Eluceo\\iCal\\Util\\DateUtil' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Util/DateUtil.php', + 'Eluceo\\iCal\\Util\\PropertyValueUtil' => __DIR__ . '/..' . '/eluceo/ical/src/Eluceo/iCal/Util/PropertyValueUtil.php', + 'Gregwar\\Captcha\\CaptchaBuilder' => __DIR__ . '/..' . '/gregwar/captcha/CaptchaBuilder.php', + 'Gregwar\\Captcha\\CaptchaBuilderInterface' => __DIR__ . '/..' . '/gregwar/captcha/CaptchaBuilderInterface.php', + 'Gregwar\\Captcha\\ImageFileHandler' => __DIR__ . '/..' . '/gregwar/captcha/ImageFileHandler.php', + 'Gregwar\\Captcha\\PhraseBuilder' => __DIR__ . '/..' . '/gregwar/captcha/PhraseBuilder.php', + 'Gregwar\\Captcha\\PhraseBuilderInterface' => __DIR__ . '/..' . '/gregwar/captcha/PhraseBuilderInterface.php', + 'JsonRPC\\Client' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Client.php', + 'JsonRPC\\Exception\\AccessDeniedException' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Exception/AccessDeniedException.php', + 'JsonRPC\\Exception\\AuthenticationFailureException' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Exception/AuthenticationFailureException.php', + 'JsonRPC\\Exception\\ConnectionFailureException' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Exception/ConnectionFailureException.php', + 'JsonRPC\\Exception\\InvalidJsonFormatException' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonFormatException.php', + 'JsonRPC\\Exception\\InvalidJsonRpcFormatException' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonRpcFormatException.php', + 'JsonRPC\\Exception\\ResponseEncodingFailureException' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Exception/ResponseEncodingFailureException.php', + 'JsonRPC\\Exception\\ResponseException' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Exception/ResponseException.php', + 'JsonRPC\\Exception\\ServerErrorException' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Exception/ServerErrorException.php', + 'JsonRPC\\HttpClient' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/HttpClient.php', + 'JsonRPC\\MiddlewareHandler' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/MiddlewareHandler.php', + 'JsonRPC\\MiddlewareInterface' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/MiddlewareInterface.php', + 'JsonRPC\\ProcedureHandler' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/ProcedureHandler.php', + 'JsonRPC\\Request\\BatchRequestParser' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Request/BatchRequestParser.php', + 'JsonRPC\\Request\\RequestBuilder' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Request/RequestBuilder.php', + 'JsonRPC\\Request\\RequestParser' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Request/RequestParser.php', + 'JsonRPC\\Response\\ResponseBuilder' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Response/ResponseBuilder.php', + 'JsonRPC\\Response\\ResponseParser' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Response/ResponseParser.php', + 'JsonRPC\\Server' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Server.php', + 'JsonRPC\\Validator\\HostValidator' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Validator/HostValidator.php', + 'JsonRPC\\Validator\\JsonEncodingValidator' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Validator/JsonEncodingValidator.php', + 'JsonRPC\\Validator\\JsonFormatValidator' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Validator/JsonFormatValidator.php', + 'JsonRPC\\Validator\\RpcFormatValidator' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Validator/RpcFormatValidator.php', + 'JsonRPC\\Validator\\UserValidator' => __DIR__ . '/..' . '/fguillot/json-rpc/src/JsonRPC/Validator/UserValidator.php', + 'Kanboard\\Action\\Base' => __DIR__ . '/../..' . '/app/Action/Base.php', + 'Kanboard\\Action\\CommentCreation' => __DIR__ . '/../..' . '/app/Action/CommentCreation.php', + 'Kanboard\\Action\\CommentCreationMoveTaskColumn' => __DIR__ . '/../..' . '/app/Action/CommentCreationMoveTaskColumn.php', + 'Kanboard\\Action\\TaskAssignCategoryColor' => __DIR__ . '/../..' . '/app/Action/TaskAssignCategoryColor.php', + 'Kanboard\\Action\\TaskAssignCategoryLabel' => __DIR__ . '/../..' . '/app/Action/TaskAssignCategoryLabel.php', + 'Kanboard\\Action\\TaskAssignCategoryLink' => __DIR__ . '/../..' . '/app/Action/TaskAssignCategoryLink.php', + 'Kanboard\\Action\\TaskAssignColorCategory' => __DIR__ . '/../..' . '/app/Action/TaskAssignColorCategory.php', + 'Kanboard\\Action\\TaskAssignColorColumn' => __DIR__ . '/../..' . '/app/Action/TaskAssignColorColumn.php', + 'Kanboard\\Action\\TaskAssignColorLink' => __DIR__ . '/../..' . '/app/Action/TaskAssignColorLink.php', + 'Kanboard\\Action\\TaskAssignColorOnDueDate' => __DIR__ . '/../..' . '/app/Action/TaskAssignColorOnDueDate.php', + 'Kanboard\\Action\\TaskAssignColorPriority' => __DIR__ . '/../..' . '/app/Action/TaskAssignColorPriority.php', + 'Kanboard\\Action\\TaskAssignColorSwimlane' => __DIR__ . '/../..' . '/app/Action/TaskAssignColorSwimlane.php', + 'Kanboard\\Action\\TaskAssignColorUser' => __DIR__ . '/../..' . '/app/Action/TaskAssignColorUser.php', + 'Kanboard\\Action\\TaskAssignCreator' => __DIR__ . '/../..' . '/app/Action/TaskAssignCreator.php', + 'Kanboard\\Action\\TaskAssignCurrentUser' => __DIR__ . '/../..' . '/app/Action/TaskAssignCurrentUser.php', + 'Kanboard\\Action\\TaskAssignCurrentUserColumn' => __DIR__ . '/../..' . '/app/Action/TaskAssignCurrentUserColumn.php', + 'Kanboard\\Action\\TaskAssignDueDateOnCreation' => __DIR__ . '/../..' . '/app/Action/TaskAssignDueDateOnCreation.php', + 'Kanboard\\Action\\TaskAssignPrioritySwimlane' => __DIR__ . '/../..' . '/app/Action/TaskAssignPrioritySwimlane.php', + 'Kanboard\\Action\\TaskAssignSpecificUser' => __DIR__ . '/../..' . '/app/Action/TaskAssignSpecificUser.php', + 'Kanboard\\Action\\TaskAssignUser' => __DIR__ . '/../..' . '/app/Action/TaskAssignUser.php', + 'Kanboard\\Action\\TaskClose' => __DIR__ . '/../..' . '/app/Action/TaskClose.php', + 'Kanboard\\Action\\TaskCloseColumn' => __DIR__ . '/../..' . '/app/Action/TaskCloseColumn.php', + 'Kanboard\\Action\\TaskCloseNoActivity' => __DIR__ . '/../..' . '/app/Action/TaskCloseNoActivity.php', + 'Kanboard\\Action\\TaskCloseNoActivityColumn' => __DIR__ . '/../..' . '/app/Action/TaskCloseNoActivityColumn.php', + 'Kanboard\\Action\\TaskCloseNotMovedColumn' => __DIR__ . '/../..' . '/app/Action/TaskCloseNotMovedColumn.php', + 'Kanboard\\Action\\TaskCreation' => __DIR__ . '/../..' . '/app/Action/TaskCreation.php', + 'Kanboard\\Action\\TaskDuplicateAnotherProject' => __DIR__ . '/../..' . '/app/Action/TaskDuplicateAnotherProject.php', + 'Kanboard\\Action\\TaskEmail' => __DIR__ . '/../..' . '/app/Action/TaskEmail.php', + 'Kanboard\\Action\\TaskEmailNoActivity' => __DIR__ . '/../..' . '/app/Action/TaskEmailNoActivity.php', + 'Kanboard\\Action\\TaskMoveAnotherProject' => __DIR__ . '/../..' . '/app/Action/TaskMoveAnotherProject.php', + 'Kanboard\\Action\\TaskMoveColumnAssigned' => __DIR__ . '/../..' . '/app/Action/TaskMoveColumnAssigned.php', + 'Kanboard\\Action\\TaskMoveColumnCategoryChange' => __DIR__ . '/../..' . '/app/Action/TaskMoveColumnCategoryChange.php', + 'Kanboard\\Action\\TaskMoveColumnClosed' => __DIR__ . '/../..' . '/app/Action/TaskMoveColumnClosed.php', + 'Kanboard\\Action\\TaskMoveColumnNotMovedPeriod' => __DIR__ . '/../..' . '/app/Action/TaskMoveColumnNotMovedPeriod.php', + 'Kanboard\\Action\\TaskMoveColumnUnAssigned' => __DIR__ . '/../..' . '/app/Action/TaskMoveColumnUnAssigned.php', + 'Kanboard\\Action\\TaskOpen' => __DIR__ . '/../..' . '/app/Action/TaskOpen.php', + 'Kanboard\\Action\\TaskUpdateStartDate' => __DIR__ . '/../..' . '/app/Action/TaskUpdateStartDate.php', + 'Kanboard\\Analytic\\AverageLeadCycleTimeAnalytic' => __DIR__ . '/../..' . '/app/Analytic/AverageLeadCycleTimeAnalytic.php', + 'Kanboard\\Analytic\\AverageTimeSpentColumnAnalytic' => __DIR__ . '/../..' . '/app/Analytic/AverageTimeSpentColumnAnalytic.php', + 'Kanboard\\Analytic\\EstimatedTimeComparisonAnalytic' => __DIR__ . '/../..' . '/app/Analytic/EstimatedTimeComparisonAnalytic.php', + 'Kanboard\\Analytic\\TaskDistributionAnalytic' => __DIR__ . '/../..' . '/app/Analytic/TaskDistributionAnalytic.php', + 'Kanboard\\Analytic\\UserDistributionAnalytic' => __DIR__ . '/../..' . '/app/Analytic/UserDistributionAnalytic.php', + 'Kanboard\\Api\\Authorization\\ActionAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/ActionAuthorization.php', + 'Kanboard\\Api\\Authorization\\CategoryAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/CategoryAuthorization.php', + 'Kanboard\\Api\\Authorization\\ColumnAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/ColumnAuthorization.php', + 'Kanboard\\Api\\Authorization\\CommentAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/CommentAuthorization.php', + 'Kanboard\\Api\\Authorization\\ProcedureAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/ProcedureAuthorization.php', + 'Kanboard\\Api\\Authorization\\ProjectAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/ProjectAuthorization.php', + 'Kanboard\\Api\\Authorization\\SubtaskAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/SubtaskAuthorization.php', + 'Kanboard\\Api\\Authorization\\TagAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/TagAuthorization.php', + 'Kanboard\\Api\\Authorization\\TaskAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/TaskAuthorization.php', + 'Kanboard\\Api\\Authorization\\TaskFileAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/TaskFileAuthorization.php', + 'Kanboard\\Api\\Authorization\\TaskLinkAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/TaskLinkAuthorization.php', + 'Kanboard\\Api\\Authorization\\UserAuthorization' => __DIR__ . '/../..' . '/app/Api/Authorization/UserAuthorization.php', + 'Kanboard\\Api\\Middleware\\AuthenticationMiddleware' => __DIR__ . '/../..' . '/app/Api/Middleware/AuthenticationMiddleware.php', + 'Kanboard\\Api\\Procedure\\ActionProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/ActionProcedure.php', + 'Kanboard\\Api\\Procedure\\AppProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/AppProcedure.php', + 'Kanboard\\Api\\Procedure\\BaseProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/BaseProcedure.php', + 'Kanboard\\Api\\Procedure\\BoardProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/BoardProcedure.php', + 'Kanboard\\Api\\Procedure\\CategoryProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/CategoryProcedure.php', + 'Kanboard\\Api\\Procedure\\ColumnProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/ColumnProcedure.php', + 'Kanboard\\Api\\Procedure\\CommentProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/CommentProcedure.php', + 'Kanboard\\Api\\Procedure\\GroupMemberProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/GroupMemberProcedure.php', + 'Kanboard\\Api\\Procedure\\GroupProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/GroupProcedure.php', + 'Kanboard\\Api\\Procedure\\LinkProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/LinkProcedure.php', + 'Kanboard\\Api\\Procedure\\MeProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/MeProcedure.php', + 'Kanboard\\Api\\Procedure\\ProjectFileProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/ProjectFileProcedure.php', + 'Kanboard\\Api\\Procedure\\ProjectPermissionProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/ProjectPermissionProcedure.php', + 'Kanboard\\Api\\Procedure\\ProjectProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/ProjectProcedure.php', + 'Kanboard\\Api\\Procedure\\SubtaskProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/SubtaskProcedure.php', + 'Kanboard\\Api\\Procedure\\SubtaskTimeTrackingProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/SubtaskTimeTrackingProcedure.php', + 'Kanboard\\Api\\Procedure\\SwimlaneProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/SwimlaneProcedure.php', + 'Kanboard\\Api\\Procedure\\TagProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/TagProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskExternalLinkProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/TaskExternalLinkProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskFileProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/TaskFileProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskLinkProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/TaskLinkProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskMetadataProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/TaskMetadataProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/TaskProcedure.php', + 'Kanboard\\Api\\Procedure\\TaskTagProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/TaskTagProcedure.php', + 'Kanboard\\Api\\Procedure\\UserProcedure' => __DIR__ . '/../..' . '/app/Api/Procedure/UserProcedure.php', + 'Kanboard\\Auth\\ApiAccessTokenAuth' => __DIR__ . '/../..' . '/app/Auth/ApiAccessTokenAuth.php', + 'Kanboard\\Auth\\DatabaseAuth' => __DIR__ . '/../..' . '/app/Auth/DatabaseAuth.php', + 'Kanboard\\Auth\\LdapAuth' => __DIR__ . '/../..' . '/app/Auth/LdapAuth.php', + 'Kanboard\\Auth\\RememberMeAuth' => __DIR__ . '/../..' . '/app/Auth/RememberMeAuth.php', + 'Kanboard\\Auth\\ReverseProxyAuth' => __DIR__ . '/../..' . '/app/Auth/ReverseProxyAuth.php', + 'Kanboard\\Auth\\TotpAuth' => __DIR__ . '/../..' . '/app/Auth/TotpAuth.php', + 'Kanboard\\Console\\BaseCommand' => __DIR__ . '/../..' . '/app/Console/BaseCommand.php', + 'Kanboard\\Console\\CronjobCommand' => __DIR__ . '/../..' . '/app/Console/CronjobCommand.php', + 'Kanboard\\Console\\DatabaseMigrationCommand' => __DIR__ . '/../..' . '/app/Console/DatabaseMigrationCommand.php', + 'Kanboard\\Console\\DatabaseVersionCommand' => __DIR__ . '/../..' . '/app/Console/DatabaseVersionCommand.php', + 'Kanboard\\Console\\JobCommand' => __DIR__ . '/../..' . '/app/Console/JobCommand.php', + 'Kanboard\\Console\\LocaleComparatorCommand' => __DIR__ . '/../..' . '/app/Console/LocaleComparatorCommand.php', + 'Kanboard\\Console\\LocaleSyncCommand' => __DIR__ . '/../..' . '/app/Console/LocaleSyncCommand.php', + 'Kanboard\\Console\\PluginInstallCommand' => __DIR__ . '/../..' . '/app/Console/PluginInstallCommand.php', + 'Kanboard\\Console\\PluginUninstallCommand' => __DIR__ . '/../..' . '/app/Console/PluginUninstallCommand.php', + 'Kanboard\\Console\\PluginUpgradeCommand' => __DIR__ . '/../..' . '/app/Console/PluginUpgradeCommand.php', + 'Kanboard\\Console\\ProjectDailyColumnStatsExportCommand' => __DIR__ . '/../..' . '/app/Console/ProjectDailyColumnStatsExportCommand.php', + 'Kanboard\\Console\\ProjectDailyStatsCalculationCommand' => __DIR__ . '/../..' . '/app/Console/ProjectDailyStatsCalculationCommand.php', + 'Kanboard\\Console\\ResetPasswordCommand' => __DIR__ . '/../..' . '/app/Console/ResetPasswordCommand.php', + 'Kanboard\\Console\\ResetTwoFactorCommand' => __DIR__ . '/../..' . '/app/Console/ResetTwoFactorCommand.php', + 'Kanboard\\Console\\SubtaskExportCommand' => __DIR__ . '/../..' . '/app/Console/SubtaskExportCommand.php', + 'Kanboard\\Console\\TaskExportCommand' => __DIR__ . '/../..' . '/app/Console/TaskExportCommand.php', + 'Kanboard\\Console\\TaskOverdueNotificationCommand' => __DIR__ . '/../..' . '/app/Console/TaskOverdueNotificationCommand.php', + 'Kanboard\\Console\\TaskTriggerCommand' => __DIR__ . '/../..' . '/app/Console/TaskTriggerCommand.php', + 'Kanboard\\Console\\TransitionExportCommand' => __DIR__ . '/../..' . '/app/Console/TransitionExportCommand.php', + 'Kanboard\\Console\\WorkerCommand' => __DIR__ . '/../..' . '/app/Console/WorkerCommand.php', + 'Kanboard\\Controller\\ActionController' => __DIR__ . '/../..' . '/app/Controller/ActionController.php', + 'Kanboard\\Controller\\ActionCreationController' => __DIR__ . '/../..' . '/app/Controller/ActionCreationController.php', + 'Kanboard\\Controller\\ActivityController' => __DIR__ . '/../..' . '/app/Controller/ActivityController.php', + 'Kanboard\\Controller\\AnalyticController' => __DIR__ . '/../..' . '/app/Controller/AnalyticController.php', + 'Kanboard\\Controller\\AppController' => __DIR__ . '/../..' . '/app/Controller/AppController.php', + 'Kanboard\\Controller\\AuthController' => __DIR__ . '/../..' . '/app/Controller/AuthController.php', + 'Kanboard\\Controller\\AvatarFileController' => __DIR__ . '/../..' . '/app/Controller/AvatarFileController.php', + 'Kanboard\\Controller\\BaseController' => __DIR__ . '/../..' . '/app/Controller/BaseController.php', + 'Kanboard\\Controller\\BoardAjaxController' => __DIR__ . '/../..' . '/app/Controller/BoardAjaxController.php', + 'Kanboard\\Controller\\BoardPopoverController' => __DIR__ . '/../..' . '/app/Controller/BoardPopoverController.php', + 'Kanboard\\Controller\\BoardTooltipController' => __DIR__ . '/../..' . '/app/Controller/BoardTooltipController.php', + 'Kanboard\\Controller\\BoardViewController' => __DIR__ . '/../..' . '/app/Controller/BoardViewController.php', + 'Kanboard\\Controller\\CaptchaController' => __DIR__ . '/../..' . '/app/Controller/CaptchaController.php', + 'Kanboard\\Controller\\CategoryController' => __DIR__ . '/../..' . '/app/Controller/CategoryController.php', + 'Kanboard\\Controller\\ColumnController' => __DIR__ . '/../..' . '/app/Controller/ColumnController.php', + 'Kanboard\\Controller\\ColumnMoveRestrictionController' => __DIR__ . '/../..' . '/app/Controller/ColumnMoveRestrictionController.php', + 'Kanboard\\Controller\\ColumnRestrictionController' => __DIR__ . '/../..' . '/app/Controller/ColumnRestrictionController.php', + 'Kanboard\\Controller\\CommentController' => __DIR__ . '/../..' . '/app/Controller/CommentController.php', + 'Kanboard\\Controller\\CommentListController' => __DIR__ . '/../..' . '/app/Controller/CommentListController.php', + 'Kanboard\\Controller\\CommentMailController' => __DIR__ . '/../..' . '/app/Controller/CommentMailController.php', + 'Kanboard\\Controller\\ConfigController' => __DIR__ . '/../..' . '/app/Controller/ConfigController.php', + 'Kanboard\\Controller\\CurrencyController' => __DIR__ . '/../..' . '/app/Controller/CurrencyController.php', + 'Kanboard\\Controller\\CustomFilterController' => __DIR__ . '/../..' . '/app/Controller/CustomFilterController.php', + 'Kanboard\\Controller\\DashboardController' => __DIR__ . '/../..' . '/app/Controller/DashboardController.php', + 'Kanboard\\Controller\\DocumentationController' => __DIR__ . '/../..' . '/app/Controller/DocumentationController.php', + 'Kanboard\\Controller\\ExportController' => __DIR__ . '/../..' . '/app/Controller/ExportController.php', + 'Kanboard\\Controller\\ExternalTaskCreationController' => __DIR__ . '/../..' . '/app/Controller/ExternalTaskCreationController.php', + 'Kanboard\\Controller\\ExternalTaskViewController' => __DIR__ . '/../..' . '/app/Controller/ExternalTaskViewController.php', + 'Kanboard\\Controller\\FeedController' => __DIR__ . '/../..' . '/app/Controller/FeedController.php', + 'Kanboard\\Controller\\FileViewerController' => __DIR__ . '/../..' . '/app/Controller/FileViewerController.php', + 'Kanboard\\Controller\\GroupAjaxController' => __DIR__ . '/../..' . '/app/Controller/GroupAjaxController.php', + 'Kanboard\\Controller\\GroupCreationController' => __DIR__ . '/../..' . '/app/Controller/GroupCreationController.php', + 'Kanboard\\Controller\\GroupListController' => __DIR__ . '/../..' . '/app/Controller/GroupListController.php', + 'Kanboard\\Controller\\GroupModificationController' => __DIR__ . '/../..' . '/app/Controller/GroupModificationController.php', + 'Kanboard\\Controller\\ICalendarController' => __DIR__ . '/../..' . '/app/Controller/ICalendarController.php', + 'Kanboard\\Controller\\LinkController' => __DIR__ . '/../..' . '/app/Controller/LinkController.php', + 'Kanboard\\Controller\\OAuthController' => __DIR__ . '/../..' . '/app/Controller/OAuthController.php', + 'Kanboard\\Controller\\PasswordResetController' => __DIR__ . '/../..' . '/app/Controller/PasswordResetController.php', + 'Kanboard\\Controller\\PluginController' => __DIR__ . '/../..' . '/app/Controller/PluginController.php', + 'Kanboard\\Controller\\PredefinedTaskDescriptionController' => __DIR__ . '/../..' . '/app/Controller/PredefinedTaskDescriptionController.php', + 'Kanboard\\Controller\\ProjectActionDuplicationController' => __DIR__ . '/../..' . '/app/Controller/ProjectActionDuplicationController.php', + 'Kanboard\\Controller\\ProjectCreationController' => __DIR__ . '/../..' . '/app/Controller/ProjectCreationController.php', + 'Kanboard\\Controller\\ProjectEditController' => __DIR__ . '/../..' . '/app/Controller/ProjectEditController.php', + 'Kanboard\\Controller\\ProjectFileController' => __DIR__ . '/../..' . '/app/Controller/ProjectFileController.php', + 'Kanboard\\Controller\\ProjectListController' => __DIR__ . '/../..' . '/app/Controller/ProjectListController.php', + 'Kanboard\\Controller\\ProjectOverviewController' => __DIR__ . '/../..' . '/app/Controller/ProjectOverviewController.php', + 'Kanboard\\Controller\\ProjectPermissionController' => __DIR__ . '/../..' . '/app/Controller/ProjectPermissionController.php', + 'Kanboard\\Controller\\ProjectPredefinedContentController' => __DIR__ . '/../..' . '/app/Controller/ProjectPredefinedContentController.php', + 'Kanboard\\Controller\\ProjectRoleController' => __DIR__ . '/../..' . '/app/Controller/ProjectRoleController.php', + 'Kanboard\\Controller\\ProjectRoleRestrictionController' => __DIR__ . '/../..' . '/app/Controller/ProjectRoleRestrictionController.php', + 'Kanboard\\Controller\\ProjectStatusController' => __DIR__ . '/../..' . '/app/Controller/ProjectStatusController.php', + 'Kanboard\\Controller\\ProjectTagController' => __DIR__ . '/../..' . '/app/Controller/ProjectTagController.php', + 'Kanboard\\Controller\\ProjectUserOverviewController' => __DIR__ . '/../..' . '/app/Controller/ProjectUserOverviewController.php', + 'Kanboard\\Controller\\ProjectViewController' => __DIR__ . '/../..' . '/app/Controller/ProjectViewController.php', + 'Kanboard\\Controller\\SearchController' => __DIR__ . '/../..' . '/app/Controller/SearchController.php', + 'Kanboard\\Controller\\SubtaskController' => __DIR__ . '/../..' . '/app/Controller/SubtaskController.php', + 'Kanboard\\Controller\\SubtaskConverterController' => __DIR__ . '/../..' . '/app/Controller/SubtaskConverterController.php', + 'Kanboard\\Controller\\SubtaskRestrictionController' => __DIR__ . '/../..' . '/app/Controller/SubtaskRestrictionController.php', + 'Kanboard\\Controller\\SubtaskStatusController' => __DIR__ . '/../..' . '/app/Controller/SubtaskStatusController.php', + 'Kanboard\\Controller\\SwimlaneController' => __DIR__ . '/../..' . '/app/Controller/SwimlaneController.php', + 'Kanboard\\Controller\\TagController' => __DIR__ . '/../..' . '/app/Controller/TagController.php', + 'Kanboard\\Controller\\TaskAjaxController' => __DIR__ . '/../..' . '/app/Controller/TaskAjaxController.php', + 'Kanboard\\Controller\\TaskBulkController' => __DIR__ . '/../..' . '/app/Controller/TaskBulkController.php', + 'Kanboard\\Controller\\TaskCreationController' => __DIR__ . '/../..' . '/app/Controller/TaskCreationController.php', + 'Kanboard\\Controller\\TaskDuplicationController' => __DIR__ . '/../..' . '/app/Controller/TaskDuplicationController.php', + 'Kanboard\\Controller\\TaskExternalLinkController' => __DIR__ . '/../..' . '/app/Controller/TaskExternalLinkController.php', + 'Kanboard\\Controller\\TaskFileController' => __DIR__ . '/../..' . '/app/Controller/TaskFileController.php', + 'Kanboard\\Controller\\TaskImportController' => __DIR__ . '/../..' . '/app/Controller/TaskImportController.php', + 'Kanboard\\Controller\\TaskInternalLinkController' => __DIR__ . '/../..' . '/app/Controller/TaskInternalLinkController.php', + 'Kanboard\\Controller\\TaskListController' => __DIR__ . '/../..' . '/app/Controller/TaskListController.php', + 'Kanboard\\Controller\\TaskMailController' => __DIR__ . '/../..' . '/app/Controller/TaskMailController.php', + 'Kanboard\\Controller\\TaskModificationController' => __DIR__ . '/../..' . '/app/Controller/TaskModificationController.php', + 'Kanboard\\Controller\\TaskMovePositionController' => __DIR__ . '/../..' . '/app/Controller/TaskMovePositionController.php', + 'Kanboard\\Controller\\TaskPopoverController' => __DIR__ . '/../..' . '/app/Controller/TaskPopoverController.php', + 'Kanboard\\Controller\\TaskRecurrenceController' => __DIR__ . '/../..' . '/app/Controller/TaskRecurrenceController.php', + 'Kanboard\\Controller\\TaskStatusController' => __DIR__ . '/../..' . '/app/Controller/TaskStatusController.php', + 'Kanboard\\Controller\\TaskSuppressionController' => __DIR__ . '/../..' . '/app/Controller/TaskSuppressionController.php', + 'Kanboard\\Controller\\TaskViewController' => __DIR__ . '/../..' . '/app/Controller/TaskViewController.php', + 'Kanboard\\Controller\\TwoFactorController' => __DIR__ . '/../..' . '/app/Controller/TwoFactorController.php', + 'Kanboard\\Controller\\UserAjaxController' => __DIR__ . '/../..' . '/app/Controller/UserAjaxController.php', + 'Kanboard\\Controller\\UserApiAccessController' => __DIR__ . '/../..' . '/app/Controller/UserApiAccessController.php', + 'Kanboard\\Controller\\UserCreationController' => __DIR__ . '/../..' . '/app/Controller/UserCreationController.php', + 'Kanboard\\Controller\\UserCredentialController' => __DIR__ . '/../..' . '/app/Controller/UserCredentialController.php', + 'Kanboard\\Controller\\UserImportController' => __DIR__ . '/../..' . '/app/Controller/UserImportController.php', + 'Kanboard\\Controller\\UserInviteController' => __DIR__ . '/../..' . '/app/Controller/UserInviteController.php', + 'Kanboard\\Controller\\UserListController' => __DIR__ . '/../..' . '/app/Controller/UserListController.php', + 'Kanboard\\Controller\\UserModificationController' => __DIR__ . '/../..' . '/app/Controller/UserModificationController.php', + 'Kanboard\\Controller\\UserStatusController' => __DIR__ . '/../..' . '/app/Controller/UserStatusController.php', + 'Kanboard\\Controller\\UserViewController' => __DIR__ . '/../..' . '/app/Controller/UserViewController.php', + 'Kanboard\\Controller\\WebNotificationController' => __DIR__ . '/../..' . '/app/Controller/WebNotificationController.php', + 'Kanboard\\Core\\Action\\ActionManager' => __DIR__ . '/../..' . '/app/Core/Action/ActionManager.php', + 'Kanboard\\Core\\Base' => __DIR__ . '/../..' . '/app/Core/Base.php', + 'Kanboard\\Core\\Cache\\BaseCache' => __DIR__ . '/../..' . '/app/Core/Cache/BaseCache.php', + 'Kanboard\\Core\\Cache\\CacheInterface' => __DIR__ . '/../..' . '/app/Core/Cache/CacheInterface.php', + 'Kanboard\\Core\\Cache\\FileCache' => __DIR__ . '/../..' . '/app/Core/Cache/FileCache.php', + 'Kanboard\\Core\\Cache\\MemoryCache' => __DIR__ . '/../..' . '/app/Core/Cache/MemoryCache.php', + 'Kanboard\\Core\\Controller\\AccessForbiddenException' => __DIR__ . '/../..' . '/app/Core/Controller/AccessForbiddenException.php', + 'Kanboard\\Core\\Controller\\BaseException' => __DIR__ . '/../..' . '/app/Core/Controller/BaseException.php', + 'Kanboard\\Core\\Controller\\BaseMiddleware' => __DIR__ . '/../..' . '/app/Core/Controller/BaseMiddleware.php', + 'Kanboard\\Core\\Controller\\PageNotFoundException' => __DIR__ . '/../..' . '/app/Core/Controller/PageNotFoundException.php', + 'Kanboard\\Core\\Controller\\Runner' => __DIR__ . '/../..' . '/app/Core/Controller/Runner.php', + 'Kanboard\\Core\\Csv' => __DIR__ . '/../..' . '/app/Core/Csv.php', + 'Kanboard\\Core\\DateParser' => __DIR__ . '/../..' . '/app/Core/DateParser.php', + 'Kanboard\\Core\\Event\\EventManager' => __DIR__ . '/../..' . '/app/Core/Event/EventManager.php', + 'Kanboard\\Core\\ExternalLink\\ExternalLinkInterface' => __DIR__ . '/../..' . '/app/Core/ExternalLink/ExternalLinkInterface.php', + 'Kanboard\\Core\\ExternalLink\\ExternalLinkManager' => __DIR__ . '/../..' . '/app/Core/ExternalLink/ExternalLinkManager.php', + 'Kanboard\\Core\\ExternalLink\\ExternalLinkProviderInterface' => __DIR__ . '/../..' . '/app/Core/ExternalLink/ExternalLinkProviderInterface.php', + 'Kanboard\\Core\\ExternalLink\\ExternalLinkProviderNotFound' => __DIR__ . '/../..' . '/app/Core/ExternalLink/ExternalLinkProviderNotFound.php', + 'Kanboard\\Core\\ExternalTask\\AccessForbiddenException' => __DIR__ . '/../..' . '/app/Core/ExternalTask/AccessForbiddenException.php', + 'Kanboard\\Core\\ExternalTask\\ExternalTaskException' => __DIR__ . '/../..' . '/app/Core/ExternalTask/ExternalTaskException.php', + 'Kanboard\\Core\\ExternalTask\\ExternalTaskInterface' => __DIR__ . '/../..' . '/app/Core/ExternalTask/ExternalTaskInterface.php', + 'Kanboard\\Core\\ExternalTask\\ExternalTaskManager' => __DIR__ . '/../..' . '/app/Core/ExternalTask/ExternalTaskManager.php', + 'Kanboard\\Core\\ExternalTask\\ExternalTaskProviderInterface' => __DIR__ . '/../..' . '/app/Core/ExternalTask/ExternalTaskProviderInterface.php', + 'Kanboard\\Core\\ExternalTask\\NotFoundException' => __DIR__ . '/../..' . '/app/Core/ExternalTask/NotFoundException.php', + 'Kanboard\\Core\\ExternalTask\\ProviderNotFoundException' => __DIR__ . '/../..' . '/app/Core/ExternalTask/ProviderNotFoundException.php', + 'Kanboard\\Core\\Filter\\CriteriaInterface' => __DIR__ . '/../..' . '/app/Core/Filter/CriteriaInterface.php', + 'Kanboard\\Core\\Filter\\FilterInterface' => __DIR__ . '/../..' . '/app/Core/Filter/FilterInterface.php', + 'Kanboard\\Core\\Filter\\FormatterInterface' => __DIR__ . '/../..' . '/app/Core/Filter/FormatterInterface.php', + 'Kanboard\\Core\\Filter\\Lexer' => __DIR__ . '/../..' . '/app/Core/Filter/Lexer.php', + 'Kanboard\\Core\\Filter\\LexerBuilder' => __DIR__ . '/../..' . '/app/Core/Filter/LexerBuilder.php', + 'Kanboard\\Core\\Filter\\OrCriteria' => __DIR__ . '/../..' . '/app/Core/Filter/OrCriteria.php', + 'Kanboard\\Core\\Filter\\QueryBuilder' => __DIR__ . '/../..' . '/app/Core/Filter/QueryBuilder.php', + 'Kanboard\\Core\\Group\\GroupBackendProviderInterface' => __DIR__ . '/../..' . '/app/Core/Group/GroupBackendProviderInterface.php', + 'Kanboard\\Core\\Group\\GroupManager' => __DIR__ . '/../..' . '/app/Core/Group/GroupManager.php', + 'Kanboard\\Core\\Group\\GroupProviderInterface' => __DIR__ . '/../..' . '/app/Core/Group/GroupProviderInterface.php', + 'Kanboard\\Core\\Helper' => __DIR__ . '/../..' . '/app/Core/Helper.php', + 'Kanboard\\Core\\Http\\Client' => __DIR__ . '/../..' . '/app/Core/Http/Client.php', + 'Kanboard\\Core\\Http\\OAuth2' => __DIR__ . '/../..' . '/app/Core/Http/OAuth2.php', + 'Kanboard\\Core\\Http\\RememberMeCookie' => __DIR__ . '/../..' . '/app/Core/Http/RememberMeCookie.php', + 'Kanboard\\Core\\Http\\Request' => __DIR__ . '/../..' . '/app/Core/Http/Request.php', + 'Kanboard\\Core\\Http\\Response' => __DIR__ . '/../..' . '/app/Core/Http/Response.php', + 'Kanboard\\Core\\Http\\Route' => __DIR__ . '/../..' . '/app/Core/Http/Route.php', + 'Kanboard\\Core\\Http\\Router' => __DIR__ . '/../..' . '/app/Core/Http/Router.php', + 'Kanboard\\Core\\Ldap\\Client' => __DIR__ . '/../..' . '/app/Core/Ldap/Client.php', + 'Kanboard\\Core\\Ldap\\ClientException' => __DIR__ . '/../..' . '/app/Core/Ldap/ClientException.php', + 'Kanboard\\Core\\Ldap\\ConnectionException' => __DIR__ . '/../..' . '/app/Core/Ldap/ConnectionException.php', + 'Kanboard\\Core\\Ldap\\Entries' => __DIR__ . '/../..' . '/app/Core/Ldap/Entries.php', + 'Kanboard\\Core\\Ldap\\Entry' => __DIR__ . '/../..' . '/app/Core/Ldap/Entry.php', + 'Kanboard\\Core\\Ldap\\Group' => __DIR__ . '/../..' . '/app/Core/Ldap/Group.php', + 'Kanboard\\Core\\Ldap\\Query' => __DIR__ . '/../..' . '/app/Core/Ldap/Query.php', + 'Kanboard\\Core\\Ldap\\User' => __DIR__ . '/../..' . '/app/Core/Ldap/User.php', + 'Kanboard\\Core\\Mail\\Client' => __DIR__ . '/../..' . '/app/Core/Mail/Client.php', + 'Kanboard\\Core\\Mail\\ClientInterface' => __DIR__ . '/../..' . '/app/Core/Mail/ClientInterface.php', + 'Kanboard\\Core\\Mail\\Transport\\Mail' => __DIR__ . '/../..' . '/app/Core/Mail/Transport/Mail.php', + 'Kanboard\\Core\\Mail\\Transport\\Sendmail' => __DIR__ . '/../..' . '/app/Core/Mail/Transport/Sendmail.php', + 'Kanboard\\Core\\Mail\\Transport\\Smtp' => __DIR__ . '/../..' . '/app/Core/Mail/Transport/Smtp.php', + 'Kanboard\\Core\\Markdown' => __DIR__ . '/../..' . '/app/Core/Markdown.php', + 'Kanboard\\Core\\Notification\\NotificationInterface' => __DIR__ . '/../..' . '/app/Core/Notification/NotificationInterface.php', + 'Kanboard\\Core\\ObjectStorage\\FileStorage' => __DIR__ . '/../..' . '/app/Core/ObjectStorage/FileStorage.php', + 'Kanboard\\Core\\ObjectStorage\\ObjectStorageException' => __DIR__ . '/../..' . '/app/Core/ObjectStorage/ObjectStorageException.php', + 'Kanboard\\Core\\ObjectStorage\\ObjectStorageInterface' => __DIR__ . '/../..' . '/app/Core/ObjectStorage/ObjectStorageInterface.php', + 'Kanboard\\Core\\Paginator' => __DIR__ . '/../..' . '/app/Core/Paginator.php', + 'Kanboard\\Core\\Plugin\\Base' => __DIR__ . '/../..' . '/app/Core/Plugin/Base.php', + 'Kanboard\\Core\\Plugin\\Directory' => __DIR__ . '/../..' . '/app/Core/Plugin/Directory.php', + 'Kanboard\\Core\\Plugin\\Hook' => __DIR__ . '/../..' . '/app/Core/Plugin/Hook.php', + 'Kanboard\\Core\\Plugin\\Installer' => __DIR__ . '/../..' . '/app/Core/Plugin/Installer.php', + 'Kanboard\\Core\\Plugin\\Loader' => __DIR__ . '/../..' . '/app/Core/Plugin/Loader.php', + 'Kanboard\\Core\\Plugin\\PluginException' => __DIR__ . '/../..' . '/app/Core/Plugin/PluginException.php', + 'Kanboard\\Core\\Plugin\\PluginInstallerException' => __DIR__ . '/../..' . '/app/Core/Plugin/PluginInstallerException.php', + 'Kanboard\\Core\\Plugin\\SchemaHandler' => __DIR__ . '/../..' . '/app/Core/Plugin/SchemaHandler.php', + 'Kanboard\\Core\\Plugin\\Version' => __DIR__ . '/../..' . '/app/Core/Plugin/Version.php', + 'Kanboard\\Core\\Queue\\JobHandler' => __DIR__ . '/../..' . '/app/Core/Queue/JobHandler.php', + 'Kanboard\\Core\\Queue\\QueueManager' => __DIR__ . '/../..' . '/app/Core/Queue/QueueManager.php', + 'Kanboard\\Core\\Security\\AccessMap' => __DIR__ . '/../..' . '/app/Core/Security/AccessMap.php', + 'Kanboard\\Core\\Security\\AuthenticationManager' => __DIR__ . '/../..' . '/app/Core/Security/AuthenticationManager.php', + 'Kanboard\\Core\\Security\\AuthenticationProviderInterface' => __DIR__ . '/../..' . '/app/Core/Security/AuthenticationProviderInterface.php', + 'Kanboard\\Core\\Security\\Authorization' => __DIR__ . '/../..' . '/app/Core/Security/Authorization.php', + 'Kanboard\\Core\\Security\\OAuthAuthenticationProviderInterface' => __DIR__ . '/../..' . '/app/Core/Security/OAuthAuthenticationProviderInterface.php', + 'Kanboard\\Core\\Security\\PasswordAuthenticationProviderInterface' => __DIR__ . '/../..' . '/app/Core/Security/PasswordAuthenticationProviderInterface.php', + 'Kanboard\\Core\\Security\\PostAuthenticationProviderInterface' => __DIR__ . '/../..' . '/app/Core/Security/PostAuthenticationProviderInterface.php', + 'Kanboard\\Core\\Security\\PreAuthenticationProviderInterface' => __DIR__ . '/../..' . '/app/Core/Security/PreAuthenticationProviderInterface.php', + 'Kanboard\\Core\\Security\\Role' => __DIR__ . '/../..' . '/app/Core/Security/Role.php', + 'Kanboard\\Core\\Security\\SessionCheckProviderInterface' => __DIR__ . '/../..' . '/app/Core/Security/SessionCheckProviderInterface.php', + 'Kanboard\\Core\\Security\\Token' => __DIR__ . '/../..' . '/app/Core/Security/Token.php', + 'Kanboard\\Core\\Session\\FlashMessage' => __DIR__ . '/../..' . '/app/Core/Session/FlashMessage.php', + 'Kanboard\\Core\\Session\\SessionManager' => __DIR__ . '/../..' . '/app/Core/Session/SessionManager.php', + 'Kanboard\\Core\\Session\\SessionStorage' => __DIR__ . '/../..' . '/app/Core/Session/SessionStorage.php', + 'Kanboard\\Core\\Template' => __DIR__ . '/../..' . '/app/Core/Template.php', + 'Kanboard\\Core\\Thumbnail' => __DIR__ . '/../..' . '/app/Core/Thumbnail.php', + 'Kanboard\\Core\\Tool' => __DIR__ . '/../..' . '/app/Core/Tool.php', + 'Kanboard\\Core\\Translator' => __DIR__ . '/../..' . '/app/Core/Translator.php', + 'Kanboard\\Core\\User\\Avatar\\AvatarManager' => __DIR__ . '/../..' . '/app/Core/User/Avatar/AvatarManager.php', + 'Kanboard\\Core\\User\\Avatar\\AvatarProviderInterface' => __DIR__ . '/../..' . '/app/Core/User/Avatar/AvatarProviderInterface.php', + 'Kanboard\\Core\\User\\GroupSync' => __DIR__ . '/../..' . '/app/Core/User/GroupSync.php', + 'Kanboard\\Core\\User\\UserBackendProviderInterface' => __DIR__ . '/../..' . '/app/Core/User/UserBackendProviderInterface.php', + 'Kanboard\\Core\\User\\UserManager' => __DIR__ . '/../..' . '/app/Core/User/UserManager.php', + 'Kanboard\\Core\\User\\UserProfile' => __DIR__ . '/../..' . '/app/Core/User/UserProfile.php', + 'Kanboard\\Core\\User\\UserProperty' => __DIR__ . '/../..' . '/app/Core/User/UserProperty.php', + 'Kanboard\\Core\\User\\UserProviderInterface' => __DIR__ . '/../..' . '/app/Core/User/UserProviderInterface.php', + 'Kanboard\\Core\\User\\UserSession' => __DIR__ . '/../..' . '/app/Core/User/UserSession.php', + 'Kanboard\\Core\\User\\UserSync' => __DIR__ . '/../..' . '/app/Core/User/UserSync.php', + 'Kanboard\\Decorator\\ColumnMoveRestrictionCacheDecorator' => __DIR__ . '/../..' . '/app/Decorator/ColumnMoveRestrictionCacheDecorator.php', + 'Kanboard\\Decorator\\ColumnRestrictionCacheDecorator' => __DIR__ . '/../..' . '/app/Decorator/ColumnRestrictionCacheDecorator.php', + 'Kanboard\\Decorator\\MetadataCacheDecorator' => __DIR__ . '/../..' . '/app/Decorator/MetadataCacheDecorator.php', + 'Kanboard\\Decorator\\ProjectRoleRestrictionCacheDecorator' => __DIR__ . '/../..' . '/app/Decorator/ProjectRoleRestrictionCacheDecorator.php', + 'Kanboard\\Decorator\\UserCacheDecorator' => __DIR__ . '/../..' . '/app/Decorator/UserCacheDecorator.php', + 'Kanboard\\EventBuilder\\BaseEventBuilder' => __DIR__ . '/../..' . '/app/EventBuilder/BaseEventBuilder.php', + 'Kanboard\\EventBuilder\\CommentEventBuilder' => __DIR__ . '/../..' . '/app/EventBuilder/CommentEventBuilder.php', + 'Kanboard\\EventBuilder\\EventIteratorBuilder' => __DIR__ . '/../..' . '/app/EventBuilder/EventIteratorBuilder.php', + 'Kanboard\\EventBuilder\\ProjectFileEventBuilder' => __DIR__ . '/../..' . '/app/EventBuilder/ProjectFileEventBuilder.php', + 'Kanboard\\EventBuilder\\SubtaskEventBuilder' => __DIR__ . '/../..' . '/app/EventBuilder/SubtaskEventBuilder.php', + 'Kanboard\\EventBuilder\\TaskEventBuilder' => __DIR__ . '/../..' . '/app/EventBuilder/TaskEventBuilder.php', + 'Kanboard\\EventBuilder\\TaskFileEventBuilder' => __DIR__ . '/../..' . '/app/EventBuilder/TaskFileEventBuilder.php', + 'Kanboard\\EventBuilder\\TaskLinkEventBuilder' => __DIR__ . '/../..' . '/app/EventBuilder/TaskLinkEventBuilder.php', + 'Kanboard\\Event\\AuthFailureEvent' => __DIR__ . '/../..' . '/app/Event/AuthFailureEvent.php', + 'Kanboard\\Event\\AuthSuccessEvent' => __DIR__ . '/../..' . '/app/Event/AuthSuccessEvent.php', + 'Kanboard\\Event\\CommentEvent' => __DIR__ . '/../..' . '/app/Event/CommentEvent.php', + 'Kanboard\\Event\\GenericEvent' => __DIR__ . '/../..' . '/app/Event/GenericEvent.php', + 'Kanboard\\Event\\ProjectFileEvent' => __DIR__ . '/../..' . '/app/Event/ProjectFileEvent.php', + 'Kanboard\\Event\\SubtaskEvent' => __DIR__ . '/../..' . '/app/Event/SubtaskEvent.php', + 'Kanboard\\Event\\TaskEvent' => __DIR__ . '/../..' . '/app/Event/TaskEvent.php', + 'Kanboard\\Event\\TaskFileEvent' => __DIR__ . '/../..' . '/app/Event/TaskFileEvent.php', + 'Kanboard\\Event\\TaskLinkEvent' => __DIR__ . '/../..' . '/app/Event/TaskLinkEvent.php', + 'Kanboard\\Event\\TaskListEvent' => __DIR__ . '/../..' . '/app/Event/TaskListEvent.php', + 'Kanboard\\Event\\UserProfileSyncEvent' => __DIR__ . '/../..' . '/app/Event/UserProfileSyncEvent.php', + 'Kanboard\\Export\\SubtaskExport' => __DIR__ . '/../..' . '/app/Export/SubtaskExport.php', + 'Kanboard\\Export\\TaskExport' => __DIR__ . '/../..' . '/app/Export/TaskExport.php', + 'Kanboard\\Export\\TransitionExport' => __DIR__ . '/../..' . '/app/Export/TransitionExport.php', + 'Kanboard\\ExternalLink\\AttachmentLink' => __DIR__ . '/../..' . '/app/ExternalLink/AttachmentLink.php', + 'Kanboard\\ExternalLink\\AttachmentLinkProvider' => __DIR__ . '/../..' . '/app/ExternalLink/AttachmentLinkProvider.php', + 'Kanboard\\ExternalLink\\BaseLink' => __DIR__ . '/../..' . '/app/ExternalLink/BaseLink.php', + 'Kanboard\\ExternalLink\\BaseLinkProvider' => __DIR__ . '/../..' . '/app/ExternalLink/BaseLinkProvider.php', + 'Kanboard\\ExternalLink\\FileLink' => __DIR__ . '/../..' . '/app/ExternalLink/FileLink.php', + 'Kanboard\\ExternalLink\\FileLinkProvider' => __DIR__ . '/../..' . '/app/ExternalLink/FileLinkProvider.php', + 'Kanboard\\ExternalLink\\WebLink' => __DIR__ . '/../..' . '/app/ExternalLink/WebLink.php', + 'Kanboard\\ExternalLink\\WebLinkProvider' => __DIR__ . '/../..' . '/app/ExternalLink/WebLinkProvider.php', + 'Kanboard\\Filter\\BaseComparisonFilter' => __DIR__ . '/../..' . '/app/Filter/BaseComparisonFilter.php', + 'Kanboard\\Filter\\BaseDateFilter' => __DIR__ . '/../..' . '/app/Filter/BaseDateFilter.php', + 'Kanboard\\Filter\\BaseFilter' => __DIR__ . '/../..' . '/app/Filter/BaseFilter.php', + 'Kanboard\\Filter\\ProjectActivityCreationDateFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectActivityCreationDateFilter.php', + 'Kanboard\\Filter\\ProjectActivityCreatorFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectActivityCreatorFilter.php', + 'Kanboard\\Filter\\ProjectActivityProjectIdFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectActivityProjectIdFilter.php', + 'Kanboard\\Filter\\ProjectActivityProjectIdsFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectActivityProjectIdsFilter.php', + 'Kanboard\\Filter\\ProjectActivityProjectNameFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectActivityProjectNameFilter.php', + 'Kanboard\\Filter\\ProjectActivityTaskIdFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectActivityTaskIdFilter.php', + 'Kanboard\\Filter\\ProjectActivityTaskStatusFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectActivityTaskStatusFilter.php', + 'Kanboard\\Filter\\ProjectActivityTaskTitleFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectActivityTaskTitleFilter.php', + 'Kanboard\\Filter\\ProjectGroupRoleProjectFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectGroupRoleProjectFilter.php', + 'Kanboard\\Filter\\ProjectGroupRoleUsernameFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectGroupRoleUsernameFilter.php', + 'Kanboard\\Filter\\ProjectIdsFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectIdsFilter.php', + 'Kanboard\\Filter\\ProjectStatusFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectStatusFilter.php', + 'Kanboard\\Filter\\ProjectTypeFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectTypeFilter.php', + 'Kanboard\\Filter\\ProjectUserRoleProjectFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectUserRoleProjectFilter.php', + 'Kanboard\\Filter\\ProjectUserRoleUsernameFilter' => __DIR__ . '/../..' . '/app/Filter/ProjectUserRoleUsernameFilter.php', + 'Kanboard\\Filter\\TaskAssigneeFilter' => __DIR__ . '/../..' . '/app/Filter/TaskAssigneeFilter.php', + 'Kanboard\\Filter\\TaskCategoryFilter' => __DIR__ . '/../..' . '/app/Filter/TaskCategoryFilter.php', + 'Kanboard\\Filter\\TaskColorFilter' => __DIR__ . '/../..' . '/app/Filter/TaskColorFilter.php', + 'Kanboard\\Filter\\TaskColumnFilter' => __DIR__ . '/../..' . '/app/Filter/TaskColumnFilter.php', + 'Kanboard\\Filter\\TaskCommentFilter' => __DIR__ . '/../..' . '/app/Filter/TaskCommentFilter.php', + 'Kanboard\\Filter\\TaskCompletionDateFilter' => __DIR__ . '/../..' . '/app/Filter/TaskCompletionDateFilter.php', + 'Kanboard\\Filter\\TaskCreationDateFilter' => __DIR__ . '/../..' . '/app/Filter/TaskCreationDateFilter.php', + 'Kanboard\\Filter\\TaskCreatorFilter' => __DIR__ . '/../..' . '/app/Filter/TaskCreatorFilter.php', + 'Kanboard\\Filter\\TaskDescriptionFilter' => __DIR__ . '/../..' . '/app/Filter/TaskDescriptionFilter.php', + 'Kanboard\\Filter\\TaskDueDateFilter' => __DIR__ . '/../..' . '/app/Filter/TaskDueDateFilter.php', + 'Kanboard\\Filter\\TaskDueDateRangeFilter' => __DIR__ . '/../..' . '/app/Filter/TaskDueDateRangeFilter.php', + 'Kanboard\\Filter\\TaskIdExclusionFilter' => __DIR__ . '/../..' . '/app/Filter/TaskIdExclusionFilter.php', + 'Kanboard\\Filter\\TaskIdFilter' => __DIR__ . '/../..' . '/app/Filter/TaskIdFilter.php', + 'Kanboard\\Filter\\TaskLinkFilter' => __DIR__ . '/../..' . '/app/Filter/TaskLinkFilter.php', + 'Kanboard\\Filter\\TaskModificationDateFilter' => __DIR__ . '/../..' . '/app/Filter/TaskModificationDateFilter.php', + 'Kanboard\\Filter\\TaskMovedDateFilter' => __DIR__ . '/../..' . '/app/Filter/TaskMovedDateFilter.php', + 'Kanboard\\Filter\\TaskPriorityFilter' => __DIR__ . '/../..' . '/app/Filter/TaskPriorityFilter.php', + 'Kanboard\\Filter\\TaskProjectFilter' => __DIR__ . '/../..' . '/app/Filter/TaskProjectFilter.php', + 'Kanboard\\Filter\\TaskProjectsFilter' => __DIR__ . '/../..' . '/app/Filter/TaskProjectsFilter.php', + 'Kanboard\\Filter\\TaskReferenceFilter' => __DIR__ . '/../..' . '/app/Filter/TaskReferenceFilter.php', + 'Kanboard\\Filter\\TaskScoreFilter' => __DIR__ . '/../..' . '/app/Filter/TaskScoreFilter.php', + 'Kanboard\\Filter\\TaskStartDateFilter' => __DIR__ . '/../..' . '/app/Filter/TaskStartDateFilter.php', + 'Kanboard\\Filter\\TaskStartsWithIdFilter' => __DIR__ . '/../..' . '/app/Filter/TaskStartsWithIdFilter.php', + 'Kanboard\\Filter\\TaskStatusFilter' => __DIR__ . '/../..' . '/app/Filter/TaskStatusFilter.php', + 'Kanboard\\Filter\\TaskSubtaskAssigneeFilter' => __DIR__ . '/../..' . '/app/Filter/TaskSubtaskAssigneeFilter.php', + 'Kanboard\\Filter\\TaskSwimlaneFilter' => __DIR__ . '/../..' . '/app/Filter/TaskSwimlaneFilter.php', + 'Kanboard\\Filter\\TaskTagFilter' => __DIR__ . '/../..' . '/app/Filter/TaskTagFilter.php', + 'Kanboard\\Filter\\TaskTitleFilter' => __DIR__ . '/../..' . '/app/Filter/TaskTitleFilter.php', + 'Kanboard\\Filter\\UserNameFilter' => __DIR__ . '/../..' . '/app/Filter/UserNameFilter.php', + 'Kanboard\\Formatter\\BaseFormatter' => __DIR__ . '/../..' . '/app/Formatter/BaseFormatter.php', + 'Kanboard\\Formatter\\BoardColumnFormatter' => __DIR__ . '/../..' . '/app/Formatter/BoardColumnFormatter.php', + 'Kanboard\\Formatter\\BoardFormatter' => __DIR__ . '/../..' . '/app/Formatter/BoardFormatter.php', + 'Kanboard\\Formatter\\BoardSwimlaneFormatter' => __DIR__ . '/../..' . '/app/Formatter/BoardSwimlaneFormatter.php', + 'Kanboard\\Formatter\\BoardTaskFormatter' => __DIR__ . '/../..' . '/app/Formatter/BoardTaskFormatter.php', + 'Kanboard\\Formatter\\GroupAutoCompleteFormatter' => __DIR__ . '/../..' . '/app/Formatter/GroupAutoCompleteFormatter.php', + 'Kanboard\\Formatter\\ProjectActivityEventFormatter' => __DIR__ . '/../..' . '/app/Formatter/ProjectActivityEventFormatter.php', + 'Kanboard\\Formatter\\ProjectApiFormatter' => __DIR__ . '/../..' . '/app/Formatter/ProjectApiFormatter.php', + 'Kanboard\\Formatter\\ProjectsApiFormatter' => __DIR__ . '/../..' . '/app/Formatter/ProjectsApiFormatter.php', + 'Kanboard\\Formatter\\SubtaskListFormatter' => __DIR__ . '/../..' . '/app/Formatter/SubtaskListFormatter.php', + 'Kanboard\\Formatter\\SubtaskTimeTrackingCalendarFormatter' => __DIR__ . '/../..' . '/app/Formatter/SubtaskTimeTrackingCalendarFormatter.php', + 'Kanboard\\Formatter\\TaskApiFormatter' => __DIR__ . '/../..' . '/app/Formatter/TaskApiFormatter.php', + 'Kanboard\\Formatter\\TaskAutoCompleteFormatter' => __DIR__ . '/../..' . '/app/Formatter/TaskAutoCompleteFormatter.php', + 'Kanboard\\Formatter\\TaskICalFormatter' => __DIR__ . '/../..' . '/app/Formatter/TaskICalFormatter.php', + 'Kanboard\\Formatter\\TaskListFormatter' => __DIR__ . '/../..' . '/app/Formatter/TaskListFormatter.php', + 'Kanboard\\Formatter\\TaskListSubtaskAssigneeFormatter' => __DIR__ . '/../..' . '/app/Formatter/TaskListSubtaskAssigneeFormatter.php', + 'Kanboard\\Formatter\\TaskListSubtaskFormatter' => __DIR__ . '/../..' . '/app/Formatter/TaskListSubtaskFormatter.php', + 'Kanboard\\Formatter\\TaskSuggestMenuFormatter' => __DIR__ . '/../..' . '/app/Formatter/TaskSuggestMenuFormatter.php', + 'Kanboard\\Formatter\\TasksApiFormatter' => __DIR__ . '/../..' . '/app/Formatter/TasksApiFormatter.php', + 'Kanboard\\Formatter\\UserAutoCompleteFormatter' => __DIR__ . '/../..' . '/app/Formatter/UserAutoCompleteFormatter.php', + 'Kanboard\\Formatter\\UserMentionFormatter' => __DIR__ . '/../..' . '/app/Formatter/UserMentionFormatter.php', + 'Kanboard\\Group\\DatabaseBackendGroupProvider' => __DIR__ . '/../..' . '/app/Group/DatabaseBackendGroupProvider.php', + 'Kanboard\\Group\\DatabaseGroupProvider' => __DIR__ . '/../..' . '/app/Group/DatabaseGroupProvider.php', + 'Kanboard\\Group\\LdapBackendGroupProvider' => __DIR__ . '/../..' . '/app/Group/LdapBackendGroupProvider.php', + 'Kanboard\\Group\\LdapGroupProvider' => __DIR__ . '/../..' . '/app/Group/LdapGroupProvider.php', + 'Kanboard\\Helper\\AppHelper' => __DIR__ . '/../..' . '/app/Helper/AppHelper.php', + 'Kanboard\\Helper\\AssetHelper' => __DIR__ . '/../..' . '/app/Helper/AssetHelper.php', + 'Kanboard\\Helper\\AvatarHelper' => __DIR__ . '/../..' . '/app/Helper/AvatarHelper.php', + 'Kanboard\\Helper\\BoardHelper' => __DIR__ . '/../..' . '/app/Helper/BoardHelper.php', + 'Kanboard\\Helper\\CommentHelper' => __DIR__ . '/../..' . '/app/Helper/CommentHelper.php', + 'Kanboard\\Helper\\DateHelper' => __DIR__ . '/../..' . '/app/Helper/DateHelper.php', + 'Kanboard\\Helper\\FileHelper' => __DIR__ . '/../..' . '/app/Helper/FileHelper.php', + 'Kanboard\\Helper\\FormHelper' => __DIR__ . '/../..' . '/app/Helper/FormHelper.php', + 'Kanboard\\Helper\\HookHelper' => __DIR__ . '/../..' . '/app/Helper/HookHelper.php', + 'Kanboard\\Helper\\LayoutHelper' => __DIR__ . '/../..' . '/app/Helper/LayoutHelper.php', + 'Kanboard\\Helper\\MailHelper' => __DIR__ . '/../..' . '/app/Helper/MailHelper.php', + 'Kanboard\\Helper\\ModalHelper' => __DIR__ . '/../..' . '/app/Helper/ModalHelper.php', + 'Kanboard\\Helper\\ModelHelper' => __DIR__ . '/../..' . '/app/Helper/ModelHelper.php', + 'Kanboard\\Helper\\ProjectActivityHelper' => __DIR__ . '/../..' . '/app/Helper/ProjectActivityHelper.php', + 'Kanboard\\Helper\\ProjectHeaderHelper' => __DIR__ . '/../..' . '/app/Helper/ProjectHeaderHelper.php', + 'Kanboard\\Helper\\ProjectRoleHelper' => __DIR__ . '/../..' . '/app/Helper/ProjectRoleHelper.php', + 'Kanboard\\Helper\\SubtaskHelper' => __DIR__ . '/../..' . '/app/Helper/SubtaskHelper.php', + 'Kanboard\\Helper\\TaskHelper' => __DIR__ . '/../..' . '/app/Helper/TaskHelper.php', + 'Kanboard\\Helper\\TextHelper' => __DIR__ . '/../..' . '/app/Helper/TextHelper.php', + 'Kanboard\\Helper\\UrlHelper' => __DIR__ . '/../..' . '/app/Helper/UrlHelper.php', + 'Kanboard\\Helper\\UserHelper' => __DIR__ . '/../..' . '/app/Helper/UserHelper.php', + 'Kanboard\\Import\\TaskImport' => __DIR__ . '/../..' . '/app/Import/TaskImport.php', + 'Kanboard\\Import\\UserImport' => __DIR__ . '/../..' . '/app/Import/UserImport.php', + 'Kanboard\\Job\\BaseJob' => __DIR__ . '/../..' . '/app/Job/BaseJob.php', + 'Kanboard\\Job\\CommentEventJob' => __DIR__ . '/../..' . '/app/Job/CommentEventJob.php', + 'Kanboard\\Job\\EmailJob' => __DIR__ . '/../..' . '/app/Job/EmailJob.php', + 'Kanboard\\Job\\HttpAsyncJob' => __DIR__ . '/../..' . '/app/Job/HttpAsyncJob.php', + 'Kanboard\\Job\\NotificationJob' => __DIR__ . '/../..' . '/app/Job/NotificationJob.php', + 'Kanboard\\Job\\ProjectFileEventJob' => __DIR__ . '/../..' . '/app/Job/ProjectFileEventJob.php', + 'Kanboard\\Job\\ProjectMetricJob' => __DIR__ . '/../..' . '/app/Job/ProjectMetricJob.php', + 'Kanboard\\Job\\SubtaskEventJob' => __DIR__ . '/../..' . '/app/Job/SubtaskEventJob.php', + 'Kanboard\\Job\\TaskEventJob' => __DIR__ . '/../..' . '/app/Job/TaskEventJob.php', + 'Kanboard\\Job\\TaskFileEventJob' => __DIR__ . '/../..' . '/app/Job/TaskFileEventJob.php', + 'Kanboard\\Job\\TaskLinkEventJob' => __DIR__ . '/../..' . '/app/Job/TaskLinkEventJob.php', + 'Kanboard\\Job\\UserMentionJob' => __DIR__ . '/../..' . '/app/Job/UserMentionJob.php', + 'Kanboard\\Middleware\\ApplicationAuthorizationMiddleware' => __DIR__ . '/../..' . '/app/Middleware/ApplicationAuthorizationMiddleware.php', + 'Kanboard\\Middleware\\AuthenticationMiddleware' => __DIR__ . '/../..' . '/app/Middleware/AuthenticationMiddleware.php', + 'Kanboard\\Middleware\\BootstrapMiddleware' => __DIR__ . '/../..' . '/app/Middleware/BootstrapMiddleware.php', + 'Kanboard\\Middleware\\PostAuthenticationMiddleware' => __DIR__ . '/../..' . '/app/Middleware/PostAuthenticationMiddleware.php', + 'Kanboard\\Middleware\\ProjectAuthorizationMiddleware' => __DIR__ . '/../..' . '/app/Middleware/ProjectAuthorizationMiddleware.php', + 'Kanboard\\Model\\ActionModel' => __DIR__ . '/../..' . '/app/Model/ActionModel.php', + 'Kanboard\\Model\\ActionParameterModel' => __DIR__ . '/../..' . '/app/Model/ActionParameterModel.php', + 'Kanboard\\Model\\AvatarFileModel' => __DIR__ . '/../..' . '/app/Model/AvatarFileModel.php', + 'Kanboard\\Model\\BoardModel' => __DIR__ . '/../..' . '/app/Model/BoardModel.php', + 'Kanboard\\Model\\CategoryModel' => __DIR__ . '/../..' . '/app/Model/CategoryModel.php', + 'Kanboard\\Model\\ColorModel' => __DIR__ . '/../..' . '/app/Model/ColorModel.php', + 'Kanboard\\Model\\ColumnModel' => __DIR__ . '/../..' . '/app/Model/ColumnModel.php', + 'Kanboard\\Model\\ColumnMoveRestrictionModel' => __DIR__ . '/../..' . '/app/Model/ColumnMoveRestrictionModel.php', + 'Kanboard\\Model\\ColumnRestrictionModel' => __DIR__ . '/../..' . '/app/Model/ColumnRestrictionModel.php', + 'Kanboard\\Model\\CommentModel' => __DIR__ . '/../..' . '/app/Model/CommentModel.php', + 'Kanboard\\Model\\ConfigModel' => __DIR__ . '/../..' . '/app/Model/ConfigModel.php', + 'Kanboard\\Model\\CurrencyModel' => __DIR__ . '/../..' . '/app/Model/CurrencyModel.php', + 'Kanboard\\Model\\CustomFilterModel' => __DIR__ . '/../..' . '/app/Model/CustomFilterModel.php', + 'Kanboard\\Model\\FileModel' => __DIR__ . '/../..' . '/app/Model/FileModel.php', + 'Kanboard\\Model\\GroupMemberModel' => __DIR__ . '/../..' . '/app/Model/GroupMemberModel.php', + 'Kanboard\\Model\\GroupModel' => __DIR__ . '/../..' . '/app/Model/GroupModel.php', + 'Kanboard\\Model\\InviteModel' => __DIR__ . '/../..' . '/app/Model/InviteModel.php', + 'Kanboard\\Model\\LanguageModel' => __DIR__ . '/../..' . '/app/Model/LanguageModel.php', + 'Kanboard\\Model\\LastLoginModel' => __DIR__ . '/../..' . '/app/Model/LastLoginModel.php', + 'Kanboard\\Model\\LinkModel' => __DIR__ . '/../..' . '/app/Model/LinkModel.php', + 'Kanboard\\Model\\MetadataModel' => __DIR__ . '/../..' . '/app/Model/MetadataModel.php', + 'Kanboard\\Model\\NotificationModel' => __DIR__ . '/../..' . '/app/Model/NotificationModel.php', + 'Kanboard\\Model\\NotificationTypeModel' => __DIR__ . '/../..' . '/app/Model/NotificationTypeModel.php', + 'Kanboard\\Model\\PasswordResetModel' => __DIR__ . '/../..' . '/app/Model/PasswordResetModel.php', + 'Kanboard\\Model\\PredefinedTaskDescriptionModel' => __DIR__ . '/../..' . '/app/Model/PredefinedTaskDescriptionModel.php', + 'Kanboard\\Model\\ProjectActivityModel' => __DIR__ . '/../..' . '/app/Model/ProjectActivityModel.php', + 'Kanboard\\Model\\ProjectDailyColumnStatsModel' => __DIR__ . '/../..' . '/app/Model/ProjectDailyColumnStatsModel.php', + 'Kanboard\\Model\\ProjectDailyStatsModel' => __DIR__ . '/../..' . '/app/Model/ProjectDailyStatsModel.php', + 'Kanboard\\Model\\ProjectDuplicationModel' => __DIR__ . '/../..' . '/app/Model/ProjectDuplicationModel.php', + 'Kanboard\\Model\\ProjectFileModel' => __DIR__ . '/../..' . '/app/Model/ProjectFileModel.php', + 'Kanboard\\Model\\ProjectGroupRoleModel' => __DIR__ . '/../..' . '/app/Model/ProjectGroupRoleModel.php', + 'Kanboard\\Model\\ProjectMetadataModel' => __DIR__ . '/../..' . '/app/Model/ProjectMetadataModel.php', + 'Kanboard\\Model\\ProjectModel' => __DIR__ . '/../..' . '/app/Model/ProjectModel.php', + 'Kanboard\\Model\\ProjectNotificationModel' => __DIR__ . '/../..' . '/app/Model/ProjectNotificationModel.php', + 'Kanboard\\Model\\ProjectNotificationTypeModel' => __DIR__ . '/../..' . '/app/Model/ProjectNotificationTypeModel.php', + 'Kanboard\\Model\\ProjectPermissionModel' => __DIR__ . '/../..' . '/app/Model/ProjectPermissionModel.php', + 'Kanboard\\Model\\ProjectRoleModel' => __DIR__ . '/../..' . '/app/Model/ProjectRoleModel.php', + 'Kanboard\\Model\\ProjectRoleRestrictionModel' => __DIR__ . '/../..' . '/app/Model/ProjectRoleRestrictionModel.php', + 'Kanboard\\Model\\ProjectTaskDuplicationModel' => __DIR__ . '/../..' . '/app/Model/ProjectTaskDuplicationModel.php', + 'Kanboard\\Model\\ProjectTaskPriorityModel' => __DIR__ . '/../..' . '/app/Model/ProjectTaskPriorityModel.php', + 'Kanboard\\Model\\ProjectUserRoleModel' => __DIR__ . '/../..' . '/app/Model/ProjectUserRoleModel.php', + 'Kanboard\\Model\\RememberMeSessionModel' => __DIR__ . '/../..' . '/app/Model/RememberMeSessionModel.php', + 'Kanboard\\Model\\SettingModel' => __DIR__ . '/../..' . '/app/Model/SettingModel.php', + 'Kanboard\\Model\\SubtaskModel' => __DIR__ . '/../..' . '/app/Model/SubtaskModel.php', + 'Kanboard\\Model\\SubtaskPositionModel' => __DIR__ . '/../..' . '/app/Model/SubtaskPositionModel.php', + 'Kanboard\\Model\\SubtaskStatusModel' => __DIR__ . '/../..' . '/app/Model/SubtaskStatusModel.php', + 'Kanboard\\Model\\SubtaskTaskConversionModel' => __DIR__ . '/../..' . '/app/Model/SubtaskTaskConversionModel.php', + 'Kanboard\\Model\\SubtaskTimeTrackingModel' => __DIR__ . '/../..' . '/app/Model/SubtaskTimeTrackingModel.php', + 'Kanboard\\Model\\SwimlaneModel' => __DIR__ . '/../..' . '/app/Model/SwimlaneModel.php', + 'Kanboard\\Model\\TagDuplicationModel' => __DIR__ . '/../..' . '/app/Model/TagDuplicationModel.php', + 'Kanboard\\Model\\TagModel' => __DIR__ . '/../..' . '/app/Model/TagModel.php', + 'Kanboard\\Model\\TaskAnalyticModel' => __DIR__ . '/../..' . '/app/Model/TaskAnalyticModel.php', + 'Kanboard\\Model\\TaskCreationModel' => __DIR__ . '/../..' . '/app/Model/TaskCreationModel.php', + 'Kanboard\\Model\\TaskDuplicationModel' => __DIR__ . '/../..' . '/app/Model/TaskDuplicationModel.php', + 'Kanboard\\Model\\TaskExternalLinkModel' => __DIR__ . '/../..' . '/app/Model/TaskExternalLinkModel.php', + 'Kanboard\\Model\\TaskFileModel' => __DIR__ . '/../..' . '/app/Model/TaskFileModel.php', + 'Kanboard\\Model\\TaskFinderModel' => __DIR__ . '/../..' . '/app/Model/TaskFinderModel.php', + 'Kanboard\\Model\\TaskLinkModel' => __DIR__ . '/../..' . '/app/Model/TaskLinkModel.php', + 'Kanboard\\Model\\TaskMetadataModel' => __DIR__ . '/../..' . '/app/Model/TaskMetadataModel.php', + 'Kanboard\\Model\\TaskModel' => __DIR__ . '/../..' . '/app/Model/TaskModel.php', + 'Kanboard\\Model\\TaskModificationModel' => __DIR__ . '/../..' . '/app/Model/TaskModificationModel.php', + 'Kanboard\\Model\\TaskPositionModel' => __DIR__ . '/../..' . '/app/Model/TaskPositionModel.php', + 'Kanboard\\Model\\TaskProjectDuplicationModel' => __DIR__ . '/../..' . '/app/Model/TaskProjectDuplicationModel.php', + 'Kanboard\\Model\\TaskProjectMoveModel' => __DIR__ . '/../..' . '/app/Model/TaskProjectMoveModel.php', + 'Kanboard\\Model\\TaskRecurrenceModel' => __DIR__ . '/../..' . '/app/Model/TaskRecurrenceModel.php', + 'Kanboard\\Model\\TaskStatusModel' => __DIR__ . '/../..' . '/app/Model/TaskStatusModel.php', + 'Kanboard\\Model\\TaskTagModel' => __DIR__ . '/../..' . '/app/Model/TaskTagModel.php', + 'Kanboard\\Model\\TimezoneModel' => __DIR__ . '/../..' . '/app/Model/TimezoneModel.php', + 'Kanboard\\Model\\TransitionModel' => __DIR__ . '/../..' . '/app/Model/TransitionModel.php', + 'Kanboard\\Model\\UserLockingModel' => __DIR__ . '/../..' . '/app/Model/UserLockingModel.php', + 'Kanboard\\Model\\UserMetadataModel' => __DIR__ . '/../..' . '/app/Model/UserMetadataModel.php', + 'Kanboard\\Model\\UserModel' => __DIR__ . '/../..' . '/app/Model/UserModel.php', + 'Kanboard\\Model\\UserNotificationFilterModel' => __DIR__ . '/../..' . '/app/Model/UserNotificationFilterModel.php', + 'Kanboard\\Model\\UserNotificationModel' => __DIR__ . '/../..' . '/app/Model/UserNotificationModel.php', + 'Kanboard\\Model\\UserNotificationTypeModel' => __DIR__ . '/../..' . '/app/Model/UserNotificationTypeModel.php', + 'Kanboard\\Model\\UserUnreadNotificationModel' => __DIR__ . '/../..' . '/app/Model/UserUnreadNotificationModel.php', + 'Kanboard\\Notification\\ActivityStreamNotification' => __DIR__ . '/../..' . '/app/Notification/ActivityStreamNotification.php', + 'Kanboard\\Notification\\MailNotification' => __DIR__ . '/../..' . '/app/Notification/MailNotification.php', + 'Kanboard\\Notification\\WebNotification' => __DIR__ . '/../..' . '/app/Notification/WebNotification.php', + 'Kanboard\\Notification\\WebhookNotification' => __DIR__ . '/../..' . '/app/Notification/WebhookNotification.php', + 'Kanboard\\Pagination\\DashboardPagination' => __DIR__ . '/../..' . '/app/Pagination/DashboardPagination.php', + 'Kanboard\\Pagination\\ProjectPagination' => __DIR__ . '/../..' . '/app/Pagination/ProjectPagination.php', + 'Kanboard\\Pagination\\SubtaskPagination' => __DIR__ . '/../..' . '/app/Pagination/SubtaskPagination.php', + 'Kanboard\\Pagination\\TaskPagination' => __DIR__ . '/../..' . '/app/Pagination/TaskPagination.php', + 'Kanboard\\Pagination\\UserPagination' => __DIR__ . '/../..' . '/app/Pagination/UserPagination.php', + 'Kanboard\\ServiceProvider\\ActionProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/ActionProvider.php', + 'Kanboard\\ServiceProvider\\ApiProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/ApiProvider.php', + 'Kanboard\\ServiceProvider\\AuthenticationProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/AuthenticationProvider.php', + 'Kanboard\\ServiceProvider\\AvatarProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/AvatarProvider.php', + 'Kanboard\\ServiceProvider\\CacheProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/CacheProvider.php', + 'Kanboard\\ServiceProvider\\ClassProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/ClassProvider.php', + 'Kanboard\\ServiceProvider\\CommandProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/CommandProvider.php', + 'Kanboard\\ServiceProvider\\DatabaseProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/DatabaseProvider.php', + 'Kanboard\\ServiceProvider\\EventDispatcherProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/EventDispatcherProvider.php', + 'Kanboard\\ServiceProvider\\ExternalLinkProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/ExternalLinkProvider.php', + 'Kanboard\\ServiceProvider\\ExternalTaskProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/ExternalTaskProvider.php', + 'Kanboard\\ServiceProvider\\FilterProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/FilterProvider.php', + 'Kanboard\\ServiceProvider\\FormatterProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/FormatterProvider.php', + 'Kanboard\\ServiceProvider\\GroupProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/GroupProvider.php', + 'Kanboard\\ServiceProvider\\HelperProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/HelperProvider.php', + 'Kanboard\\ServiceProvider\\JobProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/JobProvider.php', + 'Kanboard\\ServiceProvider\\LoggingProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/LoggingProvider.php', + 'Kanboard\\ServiceProvider\\MailProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/MailProvider.php', + 'Kanboard\\ServiceProvider\\NotificationProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/NotificationProvider.php', + 'Kanboard\\ServiceProvider\\ObjectStorageProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/ObjectStorageProvider.php', + 'Kanboard\\ServiceProvider\\PluginProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/PluginProvider.php', + 'Kanboard\\ServiceProvider\\QueueProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/QueueProvider.php', + 'Kanboard\\ServiceProvider\\RouteProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/RouteProvider.php', + 'Kanboard\\ServiceProvider\\SessionProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/SessionProvider.php', + 'Kanboard\\ServiceProvider\\UserProvider' => __DIR__ . '/../..' . '/app/ServiceProvider/UserProvider.php', + 'Kanboard\\Subscriber\\AuthSubscriber' => __DIR__ . '/../..' . '/app/Subscriber/AuthSubscriber.php', + 'Kanboard\\Subscriber\\BaseSubscriber' => __DIR__ . '/../..' . '/app/Subscriber/BaseSubscriber.php', + 'Kanboard\\Subscriber\\BootstrapSubscriber' => __DIR__ . '/../..' . '/app/Subscriber/BootstrapSubscriber.php', + 'Kanboard\\Subscriber\\LdapUserPhotoSubscriber' => __DIR__ . '/../..' . '/app/Subscriber/LdapUserPhotoSubscriber.php', + 'Kanboard\\Subscriber\\NotificationSubscriber' => __DIR__ . '/../..' . '/app/Subscriber/NotificationSubscriber.php', + 'Kanboard\\Subscriber\\ProjectDailySummarySubscriber' => __DIR__ . '/../..' . '/app/Subscriber/ProjectDailySummarySubscriber.php', + 'Kanboard\\Subscriber\\ProjectModificationDateSubscriber' => __DIR__ . '/../..' . '/app/Subscriber/ProjectModificationDateSubscriber.php', + 'Kanboard\\Subscriber\\RecurringTaskSubscriber' => __DIR__ . '/../..' . '/app/Subscriber/RecurringTaskSubscriber.php', + 'Kanboard\\Subscriber\\TransitionSubscriber' => __DIR__ . '/../..' . '/app/Subscriber/TransitionSubscriber.php', + 'Kanboard\\User\\Avatar\\AvatarFileProvider' => __DIR__ . '/../..' . '/app/User/Avatar/AvatarFileProvider.php', + 'Kanboard\\User\\Avatar\\LetterAvatarProvider' => __DIR__ . '/../..' . '/app/User/Avatar/LetterAvatarProvider.php', + 'Kanboard\\User\\DatabaseBackendUserProvider' => __DIR__ . '/../..' . '/app/User/DatabaseBackendUserProvider.php', + 'Kanboard\\User\\DatabaseUserProvider' => __DIR__ . '/../..' . '/app/User/DatabaseUserProvider.php', + 'Kanboard\\User\\LdapUserProvider' => __DIR__ . '/../..' . '/app/User/LdapUserProvider.php', + 'Kanboard\\User\\OAuthUserProvider' => __DIR__ . '/../..' . '/app/User/OAuthUserProvider.php', + 'Kanboard\\User\\ReverseProxyUserProvider' => __DIR__ . '/../..' . '/app/User/ReverseProxyUserProvider.php', + 'Kanboard\\Validator\\ActionValidator' => __DIR__ . '/../..' . '/app/Validator/ActionValidator.php', + 'Kanboard\\Validator\\AuthValidator' => __DIR__ . '/../..' . '/app/Validator/AuthValidator.php', + 'Kanboard\\Validator\\BaseValidator' => __DIR__ . '/../..' . '/app/Validator/BaseValidator.php', + 'Kanboard\\Validator\\CategoryValidator' => __DIR__ . '/../..' . '/app/Validator/CategoryValidator.php', + 'Kanboard\\Validator\\ColumnMoveRestrictionValidator' => __DIR__ . '/../..' . '/app/Validator/ColumnMoveRestrictionValidator.php', + 'Kanboard\\Validator\\ColumnRestrictionValidator' => __DIR__ . '/../..' . '/app/Validator/ColumnRestrictionValidator.php', + 'Kanboard\\Validator\\ColumnValidator' => __DIR__ . '/../..' . '/app/Validator/ColumnValidator.php', + 'Kanboard\\Validator\\CommentValidator' => __DIR__ . '/../..' . '/app/Validator/CommentValidator.php', + 'Kanboard\\Validator\\CurrencyValidator' => __DIR__ . '/../..' . '/app/Validator/CurrencyValidator.php', + 'Kanboard\\Validator\\CustomFilterValidator' => __DIR__ . '/../..' . '/app/Validator/CustomFilterValidator.php', + 'Kanboard\\Validator\\ExternalLinkValidator' => __DIR__ . '/../..' . '/app/Validator/ExternalLinkValidator.php', + 'Kanboard\\Validator\\GroupValidator' => __DIR__ . '/../..' . '/app/Validator/GroupValidator.php', + 'Kanboard\\Validator\\LinkValidator' => __DIR__ . '/../..' . '/app/Validator/LinkValidator.php', + 'Kanboard\\Validator\\PasswordResetValidator' => __DIR__ . '/../..' . '/app/Validator/PasswordResetValidator.php', + 'Kanboard\\Validator\\PredefinedTaskDescriptionValidator' => __DIR__ . '/../..' . '/app/Validator/PredefinedTaskDescriptionValidator.php', + 'Kanboard\\Validator\\ProjectRoleValidator' => __DIR__ . '/../..' . '/app/Validator/ProjectRoleValidator.php', + 'Kanboard\\Validator\\ProjectValidator' => __DIR__ . '/../..' . '/app/Validator/ProjectValidator.php', + 'Kanboard\\Validator\\SubtaskValidator' => __DIR__ . '/../..' . '/app/Validator/SubtaskValidator.php', + 'Kanboard\\Validator\\SwimlaneValidator' => __DIR__ . '/../..' . '/app/Validator/SwimlaneValidator.php', + 'Kanboard\\Validator\\TagValidator' => __DIR__ . '/../..' . '/app/Validator/TagValidator.php', + 'Kanboard\\Validator\\TaskLinkValidator' => __DIR__ . '/../..' . '/app/Validator/TaskLinkValidator.php', + 'Kanboard\\Validator\\TaskValidator' => __DIR__ . '/../..' . '/app/Validator/TaskValidator.php', + 'Kanboard\\Validator\\UserValidator' => __DIR__ . '/../..' . '/app/Validator/UserValidator.php', + 'Otp\\GoogleAuthenticator' => __DIR__ . '/..' . '/christian-riesen/otp/src/Otp/GoogleAuthenticator.php', + 'Otp\\Otp' => __DIR__ . '/..' . '/christian-riesen/otp/src/Otp/Otp.php', + 'Otp\\OtpInterface' => __DIR__ . '/..' . '/christian-riesen/otp/src/Otp/OtpInterface.php', + 'PHPQRCode' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode.php', + 'PHPQRCode\\Autoloader' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/Autoloader.php', + 'PHPQRCode\\Constants' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/Constants.php', + 'PHPQRCode\\FrameFiller' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/FrameFiller.php', + 'PHPQRCode\\QRbitstream' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRbitstream.php', + 'PHPQRCode\\QRcode' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRcode.php', + 'PHPQRCode\\QRencode' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRencode.php', + 'PHPQRCode\\QRimage' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRimage.php', + 'PHPQRCode\\QRinput' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRinput.php', + 'PHPQRCode\\QRinputItem' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRinputItem.php', + 'PHPQRCode\\QRmask' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRmask.php', + 'PHPQRCode\\QRrawcode' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRrawcode.php', + 'PHPQRCode\\QRrs' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRrs.php', + 'PHPQRCode\\QRrsItem' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRrsItem.php', + 'PHPQRCode\\QRrsblock' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRrsblock.php', + 'PHPQRCode\\QRspec' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRspec.php', + 'PHPQRCode\\QRsplit' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRsplit.php', + 'PHPQRCode\\QRstr' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRstr.php', + 'PHPQRCode\\QRtools' => __DIR__ . '/..' . '/aferrandini/phpqrcode/lib/PHPQRCode/QRtools.php', + 'Parsedown' => __DIR__ . '/..' . '/erusev/parsedown/Parsedown.php', + 'ParsedownTest' => __DIR__ . '/..' . '/erusev/parsedown/test/ParsedownTest.php', + 'PicoDb\\Builder\\BaseBuilder' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Builder/BaseBuilder.php', + 'PicoDb\\Builder\\ConditionBuilder' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Builder/ConditionBuilder.php', + 'PicoDb\\Builder\\InsertBuilder' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Builder/InsertBuilder.php', + 'PicoDb\\Builder\\OrConditionBuilder' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Builder/OrConditionBuilder.php', + 'PicoDb\\Builder\\UpdateBuilder' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Builder/UpdateBuilder.php', + 'PicoDb\\Database' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Database.php', + 'PicoDb\\DriverFactory' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/DriverFactory.php', + 'PicoDb\\Driver\\Base' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Driver/Base.php', + 'PicoDb\\Driver\\Mssql' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Driver/Mssql.php', + 'PicoDb\\Driver\\Mysql' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Driver/Mysql.php', + 'PicoDb\\Driver\\Postgres' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Driver/Postgres.php', + 'PicoDb\\Driver\\Sqlite' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php', + 'PicoDb\\Hashtable' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Hashtable.php', + 'PicoDb\\LargeObject' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/LargeObject.php', + 'PicoDb\\SQLException' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/SQLException.php', + 'PicoDb\\Schema' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Schema.php', + 'PicoDb\\StatementHandler' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/StatementHandler.php', + 'PicoDb\\Table' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Table.php', + 'PicoDb\\UrlParser' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/UrlParser.php', + 'PicoFeed\\Base' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Base.php', + 'PicoFeed\\Client\\Client' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/Client.php', + 'PicoFeed\\Client\\ClientException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/ClientException.php', + 'PicoFeed\\Client\\Curl' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/Curl.php', + 'PicoFeed\\Client\\ForbiddenException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/ForbiddenException.php', + 'PicoFeed\\Client\\HttpHeaders' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/HttpHeaders.php', + 'PicoFeed\\Client\\InvalidCertificateException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php', + 'PicoFeed\\Client\\InvalidUrlException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/InvalidUrlException.php', + 'PicoFeed\\Client\\MaxRedirectException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/MaxRedirectException.php', + 'PicoFeed\\Client\\MaxSizeException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/MaxSizeException.php', + 'PicoFeed\\Client\\Stream' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/Stream.php', + 'PicoFeed\\Client\\TimeoutException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/TimeoutException.php', + 'PicoFeed\\Client\\UnauthorizedException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/UnauthorizedException.php', + 'PicoFeed\\Client\\Url' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Client/Url.php', + 'PicoFeed\\Config\\Config' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Config/Config.php', + 'PicoFeed\\Encoding\\Encoding' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Encoding/Encoding.php', + 'PicoFeed\\Filter\\Attribute' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Filter/Attribute.php', + 'PicoFeed\\Filter\\Filter' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Filter/Filter.php', + 'PicoFeed\\Filter\\Html' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Filter/Html.php', + 'PicoFeed\\Filter\\Tag' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Filter/Tag.php', + 'PicoFeed\\Generator\\ContentGeneratorInterface' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Generator/ContentGeneratorInterface.php', + 'PicoFeed\\Generator\\FileContentGenerator' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Generator/FileContentGenerator.php', + 'PicoFeed\\Generator\\YoutubeContentGenerator' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Generator/YoutubeContentGenerator.php', + 'PicoFeed\\Logging\\Logger' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Logging/Logger.php', + 'PicoFeed\\Parser\\Atom' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/Atom.php', + 'PicoFeed\\Parser\\DateParser' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/DateParser.php', + 'PicoFeed\\Parser\\Feed' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/Feed.php', + 'PicoFeed\\Parser\\Item' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/Item.php', + 'PicoFeed\\Parser\\MalformedXmlException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php', + 'PicoFeed\\Parser\\Parser' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/Parser.php', + 'PicoFeed\\Parser\\ParserException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/ParserException.php', + 'PicoFeed\\Parser\\ParserInterface' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/ParserInterface.php', + 'PicoFeed\\Parser\\Rss10' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/Rss10.php', + 'PicoFeed\\Parser\\Rss20' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/Rss20.php', + 'PicoFeed\\Parser\\Rss91' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/Rss91.php', + 'PicoFeed\\Parser\\Rss92' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/Rss92.php', + 'PicoFeed\\Parser\\XmlEntityException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/XmlEntityException.php', + 'PicoFeed\\Parser\\XmlParser' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Parser/XmlParser.php', + 'PicoFeed\\PicoFeedException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/PicoFeedException.php', + 'PicoFeed\\Processor\\ContentFilterProcessor' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Processor/ContentFilterProcessor.php', + 'PicoFeed\\Processor\\ContentGeneratorProcessor' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Processor/ContentGeneratorProcessor.php', + 'PicoFeed\\Processor\\ItemPostProcessor' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Processor/ItemPostProcessor.php', + 'PicoFeed\\Processor\\ItemProcessorInterface' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Processor/ItemProcessorInterface.php', + 'PicoFeed\\Processor\\ScraperProcessor' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Processor/ScraperProcessor.php', + 'PicoFeed\\Reader\\Favicon' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Reader/Favicon.php', + 'PicoFeed\\Reader\\Reader' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Reader/Reader.php', + 'PicoFeed\\Reader\\ReaderException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Reader/ReaderException.php', + 'PicoFeed\\Reader\\SubscriptionNotFoundException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Reader/SubscriptionNotFoundException.php', + 'PicoFeed\\Reader\\UnsupportedFeedFormatException' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Reader/UnsupportedFeedFormatException.php', + 'PicoFeed\\Scraper\\CandidateParser' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Scraper/CandidateParser.php', + 'PicoFeed\\Scraper\\ParserInterface' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Scraper/ParserInterface.php', + 'PicoFeed\\Scraper\\RuleLoader' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Scraper/RuleLoader.php', + 'PicoFeed\\Scraper\\RuleParser' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Scraper/RuleParser.php', + 'PicoFeed\\Scraper\\Scraper' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Scraper/Scraper.php', + 'PicoFeed\\Serialization\\Subscription' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Serialization/Subscription.php', + 'PicoFeed\\Serialization\\SubscriptionList' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionList.php', + 'PicoFeed\\Serialization\\SubscriptionListBuilder' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListBuilder.php', + 'PicoFeed\\Serialization\\SubscriptionListParser' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListParser.php', + 'PicoFeed\\Serialization\\SubscriptionParser' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionParser.php', + 'PicoFeed\\Syndication\\AtomFeedBuilder' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Syndication/AtomFeedBuilder.php', + 'PicoFeed\\Syndication\\AtomHelper' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Syndication/AtomHelper.php', + 'PicoFeed\\Syndication\\AtomItemBuilder' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Syndication/AtomItemBuilder.php', + 'PicoFeed\\Syndication\\FeedBuilder' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Syndication/FeedBuilder.php', + 'PicoFeed\\Syndication\\ItemBuilder' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Syndication/ItemBuilder.php', + 'PicoFeed\\Syndication\\Rss20FeedBuilder' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20FeedBuilder.php', + 'PicoFeed\\Syndication\\Rss20Helper' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20Helper.php', + 'PicoFeed\\Syndication\\Rss20ItemBuilder' => __DIR__ . '/..' . '/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20ItemBuilder.php', + 'Pimple\\Container' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Container.php', + 'Pimple\\ServiceProviderInterface' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/ServiceProviderInterface.php', + 'Pimple\\Tests\\Fixtures\\Invokable' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/Fixtures/Invokable.php', + 'Pimple\\Tests\\Fixtures\\NonInvokable' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php', + 'Pimple\\Tests\\Fixtures\\PimpleServiceProvider' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/Fixtures/PimpleServiceProvider.php', + 'Pimple\\Tests\\Fixtures\\Service' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php', + 'Pimple\\Tests\\PimpleServiceProviderInterfaceTest' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php', + 'Pimple\\Tests\\PimpleTest' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/PimpleTest.php', + 'Psr\\Log\\AbstractLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/AbstractLogger.php', + 'Psr\\Log\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/log/Psr/Log/InvalidArgumentException.php', + 'Psr\\Log\\LogLevel' => __DIR__ . '/..' . '/psr/log/Psr/Log/LogLevel.php', + 'Psr\\Log\\LoggerAwareInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerAwareInterface.php', + 'Psr\\Log\\LoggerAwareTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerAwareTrait.php', + 'Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerInterface.php', + 'Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerTrait.php', + 'Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/NullLogger.php', + 'Psr\\Log\\Test\\DummyTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', + 'Psr\\Log\\Test\\LoggerInterfaceTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', + 'SimpleLogger\\Base' => __DIR__ . '/..' . '/fguillot/simpleLogger/src/SimpleLogger/Base.php', + 'SimpleLogger\\File' => __DIR__ . '/..' . '/fguillot/simpleLogger/src/SimpleLogger/File.php', + 'SimpleLogger\\Logger' => __DIR__ . '/..' . '/fguillot/simpleLogger/src/SimpleLogger/Logger.php', + 'SimpleLogger\\Stderr' => __DIR__ . '/..' . '/fguillot/simpleLogger/src/SimpleLogger/Stderr.php', + 'SimpleLogger\\Stdout' => __DIR__ . '/..' . '/fguillot/simpleLogger/src/SimpleLogger/Stdout.php', + 'SimpleLogger\\Syslog' => __DIR__ . '/..' . '/fguillot/simpleLogger/src/SimpleLogger/Syslog.php', + 'SimpleQueue\\Adapter\\AmqpQueueAdapter' => __DIR__ . '/..' . '/fguillot/simple-queue/src/Adapter/AmqpQueueAdapter.php', + 'SimpleQueue\\Adapter\\AwsSqsQueueAdapter' => __DIR__ . '/..' . '/fguillot/simple-queue/src/Adapter/AwsSqsQueueAdapter.php', + 'SimpleQueue\\Adapter\\BeanstalkQueueAdapter' => __DIR__ . '/..' . '/fguillot/simple-queue/src/Adapter/BeanstalkQueueAdapter.php', + 'SimpleQueue\\Adapter\\DisqueQueueAdapter' => __DIR__ . '/..' . '/fguillot/simple-queue/src/Adapter/DisqueQueueAdapter.php', + 'SimpleQueue\\Adapter\\MemoryQueueAdapter' => __DIR__ . '/..' . '/fguillot/simple-queue/src/Adapter/MemoryQueueAdapter.php', + 'SimpleQueue\\Exception\\NotSupportedException' => __DIR__ . '/..' . '/fguillot/simple-queue/src/Exception/NotSupportedException.php', + 'SimpleQueue\\Job' => __DIR__ . '/..' . '/fguillot/simple-queue/src/Job.php', + 'SimpleQueue\\Queue' => __DIR__ . '/..' . '/fguillot/simple-queue/src/Queue.php', + 'SimpleQueue\\QueueAdapterInterface' => __DIR__ . '/..' . '/fguillot/simple-queue/src/QueueAdapterInterface.php', + 'SimpleValidator\\Validator' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validator.php', + 'SimpleValidator\\Validators\\Alpha' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Alpha.php', + 'SimpleValidator\\Validators\\AlphaNumeric' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/AlphaNumeric.php', + 'SimpleValidator\\Validators\\Base' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Base.php', + 'SimpleValidator\\Validators\\Date' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Date.php', + 'SimpleValidator\\Validators\\Email' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Email.php', + 'SimpleValidator\\Validators\\Equals' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Equals.php', + 'SimpleValidator\\Validators\\Exists' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Exists.php', + 'SimpleValidator\\Validators\\GreaterThan' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/GreaterThan.php', + 'SimpleValidator\\Validators\\InArray' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/InArray.php', + 'SimpleValidator\\Validators\\Integer' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Integer.php', + 'SimpleValidator\\Validators\\Ip' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Ip.php', + 'SimpleValidator\\Validators\\Length' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Length.php', + 'SimpleValidator\\Validators\\MaxLength' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/MaxLength.php', + 'SimpleValidator\\Validators\\MinLength' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/MinLength.php', + 'SimpleValidator\\Validators\\NotEmpty' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/NotEmpty.php', + 'SimpleValidator\\Validators\\NotEquals' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/NotEquals.php', + 'SimpleValidator\\Validators\\NotInArray' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/NotInArray.php', + 'SimpleValidator\\Validators\\Numeric' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Numeric.php', + 'SimpleValidator\\Validators\\Range' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Range.php', + 'SimpleValidator\\Validators\\Required' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Required.php', + 'SimpleValidator\\Validators\\Unique' => __DIR__ . '/..' . '/fguillot/simple-validator/src/SimpleValidator/Validators/Unique.php', + 'Symfony\\Component\\Console\\Application' => __DIR__ . '/..' . '/symfony/console/Application.php', + 'Symfony\\Component\\Console\\Command\\Command' => __DIR__ . '/..' . '/symfony/console/Command/Command.php', + 'Symfony\\Component\\Console\\Command\\HelpCommand' => __DIR__ . '/..' . '/symfony/console/Command/HelpCommand.php', + 'Symfony\\Component\\Console\\Command\\ListCommand' => __DIR__ . '/..' . '/symfony/console/Command/ListCommand.php', + 'Symfony\\Component\\Console\\ConsoleEvents' => __DIR__ . '/..' . '/symfony/console/ConsoleEvents.php', + 'Symfony\\Component\\Console\\Descriptor\\ApplicationDescription' => __DIR__ . '/..' . '/symfony/console/Descriptor/ApplicationDescription.php', + 'Symfony\\Component\\Console\\Descriptor\\Descriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/Descriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\DescriptorInterface' => __DIR__ . '/..' . '/symfony/console/Descriptor/DescriptorInterface.php', + 'Symfony\\Component\\Console\\Descriptor\\JsonDescriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/JsonDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\MarkdownDescriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/MarkdownDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\TextDescriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/TextDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\XmlDescriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/XmlDescriptor.php', + 'Symfony\\Component\\Console\\Event\\ConsoleCommandEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleCommandEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleExceptionEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleExceptionEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleTerminateEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleTerminateEvent.php', + 'Symfony\\Component\\Console\\Exception\\CommandNotFoundException' => __DIR__ . '/..' . '/symfony/console/Exception/CommandNotFoundException.php', + 'Symfony\\Component\\Console\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/console/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Console\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/console/Exception/InvalidArgumentException.php', + 'Symfony\\Component\\Console\\Exception\\InvalidOptionException' => __DIR__ . '/..' . '/symfony/console/Exception/InvalidOptionException.php', + 'Symfony\\Component\\Console\\Exception\\LogicException' => __DIR__ . '/..' . '/symfony/console/Exception/LogicException.php', + 'Symfony\\Component\\Console\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/console/Exception/RuntimeException.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatter' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatter.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterInterface' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatterInterface.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyle' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatterStyle.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleInterface' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatterStyleInterface.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleStack' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatterStyleStack.php', + 'Symfony\\Component\\Console\\Helper\\DebugFormatterHelper' => __DIR__ . '/..' . '/symfony/console/Helper/DebugFormatterHelper.php', + 'Symfony\\Component\\Console\\Helper\\DescriptorHelper' => __DIR__ . '/..' . '/symfony/console/Helper/DescriptorHelper.php', + 'Symfony\\Component\\Console\\Helper\\DialogHelper' => __DIR__ . '/..' . '/symfony/console/Helper/DialogHelper.php', + 'Symfony\\Component\\Console\\Helper\\FormatterHelper' => __DIR__ . '/..' . '/symfony/console/Helper/FormatterHelper.php', + 'Symfony\\Component\\Console\\Helper\\Helper' => __DIR__ . '/..' . '/symfony/console/Helper/Helper.php', + 'Symfony\\Component\\Console\\Helper\\HelperInterface' => __DIR__ . '/..' . '/symfony/console/Helper/HelperInterface.php', + 'Symfony\\Component\\Console\\Helper\\HelperSet' => __DIR__ . '/..' . '/symfony/console/Helper/HelperSet.php', + 'Symfony\\Component\\Console\\Helper\\InputAwareHelper' => __DIR__ . '/..' . '/symfony/console/Helper/InputAwareHelper.php', + 'Symfony\\Component\\Console\\Helper\\ProcessHelper' => __DIR__ . '/..' . '/symfony/console/Helper/ProcessHelper.php', + 'Symfony\\Component\\Console\\Helper\\ProgressBar' => __DIR__ . '/..' . '/symfony/console/Helper/ProgressBar.php', + 'Symfony\\Component\\Console\\Helper\\ProgressHelper' => __DIR__ . '/..' . '/symfony/console/Helper/ProgressHelper.php', + 'Symfony\\Component\\Console\\Helper\\ProgressIndicator' => __DIR__ . '/..' . '/symfony/console/Helper/ProgressIndicator.php', + 'Symfony\\Component\\Console\\Helper\\QuestionHelper' => __DIR__ . '/..' . '/symfony/console/Helper/QuestionHelper.php', + 'Symfony\\Component\\Console\\Helper\\SymfonyQuestionHelper' => __DIR__ . '/..' . '/symfony/console/Helper/SymfonyQuestionHelper.php', + 'Symfony\\Component\\Console\\Helper\\Table' => __DIR__ . '/..' . '/symfony/console/Helper/Table.php', + 'Symfony\\Component\\Console\\Helper\\TableCell' => __DIR__ . '/..' . '/symfony/console/Helper/TableCell.php', + 'Symfony\\Component\\Console\\Helper\\TableHelper' => __DIR__ . '/..' . '/symfony/console/Helper/TableHelper.php', + 'Symfony\\Component\\Console\\Helper\\TableSeparator' => __DIR__ . '/..' . '/symfony/console/Helper/TableSeparator.php', + 'Symfony\\Component\\Console\\Helper\\TableStyle' => __DIR__ . '/..' . '/symfony/console/Helper/TableStyle.php', + 'Symfony\\Component\\Console\\Input\\ArgvInput' => __DIR__ . '/..' . '/symfony/console/Input/ArgvInput.php', + 'Symfony\\Component\\Console\\Input\\ArrayInput' => __DIR__ . '/..' . '/symfony/console/Input/ArrayInput.php', + 'Symfony\\Component\\Console\\Input\\Input' => __DIR__ . '/..' . '/symfony/console/Input/Input.php', + 'Symfony\\Component\\Console\\Input\\InputArgument' => __DIR__ . '/..' . '/symfony/console/Input/InputArgument.php', + 'Symfony\\Component\\Console\\Input\\InputAwareInterface' => __DIR__ . '/..' . '/symfony/console/Input/InputAwareInterface.php', + 'Symfony\\Component\\Console\\Input\\InputDefinition' => __DIR__ . '/..' . '/symfony/console/Input/InputDefinition.php', + 'Symfony\\Component\\Console\\Input\\InputInterface' => __DIR__ . '/..' . '/symfony/console/Input/InputInterface.php', + 'Symfony\\Component\\Console\\Input\\InputOption' => __DIR__ . '/..' . '/symfony/console/Input/InputOption.php', + 'Symfony\\Component\\Console\\Input\\StringInput' => __DIR__ . '/..' . '/symfony/console/Input/StringInput.php', + 'Symfony\\Component\\Console\\Logger\\ConsoleLogger' => __DIR__ . '/..' . '/symfony/console/Logger/ConsoleLogger.php', + 'Symfony\\Component\\Console\\Output\\BufferedOutput' => __DIR__ . '/..' . '/symfony/console/Output/BufferedOutput.php', + 'Symfony\\Component\\Console\\Output\\ConsoleOutput' => __DIR__ . '/..' . '/symfony/console/Output/ConsoleOutput.php', + 'Symfony\\Component\\Console\\Output\\ConsoleOutputInterface' => __DIR__ . '/..' . '/symfony/console/Output/ConsoleOutputInterface.php', + 'Symfony\\Component\\Console\\Output\\NullOutput' => __DIR__ . '/..' . '/symfony/console/Output/NullOutput.php', + 'Symfony\\Component\\Console\\Output\\Output' => __DIR__ . '/..' . '/symfony/console/Output/Output.php', + 'Symfony\\Component\\Console\\Output\\OutputInterface' => __DIR__ . '/..' . '/symfony/console/Output/OutputInterface.php', + 'Symfony\\Component\\Console\\Output\\StreamOutput' => __DIR__ . '/..' . '/symfony/console/Output/StreamOutput.php', + 'Symfony\\Component\\Console\\Question\\ChoiceQuestion' => __DIR__ . '/..' . '/symfony/console/Question/ChoiceQuestion.php', + 'Symfony\\Component\\Console\\Question\\ConfirmationQuestion' => __DIR__ . '/..' . '/symfony/console/Question/ConfirmationQuestion.php', + 'Symfony\\Component\\Console\\Question\\Question' => __DIR__ . '/..' . '/symfony/console/Question/Question.php', + 'Symfony\\Component\\Console\\Shell' => __DIR__ . '/..' . '/symfony/console/Shell.php', + 'Symfony\\Component\\Console\\Style\\OutputStyle' => __DIR__ . '/..' . '/symfony/console/Style/OutputStyle.php', + 'Symfony\\Component\\Console\\Style\\StyleInterface' => __DIR__ . '/..' . '/symfony/console/Style/StyleInterface.php', + 'Symfony\\Component\\Console\\Style\\SymfonyStyle' => __DIR__ . '/..' . '/symfony/console/Style/SymfonyStyle.php', + 'Symfony\\Component\\Console\\Tester\\ApplicationTester' => __DIR__ . '/..' . '/symfony/console/Tester/ApplicationTester.php', + 'Symfony\\Component\\Console\\Tester\\CommandTester' => __DIR__ . '/..' . '/symfony/console/Tester/CommandTester.php', + 'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/ContainerAwareEventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcherInterface' => __DIR__ . '/..' . '/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener' => __DIR__ . '/..' . '/symfony/event-dispatcher/Debug/WrappedListener.php', + 'Symfony\\Component\\EventDispatcher\\DependencyInjection\\RegisterListenersPass' => __DIR__ . '/..' . '/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php', + 'Symfony\\Component\\EventDispatcher\\Event' => __DIR__ . '/..' . '/symfony/event-dispatcher/Event.php', + 'Symfony\\Component\\EventDispatcher\\EventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/EventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\EventDispatcherInterface' => __DIR__ . '/..' . '/symfony/event-dispatcher/EventDispatcherInterface.php', + 'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface' => __DIR__ . '/..' . '/symfony/event-dispatcher/EventSubscriberInterface.php', + 'Symfony\\Component\\EventDispatcher\\GenericEvent' => __DIR__ . '/..' . '/symfony/event-dispatcher/GenericEvent.php', + 'Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/ImmutableEventDispatcher.php', + 'Symfony\\Polyfill\\Mbstring\\Mbstring' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/Mbstring.php', + 'ZendXml\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/zendframework/zendxml/library/ZendXml/Exception/ExceptionInterface.php', + 'ZendXml\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/zendframework/zendxml/library/ZendXml/Exception/InvalidArgumentException.php', + 'ZendXml\\Exception\\RuntimeException' => __DIR__ . '/..' . '/zendframework/zendxml/library/ZendXml/Exception/RuntimeException.php', + 'ZendXml\\Security' => __DIR__ . '/..' . '/zendframework/zendxml/library/ZendXml/Security.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInit6edea6294a88689e3f5c56484bb70c9b::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit6edea6294a88689e3f5c56484bb70c9b::$prefixDirsPsr4; + $loader->prefixesPsr0 = ComposerStaticInit6edea6294a88689e3f5c56484bb70c9b::$prefixesPsr0; + $loader->classMap = ComposerStaticInit6edea6294a88689e3f5c56484bb70c9b::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json new file mode 100644 index 00000000..12b9857f --- /dev/null +++ b/vendor/composer/installed.json @@ -0,0 +1,1051 @@ +[ + { + "name": "aferrandini/phpqrcode", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "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" + }, + "time": "2013-07-08T09:39:08+00:00", + "type": "library", + "installation-source": "dist", + "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" + ] + }, + { + "name": "christian-riesen/base32", + "version": "1.3.1", + "version_normalized": "1.3.1.0", + "source": { + "type": "git", + "url": "https://github.com/ChristianRiesen/base32.git", + "reference": "0a31e50c0fa9b1692d077c86ac188eecdcbaf7fa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ChristianRiesen/base32/zipball/0a31e50c0fa9b1692d077c86ac188eecdcbaf7fa", + "reference": "0a31e50c0fa9b1692d077c86ac188eecdcbaf7fa", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "satooshi/php-coveralls": "0.*" + }, + "time": "2016-05-05T11:49:03+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Base32\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Riesen", + "email": "chris.riesen@gmail.com", + "homepage": "http://christianriesen.com", + "role": "Developer" + } + ], + "description": "Base32 encoder/decoder according to RFC 4648", + "homepage": "https://github.com/ChristianRiesen/base32", + "keywords": [ + "base32", + "decode", + "encode", + "rfc4648" + ] + }, + { + "name": "christian-riesen/otp", + "version": "1.4.3", + "version_normalized": "1.4.3.0", + "source": { + "type": "git", + "url": "https://github.com/ChristianRiesen/otp.git", + "reference": "20a539ce6280eb029030f4e7caefd5709a75e1ad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ChristianRiesen/otp/zipball/20a539ce6280eb029030f4e7caefd5709a75e1ad", + "reference": "20a539ce6280eb029030f4e7caefd5709a75e1ad", + "shasum": "" + }, + "require": { + "christian-riesen/base32": ">=1.0", + "php": ">=5.3.0" + }, + "suggest": { + "paragonie/random_compat": "Optional polyfill for a more secure random generator for pre PHP7 versions" + }, + "time": "2015-10-08T08:17:59+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Otp": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Riesen", + "email": "chris.riesen@gmail.com", + "homepage": "http://christianriesen.com", + "role": "Developer" + } + ], + "description": "One Time Passwords, hotp and totp according to RFC4226 and RFC6238", + "homepage": "https://github.com/ChristianRiesen/otp", + "keywords": [ + "googleauthenticator", + "hotp", + "otp", + "rfc4226", + "rfc6238", + "totp" + ] + }, + { + "name": "eluceo/ical", + "version": "0.10.1", + "version_normalized": "0.10.1.0", + "source": { + "type": "git", + "url": "https://github.com/markuspoerschke/iCal.git", + "reference": "2dd99c12c0aa961c541380ab0c113135e14af33e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/markuspoerschke/iCal/zipball/2dd99c12c0aa961c541380ab0c113135e14af33e", + "reference": "2dd99c12c0aa961c541380ab0c113135e14af33e", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.3" + }, + "time": "2016-06-09T09:08:55+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Eluceo\\iCal": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maciej Åebkowski", + "email": "m.lebkowski@gmail.com", + "role": "Contributor" + }, + { + "name": "Markus Poerschke", + "email": "markus@eluceo.de", + "role": "Developer" + } + ], + "description": "The eluceo/iCal package offers a abstraction layer for creating iCalendars. You can easily create iCal files by using PHP object instead of typing your *.ics file by hand. The output will follow RFC 2445 as best as possible.", + "homepage": "https://github.com/markuspoerschke/iCal", + "keywords": [ + "calendar", + "iCalendar", + "ical", + "ics", + "php calendar" + ] + }, + { + "name": "erusev/parsedown", + "version": "1.6.0", + "version_normalized": "1.6.0.0", + "source": { + "type": "git", + "url": "https://github.com/erusev/parsedown.git", + "reference": "3ebbd730b5c2cf5ce78bc1bf64071407fc6674b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/erusev/parsedown/zipball/3ebbd730b5c2cf5ce78bc1bf64071407fc6674b7", + "reference": "3ebbd730b5c2cf5ce78bc1bf64071407fc6674b7", + "shasum": "" + }, + "time": "2015-10-04T16:44:32+00:00", + "type": "library", + "installation-source": "dist", + "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" + ] + }, + { + "name": "fguillot/json-rpc", + "version": "v1.2.1", + "version_normalized": "1.2.1.0", + "source": { + "type": "git", + "url": "https://github.com/fguillot/JsonRPC.git", + "reference": "d491bb549bfa11aff4c37abcea2ffb28c9523f69" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/d491bb549bfa11aff4c37abcea2ffb28c9523f69", + "reference": "d491bb549bfa11aff4c37abcea2ffb28c9523f69", + "shasum": "" + }, + "require": { + "php": ">=5.3.4" + }, + "require-dev": { + "phpunit/phpunit": "4.8.*" + }, + "time": "2016-06-25T23:11:10+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "JsonRPC": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frédéric Guillot" + } + ], + "description": "Simple Json-RPC client/server library that just works", + "homepage": "https://github.com/fguillot/JsonRPC" + }, + { + "name": "fguillot/picodb", + "version": "v1.0.14", + "version_normalized": "1.0.14.0", + "source": { + "type": "git", + "url": "https://github.com/fguillot/picoDb.git", + "reference": "86a831302ab10af800c83dbe4b3b01c88d5433f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fguillot/picoDb/zipball/86a831302ab10af800c83dbe4b3b01c88d5433f1", + "reference": "86a831302ab10af800c83dbe4b3b01c88d5433f1", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "4.8.*" + }, + "time": "2016-07-16T22:59:59+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "PicoDb": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frédéric Guillot", + "homepage": "https://github.com/fguillot/" + } + ], + "description": "Minimalist database query builder", + "homepage": "https://github.com/fguillot/picoDb" + }, + { + "name": "fguillot/simple-queue", + "version": "v1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/fguillot/simple-queue.git", + "reference": "ce7801c507f9501bcca455129fb04c3d2107d5ff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fguillot/simple-queue/zipball/ce7801c507f9501bcca455129fb04c3d2107d5ff", + "reference": "ce7801c507f9501bcca455129fb04c3d2107d5ff", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "aws/aws-sdk-php": "~3.0", + "mariano/disque-php": "~2.0", + "pda/pheanstalk": "~3.0", + "php-amqplib/php-amqplib": "2.6.*", + "phpunit/phpunit": "5.3.*" + }, + "suggest": { + "aws/aws-sdk-php": "Required to use the AWS SQS queue driver (~3.0).", + "mariano/disque-php": "Required to use the Disque queue driver (~2.0).", + "pda/pheanstalk": "Required to use the Beanstalk queue driver (~3.0).", + "php-amqplib/php-amqplib": "Required to use the RabbitMQ queue driver (2.6.*)." + }, + "time": "2016-06-05T21:34:56+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "SimpleQueue\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frédéric Guillot" + } + ], + "description": "Abstraction layer for multiple queue systems", + "homepage": "https://github.com/fguillot/simple-queue" + }, + { + "name": "fguillot/simple-validator", + "version": "v1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/fguillot/simpleValidator.git", + "reference": "23b0a99c5f11ad74d05f8845feaafbcfd9223eda" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fguillot/simpleValidator/zipball/23b0a99c5f11ad74d05f8845feaafbcfd9223eda", + "reference": "23b0a99c5f11ad74d05f8845feaafbcfd9223eda", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2016-06-26T15:09:26+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "SimpleValidator": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frédéric Guillot" + } + ], + "description": "Simple validator library", + "homepage": "https://github.com/fguillot/simpleValidator" + }, + { + "name": "psr/log", + "version": "1.0.2", + "version_normalized": "1.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2016-10-10T12:19:37+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ] + }, + { + "name": "fguillot/simpleLogger", + "version": "v1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/fguillot/simpleLogger.git", + "reference": "c6831841193bb265b7900ecc8b6a8918371a7c98" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fguillot/simpleLogger/zipball/c6831841193bb265b7900ecc8b6a8918371a7c98", + "reference": "c6831841193bb265b7900ecc8b6a8918371a7c98", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "time": "2016-05-07T18:01:57+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "SimpleLogger": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frédéric Guillot" + } + ], + "description": "PHP library to write logs (compatible with PSR-3)", + "homepage": "https://github.com/fguillot/simpleLogger" + }, + { + "name": "gregwar/captcha", + "version": "v1.1.1", + "version_normalized": "1.1.1.0", + "source": { + "type": "git", + "url": "https://github.com/Gregwar/Captcha.git", + "reference": "1240ab993ca713680573b2d4166900da5f758623" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Gregwar/Captcha/zipball/1240ab993ca713680573b2d4166900da5f758623", + "reference": "1240ab993ca713680573b2d4166900da5f758623", + "shasum": "" + }, + "require": { + "ext-gd": "*", + "php": ">=5.3.0" + }, + "time": "2015-09-11T15:23:20+00:00", + "type": "captcha", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Gregwar\\Captcha\\": "/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Passault", + "email": "g.passault@gmail.com", + "homepage": "http://www.gregwar.com/" + }, + { + "name": "Jeremy Livingston", + "email": "jeremy.j.livingston@gmail.com" + } + ], + "description": "Captcha generator", + "homepage": "https://github.com/Gregwar/Captcha", + "keywords": [ + "bot", + "captcha", + "spam" + ] + }, + { + "name": "zendframework/zendxml", + "version": "1.0.2", + "version_normalized": "1.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/ZendXml.git", + "reference": "7b64507bc35d841c9c5802d67f6f87ef8e1a58c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/ZendXml/zipball/7b64507bc35d841c9c5802d67f6f87ef8e1a58c9", + "reference": "7b64507bc35d841c9c5802d67f6f87ef8e1a58c9", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^3.7 || ^4.0", + "squizlabs/php_codesniffer": "^1.5" + }, + "time": "2016-02-04T21:02:08+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "ZendXml\\": "library/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Utility library for XML usage, best practices, and security in PHP", + "homepage": "http://packages.zendframework.com/", + "keywords": [ + "security", + "xml", + "zf2" + ] + }, + { + "name": "miniflux/picofeed", + "version": "v0.1.34", + "version_normalized": "0.1.34.0", + "source": { + "type": "git", + "url": "https://github.com/miniflux/picoFeed.git", + "reference": "5c8a731d4e7a3589e562e4fdaa98bcb57fa8a2ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/miniflux/picoFeed/zipball/5c8a731d4e7a3589e562e4fdaa98bcb57fa8a2ea", + "reference": "5c8a731d4e7a3589e562e4fdaa98bcb57fa8a2ea", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-iconv": "*", + "ext-libxml": "*", + "ext-simplexml": "*", + "ext-xml": "*", + "php": ">=5.3.0", + "zendframework/zendxml": "^1.0" + }, + "require-dev": { + "phpdocumentor/reflection-docblock": "2.0.4", + "phpunit/phpunit": "4.8.26", + "symfony/yaml": "2.8.7" + }, + "suggest": { + "ext-curl": "PicoFeed will use cURL if present" + }, + "time": "2017-06-12T00:22:06+00:00", + "bin": [ + "picofeed" + ], + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "PicoFeed": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frédéric Guillot" + } + ], + "description": "Modern library to handle RSS/Atom feeds", + "homepage": "https://github.com/miniflux/picoFeed" + }, + { + "name": "paragonie/random_compat", + "version": "v2.0.2", + "version_normalized": "2.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "088c04e2f261c33bed6ca5245491cfca69195ccf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/088c04e2f261c33bed6ca5245491cfca69195ccf", + "reference": "088c04e2f261c33bed6ca5245491cfca69195ccf", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "time": "2016-04-03T06:00:07+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ] + }, + { + "name": "pimple/pimple", + "version": "v3.0.2", + "version_normalized": "3.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/silexphp/Pimple.git", + "reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/silexphp/Pimple/zipball/a30f7d6e57565a2e1a316e1baf2a483f788b258a", + "reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2015-09-11T15:10:35+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Pimple": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Pimple, a simple Dependency Injection Container", + "homepage": "http://pimple.sensiolabs.org", + "keywords": [ + "container", + "dependency injection" + ] + }, + { + "name": "ramsey/array_column", + "version": "1.1.3", + "version_normalized": "1.1.3.0", + "source": { + "type": "git", + "url": "https://github.com/ramsey/array_column.git", + "reference": "f8e52eb28e67eb50e613b451dd916abcf783c1db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/array_column/zipball/f8e52eb28e67eb50e613b451dd916abcf783c1db", + "reference": "f8e52eb28e67eb50e613b451dd916abcf783c1db", + "shasum": "" + }, + "require-dev": { + "jakub-onderka/php-parallel-lint": "0.8.*", + "phpunit/phpunit": "~4.5", + "satooshi/php-coveralls": "0.6.*", + "squizlabs/php_codesniffer": "~2.2" + }, + "time": "2015-03-20T22:07:39+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "src/array_column.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "homepage": "http://benramsey.com" + } + ], + "description": "Provides functionality for array_column() to projects using PHP earlier than version 5.5.", + "homepage": "https://github.com/ramsey/array_column", + "keywords": [ + "array", + "array_column", + "column" + ] + }, + { + "name": "swiftmailer/swiftmailer", + "version": "v5.4.5", + "version_normalized": "5.4.5.0", + "source": { + "type": "git", + "url": "https://github.com/swiftmailer/swiftmailer.git", + "reference": "cd142238a339459b10da3d8234220963f392540c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/cd142238a339459b10da3d8234220963f392540c", + "reference": "cd142238a339459b10da3d8234220963f392540c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "mockery/mockery": "~0.9.1", + "symfony/phpunit-bridge": "~3.2" + }, + "time": "2016-12-29T10:02:40+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "lib/swift_required.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Corbyn" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Swiftmailer, free feature-rich PHP mailer", + "homepage": "http://swiftmailer.org", + "keywords": [ + "email", + "mail", + "mailer" + ] + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.4.0", + "version_normalized": "1.4.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "f29dca382a6485c3cbe6379f0c61230167681937" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f29dca382a6485c3cbe6379f0c61230167681937", + "reference": "f29dca382a6485c3cbe6379f0c61230167681937", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "time": "2017-06-09T14:24:12+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ] + }, + { + "name": "symfony/console", + "version": "v2.8.7", + "version_normalized": "2.8.7.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3", + "reference": "5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3", + "shasum": "" + }, + "require": { + "php": ">=5.3.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/event-dispatcher": "~2.1|~3.0.0", + "symfony/process": "~2.1|~3.0.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/process": "" + }, + "time": "2016-06-06T15:06:25+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "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 Console Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/event-dispatcher", + "version": "v2.7.14", + "version_normalized": "2.7.14.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "d3e09ed1224503791f31b913d22196f65f9afed5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d3e09ed1224503791f31b913d22196f65f9afed5", + "reference": "d3e09ed1224503791f31b913d22196f65f9afed5", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.0,>=2.0.5", + "symfony/dependency-injection": "~2.6", + "symfony/expression-language": "~2.6", + "symfony/stopwatch": "~2.3" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "time": "2016-06-06T11:03:51+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "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 EventDispatcher Component", + "homepage": "https://symfony.com" + } +] diff --git a/vendor/eluceo/ical/.gitignore b/vendor/eluceo/ical/.gitignore new file mode 100644 index 00000000..4e06e7d4 --- /dev/null +++ b/vendor/eluceo/ical/.gitignore @@ -0,0 +1,3 @@ +vendor +composer.lock +bin diff --git a/vendor/eluceo/ical/.php_cs b/vendor/eluceo/ical/.php_cs new file mode 100644 index 00000000..1d54d34a --- /dev/null +++ b/vendor/eluceo/ical/.php_cs @@ -0,0 +1,26 @@ +<?php + +Symfony\CS\Fixer\Contrib\HeaderCommentFixer::setHeader(<<<EOF +This file is part of the eluceo/iCal package. + +(c) Markus Poerschke <markus@eluceo.de> + +This source file is subject to the MIT license that is bundled +with this source code in the file LICENSE. +EOF +); + +$finder = Symfony\CS\Finder\DefaultFinder::create(); +$finder->in(__DIR__ . '/src'); + +return Symfony\CS\Config\Config::create() + ->fixers(array( + 'header_comment', + 'concat_with_spaces', + 'align_equals', + 'align_double_arrow', + 'unused_use', + 'long_array_syntax', + )) + ->finder($finder) +; diff --git a/vendor/eluceo/ical/.scrutinizer.yml b/vendor/eluceo/ical/.scrutinizer.yml new file mode 100644 index 00000000..d3b381a2 --- /dev/null +++ b/vendor/eluceo/ical/.scrutinizer.yml @@ -0,0 +1,20 @@ +filter: + excluded_paths: + - tests/* + +tools: + php_cs_fixer: true + php_code_sniffer: + config: + standard: PSR2 + php_mess_detector: true + php_analyzer: true + sensiolabs_security_checker: true + external_code_coverage: + timeout: 300 + runs: 1 + +checks: + php: + code_rating: true + duplication: true diff --git a/vendor/eluceo/ical/.travis.yml b/vendor/eluceo/ical/.travis.yml new file mode 100644 index 00000000..cbe31ac9 --- /dev/null +++ b/vendor/eluceo/ical/.travis.yml @@ -0,0 +1,19 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + +before_script: + - composer self-update + - composer install + +script: ./bin/phpunit --coverage-clover=coverage.clover + +after_script: + - wget https://scrutinizer-ci.com/ocular.phar + - php ocular.phar code-coverage:upload --format=php-clover coverage.clover diff --git a/vendor/eluceo/ical/CHANGELOG.md b/vendor/eluceo/ical/CHANGELOG.md new file mode 100644 index 00000000..84320d98 --- /dev/null +++ b/vendor/eluceo/ical/CHANGELOG.md @@ -0,0 +1,31 @@ +# Change Log +All notable changes to this project will be documented in this file. + +## [0.10.1] - 2016-05-09 +### Fixed +- Problem with GEO property when importing into Google Calendar [#74](https://github.com/markuspoerschke/iCal/pull/74) + +## [0.10.0] - 2016-04-26 +### Changed +- Use 'escapeValue' to escape the new line character. [#60](https://github.com/markuspoerschke/iCal/pull/60) +- Order components by type when building ical file. [#65](https://github.com/markuspoerschke/iCal/pull/65) + +### Added +- X-ALT-DESC for HTML types with new descriptionHTML field. [#55](https://github.com/markuspoerschke/iCal/pull/55) +- Added a property and setter for calendar color. [#68](https://github.com/markuspoerschke/iCal/pull/68) +- Write also GEO property if geo location is given. [#66](https://github.com/markuspoerschke/iCal/pull/66) + +## [0.9.0] - 2015-11-13 +### Added +- CHANGELOG.md based on [’Keep a CHANGELOG’](https://github.com/olivierlacan/keep-a-changelog) +- Support event properties EXDATE and RECURRENCE-ID [#50](https://github.com/markuspoerschke/iCal/pull/53) + +### Changed +- Allow new lines in event descriptions [#53](https://github.com/markuspoerschke/iCal/pull/53) +- **Breaking Change:** Changed signature of the ```Event::setOrganizer``` method. Now there is is only one parameter that must be an instance of ```Property\Organizer```. +- Updated install section in README.md [#54](https://github.com/markuspoerschke/iCal/pull/53) + +[Unreleased]: https://github.com/markuspoerschke/iCal/compare/0.10.1...HEAD +[0.10.1]: https://github.com/markuspoerschke/iCal/compare/0.10.0...0.10.1 +[0.10.0]: https://github.com/markuspoerschke/iCal/compare/0.9.0...0.10.0 +[0.9.0]: https://github.com/markuspoerschke/iCal/compare/0.8.0...0.9.0 diff --git a/vendor/eluceo/ical/LICENSE b/vendor/eluceo/ical/LICENSE new file mode 100644 index 00000000..92be8d06 --- /dev/null +++ b/vendor/eluceo/ical/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012-2015 Markus Poerschke + +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. diff --git a/vendor/eluceo/ical/README.md b/vendor/eluceo/ical/README.md new file mode 100644 index 00000000..521d537d --- /dev/null +++ b/vendor/eluceo/ical/README.md @@ -0,0 +1,158 @@ +# eluceo — iCal + +[](https://scrutinizer-ci.com/g/markuspoerschke/iCal/?branch=master) [](https://scrutinizer-ci.com/g/markuspoerschke/iCal/?branch=master) [](https://travis-ci.org/markuspoerschke/iCal) + +This package offers a abstraction layer for creating iCalendars. The output will +follow [RFC 5545](http://www.ietf.org/rfc/rfc5545.txt) as best as possible. + +The following components are supported at this time: + +* VCALENDAR +* VEVENT +* VALARM +* VTIMEZONE + +## Installation + +You can install this package by using [Composer](http://getcomposer.org), running this command: + +```sh +composer require eluceo/ical +``` +Link to Packagist: https://packagist.org/packages/eluceo/ical + +## Usage + +### Basic Usage + +#### 1. Create a Calendar object + +```PHP +$vCalendar = new \Eluceo\iCal\Component\Calendar('www.example.com'); +``` + +#### 2. Create an Event object + +```PHP +$vEvent = new \Eluceo\iCal\Component\Event(); +``` + +#### 3. Add your information to the Event + +```PHP +$vEvent + ->setDtStart(new \DateTime('2012-12-24')) + ->setDtEnd(new \DateTime('2012-12-24')) + ->setNoTime(true) + ->setSummary('Christmas') +; +``` + +#### 4. Add Event to Calendar + +```PHP +$vCalendar->addComponent($vEvent); +``` + +#### 5. Set HTTP-headers + +```PHP +header('Content-Type: text/calendar; charset=utf-8'); +header('Content-Disposition: attachment; filename="cal.ics"'); +``` + +#### 6. Send output + +```PHP +echo $vCalendar->render(); +``` + +### Timezone support + +This package supports three different types of handling timezones: + +#### 1. UTC (default) + +In the default setting, UTC/GMT will be used as Timezone. The time will be formated as following: + +``` +DTSTART:20121224T180000Z +``` + +#### 2. Use explicit timezone + +You can use an explicit timezone by calling `$vEvent->setUseTimezone(true);`. The timezone of your +`\DateTime` object will be used. In this case the non-standard field "X-WR-TIMEZONE" will be used. +Be awre that this is a simple solution which is not supported by all calendar clients. +The output will be as following: + +``` +DTSTART;TZID=Europe/Berlin:20121224T180000 +``` + +#### 3. Use explicit timezone with definition + +You can use an explicit timezone and define it using `Timezone()` and `TimezoneRule()` (see example5.php). +The timezone of your `\DateTime` object will be used. The output will be as following: + +``` +BEGIN:VTIMEZONE +TZID:Europe/Berlin +X-LIC-LOCATION:Europe/Berlin +BEGIN:DAYLIGHT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19810329T030000 +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19961027T030000 +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +... +DTSTART;TZID=Europe/Berlin:20121224T180000 +``` + +#### 4. Use locale time + +You can use local time by calling `$vEvent->setUseUtc(false);`. The output will be: + +``` +DTSTART:20121224T180000 +``` + +## Running the tests + +To setup and run tests: + +- go to the root directory of this project +- download composer: `wget https://getcomposer.org/composer.phar` +- install dev dependencies: `php composer.phar install --dev` +- run `./bin/phpunit` + +## License + +This package is released under the __MIT license__. + +Copyright (c) 2012-2015 Markus Poerschke + +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. diff --git a/vendor/eluceo/ical/UPGRADE.md b/vendor/eluceo/ical/UPGRADE.md new file mode 100644 index 00000000..951209dd --- /dev/null +++ b/vendor/eluceo/ical/UPGRADE.md @@ -0,0 +1,9 @@ +# v0.8.0 -> v0.9.0 + +- The signature of the ```Event::setOrganizer``` method was changed: +Now there is is only one parameter that must be an instance of ```Property\Organizer```. + +# v0.7.0 -> v0.8.0 + +- The signature of the ```Event::setOrganizer``` method was changed: Now there are +two parameters name and email instead of an already formatted string. diff --git a/vendor/eluceo/ical/composer.json b/vendor/eluceo/ical/composer.json new file mode 100644 index 00000000..b2477c28 --- /dev/null +++ b/vendor/eluceo/ical/composer.json @@ -0,0 +1,43 @@ +{ + "name": "eluceo/ical", + "description": "The eluceo/iCal package offers a abstraction layer for creating iCalendars. You can easily create iCal files by using PHP object instead of typing your *.ics file by hand. The output will follow RFC 2445 as best as possible.", + "license": "MIT", + "homepage": "https://github.com/markuspoerschke/iCal", + "authors": [ + { + "name": "Markus Poerschke", + "email": "markus@eluceo.de", + "role": "Developer" + }, + { + "name": "Maciej Åebkowski", + "email": "m.lebkowski@gmail.com", + "role": "Contributor" + } + ], + "keywords": [ + "ical", + "php calendar", + "icalendar", + "ics", + "calendar" + ], + "support": { + "issues": "https://github.com/markuspoerschke/iCal/issues", + "source": "https://github.com/markuspoerschke/iCal" + }, + "autoload": { + "psr-0": { + "Eluceo\\iCal": "src/" + } + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.3" + }, + "config": { + "bin-dir": "bin" + } +} diff --git a/vendor/eluceo/ical/examples/example1.php b/vendor/eluceo/ical/examples/example1.php new file mode 100644 index 00000000..4d1d32c4 --- /dev/null +++ b/vendor/eluceo/ical/examples/example1.php @@ -0,0 +1,30 @@ +<?php + +// use composer autoloader +require_once __DIR__ . '/../vendor/autoload.php'; + +// set default timezone (PHP 5.4) +date_default_timezone_set('Europe/Berlin'); + +// 1. Create new calendar +$vCalendar = new \Eluceo\iCal\Component\Calendar('www.example.com'); + +// 2. Create an event +$vEvent = new \Eluceo\iCal\Component\Event(); +$vEvent->setDtStart(new \DateTime('2012-12-24')); +$vEvent->setDtEnd(new \DateTime('2012-12-24')); +$vEvent->setNoTime(true); +$vEvent->setSummary('Christmas'); + +// Adding Timezone (optional) +$vEvent->setUseTimezone(true); + +// 3. Add event to calendar +$vCalendar->addComponent($vEvent); + +// 4. Set headers +header('Content-Type: text/calendar; charset=utf-8'); +header('Content-Disposition: attachment; filename="cal.ics"'); + +// 5. Output +echo $vCalendar->render(); diff --git a/vendor/eluceo/ical/examples/example2.php b/vendor/eluceo/ical/examples/example2.php new file mode 100644 index 00000000..bafcf033 --- /dev/null +++ b/vendor/eluceo/ical/examples/example2.php @@ -0,0 +1,31 @@ +<?php + +// use composer autoloader +require_once __DIR__ . '/../vendor/autoload.php'; + +// set default timezone (PHP 5.4) +date_default_timezone_set('Europe/Berlin'); + +// 1. Create new calendar +$vCalendar = new \Eluceo\iCal\Component\Calendar('www.example.com'); + +// 2. Create an event +$vEvent = new \Eluceo\iCal\Component\Event(); +$vEvent->setDtStart(new \DateTime('2012-12-24')); +$vEvent->setDtEnd(new \DateTime('2012-12-24')); +$vEvent->setNoTime(true); +$vEvent->setSummary('Summary with some german "umlauten" and a backslash \\: Kinder mögen Äpfel pflücken.'); +$vEvent->setCategories(['holidays']); + +// Adding Timezone (optional) +$vEvent->setUseTimezone(true); + +// 3. Add event to calendar +$vCalendar->addComponent($vEvent); + +// 4. Set headers +header('Content-Type: text/calendar; charset=utf-8'); +header('Content-Disposition: attachment; filename="cal.ics"'); + +// 5. Output +echo $vCalendar->render(); diff --git a/vendor/eluceo/ical/examples/example3.php b/vendor/eluceo/ical/examples/example3.php new file mode 100644 index 00000000..290ff2dc --- /dev/null +++ b/vendor/eluceo/ical/examples/example3.php @@ -0,0 +1,36 @@ +<?php + +// use composer autoloader +require_once __DIR__ . '/../vendor/autoload.php'; + +// set default timezone (PHP 5.4) +date_default_timezone_set('Europe/Berlin'); + +// 1. Create new calendar +$vCalendar = new \Eluceo\iCal\Component\Calendar('www.example.com'); + +// 2. Create an event +$vEvent = new \Eluceo\iCal\Component\Event(); +$vEvent->setDtStart(new \DateTime('2012-12-31')); +$vEvent->setDtEnd(new \DateTime('2012-12-31')); +$vEvent->setNoTime(true); +$vEvent->setSummary('New Year’s Eve'); + +// Set recurrence rule +$recurrenceRule = new \Eluceo\iCal\Property\Event\RecurrenceRule(); +$recurrenceRule->setFreq(\Eluceo\iCal\Property\Event\RecurrenceRule::FREQ_YEARLY); +$recurrenceRule->setInterval(1); +$vEvent->setRecurrenceRule($recurrenceRule); + +// Adding Timezone (optional) +$vEvent->setUseTimezone(true); + +// 3. Add event to calendar +$vCalendar->addComponent($vEvent); + +// 4. Set headers +header('Content-Type: text/calendar; charset=utf-8'); +header('Content-Disposition: attachment; filename="cal.ics"'); + +// 5. Output +echo $vCalendar->render(); diff --git a/vendor/eluceo/ical/examples/example4.php b/vendor/eluceo/ical/examples/example4.php new file mode 100644 index 00000000..7b87144c --- /dev/null +++ b/vendor/eluceo/ical/examples/example4.php @@ -0,0 +1,35 @@ +<?php + +// use composer autoloader +require_once __DIR__ . '/../vendor/autoload.php'; + +// set default timezone (PHP 5.4) +date_default_timezone_set('Europe/Berlin'); + +// 1. Create new calendar +$vCalendar = new \Eluceo\iCal\Component\Calendar('www.example.com'); + +// 2. Create an event +$vEvent = new \Eluceo\iCal\Component\Event(); +$vEvent->setDtStart(new \DateTime('2012-11-11 13:00:00')); +$vEvent->setDtEnd(new \DateTime('2012-11-11 14:30:00')); +$vEvent->setSummary('Weekly lunch with Markus'); + +// Set recurrence rule +$recurrenceRule = new \Eluceo\iCal\Property\Event\RecurrenceRule(); +$recurrenceRule->setFreq(\Eluceo\iCal\Property\Event\RecurrenceRule::FREQ_WEEKLY); +$recurrenceRule->setInterval(1); +$vEvent->setRecurrenceRule($recurrenceRule); + +// Adding Timezone (optional) +$vEvent->setUseTimezone(true); + +// 3. Add event to calendar +$vCalendar->addComponent($vEvent); + +// 4. Set headers +header('Content-Type: text/calendar; charset=utf-8'); +header('Content-Disposition: attachment; filename="cal.ics"'); + +// 5. Output +echo $vCalendar->render(); diff --git a/vendor/eluceo/ical/examples/example5.php b/vendor/eluceo/ical/examples/example5.php new file mode 100644 index 00000000..a7da6e27 --- /dev/null +++ b/vendor/eluceo/ical/examples/example5.php @@ -0,0 +1,66 @@ +<?php + +/** + * example to show how to create an ICal calendar which + * provides a full timezone definition + */ + +// use composer autoloader +require_once __DIR__ . '/../vendor/autoload.php'; + +// set default timezone (PHP 5.4) +$tz = 'Europe/Berlin'; +$dtz = new \DateTimeZone($tz); +date_default_timezone_set($tz); + +// 1. Create new calendar +$vCalendar = new \Eluceo\iCal\Component\Calendar('www.example.com'); + +// 2. Create timezone rule object for Daylight Saving Time +$vTimezoneRuleDst = new \Eluceo\iCal\Component\TimezoneRule(\Eluceo\iCal\Component\TimezoneRule::TYPE_DAYLIGHT); +$vTimezoneRuleDst->setTzName('CEST'); +$vTimezoneRuleDst->setDtStart(new \DateTime('1981-03-29 02:00:00', $dtz)); +$vTimezoneRuleDst->setTzOffsetFrom('+0100'); +$vTimezoneRuleDst->setTzOffsetTo('+0200'); +$dstRecurrenceRule = new \Eluceo\iCal\Property\Event\RecurrenceRule(); +$dstRecurrenceRule->setFreq(\Eluceo\iCal\Property\Event\RecurrenceRule::FREQ_YEARLY); +$dstRecurrenceRule->setByMonth(3); +$dstRecurrenceRule->setByDay('-1SU'); +$vTimezoneRuleDst->setRecurrenceRule($dstRecurrenceRule); + +// 3. Create timezone rule object for Standard Time +$vTimezoneRuleStd = new \Eluceo\iCal\Component\TimezoneRule(\Eluceo\iCal\Component\TimezoneRule::TYPE_STANDARD); +$vTimezoneRuleStd->setTzName('CET'); +$vTimezoneRuleStd->setDtStart(new \DateTime('1996-10-27 03:00:00', $dtz)); +$vTimezoneRuleStd->setTzOffsetFrom('+0200'); +$vTimezoneRuleStd->setTzOffsetTo('+0100'); +$stdRecurrenceRule = new \Eluceo\iCal\Property\Event\RecurrenceRule(); +$stdRecurrenceRule->setFreq(\Eluceo\iCal\Property\Event\RecurrenceRule::FREQ_YEARLY); +$stdRecurrenceRule->setByMonth(10); +$stdRecurrenceRule->setByDay('-1SU'); +$vTimezoneRuleStd->setRecurrenceRule($stdRecurrenceRule); + +// 4. Create timezone definition and add rules +$vTimezone = new \Eluceo\iCal\Component\Timezone($tz); +$vTimezone->addComponent($vTimezoneRuleDst); +$vTimezone->addComponent($vTimezoneRuleStd); +$vCalendar->setTimezone($vTimezone); + +// 5. Create an event +$vEvent = new \Eluceo\iCal\Component\Event(); +$vEvent->setDtStart(new \DateTime('2012-12-24', $dtz)); +$vEvent->setDtEnd(new \DateTime('2012-12-24', $dtz)); +$vEvent->setSummary('Summary with some german "umlauten" and a backslash \\: Kinder mögen Äpfel pflücken.'); + +// 6. Adding Timezone +$vEvent->setUseTimezone(true); + +// 7. Add event to calendar +$vCalendar->addComponent($vEvent); + +// 8. Set headers +header('Content-Type: text/calendar; charset=utf-8'); +header('Content-Disposition: attachment; filename="cal.ics"'); + +// 9. Output +echo $vCalendar->render(); diff --git a/vendor/eluceo/ical/examples/example6.php b/vendor/eluceo/ical/examples/example6.php new file mode 100644 index 00000000..10400993 --- /dev/null +++ b/vendor/eluceo/ical/examples/example6.php @@ -0,0 +1,30 @@ +<?php + +// use composer autoloader +require_once __DIR__ . '/../vendor/autoload.php'; + +// set default timezone (PHP 5.4) +date_default_timezone_set('Europe/Berlin'); + +// 1. Create new calendar +$vCalendar = new \Eluceo\iCal\Component\Calendar('www.example.com'); + +// 2. Create an event +$vEvent = new \Eluceo\iCal\Component\Event(); +$vEvent->setDtStart(new \DateTime('2012-12-24')); +$vEvent->setDtEnd(new \DateTime('2012-12-24')); +$vEvent->setNoTime(true); +$vEvent->setSummary('Christmas'); + +// add some location information for apple devices +$vEvent->setLocation("Infinite Loop\nCupertino CA 95014", 'Infinite Loop', '37.332095,-122.030743'); + +// 3. Add event to calendar +$vCalendar->addComponent($vEvent); + +// 4. Set headers +header('Content-Type: text/calendar; charset=utf-8'); +header('Content-Disposition: attachment; filename="cal.ics"'); + +// 5. Output +echo $vCalendar->render(); diff --git a/vendor/eluceo/ical/examples/example7.php b/vendor/eluceo/ical/examples/example7.php new file mode 100644 index 00000000..1f8013e7 --- /dev/null +++ b/vendor/eluceo/ical/examples/example7.php @@ -0,0 +1,33 @@ +<?php + +// use composer autoloader +require_once __DIR__ . '/../vendor/autoload.php'; + +// set default timezone (PHP 5.4) +date_default_timezone_set('Europe/Berlin'); + +// 1. Create new calendar +$vCalendar = new \Eluceo\iCal\Component\Calendar('www.example.com'); + +// 2. Create an event +$vEvent = new \Eluceo\iCal\Component\Event(); +$vEvent->setDtStart(new \DateTime('2012-12-24')); +$vEvent->setDtEnd(new \DateTime('2012-12-24')); +$vEvent->setNoTime(true); +$vEvent->setSummary('Christmas'); +$vEvent->setDescription('Happy Christmas!'); +$vEvent->setDescriptionHTML('<b>Happy Christmas!</b>'); + + +// add some location information for apple devices +$vEvent->setLocation("Infinite Loop\nCupertino CA 95014", 'Infinite Loop', '37.332095,-122.030743'); + +// 3. Add event to calendar +$vCalendar->addComponent($vEvent); + +// 4. Set headers +header('Content-Type: text/calendar; charset=utf-8'); +header('Content-Disposition: attachment; filename="cal.ics"'); + +// 5. Output +echo $vCalendar->render(); diff --git a/vendor/eluceo/ical/phpunit.xml.dist b/vendor/eluceo/ical/phpunit.xml.dist new file mode 100644 index 00000000..13e18c0e --- /dev/null +++ b/vendor/eluceo/ical/phpunit.xml.dist @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<phpunit backupGlobals="false" + backupStaticAttributes="false" + colors="true" + convertErrorsToExceptions="true" + convertNoticesToExceptions="true" + convertWarningsToExceptions="true" + processIsolation="false" + stopOnFailure="false" + syntaxCheck="false" + bootstrap="./vendor/autoload.php"> + <logging> + <log type="coverage-text" target="php://stdout" showUncoveredFiles="true" showOnlySummary="true"/> + </logging> + <testsuites> + <testsuite name="eluceo iCal Test Suite"> + <directory suffix="Test.php">./tests/Eluceo/iCal/</directory> + </testsuite> + </testsuites> +</phpunit> diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Component.php b/vendor/eluceo/ical/src/Eluceo/iCal/Component.php new file mode 100644 index 00000000..76c9b2d9 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Component.php @@ -0,0 +1,172 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal; + +use Eluceo\iCal\Util\ComponentUtil; + +/** + * Abstract Calender Component. + */ +abstract class Component +{ + /** + * Array of Components. + * + * @var Component[] + */ + protected $components = array(); + + /** + * The order in which the components will be rendered during build. + * + * Not defined components will be appended at the end. + * + * @var array + */ + private $componentsBuildOrder = array('VTIMEZONE', 'DAYLIGHT', 'STANDARD'); + + /** + * The type of the concrete Component. + * + * @abstract + * + * @return string + */ + abstract public function getType(); + + /** + * Building the PropertyBag. + * + * @abstract + * + * @return PropertyBag + */ + abstract public function buildPropertyBag(); + + /** + * Adds a Component. + * + * If $key is given, the component at $key will be replaced else the component will be append. + * + * @param Component $component The Component that will be added + * @param null $key The key of the Component + */ + public function addComponent(Component $component, $key = null) + { + if (null == $key) { + $this->components[] = $component; + } else { + $this->components[$key] = $component; + } + } + + /** + * Renders an array containing the lines of the iCal file. + * + * @return array + */ + public function build() + { + $lines = array(); + + $lines[] = sprintf('BEGIN:%s', $this->getType()); + + /** @var $property Property */ + foreach ($this->buildPropertyBag() as $property) { + foreach ($property->toLines() as $l) { + $lines[] = $l; + } + } + + $this->buildComponents($lines); + + $lines[] = sprintf('END:%s', $this->getType()); + + $ret = array(); + + foreach ($lines as $line) { + foreach (ComponentUtil::fold($line) as $l) { + $ret[] = $l; + } + } + + return $ret; + } + + /** + * Renders the output. + * + * @return string + */ + public function render() + { + return implode("\r\n", $this->build()); + } + + /** + * Renders the output when treating the class as a string. + * + * @return string + */ + public function __toString() + { + return $this->render(); + } + + /** + * @param $lines + * + * @return array + */ + private function buildComponents(array &$lines) + { + $componentsByType = array(); + + /** @var $component Component */ + foreach ($this->components as $component) { + $type = $component->getType(); + if (!isset($componentsByType[$type])) { + $componentsByType[$type] = array(); + } + $componentsByType[$type][] = $component; + } + + // render ordered components + foreach ($this->componentsBuildOrder as $type) { + if (!isset($componentsByType[$type])) { + continue; + } + foreach ($componentsByType[$type] as $component) { + $this->addComponentLines($lines, $component); + } + unset($componentsByType[$type]); + } + + // render all other + foreach ($componentsByType as $components) { + foreach ($components as $component) { + $this->addComponentLines($lines, $component); + } + } + } + + /** + * @param array $lines + * @param Component $component + */ + private function addComponentLines(array &$lines, Component $component) + { + foreach ($component->build() as $l) { + $lines[] = $l; + } + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Component/Alarm.php b/vendor/eluceo/ical/src/Eluceo/iCal/Component/Alarm.php new file mode 100644 index 00000000..9e0f0c1b --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Component/Alarm.php @@ -0,0 +1,151 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Component; + +use Eluceo\iCal\Component; +use Eluceo\iCal\PropertyBag; +use Eluceo\iCal\Property; + +/** + * Implementation of the VALARM component. + */ +class Alarm extends Component +{ + /** + * Alarm ACTION property. + * + * According to RFC 5545: 3.8.6.1. Action + * + * @link http://tools.ietf.org/html/rfc5545#section-3.8.6.1 + */ + const ACTION_AUDIO = 'AUDIO'; + const ACTION_DISPLAY = 'DISPLAY'; + const ACTION_EMAIL = 'EMAIL'; + + protected $action; + protected $repeat; + protected $duration; + protected $description; + protected $attendee; + protected $trigger; + + public function getType() + { + return 'VALARM'; + } + + public function getAction() + { + return $this->action; + } + + public function getRepeat() + { + return $this->repeat; + } + + public function getDuration() + { + return $this->duration; + } + + public function getDescription() + { + return $this->description; + } + + public function getAttendee() + { + return $this->attendee; + } + + public function getTrigger() + { + return $this->trigger; + } + + public function setAction($action) + { + $this->action = $action; + + return $this; + } + + public function setRepeat($repeat) + { + $this->repeat = $repeat; + + return $this; + } + + public function setDuration($duration) + { + $this->duration = $duration; + + return $this; + } + + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + public function setAttendee($attendee) + { + $this->attendee = $attendee; + + return $this; + } + + public function setTrigger($trigger) + { + $this->trigger = $trigger; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function buildPropertyBag() + { + $propertyBag = new PropertyBag(); + + if (null != $this->trigger) { + $propertyBag->set('TRIGGER', $this->trigger); + } + + if (null != $this->action) { + $propertyBag->set('ACTION', $this->action); + } + + if (null != $this->repeat) { + $propertyBag->set('REPEAT', $this->repeat); + } + + if (null != $this->duration) { + $propertyBag->set('DURATION', $this->duration); + } + + if (null != $this->description) { + $propertyBag->set('DESCRIPTION', $this->description); + } + + if (null != $this->attendee) { + $propertyBag->set('ATTENDEE', $this->attendee); + } + + return $propertyBag; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Component/Calendar.php b/vendor/eluceo/ical/src/Eluceo/iCal/Component/Calendar.php new file mode 100644 index 00000000..db3dd98d --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Component/Calendar.php @@ -0,0 +1,323 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Component; + +use Eluceo\iCal\Component; +use Eluceo\iCal\PropertyBag; + +class Calendar extends Component +{ + /** + * Methods for calendar components. + * + * According to RFP 5545: 3.7.2. Method + * + * @link http://tools.ietf.org/html/rfc5545#section-3.7.2 + * + * And then according to RFC 2446: 3 APPLICATION PROTOCOL ELEMENTS + * @link https://www.ietf.org/rfc/rfc2446.txt + */ + const METHOD_PUBLISH = 'PUBLISH'; + const METHOD_REQUEST = 'REQUEST'; + const METHOD_REPLY = 'REPLY'; + const METHOD_ADD = 'ADD'; + const METHOD_CANCEL = 'CANCEL'; + const METHOD_REFRESH = 'REFRESH'; + const METHOD_COUNTER = 'COUNTER'; + const METHOD_DECLINECOUNTER = 'DECLINECOUNTER'; + + /** + * This property defines the calendar scale used for the calendar information specified in the iCalendar object. + * + * According to RFC 5545: 3.7.1. Calendar Scale + * + * @link http://tools.ietf.org/html/rfc5545#section-3.7 + */ + const CALSCALE_GREGORIAN = 'GREGORIAN'; + + /** + * The Product Identifier. + * + * According to RFC 2445: 4.7.3 Product Identifier + * + * This property specifies the identifier for the product that created the Calendar object. + * + * @link http://www.ietf.org/rfc/rfc2445.txt + * + * @var string + */ + protected $prodId = null; + protected $method = null; + protected $name = null; + protected $description = null; + protected $timezone = null; + + /** + * This property defines the calendar scale used for the + * calendar information specified in the iCalendar object. + * + * Also identifies the calendar type of a non-Gregorian recurring appointment. + * + * @var string + * + * @see http://tools.ietf.org/html/rfc5545#section-3.7 + * @see http://msdn.microsoft.com/en-us/library/ee237520(v=exchg.80).aspx + */ + protected $calendarScale = null; + + /** + * Specifies whether or not the iCalendar file only contains one appointment. + * + * @var bool + * + * @see http://msdn.microsoft.com/en-us/library/ee203486(v=exchg.80).aspx + */ + protected $forceInspectOrOpen = false; + + /** + * Specifies a globally unique identifier for the calendar. + * + * @var string + * + * @see http://msdn.microsoft.com/en-us/library/ee179588(v=exchg.80).aspx + */ + protected $calId = null; + + /** + * Specifies a suggested iCalendar file download frequency for clients and + * servers with sync capabilities. + * + * @var string + * + * @see http://msdn.microsoft.com/en-us/library/ee178699(v=exchg.80).aspx + */ + protected $publishedTTL = 'P1W'; + + /** + * Specifies a color for the calendar in calendar for Apple/Outlook. + * + * @var string + * + * @see http://msdn.microsoft.com/en-us/library/ee179588(v=exchg.80).aspx + */ + protected $calendarColor = null; + + public function __construct($prodId) + { + if (empty($prodId)) { + throw new \UnexpectedValueException('PRODID cannot be empty'); + } + + $this->prodId = $prodId; + } + + /** + * {@inheritdoc} + */ + public function getType() + { + return 'VCALENDAR'; + } + + /** + * @param $method + * + * @return $this + */ + public function setMethod($method) + { + $this->method = $method; + + return $this; + } + + /** + * @param $name + * + * @return $this + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * @param $description + * + * @return $this + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * @param $timezone + * + * @return $this + */ + public function setTimezone($timezone) + { + $this->timezone = $timezone; + + return $this; + } + + /** + * @param $calendarColor + * + * @return $this + */ + public function setCalendarColor($calendarColor) + { + $this->calendarColor = $calendarColor; + + return $this; + } + + /** + * @param $calendarScale + * + * @return $this + */ + public function setCalendarScale($calendarScale) + { + $this->calendarScale = $calendarScale; + + return $this; + } + + /** + * @param bool $forceInspectOrOpen + * + * @return $this + */ + public function setForceInspectOrOpen($forceInspectOrOpen) + { + $this->forceInspectOrOpen = $forceInspectOrOpen; + + return $this; + } + + /** + * @param string $calId + * + * @return $this + */ + public function setCalId($calId) + { + $this->calId = $calId; + + return $this; + } + + /** + * @param string $ttl + * + * @return $this + */ + public function setPublishedTTL($ttl) + { + $this->publishedTTL = $ttl; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function buildPropertyBag() + { + $propertyBag = new PropertyBag(); + $propertyBag->set('VERSION', '2.0'); + $propertyBag->set('PRODID', $this->prodId); + + if ($this->method) { + $propertyBag->set('METHOD', $this->method); + } + + if ($this->calendarColor) { + $propertyBag->set('X-APPLE-CALENDAR-COLOR', $this->calendarColor); + $propertyBag->set('X-OUTLOOK-COLOR', $this->calendarColor); + $propertyBag->set('X-FUNAMBOL-COLOR', $this->calendarColor); + } + + if ($this->calendarScale) { + $propertyBag->set('CALSCALE', $this->calendarScale); + $propertyBag->set('X-MICROSOFT-CALSCALE', $this->calendarScale); + } + + if ($this->name) { + $propertyBag->set('X-WR-CALNAME', $this->name); + } + + if ($this->description) { + $propertyBag->set('X-WR-CALDESC', $this->description); + } + + if ($this->timezone) { + if ($this->timezone instanceof Timezone) { + $propertyBag->set('X-WR-TIMEZONE', $this->timezone->getZoneIdentifier()); + $this->addComponent($this->timezone); + } else { + $propertyBag->set('X-WR-TIMEZONE', $this->timezone); + $this->addComponent(new Timezone($this->timezone)); + } + } + + if ($this->forceInspectOrOpen) { + $propertyBag->set('X-MS-OLK-FORCEINSPECTOROPEN', $this->forceInspectOrOpen); + } + + if ($this->calId) { + $propertyBag->set('X-WR-RELCALID', $this->calId); + } + + if ($this->publishedTTL) { + $propertyBag->set('X-PUBLISHED-TTL', $this->publishedTTL); + } + + return $propertyBag; + } + + /** + * Adds an Event to the Calendar. + * + * Wrapper for addComponent() + * + * @see Eluceo\iCal::addComponent + * @deprecated Please, use public method addComponent() from abstract Component class + * + * @param Event $event + */ + public function addEvent(Event $event) + { + $this->addComponent($event); + } + + /** + * @return null|string + */ + public function getProdId() + { + return $this->prodId; + } + + public function getMethod() + { + return $this->method; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Component/Event.php b/vendor/eluceo/ical/src/Eluceo/iCal/Component/Event.php new file mode 100644 index 00000000..e93d506c --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Component/Event.php @@ -0,0 +1,783 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Component; + +use Eluceo\iCal\Component; +use Eluceo\iCal\Property; +use Eluceo\iCal\Property\DateTimeProperty; +use Eluceo\iCal\Property\Event\Attendees; +use Eluceo\iCal\Property\Event\Organizer; +use Eluceo\iCal\Property\Event\RecurrenceRule; +use Eluceo\iCal\Property\Event\Description; +use Eluceo\iCal\PropertyBag; +use Eluceo\iCal\Property\Event\RecurrenceId; +use Eluceo\iCal\Property\DateTimesProperty; + +/** + * Implementation of the EVENT component. + */ +class Event extends Component +{ + const TIME_TRANSPARENCY_OPAQUE = 'OPAQUE'; + const TIME_TRANSPARENCY_TRANSPARENT = 'TRANSPARENT'; + + const STATUS_TENTATIVE = 'TENTATIVE'; + const STATUS_CONFIRMED = 'CONFIRMED'; + const STATUS_CANCELLED = 'CANCELLED'; + + /** + * @var string + */ + protected $uniqueId; + + /** + * The property indicates the date/time that the instance of + * the iCalendar object was created. + * + * The value MUST be specified in the UTC time format. + * + * @var \DateTime + */ + protected $dtStamp; + + /** + * @var \DateTime + */ + protected $dtStart; + + /** + * Preferentially chosen over the duration if both are set. + * + * @var \DateTime + */ + protected $dtEnd; + + /** + * @var \DateInterval + */ + protected $duration; + + /** + * @var bool + */ + protected $noTime = false; + + /** + * @var string + */ + protected $url; + + /** + * @var string + */ + protected $location; + + /** + * @var string + */ + protected $locationTitle; + + /** + * @var string + */ + protected $locationGeo; + + /** + * @var string + */ + protected $summary; + + /** + * @var Organizer + */ + protected $organizer; + + /** + * @see http://www.ietf.org/rfc/rfc2445.txt 4.8.2.7 Time Transparency + * + * @var string + */ + protected $transparency = self::TIME_TRANSPARENCY_OPAQUE; + + /** + * If set to true the timezone will be added to the event. + * + * @var bool + */ + protected $useTimezone = false; + + /** + * @var int + */ + protected $sequence = 0; + + /** + * @var Attendees + */ + protected $attendees; + + /** + * @var string + */ + protected $description; + + /** + * @var string + */ + protected $descriptionHTML; + + /** + * @var string + */ + protected $status; + + /** + * @var RecurrenceRule + */ + protected $recurrenceRule; + + /** + * This property specifies the date and time that the calendar + * information was created. + * + * The value MUST be specified in the UTC time format. + * + * @var \DateTime + */ + protected $created; + + /** + * The property specifies the date and time that the information + * associated with the calendar component was last revised. + * + * The value MUST be specified in the UTC time format. + * + * @var \DateTime + */ + protected $modified; + + /** + * Indicates if the UTC time should be used or not. + * + * @var bool + */ + protected $useUtc = true; + + /** + * @var bool + */ + protected $cancelled; + + /** + * This property is used to specify categories or subtypes + * of the calendar component. The categories are useful in searching + * for a calendar component of a particular type and category. + * + * @see https://tools.ietf.org/html/rfc5545#section-3.8.1.2 + * + * @var array + */ + protected $categories; + + /** + * https://tools.ietf.org/html/rfc5545#section-3.8.1.3. + * + * @var bool + */ + protected $isPrivate = false; + + /** + * Dates to be excluded from a series of events. + * + * @var \DateTime[] + */ + protected $exDates = array(); + + /** + * @var RecurrenceId + */ + protected $recurrenceId; + + public function __construct($uniqueId = null) + { + if (null == $uniqueId) { + $uniqueId = uniqid(); + } + + $this->uniqueId = $uniqueId; + } + + /** + * {@inheritdoc} + */ + public function getType() + { + return 'VEVENT'; + } + + /** + * {@inheritdoc} + */ + public function buildPropertyBag() + { + $propertyBag = new PropertyBag(); + + // mandatory information + $propertyBag->set('UID', $this->uniqueId); + + $propertyBag->add(new DateTimeProperty('DTSTART', $this->dtStart, $this->noTime, $this->useTimezone, $this->useUtc)); + $propertyBag->set('SEQUENCE', $this->sequence); + $propertyBag->set('TRANSP', $this->transparency); + + if ($this->status) { + $propertyBag->set('STATUS', $this->status); + } + + // An event can have a 'dtend' or 'duration', but not both. + if (null != $this->dtEnd) { + $propertyBag->add(new DateTimeProperty('DTEND', $this->dtEnd, $this->noTime, $this->useTimezone, $this->useUtc)); + } elseif (null != $this->duration) { + $propertyBag->set('DURATION', $this->duration->format('P%dDT%hH%iM%sS')); + } + + // optional information + if (null != $this->url) { + $propertyBag->set('URL', $this->url); + } + + if (null != $this->location) { + $propertyBag->set('LOCATION', $this->location); + + if (null != $this->locationGeo) { + $propertyBag->add( + new Property( + 'X-APPLE-STRUCTURED-LOCATION', + 'geo:' . $this->locationGeo, + array( + 'VALUE' => 'URI', + 'X-ADDRESS' => $this->location, + 'X-APPLE-RADIUS' => 49, + 'X-TITLE' => $this->locationTitle, + ) + ) + ); + $propertyBag->set('GEO', str_replace(',', ';', $this->locationGeo)); + } + } + + if (null != $this->summary) { + $propertyBag->set('SUMMARY', $this->summary); + } + + if (null != $this->attendees) { + $propertyBag->add($this->attendees); + } + + $propertyBag->set('CLASS', $this->isPrivate ? 'PRIVATE' : 'PUBLIC'); + + if (null != $this->description) { + $propertyBag->set('DESCRIPTION', new Description($this->description)); + } + + if (null != $this->descriptionHTML) { + $propertyBag->add( + new Property( + 'X-ALT-DESC', + $this->descriptionHTML, + array( + 'FMTTYPE' => 'text/html', + ) + ) + ); + } + + if (null != $this->recurrenceRule) { + $propertyBag->set('RRULE', $this->recurrenceRule); + } + + if (null != $this->recurrenceId) { + $this->recurrenceId->applyTimeSettings($this->noTime, $this->useTimezone, $this->useUtc); + $propertyBag->add($this->recurrenceId); + } + + if (!empty($this->exDates)) { + $propertyBag->add(new DateTimesProperty('EXDATE', $this->exDates, $this->noTime, $this->useTimezone, $this->useUtc)); + } + + if ($this->cancelled) { + $propertyBag->set('STATUS', 'CANCELLED'); + } + + if (null != $this->organizer) { + $propertyBag->add($this->organizer); + } + + if ($this->noTime) { + $propertyBag->set('X-MICROSOFT-CDO-ALLDAYEVENT', 'TRUE'); + } + + if (null != $this->categories) { + $propertyBag->set('CATEGORIES', $this->categories); + } + + $propertyBag->add( + new DateTimeProperty('DTSTAMP', $this->dtStamp ?: new \DateTime(), false, false, true) + ); + + if ($this->created) { + $propertyBag->add(new DateTimeProperty('CREATED', $this->created, false, false, true)); + } + + if ($this->modified) { + $propertyBag->add(new DateTimeProperty('LAST-MODIFIED', $this->modified, false, false, true)); + } + + return $propertyBag; + } + + /** + * @param $dtEnd + * + * @return $this + */ + public function setDtEnd($dtEnd) + { + $this->dtEnd = $dtEnd; + + return $this; + } + + public function getDtEnd() + { + return $this->dtEnd; + } + + public function setDtStart($dtStart) + { + $this->dtStart = $dtStart; + + return $this; + } + + /** + * @param $dtStamp + * + * @return $this + */ + public function setDtStamp($dtStamp) + { + $this->dtStamp = $dtStamp; + + return $this; + } + + /** + * @param $duration + * + * @return $this + */ + public function setDuration($duration) + { + $this->duration = $duration; + + return $this; + } + + /** + * @param $location + * @param string $title + * @param null $geo + * + * @return $this + */ + public function setLocation($location, $title = '', $geo = null) + { + $this->location = $location; + $this->locationTitle = $title; + $this->locationGeo = $geo; + + return $this; + } + + /** + * @param $noTime + * + * @return $this + */ + public function setNoTime($noTime) + { + $this->noTime = $noTime; + + return $this; + } + + /** + * @param int $sequence + * + * @return $this + */ + public function setSequence($sequence) + { + $this->sequence = $sequence; + + return $this; + } + + /** + * @return int + */ + public function getSequence() + { + return $this->sequence; + } + + /** + * @param Organizer $organizer + * + * @return $this + */ + public function setOrganizer(Organizer $organizer) + { + $this->organizer = $organizer; + + return $this; + } + + /** + * @param $summary + * + * @return $this + */ + public function setSummary($summary) + { + $this->summary = $summary; + + return $this; + } + + /** + * @param $uniqueId + * + * @return $this + */ + public function setUniqueId($uniqueId) + { + $this->uniqueId = $uniqueId; + + return $this; + } + + /** + * @return string + */ + public function getUniqueId() + { + return $this->uniqueId; + } + + /** + * @param $url + * + * @return $this + */ + public function setUrl($url) + { + $this->url = $url; + + return $this; + } + + /** + * @param $useTimezone + * + * @return $this + */ + public function setUseTimezone($useTimezone) + { + $this->useTimezone = $useTimezone; + + return $this; + } + + /** + * @return bool + */ + public function getUseTimezone() + { + return $this->useTimezone; + } + + /** + * @param Attendees $attendees + * + * @return $this + */ + public function setAttendees(Attendees $attendees) + { + $this->attendees = $attendees; + + return $this; + } + + /** + * @param string $attendee + * @param array $params + * + * @return $this + */ + public function addAttendee($attendee, $params = array()) + { + if (!isset($this->attendees)) { + $this->attendees = new Attendees(); + } + $this->attendees->add($attendee, $params); + + return $this; + } + + /** + * @return Attendees + */ + public function getAttendees() + { + return $this->attendees; + } + + /** + * @param $description + * + * @return $this + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * @param $descriptionHTML + * + * @return $this + */ + public function setDescriptionHTML($descriptionHTML) + { + $this->descriptionHTML = $descriptionHTML; + + return $this; + } + + /** + * @param bool $useUtc + * + * @return $this + */ + public function setUseUtc($useUtc = true) + { + $this->useUtc = $useUtc; + + return $this; + } + + /** + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * @return string + */ + public function getDescriptionHTML() + { + return $this->descriptionHTML; + } + + /** + * @param $status + * + * @return $this + */ + public function setCancelled($status) + { + $this->cancelled = (bool) $status; + + return $this; + } + + /** + * @param $transparency + * + * @return $this + * + * @throws \InvalidArgumentException + */ + public function setTimeTransparency($transparency) + { + $transparency = strtoupper($transparency); + if ($transparency === self::TIME_TRANSPARENCY_OPAQUE + || $transparency === self::TIME_TRANSPARENCY_TRANSPARENT + ) { + $this->transparency = $transparency; + } else { + throw new \InvalidArgumentException('Invalid value for transparancy'); + } + + return $this; + } + + /** + * @param $status + * + * @return $this + * + * @throws \InvalidArgumentException + */ + public function setStatus($status) + { + $status = strtoupper($status); + if ($status == self::STATUS_CANCELLED + || $status == self::STATUS_CONFIRMED + || $status == self::STATUS_TENTATIVE + ) { + $this->status = $status; + } else { + throw new \InvalidArgumentException('Invalid value for status'); + } + + return $this; + } + + /** + * @param RecurrenceRule $recurrenceRule + * + * @return $this + */ + public function setRecurrenceRule(RecurrenceRule $recurrenceRule) + { + $this->recurrenceRule = $recurrenceRule; + + return $this; + } + + /** + * @return RecurrenceRule + */ + public function getRecurrenceRule() + { + return $this->recurrenceRule; + } + + /** + * @param $dtStamp + * + * @return $this + */ + public function setCreated($dtStamp) + { + $this->created = $dtStamp; + + return $this; + } + + /** + * @param $dtStamp + * + * @return $this + */ + public function setModified($dtStamp) + { + $this->modified = $dtStamp; + + return $this; + } + + /** + * @param $categories + * + * @return $this + */ + public function setCategories($categories) + { + $this->categories = $categories; + + return $this; + } + + /** + * Sets the event privacy. + * + * @param bool $flag + * + * @return $this + */ + public function setIsPrivate($flag) + { + $this->isPrivate = (bool) $flag; + + return $this; + } + + /** + * @param \DateTime $dateTime + * + * @return \Eluceo\iCal\Component\Event + */ + public function addExDate(\DateTime $dateTime) + { + $this->exDates[] = $dateTime; + + return $this; + } + + /** + * @return \DateTime[] + */ + public function getExDates() + { + return $this->exDates; + } + + /** + * @param \DateTime[] + * + * @return \Eluceo\iCal\Component\Event + */ + public function setExDates(array $exDates) + { + $this->exDates = $exDates; + + return $this; + } + + /** + * @return \Eluceo\iCal\Property\Event\RecurrenceId + */ + public function getRecurrenceId() + { + return $this->recurrenceId; + } + + /** + * @param RecurrenceId $recurrenceId + * + * @return \Eluceo\iCal\Component\Event + */ + public function setRecurrenceId(RecurrenceId $recurrenceId) + { + $this->recurrenceId = $recurrenceId; + + return $this; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Component/Timezone.php b/vendor/eluceo/ical/src/Eluceo/iCal/Component/Timezone.php new file mode 100644 index 00000000..c820d75b --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Component/Timezone.php @@ -0,0 +1,57 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Component; + +use Eluceo\iCal\Component; +use Eluceo\iCal\PropertyBag; + +/** + * Implementation of the TIMEZONE component. + */ +class Timezone extends Component +{ + /** + * @var string + */ + protected $timezone; + + public function __construct($timezone) + { + $this->timezone = $timezone; + } + + /** + * {@inheritdoc} + */ + public function getType() + { + return 'VTIMEZONE'; + } + + /** + * {@inheritdoc} + */ + public function buildPropertyBag() + { + $propertyBag = new PropertyBag(); + + $propertyBag->set('TZID', $this->timezone); + $propertyBag->set('X-LIC-LOCATION', $this->timezone); + + return $propertyBag; + } + + public function getZoneIdentifier() + { + return $this->timezone; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Component/TimezoneRule.php b/vendor/eluceo/ical/src/Eluceo/iCal/Component/TimezoneRule.php new file mode 100644 index 00000000..97da4911 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Component/TimezoneRule.php @@ -0,0 +1,215 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Component; + +use Eluceo\iCal\Component; +use Eluceo\iCal\PropertyBag; +use Eluceo\iCal\Property\Event\RecurrenceRule; + +/** + * Implementation of Standard Time and Daylight Saving Time observances (or rules) + * which define the TIMEZONE component. + */ +class TimezoneRule extends Component +{ + const TYPE_DAYLIGHT = 'DAYLIGHT'; + const TYPE_STANDARD = 'STANDARD'; + + /** + * @var string + */ + protected $type; + + /** + * @var string + */ + protected $tzOffsetFrom; + + /** + * @var string + */ + protected $tzOffsetTo; + + /** + * @var string + */ + protected $tzName; + + /** + * @var \DateTime + */ + protected $dtStart; + + /** + * @var RecurrenceRule + */ + protected $recurrenceRule; + + /** + * create new Timezone Rule object by giving a rule type identifier. + * + * @param string $ruleType one of DAYLIGHT or STANDARD + * + * @throws \InvalidArgumentException + */ + public function __construct($ruleType) + { + $ruleType = strtoupper($ruleType); + if ($ruleType === self::TYPE_DAYLIGHT || $ruleType === self::TYPE_STANDARD) { + $this->type = $ruleType; + } else { + throw new \InvalidArgumentException('Invalid value for timezone rule type'); + } + } + + /** + * {@inheritdoc} + */ + public function buildPropertyBag() + { + $propertyBag = new PropertyBag(); + + if ($this->getTzName()) { + $propertyBag->set('TZNAME', $this->getTzName()); + } + + if ($this->getTzOffsetFrom()) { + $propertyBag->set('TZOFFSETFROM', $this->getTzOffsetFrom()); + } + + if ($this->getTzOffsetTo()) { + $propertyBag->set('TZOFFSETTO', $this->getTzOffsetTo()); + } + + if ($this->getDtStart()) { + $propertyBag->set('DTSTART', $this->getDtStart()); + } + + if ($this->recurrenceRule) { + $propertyBag->set('RRULE', $this->recurrenceRule); + } + + return $propertyBag; + } + + /** + * @param $offset + * + * @return $this + */ + public function setTzOffsetFrom($offset) + { + $this->tzOffsetFrom = $offset; + + return $this; + } + + /** + * @param $offset + * + * @return $this + */ + public function setTzOffsetTo($offset) + { + $this->tzOffsetTo = $offset; + + return $this; + } + + /** + * @param $name + * + * @return $this + */ + public function setTzName($name) + { + $this->tzName = $name; + + return $this; + } + + /** + * @param \DateTime $dtStart + * + * @return $this + */ + public function setDtStart(\DateTime $dtStart) + { + $this->dtStart = $dtStart; + + return $this; + } + + /** + * @param RecurrenceRule $recurrenceRule + * + * @return $this + */ + public function setRecurrenceRule(RecurrenceRule $recurrenceRule) + { + $this->recurrenceRule = $recurrenceRule; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getType() + { + return $this->type; + } + + /** + * @return string + */ + public function getTzOffsetFrom() + { + return $this->tzOffsetFrom; + } + + /** + * @return string + */ + public function getTzOffsetTo() + { + return $this->tzOffsetTo; + } + + /** + * @return string + */ + public function getTzName() + { + return $this->tzName; + } + + /** + * @return RecurrenceRule + */ + public function getRecurrenceRule() + { + return $this->recurrenceRule; + } + + /** + * @return mixed return string representation of start date or NULL if no date was given + */ + public function getDtStart() + { + if ($this->dtStart) { + return $this->dtStart->format('Ymd\THis'); + } + + return; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/ParameterBag.php b/vendor/eluceo/ical/src/Eluceo/iCal/ParameterBag.php new file mode 100644 index 00000000..9b0c24a1 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/ParameterBag.php @@ -0,0 +1,108 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal; + +class ParameterBag +{ + /** + * The params. + * + * @var array + */ + protected $params; + + public function __construct($params = array()) + { + $this->params = $params; + } + + /** + * @param string $name + * @param mixed $value + */ + public function setParam($name, $value) + { + $this->params[$name] = $value; + } + + /** + * @param $name + */ + public function getParam($name) + { + if (array_key_exists($name, $this->params)) { + return $this->params[$name]; + } + } + + /** + * Checks if there are any params. + * + * @return bool + */ + public function hasParams() + { + return count($this->params) > 0; + } + + /** + * @return string + */ + public function toString() + { + $line = ''; + foreach ($this->params as $param => $paramValues) { + if (!is_array($paramValues)) { + $paramValues = array($paramValues); + } + foreach ($paramValues as $k => $v) { + $paramValues[$k] = $this->escapeParamValue($v); + } + + if ('' != $line) { + $line .= ';'; + } + + $line .= $param . '=' . implode(',', $paramValues); + } + + return $line; + } + + /** + * Returns an escaped string for a param value. + * + * @param string $value + * + * @return string + */ + public function escapeParamValue($value) + { + $count = 0; + $value = str_replace('\\', '\\\\', $value); + $value = str_replace('"', '\"', $value, $count); + $value = str_replace("\n", '\\n', $value); + if (false !== strpos($value, ';') || false !== strpos($value, ',') || false !== strpos($value, ':') || $count) { + $value = '"' . $value . '"'; + } + + return $value; + } + + /** + * @return string + */ + public function __toString() + { + return $this->toString(); + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property.php new file mode 100644 index 00000000..1bf2c3f0 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property.php @@ -0,0 +1,148 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal; + +use Eluceo\iCal\Property\ArrayValue; +use Eluceo\iCal\Property\StringValue; +use Eluceo\iCal\Property\ValueInterface; + +/** + * The Property Class represents a property as defined in RFC 2445. + * + * The content of a line (unfolded) will be rendered in this class + */ +class Property +{ + /** + * The value of the Property. + * + * @var ValueInterface + */ + protected $value; + + /** + * The params of the Property. + * + * @var ParameterBag + */ + protected $parameterBag; + + /** + * @var string + */ + protected $name; + + /** + * @param $name + * @param $value + * @param array $params + */ + public function __construct($name, $value, $params = array()) + { + $this->name = $name; + $this->setValue($value); + $this->parameterBag = new ParameterBag($params); + } + + /** + * Renders an unfolded line. + * + * @return string + */ + public function toLine() + { + // Property-name + $line = $this->getName(); + + // Adding params + //@todo added check for $this->parameterBag because doctrine/orm proxies won't execute constructor - ok? + if ($this->parameterBag && $this->parameterBag->hasParams()) { + $line .= ';' . $this->parameterBag->toString(); + } + + // Property value + $line .= ':' . $this->value->getEscapedValue(); + + return $line; + } + + /** + * Get all unfolded lines. + * + * @return array + */ + public function toLines() + { + return array($this->toLine()); + } + + /** + * @param string $name + * @param mixed $value + * + * @return $this + */ + public function setParam($name, $value) + { + $this->parameterBag->setParam($name, $value); + + return $this; + } + + /** + * @param $name + */ + public function getParam($name) + { + return $this->parameterBag->getParam($name); + } + + /** + * @param mixed $value + * + * @return $this + * + * @throws \Exception + */ + public function setValue($value) + { + if (is_scalar($value)) { + $this->value = new StringValue($value); + } elseif (is_array($value)) { + $this->value = new ArrayValue($value); + } else { + if (!$value instanceof ValueInterface) { + throw new \Exception('The value must implement the ValueInterface.'); + } else { + $this->value = $value; + } + } + + return $this; + } + + /** + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property/ArrayValue.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property/ArrayValue.php new file mode 100644 index 00000000..314787a0 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property/ArrayValue.php @@ -0,0 +1,43 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Property; + +use Eluceo\iCal\Util\PropertyValueUtil; + +class ArrayValue implements ValueInterface +{ + /** + * The value. + * + * @var array + */ + protected $values; + + public function __construct(array $values) + { + $this->values = $values; + } + + public function setValues(array $values) + { + $this->values = $values; + + return $this; + } + + public function getEscapedValue() + { + return implode(',', array_map(function ($value) { + return PropertyValueUtil::escapeValue((string) $value); + }, $this->values)); + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property/DateTimeProperty.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property/DateTimeProperty.php new file mode 100644 index 00000000..d0fd495e --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property/DateTimeProperty.php @@ -0,0 +1,38 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Property; + +use Eluceo\iCal\Property; +use Eluceo\iCal\Util\DateUtil; + +class DateTimeProperty extends Property +{ + /** + * @param string $name + * @param \DateTime $dateTime + * @param bool $noTime + * @param bool $useTimezone + * @param bool $useUtc + */ + public function __construct( + $name, + \DateTime $dateTime = null, + $noTime = false, + $useTimezone = false, + $useUtc = false + ) { + $dateString = DateUtil::getDateString($dateTime, $noTime, $useTimezone, $useUtc); + $params = DateUtil::getDefaultParams($dateTime, $noTime, $useTimezone); + + parent::__construct($name, $dateString, $params); + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property/DateTimesProperty.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property/DateTimesProperty.php new file mode 100644 index 00000000..6242f285 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property/DateTimesProperty.php @@ -0,0 +1,41 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Property; + +use Eluceo\iCal\Property; +use Eluceo\iCal\Util\DateUtil; + +class DateTimesProperty extends Property +{ + /** + * @param string $name + * @param \DateTime[] $dateTimes + * @param bool $noTime + * @param bool $useTimezone + * @param bool $useUtc + */ + public function __construct( + $name, + $dateTimes = array(), + $noTime = false, + $useTimezone = false, + $useUtc = false + ) { + $dates = array(); + foreach ($dateTimes as $dateTime) { + $dates[] = DateUtil::getDateString($dateTime, $noTime, $useTimezone, $useUtc); + } + $params = DateUtil::getDefaultParams($dateTime, $noTime, $useTimezone); + + parent::__construct($name, $dates, $params); + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Attendees.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Attendees.php new file mode 100644 index 00000000..dbb36e7d --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Attendees.php @@ -0,0 +1,102 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Property\Event; + +use Eluceo\iCal\Property; + +class Attendees extends Property +{ + /** @var Property[] */ + protected $attendees = array(); + + const PROPERTY_NAME = 'ATTENDEES'; + + public function __construct() + { + // Overwrites constructor functionality of Property + } + + /** + * @param $value + * @param array $params + * + * @return $this + */ + public function add($value, $params = array()) + { + $this->attendees[] = new Property('ATTENDEE', $value, $params); + + return $this; + } + + /** + * @param Property[] $value + * + * @return $this + */ + public function setValue($value) + { + $this->attendees = $value; + + return $this; + } + + /** + * @return Property[] + */ + public function getValue() + { + return $this->attendees; + } + + /** + * {@inheritdoc} + */ + public function toLines() + { + $lines = array(); + foreach ($this->attendees as $attendee) { + $lines[] = $attendee->toLine(); + } + + return $lines; + } + + /** + * @param string $name + * @param mixed $value + * + * @throws \BadMethodCallException + */ + public function setParam($name, $value) + { + throw new \BadMethodCallException('Cannot call setParam on Attendees Property'); + } + + /** + * @param $name + * + * @throws \BadMethodCallException + */ + public function getParam($name) + { + throw new \BadMethodCallException('Cannot call getParam on Attendees Property'); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return self::PROPERTY_NAME; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Description.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Description.php new file mode 100644 index 00000000..b0dbb3c1 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Description.php @@ -0,0 +1,66 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Property\Event; + +use Eluceo\iCal\Property\ValueInterface; +use Eluceo\iCal\Util\PropertyValueUtil; + +/** + * Class Description + * Alows new line charectars to be in the description. + */ +class Description implements ValueInterface +{ + /** + * The value. + * + * @var string + */ + protected $value; + + public function __construct($value) + { + $this->value = $value; + } + + /** + * Return the value of the Property as an escaped string. + * + * Escape values as per RFC 2445. See http://www.kanzaki.com/docs/ical/text.html + * + * @return string + */ + public function getEscapedValue() + { + return PropertyValueUtil::escapeValue((string) $this->value); + } + + /** + * @param string $value + * + * @return $this + */ + public function setValue($value) + { + $this->value = $value; + + return $this; + } + + /** + * @return string + */ + public function getValue() + { + return $this->value; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Organizer.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Organizer.php new file mode 100644 index 00000000..a91a75d2 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/Organizer.php @@ -0,0 +1,39 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Property\Event; + +use Eluceo\iCal\Property; + +/** + * Class Organizer. + */ +class Organizer extends Property +{ + const PROPERTY_NAME = 'ORGANIZER'; + + /** + * @param string $value + * @param array $params + */ + public function __construct($value, $params = array()) + { + parent::__construct(self::PROPERTY_NAME, $value, $params); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return self::PROPERTY_NAME; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceId.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceId.php new file mode 100644 index 00000000..2a684dda --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceId.php @@ -0,0 +1,130 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Property\Event; + +use Eluceo\iCal\ParameterBag; +use Eluceo\iCal\Property; +use Eluceo\iCal\Util\DateUtil; +use Eluceo\iCal\Property\ValueInterface; + +/** + * Implementation of Recurrence Id. + * + * @see http://www.ietf.org/rfc/rfc2445.txt 4.8.4.4 Recurrence ID + */ +class RecurrenceId extends Property +{ + const PROPERTY_NAME = 'RECURRENCE-ID'; + + /** + * The effective range of recurrence instances from the instance + * specified by the recurrence identifier specified by the property. + */ + const RANGE_THISANDPRIOR = 'THISANDPRIOR'; + const RANGE_THISANDFUTURE = 'THISANDFUTURE'; + + /** + * The dateTime to identify a particular instance of a recurring event which is getting modified. + * + * @var \DateTime + */ + protected $dateTime; + + /** + * Specify the effective range of recurrence instances from the instance. + * + * @var string + */ + protected $range; + + public function __construct(\DateTime $dateTime = null) + { + $this->parameterBag = new ParameterBag(); + if (isset($dateTime)) { + $this->dateTime = $dateTime; + } + } + + public function applyTimeSettings($noTime = false, $useTimezone = false, $useUtc = false) + { + $params = DateUtil::getDefaultParams($this->dateTime, $noTime, $useTimezone, $useUtc); + foreach ($params as $name => $value) { + $this->parameterBag->setParam($name, $value); + } + + if ($this->range) { + $this->parameterBag->setParam('RANGE', $this->range); + } + + $this->setValue(DateUtil::getDateString($this->dateTime, $noTime, $useTimezone, $useUtc)); + } + + /** + * @return DateTime + */ + public function getDatetime() + { + return $this->dateTime; + } + + /** + * @param \DateTime $dateTime + * + * @return \Eluceo\iCal\Property\Event\RecurrenceId + */ + public function setDatetime(\DateTime $dateTime) + { + $this->dateTime = $dateTime; + + return $this; + } + + /** + * @return string + */ + public function getRange() + { + return $this->range; + } + + /** + * @param string $range + * + * @return \Eluceo\iCal\Property\Event\RecurrenceId + */ + public function setRange($range) + { + $this->range = $range; + } + + /** + * Get all unfolded lines. + * + * @return array + */ + public function toLines() + { + if (!$this->value instanceof ValueInterface) { + throw new \Exception('The value must implement the ValueInterface. Call RecurrenceId::applyTimeSettings() before adding RecurrenceId.'); + } else { + return parent::toLines(); + } + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return self::PROPERTY_NAME; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceRule.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceRule.php new file mode 100644 index 00000000..32533449 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property/Event/RecurrenceRule.php @@ -0,0 +1,444 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Property\Event; + +use Eluceo\iCal\Property\ValueInterface; +use Eluceo\iCal\ParameterBag; +use InvalidArgumentException; + +/** + * Implementation of Recurrence Rule. + * + * @see http://www.ietf.org/rfc/rfc2445.txt 3.3.10. Recurrence Rule + */ +class RecurrenceRule implements ValueInterface +{ + const FREQ_YEARLY = 'YEARLY'; + const FREQ_MONTHLY = 'MONTHLY'; + const FREQ_WEEKLY = 'WEEKLY'; + const FREQ_DAILY = 'DAILY'; + + const WEEKDAY_SUNDAY = 'SU'; + const WEEKDAY_MONDAY = 'MO'; + const WEEKDAY_TUESDAY = 'TU'; + const WEEKDAY_WEDNESDAY = 'WE'; + const WEEKDAY_THURSDAY = 'TH'; + const WEEKDAY_FRIDAY = 'FR'; + const WEEKDAY_SATURDAY = 'SA'; + + /** + * The frequency of an Event. + * + * @var string + */ + protected $freq = self::FREQ_YEARLY; + + /** + * @var null|int + */ + protected $interval = 1; + + /** + * @var null|int + */ + protected $count = null; + + /** + * @var null|\DateTime + */ + protected $until = null; + + /** + * @var null|string + */ + protected $wkst; + + /** + * @var null|string + */ + protected $byMonth; + + /** + * @var null|string + */ + protected $byWeekNo; + + /** + * @var null|string + */ + protected $byYearDay; + + /** + * @var null|string + */ + protected $byMonthDay; + + /** + * @var null|string + */ + protected $byDay; + + /** + * @var null|string + */ + protected $byHour; + + /** + * @var null|string + */ + protected $byMinute; + + /** + * @var null|string + */ + protected $bySecond; + + /** + * Return the value of the Property as an escaped string. + * + * Escape values as per RFC 2445. See http://www.kanzaki.com/docs/ical/text.html + * + * @return string + */ + public function getEscapedValue() + { + return $this->buildParameterBag()->toString(); + } + + /** + * @return ParameterBag + */ + protected function buildParameterBag() + { + $parameterBag = new ParameterBag(); + + $parameterBag->setParam('FREQ', $this->freq); + + if (null !== $this->interval) { + $parameterBag->setParam('INTERVAL', $this->interval); + } + + if (null !== $this->count) { + $parameterBag->setParam('COUNT', $this->count); + } + + if (null != $this->until) { + $parameterBag->setParam('UNTIL', $this->until->format('Ymd\THis\Z')); + } + + if (null !== $this->wkst) { + $parameterBag->setParam('WKST', $this->wkst); + } + + if (null !== $this->byMonth) { + $parameterBag->setParam('BYMONTH', $this->byMonth); + } + + if (null !== $this->byWeekNo) { + $parameterBag->setParam('BYWEEKNO', $this->byWeekNo); + } + + if (null !== $this->byYearDay) { + $parameterBag->setParam('BYYEARDAY', $this->byYearDay); + } + + if (null !== $this->byMonthDay) { + $parameterBag->setParam('BYMONTHDAY', $this->byMonthDay); + } + + if (null !== $this->byDay) { + $parameterBag->setParam('BYDAY', $this->byDay); + } + + if (null !== $this->byHour) { + $parameterBag->setParam('BYHOUR', $this->byHour); + } + + if (null !== $this->byMinute) { + $parameterBag->setParam('BYMINUTE', $this->byMinute); + } + + if (null !== $this->bySecond) { + $parameterBag->setParam('BYSECOND', $this->bySecond); + } + + return $parameterBag; + } + + /** + * @param int|null $count + * + * @return $this + */ + public function setCount($count) + { + $this->count = $count; + + return $this; + } + + /** + * @return int|null + */ + public function getCount() + { + return $this->count; + } + + /** + * @param \DateTime|null $until + * + * @return $this + */ + public function setUntil(\DateTime $until = null) + { + $this->until = $until; + + return $this; + } + + /** + * @return \DateTime|null + */ + public function getUntil() + { + return $this->until; + } + + /** + * The FREQ rule part identifies the type of recurrence rule. This + * rule part MUST be specified in the recurrence rule. Valid values + * include. + * + * SECONDLY, to specify repeating events based on an interval of a second or more; + * MINUTELY, to specify repeating events based on an interval of a minute or more; + * HOURLY, to specify repeating events based on an interval of an hour or more; + * DAILY, to specify repeating events based on an interval of a day or more; + * WEEKLY, to specify repeating events based on an interval of a week or more; + * MONTHLY, to specify repeating events based on an interval of a month or more; + * YEARLY, to specify repeating events based on an interval of a year or more. + * + * @param string $freq + * + * @return $this + * + * @throws \InvalidArgumentException + */ + public function setFreq($freq) + { + if (self::FREQ_YEARLY === $freq || self::FREQ_MONTHLY === $freq + || self::FREQ_WEEKLY === $freq + || self::FREQ_DAILY === $freq + ) { + $this->freq = $freq; + } else { + throw new \InvalidArgumentException("The Frequency {$freq} is not supported."); + } + + return $this; + } + + /** + * @return string + */ + public function getFreq() + { + return $this->freq; + } + + /** + * The INTERVAL rule part contains a positive integer representing at + * which intervals the recurrence rule repeats. + * + * @param int|null $interval + * + * @return $this + */ + public function setInterval($interval) + { + $this->interval = $interval; + + return $this; + } + + /** + * @return int|null + */ + public function getInterval() + { + return $this->interval; + } + + /** + * The WKST rule part specifies the day on which the workweek starts. + * Valid values are MO, TU, WE, TH, FR, SA, and SU. + * + * @param string $value + * + * @return $this + */ + public function setWkst($value) + { + $this->wkst = $value; + + return $this; + } + + /** + * The BYMONTH rule part specifies a COMMA-separated list of months of the year. + * Valid values are 1 to 12. + * + * @param int $month + * + * @throws InvalidArgumentException + * + * @return $this + */ + public function setByMonth($month) + { + if (!is_integer($month) || $month < 0 || $month > 12) { + throw new InvalidArgumentException('Invalid value for BYMONTH'); + } + + $this->byMonth = $month; + + return $this; + } + + /** + * The BYWEEKNO rule part specifies a COMMA-separated list of ordinals specifying weeks of the year. + * Valid values are 1 to 53 or -53 to -1. + * + * @param int $value + * + * @return $this + */ + public function setByWeekNo($value) + { + $this->byWeekNo = $value; + + return $this; + } + + /** + * The BYYEARDAY rule part specifies a COMMA-separated list of days of the year. + * Valid values are 1 to 366 or -366 to -1. + * + * @param int $day + * + * @return $this + */ + public function setByYearDay($day) + { + $this->byYearDay = $day; + + return $this; + } + + /** + * The BYMONTHDAY rule part specifies a COMMA-separated list of days of the month. + * Valid values are 1 to 31 or -31 to -1. + * + * @param int $day + * + * @return $this + */ + public function setByMonthDay($day) + { + $this->byMonthDay = $day; + + return $this; + } + + /** + * The BYDAY rule part specifies a COMMA-separated list of days of the week;. + * + * SU indicates Sunday; MO indicates Monday; TU indicates Tuesday; + * WE indicates Wednesday; TH indicates Thursday; FR indicates Friday; and SA indicates Saturday. + * + * Each BYDAY value can also be preceded by a positive (+n) or negative (-n) integer. + * If present, this indicates the nth occurrence of a specific day within the MONTHLY or YEARLY "RRULE". + * + * @param string $day + * + * @return $this + */ + public function setByDay($day) + { + $this->byDay = $day; + + return $this; + } + + /** + * The BYHOUR rule part specifies a COMMA-separated list of hours of the day. + * Valid values are 0 to 23. + * + * @param int $value + * + * @return $this + * + * @throws \InvalidArgumentException + */ + public function setByHour($value) + { + if (!is_integer($value) || $value < 0 || $value > 23) { + throw new \InvalidArgumentException('Invalid value for BYHOUR'); + } + + $this->byHour = $value; + + return $this; + } + + /** + * The BYMINUTE rule part specifies a COMMA-separated list of minutes within an hour. + * Valid values are 0 to 59. + * + * @param int $value + * + * @return $this + * + * @throws \InvalidArgumentException + */ + public function setByMinute($value) + { + if (!is_integer($value) || $value < 0 || $value > 59) { + throw new \InvalidArgumentException('Invalid value for BYMINUTE'); + } + + $this->byMinute = $value; + + return $this; + } + + /** + * The BYSECOND rule part specifies a COMMA-separated list of seconds within a minute. + * Valid values are 0 to 60. + * + * @param int $value + * + * @return $this + * + * @throws \InvalidArgumentException + */ + public function setBySecond($value) + { + if (!is_integer($value) || $value < 0 || $value > 60) { + throw new \InvalidArgumentException('Invalid value for BYSECOND'); + } + + $this->bySecond = $value; + + return $this; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property/StringValue.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property/StringValue.php new file mode 100644 index 00000000..4995723b --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property/StringValue.php @@ -0,0 +1,61 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Property; + +use Eluceo\iCal\Util\PropertyValueUtil; + +class StringValue implements ValueInterface +{ + /** + * The value. + * + * @var string + */ + protected $value; + + public function __construct($value) + { + $this->value = $value; + } + + /** + * Return the value of the Property as an escaped string. + * + * Escape values as per RFC 2445. See http://www.kanzaki.com/docs/ical/text.html + * + * @return string + */ + public function getEscapedValue() + { + return PropertyValueUtil::escapeValue((string) $this->value); + } + + /** + * @param string $value + * + * @return $this + */ + public function setValue($value) + { + $this->value = $value; + + return $this; + } + + /** + * @return string + */ + public function getValue() + { + return $this->value; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Property/ValueInterface.php b/vendor/eluceo/ical/src/Eluceo/iCal/Property/ValueInterface.php new file mode 100644 index 00000000..cc3d885c --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Property/ValueInterface.php @@ -0,0 +1,24 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Property; + +interface ValueInterface +{ + /** + * Return the value of the Property as an escaped string. + * + * Escape values as per RFC 2445. See http://www.kanzaki.com/docs/ical/text.html + * + * @return string + */ + public function getEscapedValue(); +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/PropertyBag.php b/vendor/eluceo/ical/src/Eluceo/iCal/PropertyBag.php new file mode 100644 index 00000000..7032360b --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/PropertyBag.php @@ -0,0 +1,79 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal; + +class PropertyBag implements \IteratorAggregate +{ + /** + * @var array + */ + protected $elements = array(); + + /** + * Creates a new Property with $name, $value and $params. + * + * @param $name + * @param $value + * @param array $params + * + * @return $this + */ + public function set($name, $value, $params = array()) + { + $property = new Property($name, $value, $params); + $this->elements[] = $property; + + return $this; + } + + /** + * @param string $name + * + * @return null|Property + */ + public function get($name) + { + // Searching Property in elements-array + /** @var $property Property */ + foreach ($this->elements as $property) { + if ($property->getName() == $name) { + return $property; + } + } + } + + /** + * Adds a Property. If Property already exists an Exception will be thrown. + * + * @param Property $property + * + * @return $this + * + * @throws \Exception + */ + public function add(Property $property) + { + // Property already exists? + if (null !== $this->get($property->getName())) { + throw new \Exception("Property with name '{$property->getName()}' already exists"); + } + + $this->elements[] = $property; + + return $this; + } + + public function getIterator() + { + return new \ArrayObject($this->elements); + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Util/ComponentUtil.php b/vendor/eluceo/ical/src/Eluceo/iCal/Util/ComponentUtil.php new file mode 100644 index 00000000..ca6a2be0 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Util/ComponentUtil.php @@ -0,0 +1,48 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Util; + +class ComponentUtil +{ + /** + * Folds a single line. + * + * According to RFC 2445, all lines longer than 75 characters will be folded + * + * @link http://www.ietf.org/rfc/rfc2445.txt + * + * @param $string + * + * @return array + */ + public static function fold($string) + { + $lines = array(); + $array = preg_split('/(?<!^)(?!$)/u', $string); + + $line = ''; + $lineNo = 0; + foreach ($array as $char) { + $charLen = strlen($char); + $lineLen = strlen($line); + if ($lineLen + $charLen > 75) { + $line = ' ' . $char; + ++$lineNo; + } else { + $line .= $char; + } + $lines[$lineNo] = $line; + } + + return $lines; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Util/DateUtil.php b/vendor/eluceo/ical/src/Eluceo/iCal/Util/DateUtil.php new file mode 100644 index 00000000..3bd3367f --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Util/DateUtil.php @@ -0,0 +1,69 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Util; + +class DateUtil +{ + public static function getDefaultParams(\DateTime $dateTime = null, $noTime = false, $useTimezone = false) + { + $params = array(); + + if ($useTimezone) { + $timeZone = $dateTime->getTimezone()->getName(); + $params['TZID'] = $timeZone; + } + + if ($noTime) { + $params['VALUE'] = 'DATE'; + } + + return $params; + } + + /** + * Returns a formatted date string. + * + * @param \DateTime|null $dateTime The DateTime object + * @param bool $noTime Indicates if the time will be added + * @param bool $useTimezone + * @param bool $useUtc + * + * @return mixed + */ + public static function getDateString(\DateTime $dateTime = null, $noTime = false, $useTimezone = false, $useUtc = false) + { + if (empty($dateTime)) { + $dateTime = new \DateTime(); + } + + return $dateTime->format(self::getDateFormat($noTime, $useTimezone, $useUtc)); + } + + /** + * Returns the date format that can be passed to DateTime::format(). + * + * @param bool $noTime Indicates if the time will be added + * @param bool $useTimezone + * @param bool $useUtc + * + * @return string + */ + public static function getDateFormat($noTime = false, $useTimezone = false, $useUtc = false) + { + // Do not use UTC time (Z) if timezone support is enabled. + if ($useTimezone || !$useUtc) { + return $noTime ? 'Ymd' : 'Ymd\THis'; + } + + return $noTime ? 'Ymd' : 'Ymd\THis\Z'; + } +} diff --git a/vendor/eluceo/ical/src/Eluceo/iCal/Util/PropertyValueUtil.php b/vendor/eluceo/ical/src/Eluceo/iCal/Util/PropertyValueUtil.php new file mode 100644 index 00000000..40538589 --- /dev/null +++ b/vendor/eluceo/ical/src/Eluceo/iCal/Util/PropertyValueUtil.php @@ -0,0 +1,40 @@ +<?php + +/* + * This file is part of the eluceo/iCal package. + * + * (c) Markus Poerschke <markus@eluceo.de> + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Eluceo\iCal\Util; + +class PropertyValueUtil +{ + public static function escapeValue($value) + { + $value = self::escapeValueAllowNewLine($value); + $value = str_replace("\n", '\\n', $value); + + return $value; + } + + public static function escapeValueAllowNewLine($value) + { + $value = str_replace('\\', '\\\\', $value); + $value = str_replace('"', '\\"', $value); + $value = str_replace(',', '\\,', $value); + $value = str_replace(';', '\\;', $value); + $value = str_replace(array( + "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", + "\x08", "\x09", /* \n*/ "\x0B", "\x0C", "\x0D", "\x0E", "\x0F", + "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", + "\x18", "\x19", "\x1A", "\x1B", "\x1C", "\x1D", "\x1E", "\x1F", + "\x7F", + ), '', $value); + + return $value; + } +} diff --git a/vendor/eluceo/ical/tests/Eluceo/iCal/Component/CalendarIntegrationTest.php b/vendor/eluceo/ical/tests/Eluceo/iCal/Component/CalendarIntegrationTest.php new file mode 100644 index 00000000..eb869af8 --- /dev/null +++ b/vendor/eluceo/ical/tests/Eluceo/iCal/Component/CalendarIntegrationTest.php @@ -0,0 +1,64 @@ +<?php + +namespace Eluceo\iCal\Component; + +class CalendarIntegrationTest extends \PHPUnit_Framework_TestCase +{ + /** + * @coversNothing + */ + public function testExample3() + { + $timeZone = new \DateTimeZone('Europe/Berlin'); + + // 1. Create new calendar + $vCalendar = new \Eluceo\iCal\Component\Calendar('www.example.com'); + + // 2. Create an event + $vEvent = new \Eluceo\iCal\Component\Event('123456'); + $vEvent->setDtStart(new \DateTime('2012-12-31', $timeZone)); + $vEvent->setDtEnd(new \DateTime('2012-12-31', $timeZone)); + $vEvent->setNoTime(true); + $vEvent->setIsPrivate(true); + $vEvent->setSummary('New Year’s Eve'); + + // Set recurrence rule + $recurrenceRule = new \Eluceo\iCal\Property\Event\RecurrenceRule(); + $recurrenceRule->setFreq(\Eluceo\iCal\Property\Event\RecurrenceRule::FREQ_YEARLY); + $recurrenceRule->setInterval(1); + $vEvent->setRecurrenceRule($recurrenceRule); + + // Adding Timezone (optional) + $vEvent->setUseTimezone(true); + + // 3. Add event to calendar + $vCalendar->addComponent($vEvent); + + $lines = array( + '/BEGIN:VCALENDAR/', + '/VERSION:2\.0/', + '/PRODID:www\.example\.com/', + '/X-PUBLISHED-TTL:P1W/', + '/BEGIN:VEVENT/', + '/UID:123456/', + '/DTSTART;TZID=Europe\/Berlin;VALUE=DATE:20121231/', + '/SEQUENCE:0/', + '/TRANSP:OPAQUE/', + '/DTEND;TZID=Europe\/Berlin;VALUE=DATE:20121231/', + '/SUMMARY:New Year’s Eve/', + '/CLASS:PRIVATE/', + '/RRULE:FREQ=YEARLY;INTERVAL=1/', + '/X-MICROSOFT-CDO-ALLDAYEVENT:TRUE/', + '/DTSTAMP:20\d{6}T\d{6}Z/', + '/END:VEVENT/', + '/END:VCALENDAR/', + ); + + foreach (explode("\n", $vCalendar->render()) as $key => $line) + { + $this->assertTrue(isset($lines[$key]), 'Too many lines... ' . $line); + + $this->assertRegExp($lines[$key], $line); + } + } +} diff --git a/vendor/eluceo/ical/tests/Eluceo/iCal/ComponentTest.php b/vendor/eluceo/ical/tests/Eluceo/iCal/ComponentTest.php new file mode 100644 index 00000000..5cac7c5d --- /dev/null +++ b/vendor/eluceo/ical/tests/Eluceo/iCal/ComponentTest.php @@ -0,0 +1,45 @@ +<?php + +namespace Eluceo\iCal; + +class ComponentTest extends \PHPUnit_Framework_TestCase +{ + public function testFoldWithMultibyte() + { + $input = "x" . str_repeat("ã‚ã„ã†ãˆãŠ", 5); + + $vCalendar = new \Eluceo\iCal\Component\Calendar('www.example.com'); + $vEvent = new \Eluceo\iCal\Component\Event(); + $vEvent->setDtStart(new \DateTime('2014-12-24')); + $vEvent->setDtEnd(new \DateTime('2014-12-24')); + $vEvent->setDescription($input); + + $vAlarm = new \Eluceo\iCal\Component\Alarm; + $vAlarm->setAction(\Eluceo\iCal\Component\Alarm::ACTION_DISPLAY); + $vAlarm->setDescription($input); + $vAlarm->setTrigger('PT0S', true); + $vEvent->addComponent($vAlarm); + + $vCalendar->addComponent($vEvent); + + $output = $vCalendar->render(); + $output = preg_replace('/\r\n /u', '', $output); + $this->assertContains($input, $output); + } + + public function testDescriptionWithNewLines() + { + $input = "new string \n new line \n new line \n new string"; + + $vCalendar = new \Eluceo\iCal\Component\Calendar('www.example.com'); + $vEvent = new \Eluceo\iCal\Component\Event(); + $vEvent->setDtStart(new \DateTime('2014-12-24')); + $vEvent->setDtEnd(new \DateTime('2014-12-24')); + $vEvent->setDescription($input); + + $vCalendar->addComponent($vEvent); + + $output = $vCalendar->render(); + $this->assertContains(str_replace("\n", "\\n", $input), $output); + } +} diff --git a/vendor/eluceo/ical/tests/Eluceo/iCal/ParameterBagTest.php b/vendor/eluceo/ical/tests/Eluceo/iCal/ParameterBagTest.php new file mode 100644 index 00000000..0fb6e84c --- /dev/null +++ b/vendor/eluceo/ical/tests/Eluceo/iCal/ParameterBagTest.php @@ -0,0 +1,35 @@ +<?php + +namespace Eluceo\iCal; + +class ParameterBagTest extends \PHPUnit_Framework_TestCase +{ + public function testEscapeParamValue() + { + $propertyObject = new ParameterBag; + + $this->assertEquals( + 'test string', + $propertyObject->escapeParamValue('test string'), + 'No escaping necessary' + ); + + $this->assertEquals( + '"Containing \\"double-quotes\\""', + $propertyObject->escapeParamValue('Containing "double-quotes"'), + 'Text contains double quotes' + ); + + $this->assertEquals( + '"Containing forbidden chars like a ;"', + $propertyObject->escapeParamValue('Containing forbidden chars like a ;'), + 'Text with semicolon' + ); + + $this->assertEquals( + '"Containing forbidden chars like a :"', + $propertyObject->escapeParamValue('Containing forbidden chars like a :'), + 'Text with colon' + ); + } +} diff --git a/vendor/eluceo/ical/tests/Eluceo/iCal/Property/ArrayValueTest.php b/vendor/eluceo/ical/tests/Eluceo/iCal/Property/ArrayValueTest.php new file mode 100644 index 00000000..1d1b3331 --- /dev/null +++ b/vendor/eluceo/ical/tests/Eluceo/iCal/Property/ArrayValueTest.php @@ -0,0 +1,26 @@ +<?php + +namespace Eluceo\iCal\Property; + +class ArrayValueTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider arrayValuesProvider + */ + public function testArrayValue($values, $expectedOutput) + { + $arrayValue = new ArrayValue($values); + + $this->assertEquals($expectedOutput, $arrayValue->getEscapedValue()); + } + + public function arrayValuesProvider() + { + return array( + array(array(), ''), + array(array('Lorem'), 'Lorem'), + array(array('Lorem', 'Ipsum'), 'Lorem,Ipsum'), + array(array('Lorem', '"doublequotes"'), 'Lorem,\"doublequotes\"'), + ); + } +} diff --git a/vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/DescriptionTest.php b/vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/DescriptionTest.php new file mode 100644 index 00000000..0ad16bcc --- /dev/null +++ b/vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/DescriptionTest.php @@ -0,0 +1,17 @@ +<?php + +namespace Eluceo\iCal\Property\Event; + +class DescriptionTest extends \PHPUnit_Framework_TestCase +{ + public function testAllowsNewLines() + { + $testString = "New String \n New Line"; + $description = new Description($testString); + + $this->assertEquals( + str_replace("\n", "\\n", $testString), + $description->getEscapedValue() + ); + } +} diff --git a/vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/OrganizerTest.php b/vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/OrganizerTest.php new file mode 100644 index 00000000..71acdce9 --- /dev/null +++ b/vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/OrganizerTest.php @@ -0,0 +1,63 @@ +<?php +/** + * Eluceo\iCal\Property\Event\OrganizerTest + * + * @author Giulio Troccoli <giulio@troccoli.it> + */ + +namespace Eluceo\iCal\Property\Event; + +/** + * OrganizerTest + */ +class OrganizerTest extends \PHPUnit_Framework_TestCase +{ + public function testOrganizerValueOnly() + { + $value = "MAILTO:name.lastname@example.com"; + $expected = "ORGANIZER:$value"; + + $vCalendar = $this->createCalendarWithOrganizer( + new \Eluceo\iCal\Property\Event\Organizer($value) + ); + + foreach (explode("\n", $vCalendar->render()) as $line) + { + if (preg_match('/^ORGANIZER[:;](.*)$/', $line)) { + $this->assertEquals($expected, trim($line)); + } + } + } + + public function testOrganizerValueAndParameter() + { + $value = "MAILTO:name.lastname@example.com"; + $param = "Name LastName"; + $expected = "ORGANIZER;CN=$param:$value"; + + $vCalendar = $this->createCalendarWithOrganizer( + new \Eluceo\iCal\Property\Event\Organizer($value, array('CN' => $param)) + ); + + foreach (explode("\n", $vCalendar->render()) as $line) + { + if (preg_match('/^ORGANIZER[:;](.*)$/', $line)) { + $this->assertEquals($expected, trim($line)); + } + } + + } + + /** + * @param Organizer $vOrganizer + * @return \Eluceo\iCal\Component\Calendar + */ + private function createCalendarWithOrganizer(\Eluceo\iCal\Property\Event\Organizer $vOrganizer) + { + $vCalendar = new \Eluceo\iCal\Component\Calendar('www.example.com'); + $vEvent = new \Eluceo\iCal\Component\Event('123456'); + $vEvent->setOrganizer($vOrganizer); + $vCalendar->addComponent($vEvent); + return $vCalendar; + } +} diff --git a/vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/RecurrenceRuleTest.php b/vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/RecurrenceRuleTest.php new file mode 100644 index 00000000..a44b2580 --- /dev/null +++ b/vendor/eluceo/ical/tests/Eluceo/iCal/Property/Event/RecurrenceRuleTest.php @@ -0,0 +1,21 @@ +<?php + +namespace Eluceo\iCal\Property\Event; + +class RecurrenceRuleTest extends \PHPUnit_Framework_TestCase +{ + /** + * Example taken from http://www.kanzaki.com/docs/ical/rrule.html + */ + public function testUntil() + { + $rule = new RecurrenceRule(); + $rule->setFreq(RecurrenceRule::FREQ_DAILY); + $rule->setInterval(null); + $rule->setUntil(new \DateTime('1997-12-24')); + $this->assertEquals( + 'FREQ=DAILY;UNTIL=19971224T000000Z', + $rule->getEscapedValue() + ); + } +} diff --git a/vendor/eluceo/ical/tests/Eluceo/iCal/Property/StringValueTest.php b/vendor/eluceo/ical/tests/Eluceo/iCal/Property/StringValueTest.php new file mode 100644 index 00000000..afa70df1 --- /dev/null +++ b/vendor/eluceo/ical/tests/Eluceo/iCal/Property/StringValueTest.php @@ -0,0 +1,63 @@ +<?php + +namespace Eluceo\iCal\Property; + +use Eluceo\iCal\Property\StringValue; + +class StringValueTest extends \PHPUnit_Framework_TestCase +{ + public function testNoEscapeNeeded() + { + $stringValue = new StringValue('LOREM'); + + $this->assertEquals( + 'LOREM', + $stringValue->getEscapedValue(), + 'No escaping necessary' + ); + } + + public function testValueContainsBackslash() + { + $stringValue = new StringValue('text contains backslash: \\'); + + $this->assertEquals( + 'text contains backslash: \\\\', + $stringValue->getEscapedValue(), + 'Text contains backslash' + ); + } + + public function testEscapingDoubleQuotes() + { + $stringValue = new StringValue('text with "doublequotes" will be escaped'); + + $this->assertEquals( + 'text with \\"doublequotes\\" will be escaped', + $stringValue->getEscapedValue(), + 'Escaping double quotes' + ); + } + + public function testEscapingSemicolonAndComma() + { + $stringValue = new StringValue('text with , and ; will also be escaped'); + + $this->assertEquals( + 'text with \\, and \\; will also be escaped', + $stringValue->getEscapedValue(), + 'Escaping ; and ,' + ); + } + + public function testNewLineEscaping() + { + $stringValue = new StringValue("Text with new\n line"); + + $this->assertEquals( + 'Text with new\\n line', + $stringValue->getEscapedValue(), + 'Escape new line to text' + ); + } +} diff --git a/vendor/eluceo/ical/tests/Eluceo/iCal/PropertyBagTest.php b/vendor/eluceo/ical/tests/Eluceo/iCal/PropertyBagTest.php new file mode 100644 index 00000000..a7f8d8ce --- /dev/null +++ b/vendor/eluceo/ical/tests/Eluceo/iCal/PropertyBagTest.php @@ -0,0 +1,18 @@ +<?php + +namespace Eluceo\iCal; + +class PropertyBagTest extends \PHPUnit_Framework_TestCase +{ + /** + * @todo Use Mocks instead of a real object! + */ + public function testPropertyAlreadyExistsOnAddingProperty() + { + $this->setExpectedException('\\Exception', "Property with name 'propName' already exists"); + + $propertyBag = new PropertyBag(); + $propertyBag->add(new Property('propName', '')); + $propertyBag->add(new Property('propName', '')); + } +} diff --git a/vendor/eluceo/ical/tests/Eluceo/iCal/PropertyTest.php b/vendor/eluceo/ical/tests/Eluceo/iCal/PropertyTest.php new file mode 100644 index 00000000..b30f5a3a --- /dev/null +++ b/vendor/eluceo/ical/tests/Eluceo/iCal/PropertyTest.php @@ -0,0 +1,42 @@ +<?php + +namespace Eluceo\iCal; + +class PropertyTest extends \PHPUnit_Framework_TestCase +{ + public function testPropertyWithSingleValue() + { + $property = new Property('DTSTAMP', '20131020T153112'); + $this->assertEquals( + 'DTSTAMP:20131020T153112', + $property->toLine() + ); + } + + public function testPropertyWithValueAndParams() + { + $property = new Property('DTSTAMP', '20131020T153112', array('TZID' => 'Europe/Berlin')); + $this->assertEquals( + 'DTSTAMP;TZID=Europe/Berlin:20131020T153112', + $property->toLine() + ); + } + + public function testPropertyWithEscapedSingleValue() + { + $property = new Property('SOMEPROP', 'Escape me!"'); + $this->assertEquals( + 'SOMEPROP:Escape me!\\"', + $property->toLine() + ); + } + + public function testPropertyWithEscapedValues() + { + $property = new Property('SOMEPROP', 'Escape me!"', array('TEST' => 'Lorem "test" ipsum')); + $this->assertEquals( + 'SOMEPROP;TEST="Lorem \\"test\\" ipsum":Escape me!\\"', + $property->toLine() + ); + } +} diff --git a/vendor/erusev/parsedown/.travis.yml b/vendor/erusev/parsedown/.travis.yml new file mode 100644 index 00000000..5df49dcb --- /dev/null +++ b/vendor/erusev/parsedown/.travis.yml @@ -0,0 +1,16 @@ +language: php + +php: + - 7.0 + - 5.6 + - 5.5 + - 5.4 + - 5.3 + - hhvm + - hhvm-nightly + +matrix: + fast_finish: true + allow_failures: + - php: 7.0 + - php: hhvm-nightly diff --git a/vendor/erusev/parsedown/LICENSE.txt b/vendor/erusev/parsedown/LICENSE.txt new file mode 100644 index 00000000..baca86f5 --- /dev/null +++ b/vendor/erusev/parsedown/LICENSE.txt @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Emanuil Rusev, erusev.com + +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.
\ No newline at end of file diff --git a/vendor/erusev/parsedown/Parsedown.php b/vendor/erusev/parsedown/Parsedown.php new file mode 100755 index 00000000..c8c92a39 --- /dev/null +++ b/vendor/erusev/parsedown/Parsedown.php @@ -0,0 +1,1528 @@ +<?php + +# +# +# Parsedown +# http://parsedown.org +# +# (c) Emanuil Rusev +# http://erusev.com +# +# For the full license information, view the LICENSE file that was distributed +# with this source code. +# +# + +class Parsedown +{ + # ~ + + const version = '1.6.0'; + + # ~ + + function text($text) + { + # make sure no definitions are set + $this->DefinitionData = array(); + + # standardize line breaks + $text = str_replace(array("\r\n", "\r"), "\n", $text); + + # remove surrounding line breaks + $text = trim($text, "\n"); + + # split text into lines + $lines = explode("\n", $text); + + # iterate through lines to identify blocks + $markup = $this->lines($lines); + + # trim line breaks + $markup = trim($markup, "\n"); + + return $markup; + } + + # + # Setters + # + + function setBreaksEnabled($breaksEnabled) + { + $this->breaksEnabled = $breaksEnabled; + + return $this; + } + + protected $breaksEnabled; + + function setMarkupEscaped($markupEscaped) + { + $this->markupEscaped = $markupEscaped; + + return $this; + } + + protected $markupEscaped; + + function setUrlsLinked($urlsLinked) + { + $this->urlsLinked = $urlsLinked; + + return $this; + } + + protected $urlsLinked = true; + + # + # Lines + # + + protected $BlockTypes = array( + '#' => array('Header'), + '*' => array('Rule', 'List'), + '+' => array('List'), + '-' => array('SetextHeader', 'Table', 'Rule', 'List'), + '0' => array('List'), + '1' => array('List'), + '2' => array('List'), + '3' => array('List'), + '4' => array('List'), + '5' => array('List'), + '6' => array('List'), + '7' => array('List'), + '8' => array('List'), + '9' => array('List'), + ':' => array('Table'), + '<' => array('Comment', 'Markup'), + '=' => array('SetextHeader'), + '>' => array('Quote'), + '[' => array('Reference'), + '_' => array('Rule'), + '`' => array('FencedCode'), + '|' => array('Table'), + '~' => array('FencedCode'), + ); + + # ~ + + protected $unmarkedBlockTypes = array( + 'Code', + ); + + # + # Blocks + # + + private function lines(array $lines) + { + $CurrentBlock = null; + + foreach ($lines as $line) + { + if (chop($line) === '') + { + if (isset($CurrentBlock)) + { + $CurrentBlock['interrupted'] = true; + } + + continue; + } + + if (strpos($line, "\t") !== false) + { + $parts = explode("\t", $line); + + $line = $parts[0]; + + unset($parts[0]); + + foreach ($parts as $part) + { + $shortage = 4 - mb_strlen($line, 'utf-8') % 4; + + $line .= str_repeat(' ', $shortage); + $line .= $part; + } + } + + $indent = 0; + + while (isset($line[$indent]) and $line[$indent] === ' ') + { + $indent ++; + } + + $text = $indent > 0 ? substr($line, $indent) : $line; + + # ~ + + $Line = array('body' => $line, 'indent' => $indent, 'text' => $text); + + # ~ + + if (isset($CurrentBlock['continuable'])) + { + $Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock); + + if (isset($Block)) + { + $CurrentBlock = $Block; + + continue; + } + else + { + if (method_exists($this, 'block'.$CurrentBlock['type'].'Complete')) + { + $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); + } + } + } + + # ~ + + $marker = $text[0]; + + # ~ + + $blockTypes = $this->unmarkedBlockTypes; + + if (isset($this->BlockTypes[$marker])) + { + foreach ($this->BlockTypes[$marker] as $blockType) + { + $blockTypes []= $blockType; + } + } + + # + # ~ + + foreach ($blockTypes as $blockType) + { + $Block = $this->{'block'.$blockType}($Line, $CurrentBlock); + + if (isset($Block)) + { + $Block['type'] = $blockType; + + if ( ! isset($Block['identified'])) + { + $Blocks []= $CurrentBlock; + + $Block['identified'] = true; + } + + if (method_exists($this, 'block'.$blockType.'Continue')) + { + $Block['continuable'] = true; + } + + $CurrentBlock = $Block; + + continue 2; + } + } + + # ~ + + if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted'])) + { + $CurrentBlock['element']['text'] .= "\n".$text; + } + else + { + $Blocks []= $CurrentBlock; + + $CurrentBlock = $this->paragraph($Line); + + $CurrentBlock['identified'] = true; + } + } + + # ~ + + if (isset($CurrentBlock['continuable']) and method_exists($this, 'block'.$CurrentBlock['type'].'Complete')) + { + $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); + } + + # ~ + + $Blocks []= $CurrentBlock; + + unset($Blocks[0]); + + # ~ + + $markup = ''; + + foreach ($Blocks as $Block) + { + if (isset($Block['hidden'])) + { + continue; + } + + $markup .= "\n"; + $markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']); + } + + $markup .= "\n"; + + # ~ + + return $markup; + } + + # + # Code + + protected function blockCode($Line, $Block = null) + { + if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted'])) + { + return; + } + + if ($Line['indent'] >= 4) + { + $text = substr($Line['body'], 4); + + $Block = array( + 'element' => array( + 'name' => 'pre', + 'handler' => 'element', + 'text' => array( + 'name' => 'code', + 'text' => $text, + ), + ), + ); + + return $Block; + } + } + + protected function blockCodeContinue($Line, $Block) + { + if ($Line['indent'] >= 4) + { + if (isset($Block['interrupted'])) + { + $Block['element']['text']['text'] .= "\n"; + + unset($Block['interrupted']); + } + + $Block['element']['text']['text'] .= "\n"; + + $text = substr($Line['body'], 4); + + $Block['element']['text']['text'] .= $text; + + return $Block; + } + } + + protected function blockCodeComplete($Block) + { + $text = $Block['element']['text']['text']; + + $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8'); + + $Block['element']['text']['text'] = $text; + + return $Block; + } + + # + # Comment + + protected function blockComment($Line) + { + if ($this->markupEscaped) + { + return; + } + + if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!') + { + $Block = array( + 'markup' => $Line['body'], + ); + + if (preg_match('/-->$/', $Line['text'])) + { + $Block['closed'] = true; + } + + return $Block; + } + } + + protected function blockCommentContinue($Line, array $Block) + { + if (isset($Block['closed'])) + { + return; + } + + $Block['markup'] .= "\n" . $Line['body']; + + if (preg_match('/-->$/', $Line['text'])) + { + $Block['closed'] = true; + } + + return $Block; + } + + # + # Fenced Code + + protected function blockFencedCode($Line) + { + if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches)) + { + $Element = array( + 'name' => 'code', + 'text' => '', + ); + + if (isset($matches[1])) + { + $class = 'language-'.$matches[1]; + + $Element['attributes'] = array( + 'class' => $class, + ); + } + + $Block = array( + 'char' => $Line['text'][0], + 'element' => array( + 'name' => 'pre', + 'handler' => 'element', + 'text' => $Element, + ), + ); + + return $Block; + } + } + + protected function blockFencedCodeContinue($Line, $Block) + { + if (isset($Block['complete'])) + { + return; + } + + if (isset($Block['interrupted'])) + { + $Block['element']['text']['text'] .= "\n"; + + unset($Block['interrupted']); + } + + if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text'])) + { + $Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1); + + $Block['complete'] = true; + + return $Block; + } + + $Block['element']['text']['text'] .= "\n".$Line['body'];; + + return $Block; + } + + protected function blockFencedCodeComplete($Block) + { + $text = $Block['element']['text']['text']; + + $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8'); + + $Block['element']['text']['text'] = $text; + + return $Block; + } + + # + # Header + + protected function blockHeader($Line) + { + if (isset($Line['text'][1])) + { + $level = 1; + + while (isset($Line['text'][$level]) and $Line['text'][$level] === '#') + { + $level ++; + } + + if ($level > 6) + { + return; + } + + $text = trim($Line['text'], '# '); + + $Block = array( + 'element' => array( + 'name' => 'h' . min(6, $level), + 'text' => $text, + 'handler' => 'line', + ), + ); + + return $Block; + } + } + + # + # List + + protected function blockList($Line) + { + list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]'); + + if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches)) + { + $Block = array( + 'indent' => $Line['indent'], + 'pattern' => $pattern, + 'element' => array( + 'name' => $name, + 'handler' => 'elements', + ), + ); + + $Block['li'] = array( + 'name' => 'li', + 'handler' => 'li', + 'text' => array( + $matches[2], + ), + ); + + $Block['element']['text'] []= & $Block['li']; + + return $Block; + } + } + + protected function blockListContinue($Line, array $Block) + { + if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches)) + { + if (isset($Block['interrupted'])) + { + $Block['li']['text'] []= ''; + + unset($Block['interrupted']); + } + + unset($Block['li']); + + $text = isset($matches[1]) ? $matches[1] : ''; + + $Block['li'] = array( + 'name' => 'li', + 'handler' => 'li', + 'text' => array( + $text, + ), + ); + + $Block['element']['text'] []= & $Block['li']; + + return $Block; + } + + if ($Line['text'][0] === '[' and $this->blockReference($Line)) + { + return $Block; + } + + if ( ! isset($Block['interrupted'])) + { + $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']); + + $Block['li']['text'] []= $text; + + return $Block; + } + + if ($Line['indent'] > 0) + { + $Block['li']['text'] []= ''; + + $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']); + + $Block['li']['text'] []= $text; + + unset($Block['interrupted']); + + return $Block; + } + } + + # + # Quote + + protected function blockQuote($Line) + { + if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) + { + $Block = array( + 'element' => array( + 'name' => 'blockquote', + 'handler' => 'lines', + 'text' => (array) $matches[1], + ), + ); + + return $Block; + } + } + + protected function blockQuoteContinue($Line, array $Block) + { + if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) + { + if (isset($Block['interrupted'])) + { + $Block['element']['text'] []= ''; + + unset($Block['interrupted']); + } + + $Block['element']['text'] []= $matches[1]; + + return $Block; + } + + if ( ! isset($Block['interrupted'])) + { + $Block['element']['text'] []= $Line['text']; + + return $Block; + } + } + + # + # Rule + + protected function blockRule($Line) + { + if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text'])) + { + $Block = array( + 'element' => array( + 'name' => 'hr' + ), + ); + + return $Block; + } + } + + # + # Setext + + protected function blockSetextHeader($Line, array $Block = null) + { + if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) + { + return; + } + + if (chop($Line['text'], $Line['text'][0]) === '') + { + $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2'; + + return $Block; + } + } + + # + # Markup + + protected function blockMarkup($Line) + { + if ($this->markupEscaped) + { + return; + } + + if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches)) + { + $element = strtolower($matches[1]); + + if (in_array($element, $this->textLevelElements)) + { + return; + } + + $Block = array( + 'name' => $matches[1], + 'depth' => 0, + 'markup' => $Line['text'], + ); + + $length = strlen($matches[0]); + + $remainder = substr($Line['text'], $length); + + if (trim($remainder) === '') + { + if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) + { + $Block['closed'] = true; + + $Block['void'] = true; + } + } + else + { + if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) + { + return; + } + + if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder)) + { + $Block['closed'] = true; + } + } + + return $Block; + } + } + + protected function blockMarkupContinue($Line, array $Block) + { + if (isset($Block['closed'])) + { + return; + } + + if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open + { + $Block['depth'] ++; + } + + if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close + { + if ($Block['depth'] > 0) + { + $Block['depth'] --; + } + else + { + $Block['closed'] = true; + } + } + + if (isset($Block['interrupted'])) + { + $Block['markup'] .= "\n"; + + unset($Block['interrupted']); + } + + $Block['markup'] .= "\n".$Line['body']; + + return $Block; + } + + # + # Reference + + protected function blockReference($Line) + { + if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches)) + { + $id = strtolower($matches[1]); + + $Data = array( + 'url' => $matches[2], + 'title' => null, + ); + + if (isset($matches[3])) + { + $Data['title'] = $matches[3]; + } + + $this->DefinitionData['Reference'][$id] = $Data; + + $Block = array( + 'hidden' => true, + ); + + return $Block; + } + } + + # + # Table + + protected function blockTable($Line, array $Block = null) + { + if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) + { + return; + } + + if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '') + { + $alignments = array(); + + $divider = $Line['text']; + + $divider = trim($divider); + $divider = trim($divider, '|'); + + $dividerCells = explode('|', $divider); + + foreach ($dividerCells as $dividerCell) + { + $dividerCell = trim($dividerCell); + + if ($dividerCell === '') + { + continue; + } + + $alignment = null; + + if ($dividerCell[0] === ':') + { + $alignment = 'left'; + } + + if (substr($dividerCell, - 1) === ':') + { + $alignment = $alignment === 'left' ? 'center' : 'right'; + } + + $alignments []= $alignment; + } + + # ~ + + $HeaderElements = array(); + + $header = $Block['element']['text']; + + $header = trim($header); + $header = trim($header, '|'); + + $headerCells = explode('|', $header); + + foreach ($headerCells as $index => $headerCell) + { + $headerCell = trim($headerCell); + + $HeaderElement = array( + 'name' => 'th', + 'text' => $headerCell, + 'handler' => 'line', + ); + + if (isset($alignments[$index])) + { + $alignment = $alignments[$index]; + + $HeaderElement['attributes'] = array( + 'style' => 'text-align: '.$alignment.';', + ); + } + + $HeaderElements []= $HeaderElement; + } + + # ~ + + $Block = array( + 'alignments' => $alignments, + 'identified' => true, + 'element' => array( + 'name' => 'table', + 'handler' => 'elements', + ), + ); + + $Block['element']['text'] []= array( + 'name' => 'thead', + 'handler' => 'elements', + ); + + $Block['element']['text'] []= array( + 'name' => 'tbody', + 'handler' => 'elements', + 'text' => array(), + ); + + $Block['element']['text'][0]['text'] []= array( + 'name' => 'tr', + 'handler' => 'elements', + 'text' => $HeaderElements, + ); + + return $Block; + } + } + + protected function blockTableContinue($Line, array $Block) + { + if (isset($Block['interrupted'])) + { + return; + } + + if ($Line['text'][0] === '|' or strpos($Line['text'], '|')) + { + $Elements = array(); + + $row = $Line['text']; + + $row = trim($row); + $row = trim($row, '|'); + + preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches); + + foreach ($matches[0] as $index => $cell) + { + $cell = trim($cell); + + $Element = array( + 'name' => 'td', + 'handler' => 'line', + 'text' => $cell, + ); + + if (isset($Block['alignments'][$index])) + { + $Element['attributes'] = array( + 'style' => 'text-align: '.$Block['alignments'][$index].';', + ); + } + + $Elements []= $Element; + } + + $Element = array( + 'name' => 'tr', + 'handler' => 'elements', + 'text' => $Elements, + ); + + $Block['element']['text'][1]['text'] []= $Element; + + return $Block; + } + } + + # + # ~ + # + + protected function paragraph($Line) + { + $Block = array( + 'element' => array( + 'name' => 'p', + 'text' => $Line['text'], + 'handler' => 'line', + ), + ); + + return $Block; + } + + # + # Inline Elements + # + + protected $InlineTypes = array( + '"' => array('SpecialCharacter'), + '!' => array('Image'), + '&' => array('SpecialCharacter'), + '*' => array('Emphasis'), + ':' => array('Url'), + '<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'), + '>' => array('SpecialCharacter'), + '[' => array('Link'), + '_' => array('Emphasis'), + '`' => array('Code'), + '~' => array('Strikethrough'), + '\\' => array('EscapeSequence'), + ); + + # ~ + + protected $inlineMarkerList = '!"*_&[:<>`~\\'; + + # + # ~ + # + + public function line($text) + { + $markup = ''; + + # $excerpt is based on the first occurrence of a marker + + while ($excerpt = strpbrk($text, $this->inlineMarkerList)) + { + $marker = $excerpt[0]; + + $markerPosition = strpos($text, $marker); + + $Excerpt = array('text' => $excerpt, 'context' => $text); + + foreach ($this->InlineTypes[$marker] as $inlineType) + { + $Inline = $this->{'inline'.$inlineType}($Excerpt); + + if ( ! isset($Inline)) + { + continue; + } + + # makes sure that the inline belongs to "our" marker + + if (isset($Inline['position']) and $Inline['position'] > $markerPosition) + { + continue; + } + + # sets a default inline position + + if ( ! isset($Inline['position'])) + { + $Inline['position'] = $markerPosition; + } + + # the text that comes before the inline + $unmarkedText = substr($text, 0, $Inline['position']); + + # compile the unmarked text + $markup .= $this->unmarkedText($unmarkedText); + + # compile the inline + $markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']); + + # remove the examined text + $text = substr($text, $Inline['position'] + $Inline['extent']); + + continue 2; + } + + # the marker does not belong to an inline + + $unmarkedText = substr($text, 0, $markerPosition + 1); + + $markup .= $this->unmarkedText($unmarkedText); + + $text = substr($text, $markerPosition + 1); + } + + $markup .= $this->unmarkedText($text); + + return $markup; + } + + # + # ~ + # + + protected function inlineCode($Excerpt) + { + $marker = $Excerpt['text'][0]; + + if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/s', $Excerpt['text'], $matches)) + { + $text = $matches[2]; + $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8'); + $text = preg_replace("/[ ]*\n/", ' ', $text); + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'code', + 'text' => $text, + ), + ); + } + } + + protected function inlineEmailTag($Excerpt) + { + if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches)) + { + $url = $matches[1]; + + if ( ! isset($matches[2])) + { + $url = 'mailto:' . $url; + } + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'a', + 'text' => $matches[1], + 'attributes' => array( + 'href' => $url, + ), + ), + ); + } + } + + protected function inlineEmphasis($Excerpt) + { + if ( ! isset($Excerpt['text'][1])) + { + return; + } + + $marker = $Excerpt['text'][0]; + + if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches)) + { + $emphasis = 'strong'; + } + elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches)) + { + $emphasis = 'em'; + } + else + { + return; + } + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => $emphasis, + 'handler' => 'line', + 'text' => $matches[1], + ), + ); + } + + protected function inlineEscapeSequence($Excerpt) + { + if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters)) + { + return array( + 'markup' => $Excerpt['text'][1], + 'extent' => 2, + ); + } + } + + protected function inlineImage($Excerpt) + { + if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[') + { + return; + } + + $Excerpt['text']= substr($Excerpt['text'], 1); + + $Link = $this->inlineLink($Excerpt); + + if ($Link === null) + { + return; + } + + $Inline = array( + 'extent' => $Link['extent'] + 1, + 'element' => array( + 'name' => 'img', + 'attributes' => array( + 'src' => $Link['element']['attributes']['href'], + 'alt' => $Link['element']['text'], + ), + ), + ); + + $Inline['element']['attributes'] += $Link['element']['attributes']; + + unset($Inline['element']['attributes']['href']); + + return $Inline; + } + + protected function inlineLink($Excerpt) + { + $Element = array( + 'name' => 'a', + 'handler' => 'line', + 'text' => null, + 'attributes' => array( + 'href' => null, + 'title' => null, + ), + ); + + $extent = 0; + + $remainder = $Excerpt['text']; + + if (preg_match('/\[((?:[^][]|(?R))*)\]/', $remainder, $matches)) + { + $Element['text'] = $matches[1]; + + $extent += strlen($matches[0]); + + $remainder = substr($remainder, $extent); + } + else + { + return; + } + + if (preg_match('/^[(]((?:[^ ()]|[(][^ )]+[)])+)(?:[ ]+("[^"]*"|\'[^\']*\'))?[)]/', $remainder, $matches)) + { + $Element['attributes']['href'] = $matches[1]; + + if (isset($matches[2])) + { + $Element['attributes']['title'] = substr($matches[2], 1, - 1); + } + + $extent += strlen($matches[0]); + } + else + { + if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches)) + { + $definition = strlen($matches[1]) ? $matches[1] : $Element['text']; + $definition = strtolower($definition); + + $extent += strlen($matches[0]); + } + else + { + $definition = strtolower($Element['text']); + } + + if ( ! isset($this->DefinitionData['Reference'][$definition])) + { + return; + } + + $Definition = $this->DefinitionData['Reference'][$definition]; + + $Element['attributes']['href'] = $Definition['url']; + $Element['attributes']['title'] = $Definition['title']; + } + + $Element['attributes']['href'] = str_replace(array('&', '<'), array('&', '<'), $Element['attributes']['href']); + + return array( + 'extent' => $extent, + 'element' => $Element, + ); + } + + protected function inlineMarkup($Excerpt) + { + if ($this->markupEscaped or strpos($Excerpt['text'], '>') === false) + { + return; + } + + if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w*[ ]*>/s', $Excerpt['text'], $matches)) + { + return array( + 'markup' => $matches[0], + 'extent' => strlen($matches[0]), + ); + } + + if ($Excerpt['text'][1] === '!' and preg_match('/^<!---?[^>-](?:-?[^-])*-->/s', $Excerpt['text'], $matches)) + { + return array( + 'markup' => $matches[0], + 'extent' => strlen($matches[0]), + ); + } + + if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches)) + { + return array( + 'markup' => $matches[0], + 'extent' => strlen($matches[0]), + ); + } + } + + protected function inlineSpecialCharacter($Excerpt) + { + if ($Excerpt['text'][0] === '&' and ! preg_match('/^&#?\w+;/', $Excerpt['text'])) + { + return array( + 'markup' => '&', + 'extent' => 1, + ); + } + + $SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot'); + + if (isset($SpecialCharacter[$Excerpt['text'][0]])) + { + return array( + 'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';', + 'extent' => 1, + ); + } + } + + protected function inlineStrikethrough($Excerpt) + { + if ( ! isset($Excerpt['text'][1])) + { + return; + } + + if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches)) + { + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'del', + 'text' => $matches[1], + 'handler' => 'line', + ), + ); + } + } + + protected function inlineUrl($Excerpt) + { + if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/') + { + return; + } + + if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)) + { + $Inline = array( + 'extent' => strlen($matches[0][0]), + 'position' => $matches[0][1], + 'element' => array( + 'name' => 'a', + 'text' => $matches[0][0], + 'attributes' => array( + 'href' => $matches[0][0], + ), + ), + ); + + return $Inline; + } + } + + protected function inlineUrlTag($Excerpt) + { + if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches)) + { + $url = str_replace(array('&', '<'), array('&', '<'), $matches[1]); + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'a', + 'text' => $url, + 'attributes' => array( + 'href' => $url, + ), + ), + ); + } + } + + # ~ + + protected function unmarkedText($text) + { + if ($this->breaksEnabled) + { + $text = preg_replace('/[ ]*\n/', "<br />\n", $text); + } + else + { + $text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "<br />\n", $text); + $text = str_replace(" \n", "\n", $text); + } + + return $text; + } + + # + # Handlers + # + + protected function element(array $Element) + { + $markup = '<'.$Element['name']; + + if (isset($Element['attributes'])) + { + foreach ($Element['attributes'] as $name => $value) + { + if ($value === null) + { + continue; + } + + $markup .= ' '.$name.'="'.$value.'"'; + } + } + + if (isset($Element['text'])) + { + $markup .= '>'; + + if (isset($Element['handler'])) + { + $markup .= $this->{$Element['handler']}($Element['text']); + } + else + { + $markup .= $Element['text']; + } + + $markup .= '</'.$Element['name'].'>'; + } + else + { + $markup .= ' />'; + } + + return $markup; + } + + protected function elements(array $Elements) + { + $markup = ''; + + foreach ($Elements as $Element) + { + $markup .= "\n" . $this->element($Element); + } + + $markup .= "\n"; + + return $markup; + } + + # ~ + + protected function li($lines) + { + $markup = $this->lines($lines); + + $trimmedMarkup = trim($markup); + + if ( ! in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '<p>') + { + $markup = $trimmedMarkup; + $markup = substr($markup, 3); + + $position = strpos($markup, "</p>"); + + $markup = substr_replace($markup, '', $position, 4); + } + + return $markup; + } + + # + # Deprecated Methods + # + + function parse($text) + { + $markup = $this->text($text); + + return $markup; + } + + # + # Static Methods + # + + static function instance($name = 'default') + { + if (isset(self::$instances[$name])) + { + return self::$instances[$name]; + } + + $instance = new static(); + + self::$instances[$name] = $instance; + + return $instance; + } + + private static $instances = array(); + + # + # Fields + # + + protected $DefinitionData; + + # + # Read-Only + + protected $specialCharacters = array( + '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|', + ); + + protected $StrongRegex = array( + '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s', + '_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us', + ); + + protected $EmRegex = array( + '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s', + '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us', + ); + + protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?'; + + protected $voidElements = array( + 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', + ); + + protected $textLevelElements = array( + 'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont', + 'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing', + 'i', 'rp', 'del', 'code', 'strike', 'marquee', + 'q', 'rt', 'ins', 'font', 'strong', + 's', 'tt', 'sub', 'mark', + 'u', 'xm', 'sup', 'nobr', + 'var', 'ruby', + 'wbr', 'span', + 'time', + ); +} diff --git a/vendor/erusev/parsedown/README.md b/vendor/erusev/parsedown/README.md new file mode 100644 index 00000000..6f9f6498 --- /dev/null +++ b/vendor/erusev/parsedown/README.md @@ -0,0 +1,57 @@ +## Parsedown + +[](https://travis-ci.org/erusev/parsedown) +<!--[](https://packagist.org/packages/erusev/parsedown)--> + +Better Markdown Parser in PHP + +[Demo](http://parsedown.org/demo) | +[Benchmarks](http://parsedown.org/speed) | +[Tests](http://parsedown.org/tests/) | +[Documentation](https://github.com/erusev/parsedown/wiki/) + +### Features + +* Super Fast +* [GitHub flavored](https://help.github.com/articles/github-flavored-markdown) +* Extensible +* Tested in 5.3 to 5.6 +* [Markdown Extra extension](https://github.com/erusev/parsedown-extra) + +### Installation + +Include `Parsedown.php` or install [the composer package](https://packagist.org/packages/erusev/parsedown). + +### Example + +``` php +$Parsedown = new Parsedown(); + +echo $Parsedown->text('Hello _Parsedown_!'); # prints: <p>Hello <em>Parsedown</em>!</p> +``` + +More examples in [the wiki](https://github.com/erusev/parsedown/wiki/) and in [this video tutorial](http://youtu.be/wYZBY8DEikI). + +### Questions + +**How does Parsedown work?** + +It tries to read Markdown like a human. First, it looks at the lines. It’s interested in how the lines start. This helps it recognise blocks. It knows, for example, that if a line start with a `-` then it perhaps belong to a list. Once it recognises the blocks, it continues to the content. As it reads, it watches out for special characters. This helps it recognise inline elements (or inlines). + +We call this approach "line based". We believe that Parsedown is the first Markdown parser to use it. Since the release of Parsedown, other developers have used the same approach to develop other Markdown parsers in PHP and in other languages. + +**Is it compliant with CommonMark?** + +It passes most of the CommonMark tests. Most of the tests that don't pass deal with cases that are quite uncommon. Still, as CommonMark matures, compliance should improve. + +**Who uses it?** + +[phpDocumentor](http://www.phpdoc.org/), [October CMS](http://octobercms.com/), [Bolt CMS](http://bolt.cm/), [Kirby CMS](http://getkirby.com/), [Grav CMS](http://getgrav.org/), [Statamic CMS](http://www.statamic.com/), [RaspberryPi.org](http://www.raspberrypi.org/) and [more](https://www.versioneye.com/php/erusev:parsedown/references). + +**How can I help?** + +Use it, star it, share it and if you feel generous, [donate](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=528P3NZQMP8N2). + +--- + +You might also like [Caret](http://caret.io) - our Markdown editor for the desktop. diff --git a/vendor/erusev/parsedown/composer.json b/vendor/erusev/parsedown/composer.json new file mode 100644 index 00000000..1439b824 --- /dev/null +++ b/vendor/erusev/parsedown/composer.json @@ -0,0 +1,18 @@ +{ + "name": "erusev/parsedown", + "description": "Parser for Markdown.", + "keywords": ["markdown", "parser"], + "homepage": "http://parsedown.org", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Emanuil Rusev", + "email": "hello@erusev.com", + "homepage": "http://erusev.com" + } + ], + "autoload": { + "psr-0": {"Parsedown": ""} + } +}
\ No newline at end of file diff --git a/vendor/erusev/parsedown/phpunit.xml.dist b/vendor/erusev/parsedown/phpunit.xml.dist new file mode 100644 index 00000000..b2d5e9d4 --- /dev/null +++ b/vendor/erusev/parsedown/phpunit.xml.dist @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<phpunit bootstrap="test/bootstrap.php" colors="true"> + <testsuites> + <testsuite> + <file>test/ParsedownTest.php</file> + </testsuite> + </testsuites> +</phpunit>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/CommonMarkTest.php b/vendor/erusev/parsedown/test/CommonMarkTest.php new file mode 100644 index 00000000..9b8d1162 --- /dev/null +++ b/vendor/erusev/parsedown/test/CommonMarkTest.php @@ -0,0 +1,74 @@ +<?php + +/** + * Test Parsedown against the CommonMark spec. + * + * Some code based on the original JavaScript test runner by jgm. + * + * @link http://commonmark.org/ CommonMark + * @link http://git.io/8WtRvQ JavaScript test runner + */ +class CommonMarkTest extends PHPUnit_Framework_TestCase +{ + const SPEC_URL = 'https://raw.githubusercontent.com/jgm/stmd/master/spec.txt'; + + /** + * @dataProvider data + * @param $section + * @param $markdown + * @param $expectedHtml + */ + function test_($section, $markdown, $expectedHtml) + { + $Parsedown = new Parsedown(); + $Parsedown->setUrlsLinked(false); + + $actualHtml = $Parsedown->text($markdown); + $actualHtml = $this->normalizeMarkup($actualHtml); + + $this->assertEquals($expectedHtml, $actualHtml); + } + + function data() + { + $spec = file_get_contents(self::SPEC_URL); + $spec = strstr($spec, '<!-- END TESTS -->', true); + + $tests = array(); + $currentSection = ''; + + preg_replace_callback( + '/^\.\n([\s\S]*?)^\.\n([\s\S]*?)^\.$|^#{1,6} *(.*)$/m', + function($matches) use ( & $tests, & $currentSection, & $testCount) { + if (isset($matches[3]) and $matches[3]) { + $currentSection = $matches[3]; + } else { + $testCount++; + $markdown = $matches[1]; + $markdown = preg_replace('/→/', "\t", $markdown); + $expectedHtml = $matches[2]; + $expectedHtml = $this->normalizeMarkup($expectedHtml); + $tests []= array( + $currentSection, # section + $markdown, # markdown + $expectedHtml, # html + ); + } + }, + $spec + ); + + return $tests; + } + + private function normalizeMarkup($markup) + { + $markup = preg_replace("/\n+/", "\n", $markup); + $markup = preg_replace('/^\s+/m', '', $markup); + $markup = preg_replace('/^((?:<[\w]+>)+)\n/m', '$1', $markup); + $markup = preg_replace('/\n((?:<\/[\w]+>)+)$/m', '$1', $markup); + $markup = trim($markup); + + return $markup; + } +} diff --git a/vendor/erusev/parsedown/test/ParsedownTest.php b/vendor/erusev/parsedown/test/ParsedownTest.php new file mode 100644 index 00000000..c922ab1f --- /dev/null +++ b/vendor/erusev/parsedown/test/ParsedownTest.php @@ -0,0 +1,159 @@ +<?php + +class ParsedownTest extends PHPUnit_Framework_TestCase +{ + final function __construct($name = null, array $data = array(), $dataName = '') + { + $this->dirs = $this->initDirs(); + $this->Parsedown = $this->initParsedown(); + + parent::__construct($name, $data, $dataName); + } + + private $dirs, $Parsedown; + + /** + * @return array + */ + protected function initDirs() + { + $dirs []= dirname(__FILE__).'/data/'; + + return $dirs; + } + + /** + * @return Parsedown + */ + protected function initParsedown() + { + $Parsedown = new Parsedown(); + + return $Parsedown; + } + + /** + * @dataProvider data + * @param $test + * @param $dir + */ + function test_($test, $dir) + { + $markdown = file_get_contents($dir . $test . '.md'); + + $expectedMarkup = file_get_contents($dir . $test . '.html'); + + $expectedMarkup = str_replace("\r\n", "\n", $expectedMarkup); + $expectedMarkup = str_replace("\r", "\n", $expectedMarkup); + + $actualMarkup = $this->Parsedown->text($markdown); + + $this->assertEquals($expectedMarkup, $actualMarkup); + } + + function data() + { + $data = array(); + + foreach ($this->dirs as $dir) + { + $Folder = new DirectoryIterator($dir); + + foreach ($Folder as $File) + { + /** @var $File DirectoryIterator */ + + if ( ! $File->isFile()) + { + continue; + } + + $filename = $File->getFilename(); + + $extension = pathinfo($filename, PATHINFO_EXTENSION); + + if ($extension !== 'md') + { + continue; + } + + $basename = $File->getBasename('.md'); + + if (file_exists($dir . $basename . '.html')) + { + $data []= array($basename, $dir); + } + } + } + + return $data; + } + + public function test_no_markup() + { + $markdownWithHtml = <<<MARKDOWN_WITH_MARKUP +<div>_content_</div> + +sparse: + +<div> +<div class="inner"> +_content_ +</div> +</div> + +paragraph + +<style type="text/css"> + p { + color: red; + } +</style> + +comment + +<!-- html comment --> +MARKDOWN_WITH_MARKUP; + + $expectedHtml = <<<EXPECTED_HTML +<p><div><em>content</em></div></p> +<p>sparse:</p> +<p><div> +<div class="inner"> +<em>content</em> +</div> +</div></p> +<p>paragraph</p> +<p><style type="text/css"> +p { +color: red; +} +</style></p> +<p>comment</p> +<p><!-- html comment --></p> +EXPECTED_HTML; + $parsedownWithNoMarkup = new Parsedown(); + $parsedownWithNoMarkup->setMarkupEscaped(true); + $this->assertEquals($expectedHtml, $parsedownWithNoMarkup->text($markdownWithHtml)); + } + + public function testLateStaticBinding() + { + include 'test/TestParsedown.php'; + + $parsedown = Parsedown::instance(); + $this->assertInstanceOf('Parsedown', $parsedown); + + // After instance is already called on Parsedown + // subsequent calls with the same arguments return the same instance + $sameParsedown = TestParsedown::instance(); + $this->assertInstanceOf('Parsedown', $sameParsedown); + $this->assertSame($parsedown, $sameParsedown); + + $testParsedown = TestParsedown::instance('test late static binding'); + $this->assertInstanceOf('TestParsedown', $testParsedown); + + $sameInstanceAgain = TestParsedown::instance('test late static binding'); + $this->assertSame($testParsedown, $sameInstanceAgain); + } +} diff --git a/vendor/erusev/parsedown/test/TestParsedown.php b/vendor/erusev/parsedown/test/TestParsedown.php new file mode 100644 index 00000000..7024dfbc --- /dev/null +++ b/vendor/erusev/parsedown/test/TestParsedown.php @@ -0,0 +1,5 @@ +<?php + +class TestParsedown extends Parsedown +{ +} diff --git a/vendor/erusev/parsedown/test/bootstrap.php b/vendor/erusev/parsedown/test/bootstrap.php new file mode 100644 index 00000000..5f264d2f --- /dev/null +++ b/vendor/erusev/parsedown/test/bootstrap.php @@ -0,0 +1,3 @@ +<?php + +include 'Parsedown.php';
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/aesthetic_table.html b/vendor/erusev/parsedown/test/data/aesthetic_table.html new file mode 100644 index 00000000..88e1c2bd --- /dev/null +++ b/vendor/erusev/parsedown/test/data/aesthetic_table.html @@ -0,0 +1,18 @@ +<table> +<thead> +<tr> +<th>header 1</th> +<th>header 2</th> +</tr> +</thead> +<tbody> +<tr> +<td>cell 1.1</td> +<td>cell 1.2</td> +</tr> +<tr> +<td>cell 2.1</td> +<td>cell 2.2</td> +</tr> +</tbody> +</table>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/aesthetic_table.md b/vendor/erusev/parsedown/test/data/aesthetic_table.md new file mode 100644 index 00000000..5245e6c9 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/aesthetic_table.md @@ -0,0 +1,4 @@ +| header 1 | header 2 | +| -------- | -------- | +| cell 1.1 | cell 1.2 | +| cell 2.1 | cell 2.2 |
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/aligned_table.html b/vendor/erusev/parsedown/test/data/aligned_table.html new file mode 100644 index 00000000..c4acfcb6 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/aligned_table.html @@ -0,0 +1,21 @@ +<table> +<thead> +<tr> +<th style="text-align: left;">header 1</th> +<th style="text-align: center;">header 2</th> +<th style="text-align: right;">header 2</th> +</tr> +</thead> +<tbody> +<tr> +<td style="text-align: left;">cell 1.1</td> +<td style="text-align: center;">cell 1.2</td> +<td style="text-align: right;">cell 1.3</td> +</tr> +<tr> +<td style="text-align: left;">cell 2.1</td> +<td style="text-align: center;">cell 2.2</td> +<td style="text-align: right;">cell 2.3</td> +</tr> +</tbody> +</table>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/aligned_table.md b/vendor/erusev/parsedown/test/data/aligned_table.md new file mode 100644 index 00000000..69a45f90 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/aligned_table.md @@ -0,0 +1,4 @@ +| header 1 | header 2 | header 2 | +| :------- | :------: | -------: | +| cell 1.1 | cell 1.2 | cell 1.3 | +| cell 2.1 | cell 2.2 | cell 2.3 |
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/atx_heading.html b/vendor/erusev/parsedown/test/data/atx_heading.html new file mode 100644 index 00000000..751f8739 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/atx_heading.html @@ -0,0 +1,9 @@ +<h1>h1</h1> +<h2>h2</h2> +<h3>h3</h3> +<h4>h4</h4> +<h5>h5</h5> +<h6>h6</h6> +<p>####### not a heading</p> +<h1>closed h1</h1> +<p>#</p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/atx_heading.md b/vendor/erusev/parsedown/test/data/atx_heading.md new file mode 100644 index 00000000..ad97b44c --- /dev/null +++ b/vendor/erusev/parsedown/test/data/atx_heading.md @@ -0,0 +1,17 @@ +# h1 + +## h2 + +### h3 + +#### h4 + +##### h5 + +###### h6 + +####### not a heading + +# closed h1 # + +#
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/automatic_link.html b/vendor/erusev/parsedown/test/data/automatic_link.html new file mode 100644 index 00000000..50a94ba0 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/automatic_link.html @@ -0,0 +1 @@ +<p><a href="http://example.com">http://example.com</a></p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/automatic_link.md b/vendor/erusev/parsedown/test/data/automatic_link.md new file mode 100644 index 00000000..08d3bf46 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/automatic_link.md @@ -0,0 +1 @@ +<http://example.com>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/block-level_html.html b/vendor/erusev/parsedown/test/data/block-level_html.html new file mode 100644 index 00000000..6443a4a6 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/block-level_html.html @@ -0,0 +1,12 @@ +<div>_content_</div> +<p>paragraph</p> +<div> + <div class="inner"> + _content_ + </div> +</div> +<style type="text/css"> + p {color: #789;} +</style> +<div> + <a href="/">home</a></div>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/block-level_html.md b/vendor/erusev/parsedown/test/data/block-level_html.md new file mode 100644 index 00000000..17cbc22d --- /dev/null +++ b/vendor/erusev/parsedown/test/data/block-level_html.md @@ -0,0 +1,16 @@ +<div>_content_</div> + +paragraph + +<div> + <div class="inner"> + _content_ + </div> +</div> + +<style type="text/css"> + p {color: #789;} +</style> + +<div> + <a href="/">home</a></div>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/code_block.html b/vendor/erusev/parsedown/test/data/code_block.html new file mode 100644 index 00000000..889b02d9 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/code_block.html @@ -0,0 +1,8 @@ +<pre><code><?php + +$message = 'Hello World!'; +echo $message;</code></pre> +<hr /> +<pre><code>> not a quote +- not a list item +[not a reference]: http://foo.com</code></pre>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/code_block.md b/vendor/erusev/parsedown/test/data/code_block.md new file mode 100644 index 00000000..2cfc953c --- /dev/null +++ b/vendor/erusev/parsedown/test/data/code_block.md @@ -0,0 +1,10 @@ + <?php + + $message = 'Hello World!'; + echo $message; + +--- + + > not a quote + - not a list item + [not a reference]: http://foo.com
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/code_span.html b/vendor/erusev/parsedown/test/data/code_span.html new file mode 100644 index 00000000..5c4c231e --- /dev/null +++ b/vendor/erusev/parsedown/test/data/code_span.html @@ -0,0 +1,6 @@ +<p>a <code>code span</code></p> +<p><code>this is also a codespan</code> trailing text</p> +<p><code>and look at this one!</code></p> +<p>single backtick in a code span: <code>`</code></p> +<p>backtick-delimited string in a code span: <code>`foo`</code></p> +<p><code>sth `` sth</code></p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/code_span.md b/vendor/erusev/parsedown/test/data/code_span.md new file mode 100644 index 00000000..c2f1a744 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/code_span.md @@ -0,0 +1,11 @@ +a `code span` + +`this is also a codespan` trailing text + +`and look at this one!` + +single backtick in a code span: `` ` `` + +backtick-delimited string in a code span: `` `foo` `` + +`sth `` sth`
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/compound_blockquote.html b/vendor/erusev/parsedown/test/data/compound_blockquote.html new file mode 100644 index 00000000..37afb57a --- /dev/null +++ b/vendor/erusev/parsedown/test/data/compound_blockquote.html @@ -0,0 +1,9 @@ +<blockquote> +<h2>header</h2> +<p>paragraph</p> +<ul> +<li>li</li> +</ul> +<hr /> +<p>paragraph</p> +</blockquote>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/compound_blockquote.md b/vendor/erusev/parsedown/test/data/compound_blockquote.md new file mode 100644 index 00000000..80c4aed1 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/compound_blockquote.md @@ -0,0 +1,10 @@ +> header +> ------ +> +> paragraph +> +> - li +> +> --- +> +> paragraph
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/compound_emphasis.html b/vendor/erusev/parsedown/test/data/compound_emphasis.html new file mode 100644 index 00000000..178dd54b --- /dev/null +++ b/vendor/erusev/parsedown/test/data/compound_emphasis.html @@ -0,0 +1,2 @@ +<p><em><code>code</code></em> <strong><code>code</code></strong></p> +<p><em><code>code</code><strong><code>code</code></strong><code>code</code></em></p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/compound_emphasis.md b/vendor/erusev/parsedown/test/data/compound_emphasis.md new file mode 100644 index 00000000..6fe07f26 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/compound_emphasis.md @@ -0,0 +1,4 @@ +_`code`_ __`code`__ + +*`code`**`code`**`code`* + diff --git a/vendor/erusev/parsedown/test/data/compound_list.html b/vendor/erusev/parsedown/test/data/compound_list.html new file mode 100644 index 00000000..f5593c14 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/compound_list.html @@ -0,0 +1,12 @@ +<ul> +<li> +<p>paragraph</p> +<p>paragraph</p> +</li> +<li> +<p>paragraph</p> +<blockquote> +<p>quote</p> +</blockquote> +</li> +</ul>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/compound_list.md b/vendor/erusev/parsedown/test/data/compound_list.md new file mode 100644 index 00000000..ed7f0c60 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/compound_list.md @@ -0,0 +1,7 @@ +- paragraph + + paragraph + +- paragraph + + > quote
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/deeply_nested_list.html b/vendor/erusev/parsedown/test/data/deeply_nested_list.html new file mode 100644 index 00000000..d2c7e5ac --- /dev/null +++ b/vendor/erusev/parsedown/test/data/deeply_nested_list.html @@ -0,0 +1,12 @@ +<ul> +<li>li +<ul> +<li>li +<ul> +<li>li</li> +<li>li</li> +</ul></li> +<li>li</li> +</ul></li> +<li>li</li> +</ul>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/deeply_nested_list.md b/vendor/erusev/parsedown/test/data/deeply_nested_list.md new file mode 100644 index 00000000..76b7552d --- /dev/null +++ b/vendor/erusev/parsedown/test/data/deeply_nested_list.md @@ -0,0 +1,6 @@ +- li + - li + - li + - li + - li +- li
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/em_strong.html b/vendor/erusev/parsedown/test/data/em_strong.html new file mode 100644 index 00000000..323d60ae --- /dev/null +++ b/vendor/erusev/parsedown/test/data/em_strong.html @@ -0,0 +1,8 @@ +<p><strong><em>em strong</em></strong></p> +<p><strong><em>em strong</em> strong</strong></p> +<p><strong>strong <em>em strong</em></strong></p> +<p><strong>strong <em>em strong</em> strong</strong></p> +<p><strong><em>em strong</em></strong></p> +<p><strong><em>em strong</em> strong</strong></p> +<p><strong>strong <em>em strong</em></strong></p> +<p><strong>strong <em>em strong</em> strong</strong></p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/em_strong.md b/vendor/erusev/parsedown/test/data/em_strong.md new file mode 100644 index 00000000..9abeb3fd --- /dev/null +++ b/vendor/erusev/parsedown/test/data/em_strong.md @@ -0,0 +1,15 @@ +___em strong___ + +___em strong_ strong__ + +__strong _em strong___ + +__strong _em strong_ strong__ + +***em strong*** + +***em strong* strong** + +**strong *em strong*** + +**strong *em strong* strong**
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/email.html b/vendor/erusev/parsedown/test/data/email.html new file mode 100644 index 00000000..c40759c9 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/email.html @@ -0,0 +1 @@ +<p>my email is <a href="mailto:me@example.com">me@example.com</a></p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/email.md b/vendor/erusev/parsedown/test/data/email.md new file mode 100644 index 00000000..26b7b6cc --- /dev/null +++ b/vendor/erusev/parsedown/test/data/email.md @@ -0,0 +1 @@ +my email is <me@example.com>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/emphasis.html b/vendor/erusev/parsedown/test/data/emphasis.html new file mode 100644 index 00000000..60ff4bd8 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/emphasis.html @@ -0,0 +1,8 @@ +<p><em>underscore</em>, <em>asterisk</em>, <em>one two</em>, <em>three four</em>, <em>a</em>, <em>b</em></p> +<p><strong>strong</strong> and <em>em</em> and <strong>strong</strong> and <em>em</em></p> +<p><em>line +line +line</em></p> +<p>this_is_not_an_emphasis</p> +<p>an empty emphasis __ ** is not an emphasis</p> +<p>*mixed *<em>double and</em> single asterisk** spans</p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/emphasis.md b/vendor/erusev/parsedown/test/data/emphasis.md new file mode 100644 index 00000000..85b9d229 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/emphasis.md @@ -0,0 +1,13 @@ +_underscore_, *asterisk*, _one two_, *three four*, _a_, *b* + +**strong** and *em* and **strong** and *em* + +_line +line +line_ + +this_is_not_an_emphasis + +an empty emphasis __ ** is not an emphasis + +*mixed **double and* single asterisk** spans
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/escaping.html b/vendor/erusev/parsedown/test/data/escaping.html new file mode 100644 index 00000000..ab1c41fd --- /dev/null +++ b/vendor/erusev/parsedown/test/data/escaping.html @@ -0,0 +1,6 @@ +<p>escaped *emphasis*.</p> +<p><code>escaped \*emphasis\* in a code span</code></p> +<pre><code>escaped \*emphasis\* in a code block</code></pre> +<p>\ ` * _ { } [ ] ( ) > # + - . !</p> +<p><em>one_two</em> <strong>one_two</strong></p> +<p><em>one*two</em> <strong>one*two</strong></p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/escaping.md b/vendor/erusev/parsedown/test/data/escaping.md new file mode 100644 index 00000000..9f174e98 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/escaping.md @@ -0,0 +1,11 @@ +escaped \*emphasis\*. + +`escaped \*emphasis\* in a code span` + + escaped \*emphasis\* in a code block + +\\ \` \* \_ \{ \} \[ \] \( \) \> \# \+ \- \. \! + +_one\_two_ __one\_two__ + +*one\*two* **one\*two**
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/fenced_code_block.html b/vendor/erusev/parsedown/test/data/fenced_code_block.html new file mode 100644 index 00000000..8bdabba9 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/fenced_code_block.html @@ -0,0 +1,6 @@ +<pre><code><?php + +$message = 'fenced code block'; +echo $message;</code></pre> +<pre><code>tilde</code></pre> +<pre><code class="language-php">echo 'language identifier';</code></pre>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/fenced_code_block.md b/vendor/erusev/parsedown/test/data/fenced_code_block.md new file mode 100644 index 00000000..cbed8ebb --- /dev/null +++ b/vendor/erusev/parsedown/test/data/fenced_code_block.md @@ -0,0 +1,14 @@ +``` +<?php + +$message = 'fenced code block'; +echo $message; +``` + +~~~ +tilde +~~~ + +```php +echo 'language identifier'; +```
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/horizontal_rule.html b/vendor/erusev/parsedown/test/data/horizontal_rule.html new file mode 100644 index 00000000..68da03d0 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/horizontal_rule.html @@ -0,0 +1,5 @@ +<hr /> +<hr /> +<hr /> +<hr /> +<hr />
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/horizontal_rule.md b/vendor/erusev/parsedown/test/data/horizontal_rule.md new file mode 100644 index 00000000..bf461a92 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/horizontal_rule.md @@ -0,0 +1,9 @@ +--- + +- - - + + - - - + +*** + +___
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/html_comment.html b/vendor/erusev/parsedown/test/data/html_comment.html new file mode 100644 index 00000000..566dc3ad --- /dev/null +++ b/vendor/erusev/parsedown/test/data/html_comment.html @@ -0,0 +1,5 @@ +<!-- single line --> +<p>paragraph</p> +<!-- + multiline --> +<p>paragraph</p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/html_comment.md b/vendor/erusev/parsedown/test/data/html_comment.md new file mode 100644 index 00000000..6ddfdb44 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/html_comment.md @@ -0,0 +1,8 @@ +<!-- single line --> + +paragraph + +<!-- + multiline --> + +paragraph
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/html_entity.html b/vendor/erusev/parsedown/test/data/html_entity.html new file mode 100644 index 00000000..4d23e3cd --- /dev/null +++ b/vendor/erusev/parsedown/test/data/html_entity.html @@ -0,0 +1 @@ +<p>& © {</p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/html_entity.md b/vendor/erusev/parsedown/test/data/html_entity.md new file mode 100644 index 00000000..ff545ea5 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/html_entity.md @@ -0,0 +1 @@ +& © {
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/image_reference.html b/vendor/erusev/parsedown/test/data/image_reference.html new file mode 100644 index 00000000..67fbd2c8 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/image_reference.html @@ -0,0 +1,2 @@ +<p><img src="/md.png" alt="Markdown Logo" /></p> +<p>![missing reference]</p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/image_reference.md b/vendor/erusev/parsedown/test/data/image_reference.md new file mode 100644 index 00000000..1e11d947 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/image_reference.md @@ -0,0 +1,5 @@ +![Markdown Logo][image] + +[image]: /md.png + +![missing reference]
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/image_title.html b/vendor/erusev/parsedown/test/data/image_title.html new file mode 100644 index 00000000..957c9505 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/image_title.html @@ -0,0 +1,2 @@ +<p><img src="/md.png" alt="alt" title="title" /></p> +<p><img src="/md.png" alt="blank title" title="" /></p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/image_title.md b/vendor/erusev/parsedown/test/data/image_title.md new file mode 100644 index 00000000..7ce2849a --- /dev/null +++ b/vendor/erusev/parsedown/test/data/image_title.md @@ -0,0 +1,3 @@ + + +
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/implicit_reference.html b/vendor/erusev/parsedown/test/data/implicit_reference.html new file mode 100644 index 00000000..24b51c1b --- /dev/null +++ b/vendor/erusev/parsedown/test/data/implicit_reference.html @@ -0,0 +1,4 @@ +<p>an <a href="http://example.com">implicit</a> reference link</p> +<p>an <a href="http://example.com">implicit</a> reference link with an empty link definition</p> +<p>an <a href="http://example.com">implicit</a> reference link followed by <a href="http://cnn.com">another</a></p> +<p>an <a href="http://example.com" title="Example">explicit</a> reference link with a title</p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/implicit_reference.md b/vendor/erusev/parsedown/test/data/implicit_reference.md new file mode 100644 index 00000000..f850df96 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/implicit_reference.md @@ -0,0 +1,13 @@ +an [implicit] reference link + +[implicit]: http://example.com + +an [implicit][] reference link with an empty link definition + +an [implicit][] reference link followed by [another][] + +[another]: http://cnn.com + +an [explicit][example] reference link with a title + +[example]: http://example.com "Example"
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/inline_link.html b/vendor/erusev/parsedown/test/data/inline_link.html new file mode 100644 index 00000000..5ad564aa --- /dev/null +++ b/vendor/erusev/parsedown/test/data/inline_link.html @@ -0,0 +1,6 @@ +<p><a href="http://example.com">link</a></p> +<p><a href="/url-(parentheses)">link</a> with parentheses in URL </p> +<p>(<a href="/index.php">link</a>) in parentheses</p> +<p><a href="http://example.com"><code>link</code></a></p> +<p><a href="http://example.com"><img src="http://parsedown.org/md.png" alt="MD Logo" /></a></p> +<p><a href="http://example.com"><img src="http://parsedown.org/md.png" alt="MD Logo" /> and text</a></p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/inline_link.md b/vendor/erusev/parsedown/test/data/inline_link.md new file mode 100644 index 00000000..6bac0b35 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/inline_link.md @@ -0,0 +1,11 @@ +[link](http://example.com) + +[link](/url-(parentheses)) with parentheses in URL + +([link](/index.php)) in parentheses + +[`link`](http://example.com) + +[](http://example.com) + +[ and text](http://example.com)
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/inline_link_title.html b/vendor/erusev/parsedown/test/data/inline_link_title.html new file mode 100644 index 00000000..ecdfd03d --- /dev/null +++ b/vendor/erusev/parsedown/test/data/inline_link_title.html @@ -0,0 +1,6 @@ +<p><a href="http://example.com" title="Title">single quotes</a></p> +<p><a href="http://example.com" title="Title">double quotes</a></p> +<p><a href="http://example.com" title="">single quotes blank</a></p> +<p><a href="http://example.com" title="">double quotes blank</a></p> +<p><a href="http://example.com" title="2 Words">space</a></p> +<p><a href="http://example.com/url-(parentheses)" title="Title">parentheses</a></p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/inline_link_title.md b/vendor/erusev/parsedown/test/data/inline_link_title.md new file mode 100644 index 00000000..6e1c5af9 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/inline_link_title.md @@ -0,0 +1,11 @@ +[single quotes](http://example.com 'Title') + +[double quotes](http://example.com "Title") + +[single quotes blank](http://example.com '') + +[double quotes blank](http://example.com "") + +[space](http://example.com "2 Words") + +[parentheses](http://example.com/url-(parentheses) "Title")
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/inline_title.html b/vendor/erusev/parsedown/test/data/inline_title.html new file mode 100644 index 00000000..bbab93b6 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/inline_title.html @@ -0,0 +1 @@ +<p><a href="http://example.com" title="Example">single quotes</a> and <a href="http://example.com" title="Example">double quotes</a></p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/inline_title.md b/vendor/erusev/parsedown/test/data/inline_title.md new file mode 100644 index 00000000..cb09344a --- /dev/null +++ b/vendor/erusev/parsedown/test/data/inline_title.md @@ -0,0 +1 @@ +[single quotes](http://example.com 'Example') and [double quotes](http://example.com "Example")
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/lazy_blockquote.html b/vendor/erusev/parsedown/test/data/lazy_blockquote.html new file mode 100644 index 00000000..0a2a2aaf --- /dev/null +++ b/vendor/erusev/parsedown/test/data/lazy_blockquote.html @@ -0,0 +1,6 @@ +<blockquote> +<p>quote +the rest of it</p> +<p>another paragraph +the rest of it</p> +</blockquote>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/lazy_blockquote.md b/vendor/erusev/parsedown/test/data/lazy_blockquote.md new file mode 100644 index 00000000..48f645f9 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/lazy_blockquote.md @@ -0,0 +1,5 @@ +> quote +the rest of it + +> another paragraph +the rest of it
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/lazy_list.html b/vendor/erusev/parsedown/test/data/lazy_list.html new file mode 100644 index 00000000..1a519924 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/lazy_list.html @@ -0,0 +1,4 @@ +<ul> +<li>li +the rest of it</li> +</ul>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/lazy_list.md b/vendor/erusev/parsedown/test/data/lazy_list.md new file mode 100644 index 00000000..62ad9d71 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/lazy_list.md @@ -0,0 +1,2 @@ +- li +the rest of it
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/line_break.html b/vendor/erusev/parsedown/test/data/line_break.html new file mode 100644 index 00000000..5f37d854 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/line_break.html @@ -0,0 +1,2 @@ +<p>line<br /> +line</p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/line_break.md b/vendor/erusev/parsedown/test/data/line_break.md new file mode 100644 index 00000000..04dff43e --- /dev/null +++ b/vendor/erusev/parsedown/test/data/line_break.md @@ -0,0 +1,2 @@ +line +line
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/multiline_list_paragraph.html b/vendor/erusev/parsedown/test/data/multiline_list_paragraph.html new file mode 100644 index 00000000..3247bd22 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/multiline_list_paragraph.html @@ -0,0 +1,7 @@ +<ul> +<li> +<p>li</p> +<p>line +line</p> +</li> +</ul>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/multiline_list_paragraph.md b/vendor/erusev/parsedown/test/data/multiline_list_paragraph.md new file mode 100644 index 00000000..f5b42729 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/multiline_list_paragraph.md @@ -0,0 +1,4 @@ +- li + + line + line
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/nested_block-level_html.html b/vendor/erusev/parsedown/test/data/nested_block-level_html.html new file mode 100644 index 00000000..bfbef54d --- /dev/null +++ b/vendor/erusev/parsedown/test/data/nested_block-level_html.html @@ -0,0 +1,10 @@ +<div> +_parent_ +<div> +_child_ +</div> +<pre> +_adopted child_ +</pre> +</div> +<p><em>outside</em></p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/nested_block-level_html.md b/vendor/erusev/parsedown/test/data/nested_block-level_html.md new file mode 100644 index 00000000..5e01e109 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/nested_block-level_html.md @@ -0,0 +1,11 @@ +<div> +_parent_ +<div> +_child_ +</div> +<pre> +_adopted child_ +</pre> +</div> + +_outside_
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/ordered_list.html b/vendor/erusev/parsedown/test/data/ordered_list.html new file mode 100644 index 00000000..b6c5216c --- /dev/null +++ b/vendor/erusev/parsedown/test/data/ordered_list.html @@ -0,0 +1,13 @@ +<ol> +<li>one</li> +<li>two</li> +</ol> +<p>repeating numbers:</p> +<ol> +<li>one</li> +<li>two</li> +</ol> +<p>large numbers:</p> +<ol> +<li>one</li> +</ol>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/ordered_list.md b/vendor/erusev/parsedown/test/data/ordered_list.md new file mode 100644 index 00000000..b307032c --- /dev/null +++ b/vendor/erusev/parsedown/test/data/ordered_list.md @@ -0,0 +1,11 @@ +1. one +2. two + +repeating numbers: + +1. one +1. two + +large numbers: + +123. one
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/paragraph_list.html b/vendor/erusev/parsedown/test/data/paragraph_list.html new file mode 100644 index 00000000..ced1c43e --- /dev/null +++ b/vendor/erusev/parsedown/test/data/paragraph_list.html @@ -0,0 +1,12 @@ +<p>paragraph</p> +<ul> +<li>li</li> +<li>li</li> +</ul> +<p>paragraph</p> +<ul> +<li> +<p>li</p> +</li> +<li>li</li> +</ul>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/paragraph_list.md b/vendor/erusev/parsedown/test/data/paragraph_list.md new file mode 100644 index 00000000..b973908c --- /dev/null +++ b/vendor/erusev/parsedown/test/data/paragraph_list.md @@ -0,0 +1,9 @@ +paragraph +- li +- li + +paragraph + + * li + + * li
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/reference_title.html b/vendor/erusev/parsedown/test/data/reference_title.html new file mode 100644 index 00000000..8f2be944 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/reference_title.html @@ -0,0 +1,2 @@ +<p><a href="http://example.com" title="example title">double quotes</a> and <a href="http://example.com" title="example title">single quotes</a> and <a href="http://example.com" title="example title">parentheses</a></p> +<p>[invalid title]: <a href="http://example.com">http://example.com</a> example title</p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/reference_title.md b/vendor/erusev/parsedown/test/data/reference_title.md new file mode 100644 index 00000000..43cb2170 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/reference_title.md @@ -0,0 +1,6 @@ +[double quotes] and [single quotes] and [parentheses] + +[double quotes]: http://example.com "example title" +[single quotes]: http://example.com 'example title' +[parentheses]: http://example.com (example title) +[invalid title]: http://example.com example title
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/self-closing_html.html b/vendor/erusev/parsedown/test/data/self-closing_html.html new file mode 100644 index 00000000..4d072b43 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/self-closing_html.html @@ -0,0 +1,12 @@ +<hr> +<p>paragraph</p> +<hr/> +<p>paragraph</p> +<hr /> +<p>paragraph</p> +<hr class="foo" id="bar" /> +<p>paragraph</p> +<hr class="foo" id="bar"/> +<p>paragraph</p> +<hr class="foo" id="bar" > +<p>paragraph</p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/self-closing_html.md b/vendor/erusev/parsedown/test/data/self-closing_html.md new file mode 100644 index 00000000..acb20327 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/self-closing_html.md @@ -0,0 +1,12 @@ +<hr> +paragraph +<hr/> +paragraph +<hr /> +paragraph +<hr class="foo" id="bar" /> +paragraph +<hr class="foo" id="bar"/> +paragraph +<hr class="foo" id="bar" > +paragraph
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/separated_nested_list.html b/vendor/erusev/parsedown/test/data/separated_nested_list.html new file mode 100644 index 00000000..80a5cae2 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/separated_nested_list.html @@ -0,0 +1,9 @@ +<ul> +<li> +<p>li</p> +<ul> +<li>li</li> +<li>li</li> +</ul> +</li> +</ul>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/separated_nested_list.md b/vendor/erusev/parsedown/test/data/separated_nested_list.md new file mode 100644 index 00000000..d7cd1af7 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/separated_nested_list.md @@ -0,0 +1,4 @@ +- li + + - li + - li
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/setext_header.html b/vendor/erusev/parsedown/test/data/setext_header.html new file mode 100644 index 00000000..60aac081 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/setext_header.html @@ -0,0 +1,5 @@ +<h1>h1</h1> +<h2>h2</h2> +<h2>single character</h2> +<p>not a header</p> +<hr />
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/setext_header.md b/vendor/erusev/parsedown/test/data/setext_header.md new file mode 100644 index 00000000..c43b52c3 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/setext_header.md @@ -0,0 +1,12 @@ +h1 +== + +h2 +-- + +single character +- + +not a header + +------------
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/simple_blockquote.html b/vendor/erusev/parsedown/test/data/simple_blockquote.html new file mode 100644 index 00000000..8225d57c --- /dev/null +++ b/vendor/erusev/parsedown/test/data/simple_blockquote.html @@ -0,0 +1,11 @@ +<blockquote> +<p>quote</p> +</blockquote> +<p>indented:</p> +<blockquote> +<p>quote</p> +</blockquote> +<p>no space after <code>></code>:</p> +<blockquote> +<p>quote</p> +</blockquote>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/simple_blockquote.md b/vendor/erusev/parsedown/test/data/simple_blockquote.md new file mode 100644 index 00000000..22b6b11a --- /dev/null +++ b/vendor/erusev/parsedown/test/data/simple_blockquote.md @@ -0,0 +1,7 @@ +> quote + +indented: + > quote + +no space after `>`: +>quote
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/simple_table.html b/vendor/erusev/parsedown/test/data/simple_table.html new file mode 100644 index 00000000..237d7efb --- /dev/null +++ b/vendor/erusev/parsedown/test/data/simple_table.html @@ -0,0 +1,37 @@ +<table> +<thead> +<tr> +<th>header 1</th> +<th>header 2</th> +</tr> +</thead> +<tbody> +<tr> +<td>cell 1.1</td> +<td>cell 1.2</td> +</tr> +<tr> +<td>cell 2.1</td> +<td>cell 2.2</td> +</tr> +</tbody> +</table> +<hr /> +<table> +<thead> +<tr> +<th style="text-align: left;">header 1</th> +<th>header 2</th> +</tr> +</thead> +<tbody> +<tr> +<td style="text-align: left;">cell 1.1</td> +<td>cell 1.2</td> +</tr> +<tr> +<td style="text-align: left;">cell 2.1</td> +<td>cell 2.2</td> +</tr> +</tbody> +</table>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/simple_table.md b/vendor/erusev/parsedown/test/data/simple_table.md new file mode 100644 index 00000000..466d140e --- /dev/null +++ b/vendor/erusev/parsedown/test/data/simple_table.md @@ -0,0 +1,11 @@ +header 1 | header 2 +-------- | -------- +cell 1.1 | cell 1.2 +cell 2.1 | cell 2.2 + +--- + +header 1 | header 2 +:------- | -------- +cell 1.1 | cell 1.2 +cell 2.1 | cell 2.2
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/span-level_html.html b/vendor/erusev/parsedown/test/data/span-level_html.html new file mode 100644 index 00000000..f852a25a --- /dev/null +++ b/vendor/erusev/parsedown/test/data/span-level_html.html @@ -0,0 +1,5 @@ +<p>an <b>important</b> <a href=''>link</a></p> +<p>broken<br/> +line</p> +<p><b>inline tag</b> at the beginning</p> +<p><span><a href="http://example.com">http://example.com</a></span></p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/span-level_html.md b/vendor/erusev/parsedown/test/data/span-level_html.md new file mode 100644 index 00000000..f2219655 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/span-level_html.md @@ -0,0 +1,8 @@ +an <b>important</b> <a href=''>link</a> + +broken<br/> +line + +<b>inline tag</b> at the beginning + +<span>http://example.com</span>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/sparse_dense_list.html b/vendor/erusev/parsedown/test/data/sparse_dense_list.html new file mode 100644 index 00000000..095bc739 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/sparse_dense_list.html @@ -0,0 +1,7 @@ +<ul> +<li> +<p>li</p> +</li> +<li>li</li> +<li>li</li> +</ul>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/sparse_dense_list.md b/vendor/erusev/parsedown/test/data/sparse_dense_list.md new file mode 100644 index 00000000..57684227 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/sparse_dense_list.md @@ -0,0 +1,4 @@ +- li + +- li +- li
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/sparse_html.html b/vendor/erusev/parsedown/test/data/sparse_html.html new file mode 100644 index 00000000..9e896274 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/sparse_html.html @@ -0,0 +1,8 @@ +<div> +line 1 + +line 2 +line 3 + +line 4 +</div>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/sparse_html.md b/vendor/erusev/parsedown/test/data/sparse_html.md new file mode 100644 index 00000000..9e896274 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/sparse_html.md @@ -0,0 +1,8 @@ +<div> +line 1 + +line 2 +line 3 + +line 4 +</div>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/sparse_list.html b/vendor/erusev/parsedown/test/data/sparse_list.html new file mode 100644 index 00000000..452b2b86 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/sparse_list.html @@ -0,0 +1,15 @@ +<ul> +<li> +<p>li</p> +</li> +<li>li</li> +</ul> +<hr /> +<ul> +<li> +<p>li</p> +<ul> +<li>indented li</li> +</ul> +</li> +</ul>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/sparse_list.md b/vendor/erusev/parsedown/test/data/sparse_list.md new file mode 100644 index 00000000..362a35f5 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/sparse_list.md @@ -0,0 +1,9 @@ +- li + +- li + +--- + +- li + + - indented li
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/special_characters.html b/vendor/erusev/parsedown/test/data/special_characters.html new file mode 100644 index 00000000..3b652c33 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/special_characters.html @@ -0,0 +1,6 @@ +<p>AT&T has an ampersand in their name</p> +<p>this & that</p> +<p>4 < 5 and 6 > 5</p> +<p><a href="http://example.com/autolink?a=1&b=2">http://example.com/autolink?a=1&b=2</a></p> +<p><a href="/script?a=1&b=2">inline link</a></p> +<p><a href="http://example.com/?a=1&b=2">reference link</a></p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/special_characters.md b/vendor/erusev/parsedown/test/data/special_characters.md new file mode 100644 index 00000000..111b03b6 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/special_characters.md @@ -0,0 +1,13 @@ +AT&T has an ampersand in their name + +this & that + +4 < 5 and 6 > 5 + +<http://example.com/autolink?a=1&b=2> + +[inline link](/script?a=1&b=2) + +[reference link][1] + +[1]: http://example.com/?a=1&b=2
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/strikethrough.html b/vendor/erusev/parsedown/test/data/strikethrough.html new file mode 100644 index 00000000..2a9da982 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/strikethrough.html @@ -0,0 +1,3 @@ +<p><del>strikethrough</del></p> +<p>here's <del>one</del> followed by <del>another one</del></p> +<p>~~ this ~~ is not one neither is ~this~</p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/strikethrough.md b/vendor/erusev/parsedown/test/data/strikethrough.md new file mode 100644 index 00000000..d169144d --- /dev/null +++ b/vendor/erusev/parsedown/test/data/strikethrough.md @@ -0,0 +1,5 @@ +~~strikethrough~~ + +here's ~~one~~ followed by ~~another one~~ + +~~ this ~~ is not one neither is ~this~
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/strong_em.html b/vendor/erusev/parsedown/test/data/strong_em.html new file mode 100644 index 00000000..b709c991 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/strong_em.html @@ -0,0 +1,6 @@ +<p><em>em <strong>strong em</strong></em></p> +<p><em><strong>strong em</strong> em</em></p> +<p><em>em <strong>strong em</strong> em</em></p> +<p><em>em <strong>strong em</strong></em></p> +<p><em><strong>strong em</strong> em</em></p> +<p><em>em <strong>strong em</strong> em</em></p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/strong_em.md b/vendor/erusev/parsedown/test/data/strong_em.md new file mode 100644 index 00000000..f2aa3c78 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/strong_em.md @@ -0,0 +1,11 @@ +*em **strong em*** + +***strong em** em* + +*em **strong em** em* + +_em __strong em___ + +___strong em__ em_ + +_em __strong em__ em_
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/tab-indented_code_block.html b/vendor/erusev/parsedown/test/data/tab-indented_code_block.html new file mode 100644 index 00000000..7c140de7 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/tab-indented_code_block.html @@ -0,0 +1,6 @@ +<pre><code><?php + +$message = 'Hello World!'; +echo $message; + +echo "following a blank line";</code></pre>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/tab-indented_code_block.md b/vendor/erusev/parsedown/test/data/tab-indented_code_block.md new file mode 100644 index 00000000..a405a160 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/tab-indented_code_block.md @@ -0,0 +1,6 @@ + <?php + + $message = 'Hello World!'; + echo $message; + + echo "following a blank line";
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/table_inline_markdown.html b/vendor/erusev/parsedown/test/data/table_inline_markdown.html new file mode 100644 index 00000000..f4cebecb --- /dev/null +++ b/vendor/erusev/parsedown/test/data/table_inline_markdown.html @@ -0,0 +1,22 @@ +<table> +<thead> +<tr> +<th><em>header</em> 1</th> +<th>header 2</th> +</tr> +</thead> +<tbody> +<tr> +<td><em>cell</em> 1.1</td> +<td><del>cell</del> 1.2</td> +</tr> +<tr> +<td><code>|</code> 2.1</td> +<td>| 2.2</td> +</tr> +<tr> +<td><code>\|</code> 2.1</td> +<td><a href="/">link</a></td> +</tr> +</tbody> +</table>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/table_inline_markdown.md b/vendor/erusev/parsedown/test/data/table_inline_markdown.md new file mode 100644 index 00000000..2f3c6200 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/table_inline_markdown.md @@ -0,0 +1,5 @@ +| _header_ 1 | header 2 | +| ------------ | ------------ | +| _cell_ 1.1 | ~~cell~~ 1.2 | +| `|` 2.1 | \| 2.2 | +| `\|` 2.1 | [link](/) |
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/text_reference.html b/vendor/erusev/parsedown/test/data/text_reference.html new file mode 100644 index 00000000..11e4d37f --- /dev/null +++ b/vendor/erusev/parsedown/test/data/text_reference.html @@ -0,0 +1,8 @@ +<p><a href="http://example.com">reference link</a></p> +<p><a href="http://example.com">one</a> with a semantic name</p> +<p>[one][404] with no definition</p> +<p><a href="http://example.com">multiline +one</a> defined on 2 lines</p> +<p><a href="http://example.com">one</a> with a mixed case label and an upper case definition</p> +<p><a href="http://example.com">one</a> with the a label on the next line</p> +<p><a href="http://example.com"><code>link</code></a></p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/text_reference.md b/vendor/erusev/parsedown/test/data/text_reference.md new file mode 100644 index 00000000..1a66a5cf --- /dev/null +++ b/vendor/erusev/parsedown/test/data/text_reference.md @@ -0,0 +1,21 @@ +[reference link][1] + +[1]: http://example.com + +[one][website] with a semantic name + +[website]: http://example.com + +[one][404] with no definition + +[multiline +one][website] defined on 2 lines + +[one][Label] with a mixed case label and an upper case definition + +[LABEL]: http://example.com + +[one] +[1] with the a label on the next line + +[`link`][website]
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/unordered_list.html b/vendor/erusev/parsedown/test/data/unordered_list.html new file mode 100644 index 00000000..cd95567b --- /dev/null +++ b/vendor/erusev/parsedown/test/data/unordered_list.html @@ -0,0 +1,10 @@ +<ul> +<li>li</li> +<li>li</li> +</ul> +<p>mixed markers:</p> +<ul> +<li>li</li> +<li>li</li> +<li>li</li> +</ul>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/unordered_list.md b/vendor/erusev/parsedown/test/data/unordered_list.md new file mode 100644 index 00000000..cf62c99f --- /dev/null +++ b/vendor/erusev/parsedown/test/data/unordered_list.md @@ -0,0 +1,8 @@ +- li +- li + +mixed markers: + +* li ++ li +- li
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/untidy_table.html b/vendor/erusev/parsedown/test/data/untidy_table.html new file mode 100644 index 00000000..88e1c2bd --- /dev/null +++ b/vendor/erusev/parsedown/test/data/untidy_table.html @@ -0,0 +1,18 @@ +<table> +<thead> +<tr> +<th>header 1</th> +<th>header 2</th> +</tr> +</thead> +<tbody> +<tr> +<td>cell 1.1</td> +<td>cell 1.2</td> +</tr> +<tr> +<td>cell 2.1</td> +<td>cell 2.2</td> +</tr> +</tbody> +</table>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/untidy_table.md b/vendor/erusev/parsedown/test/data/untidy_table.md new file mode 100644 index 00000000..8524eb18 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/untidy_table.md @@ -0,0 +1,4 @@ +| header 1 | header 2 | +| ------------- | ----------- | +| cell 1.1 | cell 1.2 | +| cell 2.1 | cell 2.2 |
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/url_autolinking.html b/vendor/erusev/parsedown/test/data/url_autolinking.html new file mode 100644 index 00000000..58ca94c6 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/url_autolinking.html @@ -0,0 +1,3 @@ +<p>an autolink <a href="http://example.com">http://example.com</a></p> +<p>inside of brackets [<a href="http://example.com">http://example.com</a>], inside of braces {<a href="http://example.com">http://example.com</a>}, inside of parentheses (<a href="http://example.com">http://example.com</a>)</p> +<p>trailing slash <a href="http://example.com/">http://example.com/</a> and <a href="http://example.com/path/">http://example.com/path/</a></p>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/url_autolinking.md b/vendor/erusev/parsedown/test/data/url_autolinking.md new file mode 100644 index 00000000..840f3540 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/url_autolinking.md @@ -0,0 +1,5 @@ +an autolink http://example.com + +inside of brackets [http://example.com], inside of braces {http://example.com}, inside of parentheses (http://example.com) + +trailing slash http://example.com/ and http://example.com/path/
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/whitespace.html b/vendor/erusev/parsedown/test/data/whitespace.html new file mode 100644 index 00000000..f2dd7a00 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/whitespace.html @@ -0,0 +1 @@ +<pre><code>code</code></pre>
\ No newline at end of file diff --git a/vendor/erusev/parsedown/test/data/whitespace.md b/vendor/erusev/parsedown/test/data/whitespace.md new file mode 100644 index 00000000..4cf926a8 --- /dev/null +++ b/vendor/erusev/parsedown/test/data/whitespace.md @@ -0,0 +1,5 @@ + + + code + +
\ No newline at end of file diff --git a/vendor/fguillot/json-rpc/LICENSE b/vendor/fguillot/json-rpc/LICENSE new file mode 100644 index 00000000..6a362bc1 --- /dev/null +++ b/vendor/fguillot/json-rpc/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Frederic Guillot + +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. diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Client.php b/vendor/fguillot/json-rpc/src/JsonRPC/Client.php new file mode 100644 index 00000000..fed1ce30 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Client.php @@ -0,0 +1,194 @@ +<?php + +namespace JsonRPC; + +use Exception; +use JsonRPC\Request\RequestBuilder; +use JsonRPC\Response\ResponseParser; + +/** + * JsonRPC client class + * + * @package JsonRPC + * @author Frederic Guillot + */ +class Client +{ + /** + * If the only argument passed to a function is an array + * assume it contains named arguments + * + * @access private + * @var boolean + */ + private $isNamedArguments = true; + + /** + * Do not immediately throw an exception on error. Return it instead. + * + * @access public + * @var boolean + */ + private $returnException = false; + + /** + * True for a batch request + * + * @access private + * @var boolean + */ + private $isBatch = false; + + /** + * Batch payload + * + * @access private + * @var array + */ + private $batch = array(); + + /** + * Http Client + * + * @access private + * @var HttpClient + */ + private $httpClient; + + /** + * Constructor + * + * @access public + * @param string $url Server URL + * @param bool $returnException Return exceptions + * @param HttpClient $httpClient HTTP client object + */ + public function __construct($url = '', $returnException = false, HttpClient $httpClient = null) + { + $this->httpClient = $httpClient ?: new HttpClient($url); + $this->returnException = $returnException; + } + + /** + * Arguments passed are always positional + * + * @access public + * @return $this + */ + public function withPositionalArguments() + { + $this->isNamedArguments = false; + return $this; + } + + /** + * Get HTTP Client + * + * @access public + * @return HttpClient + */ + public function getHttpClient() + { + return $this->httpClient; + } + + /** + * Set username and password + * + * @access public + * @param string $username + * @param string $password + * @return $this + */ + public function authentication($username, $password) + { + $this->httpClient + ->withUsername($username) + ->withPassword($password); + + return $this; + } + + /** + * Automatic mapping of procedures + * + * @access public + * @param string $method Procedure name + * @param array $params Procedure arguments + * @return mixed + */ + public function __call($method, array $params) + { + if ($this->isNamedArguments && count($params) === 1 && is_array($params[0])) { + $params = $params[0]; + } + + return $this->execute($method, $params); + } + + /** + * Start a batch request + * + * @access public + * @return Client + */ + public function batch() + { + $this->isBatch = true; + $this->batch = array(); + return $this; + } + + /** + * Send a batch request + * + * @access public + * @return array + */ + public function send() + { + $this->isBatch = false; + return $this->sendPayload('['.implode(', ', $this->batch).']'); + } + + /** + * Execute a procedure + * + * @access public + * @param string $procedure Procedure name + * @param array $params Procedure arguments + * @param array $reqattrs + * @return mixed + */ + public function execute($procedure, array $params = array(), array $reqattrs = array()) + { + $payload = RequestBuilder::create() + ->withProcedure($procedure) + ->withParams($params) + ->withRequestAttributes($reqattrs) + ->build(); + + if ($this->isBatch) { + $this->batch[] = $payload; + return $this; + } + + return $this->sendPayload($payload); + } + + /** + * Send payload + * + * @access private + * @throws Exception + * @param string $payload + * @return Exception|Client + */ + private function sendPayload($payload) + { + return ResponseParser::create() + ->withReturnException($this->returnException) + ->withPayload($this->httpClient->execute($payload)) + ->parse(); + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Exception/AccessDeniedException.php b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/AccessDeniedException.php new file mode 100644 index 00000000..8cb9bc2b --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/AccessDeniedException.php @@ -0,0 +1,15 @@ +<?php + +namespace JsonRPC\Exception; + +use Exception; + +/** + * Class AccessDeniedException + * + * @package JsonRPC\Exception + * @author Frederic Guillot + */ +class AccessDeniedException extends Exception +{ +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Exception/AuthenticationFailureException.php b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/AuthenticationFailureException.php new file mode 100644 index 00000000..6237256a --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/AuthenticationFailureException.php @@ -0,0 +1,15 @@ +<?php + +namespace JsonRPC\Exception; + +use Exception; + +/** + * Class AuthenticationFailureException + * + * @package JsonRPC\Exception + * @author Frederic Guillot + */ +class AuthenticationFailureException extends Exception +{ +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ConnectionFailureException.php b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ConnectionFailureException.php new file mode 100644 index 00000000..6f4985bc --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ConnectionFailureException.php @@ -0,0 +1,15 @@ +<?php + +namespace JsonRPC\Exception; + +use Exception; + +/** + * Class ConnectionFailureException + * + * @package JsonRPC\Exception + * @author Frederic Guillot + */ +class ConnectionFailureException extends Exception +{ +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonFormatException.php b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonFormatException.php new file mode 100644 index 00000000..e5bb31cd --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonFormatException.php @@ -0,0 +1,15 @@ +<?php + +namespace JsonRPC\Exception; + +use Exception; + +/** + * Class InvalidJsonFormatException + * + * @package JsonRPC\Exception + * @author Frederic Guillot + */ +class InvalidJsonFormatException extends Exception +{ +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonRpcFormatException.php b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonRpcFormatException.php new file mode 100644 index 00000000..e2277379 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonRpcFormatException.php @@ -0,0 +1,15 @@ +<?php + +namespace JsonRPC\Exception; + +use Exception; + +/** + * Class InvalidJsonRpcFormatException + * + * @package JsonRPC\Exception + * @author Frederic Guillot + */ +class InvalidJsonRpcFormatException extends Exception +{ +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ResponseEncodingFailureException.php b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ResponseEncodingFailureException.php new file mode 100644 index 00000000..0a40d806 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ResponseEncodingFailureException.php @@ -0,0 +1,15 @@ +<?php + +namespace JsonRPC\Exception; + +use Exception; + +/** + * Class ResponseEncodingFailureException + * + * @package JsonRPC\Exception + * @author Frederic Guillot + */ +class ResponseEncodingFailureException extends Exception +{ +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ResponseException.php b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ResponseException.php new file mode 100644 index 00000000..98fb5c67 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ResponseException.php @@ -0,0 +1,62 @@ +<?php + +namespace JsonRPC\Exception; + +use Exception; + +/** + * Class ResponseException + * + * @package JsonRPC\Exception + * @author Frederic Guillot + */ +class ResponseException extends Exception +{ + /** + * A value that contains additional information about the error. + * + * @access protected + * @link http://www.jsonrpc.org/specification#error_object + * @var mixed + */ + protected $data; + + /** + * Constructor + * + * @access public + * @param string $message [optional] The Exception message to throw. + * @param int $code [optional] The Exception code. + * @param Exception $previous [optional] The previous exception used for the exception chaining. Since 5.3.0 + * @param mixed $data [optional] A value that contains additional information about the error. + */ + public function __construct($message = '', $code = 0, Exception $previous = null, $data = null) + { + parent::__construct($message, $code, $previous); + $this->setData($data); + } + + /** + * Attach additional information + * + * @access public + * @param mixed $data [optional] A value that contains additional information about the error. + * @return \JsonRPC\Exception\ResponseException + */ + public function setData($data = null) + { + $this->data = $data; + return $this; + } + + /** + * Get additional information + * + * @access public + * @return mixed|null + */ + public function getData() + { + return $this->data; + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ServerErrorException.php b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ServerErrorException.php new file mode 100644 index 00000000..ab3ea584 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ServerErrorException.php @@ -0,0 +1,15 @@ +<?php + +namespace JsonRPC\Exception; + +use Exception; + +/** + * Class ServerErrorException + * + * @package JsonRPC\Exception + * @author Frederic Guillot + */ +class ServerErrorException extends Exception +{ +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/HttpClient.php b/vendor/fguillot/json-rpc/src/JsonRPC/HttpClient.php new file mode 100644 index 00000000..90ce705a --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/HttpClient.php @@ -0,0 +1,365 @@ +<?php + +namespace JsonRPC; + +use Closure; +use JsonRPC\Exception\AccessDeniedException; +use JsonRPC\Exception\ConnectionFailureException; +use JsonRPC\Exception\ServerErrorException; + +/** + * Class HttpClient + * + * @package JsonRPC + * @author Frederic Guillot + */ +class HttpClient +{ + /** + * URL of the server + * + * @access private + * @var string + */ + private $url; + + /** + * HTTP client timeout + * + * @access private + * @var integer + */ + private $timeout = 5; + + /** + * Default HTTP headers to send to the server + * + * @access private + * @var array + */ + private $headers = array( + 'User-Agent: JSON-RPC PHP Client <https://github.com/fguillot/JsonRPC>', + 'Content-Type: application/json', + 'Accept: application/json', + 'Connection: close', + ); + + /** + * Username for authentication + * + * @access private + * @var string + */ + private $username; + + /** + * Password for authentication + * + * @access private + * @var string + */ + private $password; + + /** + * Enable debug output to the php error log + * + * @access private + * @var boolean + */ + private $debug = false; + + /** + * Cookies + * + * @access private + * @var array + */ + private $cookies = array(); + + /** + * SSL certificates verification + * + * @access private + * @var boolean + */ + private $verifySslCertificate = true; + + /** + * Callback called before the doing the request + * + * @access private + * @var Closure + */ + private $beforeRequest; + + /** + * HttpClient constructor + * + * @access public + * @param string $url + */ + public function __construct($url = '') + { + $this->url = $url; + } + + /** + * Set URL + * + * @access public + * @param string $url + * @return $this + */ + public function withUrl($url) + { + $this->url = $url; + return $this; + } + + /** + * Set username + * + * @access public + * @param string $username + * @return $this + */ + public function withUsername($username) + { + $this->username = $username; + return $this; + } + + /** + * Set password + * + * @access public + * @param string $password + * @return $this + */ + public function withPassword($password) + { + $this->password = $password; + return $this; + } + + /** + * Set timeout + * + * @access public + * @param integer $timeout + * @return $this + */ + public function withTimeout($timeout) + { + $this->timeout = $timeout; + return $this; + } + + /** + * Set timeout + * + * @access public + * @param array $headers + * @return $this + */ + public function withHeaders(array $headers) + { + $this->headers = array_merge($this->headers, $headers); + return $this; + } + + /** + * Set cookies + * + * @access public + * @param array $cookies + * @param boolean $replace + */ + public function withCookies(array $cookies, $replace = false) + { + if ($replace) { + $this->cookies = $cookies; + } else { + $this->cookies = array_merge($this->cookies, $cookies); + } + } + + /** + * Enable debug mode + * + * @access public + * @return $this + */ + public function withDebug() + { + $this->debug = true; + return $this; + } + + /** + * Disable SSL verification + * + * @access public + * @return $this + */ + public function withoutSslVerification() + { + $this->verifySslCertificate = false; + return $this; + } + + /** + * Assign a callback before the request + * + * @access public + * @param Closure $closure + * @return $this + */ + public function withBeforeRequestCallback(Closure $closure) + { + $this->beforeRequest = $closure; + return $this; + } + + /** + * Get cookies + * + * @access public + * @return array + */ + public function getCookies() + { + return $this->cookies; + } + + /** + * Do the HTTP request + * + * @access public + * @throws ConnectionFailureException + * @param string $payload + * @return array + */ + public function execute($payload) + { + if (is_callable($this->beforeRequest)) { + call_user_func_array($this->beforeRequest, array($this, $payload)); + } + + $stream = fopen(trim($this->url), 'r', false, $this->buildContext($payload)); + + if (! is_resource($stream)) { + throw new ConnectionFailureException('Unable to establish a connection'); + } + + $metadata = stream_get_meta_data($stream); + $headers = $metadata['wrapper_data']; + $response = json_decode(stream_get_contents($stream), true); + + if ($this->debug) { + error_log('==> Request: '.PHP_EOL.(is_string($payload) ? $payload : json_encode($payload, JSON_PRETTY_PRINT))); + error_log('==> Headers: '.PHP_EOL.var_export($headers, true)); + error_log('==> Response: '.PHP_EOL.json_encode($response, JSON_PRETTY_PRINT)); + } + + $this->handleExceptions($headers); + $this->parseCookies($headers); + + return $response; + } + + /** + * Prepare stream context + * + * @access private + * @param string $payload + * @return resource + */ + private function buildContext($payload) + { + $headers = $this->headers; + + if (! empty($this->username) && ! empty($this->password)) { + $headers[] = 'Authorization: Basic '.base64_encode($this->username.':'.$this->password); + } + + if (! empty($this->cookies)){ + $cookies = array(); + + foreach ($this->cookies as $key => $value) { + $cookies[] = $key.'='.$value; + } + + $headers[] = 'Cookie: '.implode('; ', $cookies); + } + + return stream_context_create(array( + 'http' => array( + 'method' => 'POST', + 'protocol_version' => 1.1, + 'timeout' => $this->timeout, + 'max_redirects' => 2, + 'header' => implode("\r\n", $headers), + 'content' => $payload, + 'ignore_errors' => true, + ), + 'ssl' => array( + 'verify_peer' => $this->verifySslCertificate, + 'verify_peer_name' => $this->verifySslCertificate, + ) + )); + } + + /** + * Parse cookies from response + * + * @access private + * @param array $headers + */ + private function parseCookies(array $headers) + { + foreach ($headers as $header) { + $pos = stripos($header, 'Set-Cookie:'); + + if ($pos !== false) { + $cookies = explode(';', substr($header, $pos + 11)); + + foreach ($cookies as $cookie) { + $item = explode('=', $cookie); + + if (count($item) === 2) { + $name = trim($item[0]); + $value = $item[1]; + $this->cookies[$name] = $value; + } + } + } + } + } + + /** + * Throw an exception according the HTTP response + * + * @access public + * @param array $headers + * @throws AccessDeniedException + * @throws ServerErrorException + */ + public function handleExceptions(array $headers) + { + $exceptions = array( + '401' => '\JsonRPC\Exception\AccessDeniedException', + '403' => '\JsonRPC\Exception\AccessDeniedException', + '404' => '\JsonRPC\Exception\ConnectionFailureException', + '500' => '\JsonRPC\Exception\ServerErrorException', + ); + + foreach ($headers as $header) { + foreach ($exceptions as $code => $exception) { + if (strpos($header, 'HTTP/1.0 '.$code) !== false || strpos($header, 'HTTP/1.1 '.$code) !== false) { + throw new $exception('Response: '.$header); + } + } + } + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareHandler.php b/vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareHandler.php new file mode 100644 index 00000000..61d5a2d2 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareHandler.php @@ -0,0 +1,114 @@ +<?php + +namespace JsonRPC; + +/** + * Class MiddlewareHandler + * + * @package JsonRPC + * @author Frederic Guillot + */ +class MiddlewareHandler +{ + /** + * Procedure Name + * + * @access protected + * @var string + */ + protected $procedureName = ''; + + /** + * Username + * + * @access protected + * @var string + */ + protected $username = ''; + + /** + * Password + * + * @access protected + * @var string + */ + protected $password = ''; + + /** + * List of middleware to execute before to call the method + * + * @access protected + * @var MiddlewareInterface[] + */ + protected $middleware = array(); + + /** + * Set username + * + * @access public + * @param string $username + * @return $this + */ + public function withUsername($username) + { + if (! empty($username)) { + $this->username = $username; + } + + return $this; + } + + /** + * Set password + * + * @access public + * @param string $password + * @return $this + */ + public function withPassword($password) + { + if (! empty($password)) { + $this->password = $password; + } + + return $this; + } + + /** + * Set procedure name + * + * @access public + * @param string $procedureName + * @return $this + */ + public function withProcedure($procedureName) + { + $this->procedureName = $procedureName; + return $this; + } + + /** + * Add a new middleware + * + * @access public + * @param MiddlewareInterface $middleware + * @return MiddlewareHandler + */ + public function withMiddleware(MiddlewareInterface $middleware) + { + $this->middleware[] = $middleware; + return $this; + } + + /** + * Execute all middleware + * + * @access public + */ + public function execute() + { + foreach ($this->middleware as $middleware) { + $middleware->execute($this->username, $this->password, $this->procedureName); + } + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareInterface.php b/vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareInterface.php new file mode 100644 index 00000000..ab55261d --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareInterface.php @@ -0,0 +1,27 @@ +<?php + +namespace JsonRPC; + +use JsonRPC\Exception\AccessDeniedException; +use JsonRPC\Exception\AuthenticationFailureException; + +/** + * Interface MiddlewareInterface + * + * @package JsonRPC + * @author Frederic Guillot + */ +interface MiddlewareInterface +{ + /** + * Execute Middleware + * + * @access public + * @param string $username + * @param string $password + * @param string $procedureName + * @throws AccessDeniedException + * @throws AuthenticationFailureException + */ + public function execute($username, $password, $procedureName); +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/ProcedureHandler.php b/vendor/fguillot/json-rpc/src/JsonRPC/ProcedureHandler.php new file mode 100644 index 00000000..1e4fd518 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/ProcedureHandler.php @@ -0,0 +1,264 @@ +<?php + +namespace JsonRPC; + +use BadFunctionCallException; +use Closure; +use InvalidArgumentException; +use ReflectionFunction; +use ReflectionMethod; + +/** + * Class ProcedureHandler + * + * @package JsonRPC + * @author Frederic Guillot + */ +class ProcedureHandler +{ + /** + * List of procedures + * + * @access protected + * @var array + */ + protected $callbacks = array(); + + /** + * List of classes + * + * @access protected + * @var array + */ + protected $classes = array(); + + /** + * List of instances + * + * @access protected + * @var array + */ + protected $instances = array(); + + /** + * Before method name to call + * + * @access protected + * @var string + */ + protected $beforeMethodName = ''; + + /** + * Register a new procedure + * + * @access public + * @param string $procedure Procedure name + * @param closure $callback Callback + * @return $this + */ + public function withCallback($procedure, Closure $callback) + { + $this->callbacks[$procedure] = $callback; + return $this; + } + + /** + * Bind a procedure to a class + * + * @access public + * @param string $procedure Procedure name + * @param mixed $class Class name or instance + * @param string $method Procedure name + * @return $this + */ + public function withClassAndMethod($procedure, $class, $method = '') + { + if ($method === '') { + $method = $procedure; + } + + $this->classes[$procedure] = array($class, $method); + return $this; + } + + /** + * Bind a class instance + * + * @access public + * @param mixed $instance + * @return $this + */ + public function withObject($instance) + { + $this->instances[] = $instance; + return $this; + } + + /** + * Set a before method to call + * + * @access public + * @param string $methodName + * @return $this + */ + public function withBeforeMethod($methodName) + { + $this->beforeMethodName = $methodName; + return $this; + } + + /** + * Execute the procedure + * + * @access public + * @param string $procedure Procedure name + * @param array $params Procedure params + * @return mixed + */ + public function executeProcedure($procedure, array $params = array()) + { + if (isset($this->callbacks[$procedure])) { + return $this->executeCallback($this->callbacks[$procedure], $params); + } elseif (isset($this->classes[$procedure]) && method_exists($this->classes[$procedure][0], $this->classes[$procedure][1])) { + return $this->executeMethod($this->classes[$procedure][0], $this->classes[$procedure][1], $params); + } + + foreach ($this->instances as $instance) { + if (method_exists($instance, $procedure)) { + return $this->executeMethod($instance, $procedure, $params); + } + } + + throw new BadFunctionCallException('Unable to find the procedure'); + } + + /** + * Execute a callback + * + * @access public + * @param Closure $callback Callback + * @param array $params Procedure params + * @return mixed + */ + public function executeCallback(Closure $callback, $params) + { + $reflection = new ReflectionFunction($callback); + + $arguments = $this->getArguments( + $params, + $reflection->getParameters(), + $reflection->getNumberOfRequiredParameters(), + $reflection->getNumberOfParameters() + ); + + return $reflection->invokeArgs($arguments); + } + + /** + * Execute a method + * + * @access public + * @param mixed $class Class name or instance + * @param string $method Method name + * @param array $params Procedure params + * @return mixed + */ + public function executeMethod($class, $method, $params) + { + $instance = is_string($class) ? new $class : $class; + $reflection = new ReflectionMethod($class, $method); + + $this->executeBeforeMethod($instance, $method); + + $arguments = $this->getArguments( + $params, + $reflection->getParameters(), + $reflection->getNumberOfRequiredParameters(), + $reflection->getNumberOfParameters() + ); + + return $reflection->invokeArgs($instance, $arguments); + } + + /** + * Execute before method if defined + * + * @access public + * @param mixed $object + * @param string $method + */ + public function executeBeforeMethod($object, $method) + { + if ($this->beforeMethodName !== '' && method_exists($object, $this->beforeMethodName)) { + call_user_func_array(array($object, $this->beforeMethodName), array($method)); + } + } + + /** + * Get procedure arguments + * + * @access public + * @param array $requestParams Incoming arguments + * @param array $methodParams Procedure arguments + * @param integer $nbRequiredParams Number of required parameters + * @param integer $nbMaxParams Maximum number of parameters + * @return array + */ + public function getArguments(array $requestParams, array $methodParams, $nbRequiredParams, $nbMaxParams) + { + $nbParams = count($requestParams); + + if ($nbParams < $nbRequiredParams) { + throw new InvalidArgumentException('Wrong number of arguments'); + } + + if ($nbParams > $nbMaxParams) { + throw new InvalidArgumentException('Too many arguments'); + } + + if ($this->isPositionalArguments($requestParams)) { + return $requestParams; + } + + return $this->getNamedArguments($requestParams, $methodParams); + } + + /** + * Return true if we have positional parameters + * + * @access public + * @param array $request_params Incoming arguments + * @return bool + */ + public function isPositionalArguments(array $request_params) + { + return array_keys($request_params) === range(0, count($request_params) - 1); + } + + /** + * Get named arguments + * + * @access public + * @param array $requestParams Incoming arguments + * @param array $methodParams Procedure arguments + * @return array + */ + public function getNamedArguments(array $requestParams, array $methodParams) + { + $params = array(); + + foreach ($methodParams as $p) { + $name = $p->getName(); + + if (isset($requestParams[$name])) { + $params[$name] = $requestParams[$name]; + } elseif ($p->isDefaultValueAvailable()) { + $params[$name] = $p->getDefaultValue(); + } else { + throw new InvalidArgumentException('Missing argument: '.$name); + } + } + + return $params; + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Request/BatchRequestParser.php b/vendor/fguillot/json-rpc/src/JsonRPC/Request/BatchRequestParser.php new file mode 100644 index 00000000..c0fc776e --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Request/BatchRequestParser.php @@ -0,0 +1,55 @@ +<?php + +namespace JsonRPC\Request; + +/** + * Class BatchRequestParser + * + * @package JsonRPC\Request + * @author Frederic Guillot + */ +class BatchRequestParser extends RequestParser +{ + /** + * Parse incoming request + * + * @access public + * @return string + */ + public function parse() + { + $responses = array(); + + foreach ($this->payload as $payload) { + $responses[] = RequestParser::create() + ->withPayload($payload) + ->withProcedureHandler($this->procedureHandler) + ->withMiddlewareHandler($this->middlewareHandler) + ->withLocalException($this->localExceptions) + ->parse(); + } + + $responses = array_filter($responses); + return empty($responses) ? '' : '['.implode(',', $responses).']'; + } + + /** + * Return true if we have a batch request + * + * ex : [ + * 0 => '...', + * 1 => '...', + * 2 => '...', + * 3 => '...', + * ] + * + * @static + * @access public + * @param array $payload + * @return bool + */ + public static function isBatchRequest(array $payload) + { + return array_keys($payload) === range(0, count($payload) - 1); + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestBuilder.php b/vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestBuilder.php new file mode 100644 index 00000000..145d21c1 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestBuilder.php @@ -0,0 +1,129 @@ +<?php + +namespace JsonRPC\Request; + +/** + * Class RequestBuilder + * + * @package JsonRPC\Request + * @author Frederic Guillot + */ +class RequestBuilder +{ + /** + * Request ID + * + * @access private + * @var mixed + */ + private $id = null; + + /** + * Method name + * + * @access private + * @var string + */ + private $procedure = ''; + + /** + * Method arguments + * + * @access private + * @var array + */ + private $params = array(); + + /** + * Additional request attributes + * + * @access private + * @var array + */ + private $reqattrs = array(); + + /** + * Get new object instance + * + * @static + * @access public + * @return RequestBuilder + */ + public static function create() + { + return new static(); + } + + /** + * Set id + * + * @access public + * @param null $id + * @return RequestBuilder + */ + public function withId($id) + { + $this->id = $id; + return $this; + } + + /** + * Set method + * + * @access public + * @param string $procedure + * @return RequestBuilder + */ + public function withProcedure($procedure) + { + $this->procedure = $procedure; + return $this; + } + + /** + * Set parameters + * + * @access public + * @param array $params + * @return RequestBuilder + */ + public function withParams(array $params) + { + $this->params = $params; + return $this; + } + + /** + * Set additional request attributes + * + * @access public + * @param array $reqattrs + * @return RequestBuilder + */ + public function withRequestAttributes(array $reqattrs) + { + $this->reqattrs = $reqattrs; + return $this; + } + + /** + * Build the payload + * + * @access public + * @return string + */ + public function build() + { + $payload = array_merge_recursive($this->reqattrs, array( + 'jsonrpc' => '2.0', + 'method' => $this->procedure, + 'id' => $this->id ?: mt_rand(), + )); + + if (! empty($this->params)) { + $payload['params'] = $this->params; + } + + return json_encode($payload); + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestParser.php b/vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestParser.php new file mode 100644 index 00000000..ea1b7d43 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestParser.php @@ -0,0 +1,200 @@ +<?php + +namespace JsonRPC\Request; + +use Exception; +use JsonRPC\Exception\AccessDeniedException; +use JsonRPC\Exception\AuthenticationFailureException; +use JsonRPC\Exception\InvalidJsonRpcFormatException; +use JsonRPC\MiddlewareHandler; +use JsonRPC\ProcedureHandler; +use JsonRPC\Response\ResponseBuilder; +use JsonRPC\Validator\JsonFormatValidator; +use JsonRPC\Validator\RpcFormatValidator; + +/** + * Class RequestParser + * + * @package JsonRPC + * @author Frederic Guillot + */ +class RequestParser +{ + /** + * Request payload + * + * @access protected + * @var mixed + */ + protected $payload; + + /** + * List of exceptions that should not be relayed to the client + * + * @access protected + * @var array() + */ + protected $localExceptions = array( + 'JsonRPC\Exception\AuthenticationFailureException', + 'JsonRPC\Exception\AccessDeniedException', + ); + + /** + * ProcedureHandler + * + * @access protected + * @var ProcedureHandler + */ + protected $procedureHandler; + + /** + * MiddlewareHandler + * + * @access protected + * @var MiddlewareHandler + */ + protected $middlewareHandler; + + /** + * Get new object instance + * + * @static + * @access public + * @return RequestParser + */ + public static function create() + { + return new static(); + } + + /** + * Set payload + * + * @access public + * @param mixed $payload + * @return $this + */ + public function withPayload($payload) + { + $this->payload = $payload; + return $this; + } + + /** + * Exception classes that should not be relayed to the client + * + * @access public + * @param mixed $exception + * @return $this + */ + public function withLocalException($exception) + { + if (is_array($exception)) { + $this->localExceptions = array_merge($this->localExceptions, $exception); + } else { + $this->localExceptions[] = $exception; + } + + return $this; + } + + /** + * Set procedure handler + * + * @access public + * @param ProcedureHandler $procedureHandler + * @return $this + */ + public function withProcedureHandler(ProcedureHandler $procedureHandler) + { + $this->procedureHandler = $procedureHandler; + return $this; + } + + /** + * Set middleware handler + * + * @access public + * @param MiddlewareHandler $middlewareHandler + * @return $this + */ + public function withMiddlewareHandler(MiddlewareHandler $middlewareHandler) + { + $this->middlewareHandler = $middlewareHandler; + return $this; + } + + /** + * Parse incoming request + * + * @access public + * @return string + * @throws AccessDeniedException + * @throws AuthenticationFailureException + */ + public function parse() + { + try { + + JsonFormatValidator::validate($this->payload); + RpcFormatValidator::validate($this->payload); + + $this->middlewareHandler + ->withProcedure($this->payload['method']) + ->execute(); + + $result = $this->procedureHandler->executeProcedure( + $this->payload['method'], + empty($this->payload['params']) ? array() : $this->payload['params'] + ); + + if (! $this->isNotification()) { + return ResponseBuilder::create() + ->withId($this->payload['id']) + ->withResult($result) + ->build(); + } + } catch (Exception $e) { + return $this->handleExceptions($e); + } + + return ''; + } + + /** + * Handle exceptions + * + * @access protected + * @param Exception $e + * @return string + * @throws Exception + */ + protected function handleExceptions(Exception $e) + { + foreach ($this->localExceptions as $exception) { + if ($e instanceof $exception) { + throw $e; + } + } + + if ($e instanceof InvalidJsonRpcFormatException || ! $this->isNotification()) { + return ResponseBuilder::create() + ->withId(isset($this->payload['id']) ? $this->payload['id'] : null) + ->withException($e) + ->build(); + } + + return ''; + } + + /** + * Return true if the message is a notification + * + * @access protected + * @return bool + */ + protected function isNotification() + { + return is_array($this->payload) && !isset($this->payload['id']); + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseBuilder.php b/vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseBuilder.php new file mode 100644 index 00000000..c1caff92 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseBuilder.php @@ -0,0 +1,324 @@ +<?php + +namespace JsonRPC\Response; + +use BadFunctionCallException; +use Exception; +use InvalidArgumentException; +use JsonRPC\Exception\AccessDeniedException; +use JsonRPC\Exception\AuthenticationFailureException; +use JsonRPC\Exception\InvalidJsonFormatException; +use JsonRPC\Exception\InvalidJsonRpcFormatException; +use JsonRPC\Exception\ResponseEncodingFailureException; +use JsonRPC\Exception\ResponseException; +use JsonRPC\Validator\JsonEncodingValidator; + +/** + * Class ResponseBuilder + * + * @package JsonRPC + * @author Frederic Guillot + */ +class ResponseBuilder +{ + /** + * Payload ID + * + * @access private + * @var mixed + */ + private $id; + + /** + * Payload ID + * + * @access private + * @var mixed + */ + private $result; + + /** + * Payload error code + * + * @access private + * @var integer + */ + private $errorCode; + + /** + * Payload error message + * + * @access private + * @var string + */ + private $errorMessage; + + /** + * Payload error data + * + * @access private + * @var mixed + */ + private $errorData; + + /** + * HTTP Headers + * + * @access private + * @var array + */ + private $headers = array( + 'Content-Type' => 'application/json', + ); + + /** + * HTTP status + * + * @access private + * @var string + */ + private $status; + + /** + * Exception + * + * @access private + * @var ResponseException + */ + private $exception; + + /** + * Get new object instance + * + * @static + * @access public + * @return ResponseBuilder + */ + public static function create() + { + return new static(); + } + + /** + * Set id + * + * @access public + * @param mixed $id + * @return $this + */ + public function withId($id) + { + $this->id = $id; + return $this; + } + + /** + * Set result + * + * @access public + * @param mixed $result + * @return $this + */ + public function withResult($result) + { + $this->result = $result; + return $this; + } + + /** + * Set error + * + * @access public + * @param integer $code + * @param string $message + * @param string $data + * @return $this + */ + public function withError($code, $message, $data = '') + { + $this->errorCode = $code; + $this->errorMessage = $message; + $this->errorData = $data; + return $this; + } + + /** + * Set exception + * + * @access public + * @param Exception $exception + * @return $this + */ + public function withException(Exception $exception) + { + $this->exception = $exception; + return $this; + } + + /** + * Add HTTP header + * + * @access public + * @param string $name + * @param string $value + * @return $this + */ + public function withHeader($name, $value) + { + $this->headers[$name] = $value; + return $this; + } + + /** + * Add HTTP Status + * + * @access public + * @param string $status + * @return $this + */ + public function withStatus($status) + { + $this->status = $status; + return $this; + } + + /** + * Get status + * + * @access public + * @return string + */ + public function getStatus() + { + return $this->status; + } + + /** + * Get headers + * + * @access public + * @return string[] + */ + public function getHeaders() + { + return $this->headers; + } + + /** + * Build response + * + * @access public + * @return string + */ + public function build() + { + $encodedResponse = json_encode($this->buildResponse()); + JsonEncodingValidator::validate(); + + return $encodedResponse; + } + + /** + * Send HTTP headers + * + * @access public + * @return $this + */ + public function sendHeaders() + { + if (! empty($this->status)) { + header($this->status); + } + + foreach ($this->headers as $name => $value) { + header($name.': '.$value); + } + + return $this; + } + + /** + * Build response payload + * + * @access private + * @return array + */ + private function buildResponse() + { + $response = array('jsonrpc' => '2.0'); + $this->handleExceptions(); + + if (! empty($this->errorMessage)) { + $response['error'] = $this->buildErrorResponse(); + } else { + $response['result'] = $this->result; + } + + $response['id'] = $this->id; + return $response; + } + + /** + * Build response error payload + * + * @access private + * @return array + */ + private function buildErrorResponse() + { + $response = array( + 'code' => $this->errorCode, + 'message' => $this->errorMessage, + ); + + if (! empty($this->errorData)) { + $response['data'] = $this->errorData; + } + + return $response; + } + + /** + * Transform exceptions to JSON-RPC errors + * + * @access private + */ + private function handleExceptions() + { + if ($this->exception instanceof InvalidJsonFormatException) { + $this->errorCode = -32700; + $this->errorMessage = 'Parse error'; + $this->id = null; + } elseif ($this->exception instanceof InvalidJsonRpcFormatException) { + $this->errorCode = -32600; + $this->errorMessage = 'Invalid Request'; + $this->id = null; + } elseif ($this->exception instanceof BadFunctionCallException) { + $this->errorCode = -32601; + $this->errorMessage = 'Method not found'; + } elseif ($this->exception instanceof InvalidArgumentException) { + $this->errorCode = -32602; + $this->errorMessage = 'Invalid params'; + } elseif ($this->exception instanceof ResponseEncodingFailureException) { + $this->errorCode = -32603; + $this->errorMessage = 'Internal error'; + $this->errorData = $this->exception->getMessage(); + } elseif ($this->exception instanceof AuthenticationFailureException) { + $this->errorCode = 401; + $this->errorMessage = 'Unauthorized'; + $this->status = 'HTTP/1.0 401 Unauthorized'; + $this->withHeader('WWW-Authenticate', 'Basic realm="JsonRPC"'); + } elseif ($this->exception instanceof AccessDeniedException) { + $this->errorCode = 403; + $this->errorMessage = 'Forbidden'; + $this->status = 'HTTP/1.0 403 Forbidden'; + } elseif ($this->exception instanceof ResponseException) { + $this->errorCode = $this->exception->getCode(); + $this->errorMessage = $this->exception->getMessage(); + $this->errorData = $this->exception->getData(); + } elseif ($this->exception instanceof Exception) { + $this->errorCode = $this->exception->getCode(); + $this->errorMessage = $this->exception->getMessage(); + } + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseParser.php b/vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseParser.php new file mode 100644 index 00000000..02d449ba --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseParser.php @@ -0,0 +1,154 @@ +<?php + +namespace JsonRPC\Response; + +use BadFunctionCallException; +use InvalidArgumentException; +use Exception; +use JsonRPC\Exception\InvalidJsonFormatException; +use JsonRPC\Exception\InvalidJsonRpcFormatException; +use JsonRPC\Exception\ResponseException; +use JsonRPC\Validator\JsonFormatValidator; + +/** + * Class ResponseParser + * + * @package JsonRPC\Request + * @author Frederic Guillot + */ +class ResponseParser +{ + /** + * Payload + * + * @access private + * @var mixed + */ + private $payload; + + /** + * Do not immediately throw an exception on error. Return it instead. + * + * @var bool + */ + private $returnException = false; + + /** + * Get new object instance + * + * @static + * @access public + * @return ResponseParser + */ + public static function create() + { + return new static(); + } + + /** + * Set Return Exception Or Throw It + * + * @param $returnException + * @return ResponseParser + */ + public function withReturnException($returnException) + { + $this->returnException = $returnException; + return $this; + } + + /** + * Set payload + * + * @access public + * @param mixed $payload + * @return $this + */ + public function withPayload($payload) + { + $this->payload = $payload; + return $this; + } + + /** + * Parse response + * + * @return array|Exception|null + * @throws InvalidJsonFormatException + * @throws BadFunctionCallException + * @throws InvalidJsonRpcFormatException + * @throws InvalidArgumentException + * @throws Exception + * @throws ResponseException + */ + public function parse() + { + JsonFormatValidator::validate($this->payload); + + if ($this->isBatchResponse()) { + $results = array(); + + foreach ($this->payload as $response) { + $results[] = self::create() + ->withReturnException($this->returnException) + ->withPayload($response) + ->parse(); + } + + return $results; + } + + if (isset($this->payload['error']['code'])) { + try { + $this->handleExceptions(); + } catch (Exception $e) { + if ($this->returnException) { + return $e; + } + throw $e; + } + } + + return isset($this->payload['result']) ? $this->payload['result'] : null; + } + + /** + * Handle exceptions + * + * @access private + * @throws InvalidJsonFormatException + * @throws InvalidJsonRpcFormatException + * @throws ResponseException + */ + private function handleExceptions() + { + switch ($this->payload['error']['code']) { + case -32700: + throw new InvalidJsonFormatException('Parse error: '.$this->payload['error']['message']); + case -32600: + throw new InvalidJsonRpcFormatException('Invalid Request: '.$this->payload['error']['message']); + case -32601: + throw new BadFunctionCallException('Procedure not found: '.$this->payload['error']['message']); + case -32602: + throw new InvalidArgumentException('Invalid arguments: '.$this->payload['error']['message']); + default: + throw new ResponseException( + $this->payload['error']['message'], + $this->payload['error']['code'], + null, + isset($this->payload['error']['data']) ? $this->payload['error']['data'] : null + ); + } + } + + /** + * Return true if we have a batch response + * + * @access private + * @return boolean + */ + private function isBatchResponse() + { + return array_keys($this->payload) === range(0, count($this->payload) - 1); + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Server.php b/vendor/fguillot/json-rpc/src/JsonRPC/Server.php new file mode 100644 index 00000000..1ed075a4 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Server.php @@ -0,0 +1,386 @@ +<?php + +namespace JsonRPC; + +use Closure; +use Exception; +use JsonRPC\Request\BatchRequestParser; +use JsonRPC\Request\RequestParser; +use JsonRPC\Response\ResponseBuilder; +use JsonRPC\Validator\HostValidator; +use JsonRPC\Validator\JsonFormatValidator; +use JsonRPC\Validator\UserValidator; + +/** + * JsonRPC server class + * + * @package JsonRPC + * @author Frederic Guillot + */ +class Server +{ + /** + * Allowed hosts + * + * @access protected + * @var array + */ + protected $hosts = array(); + + /** + * Data received from the client + * + * @access protected + * @var array + */ + protected $payload = array(); + + /** + * List of exceptions that should not be relayed to the client + * + * @access protected + * @var array() + */ + protected $localExceptions = array(); + + /** + * Username + * + * @access protected + * @var string + */ + protected $username = ''; + + /** + * Password + * + * @access protected + * @var string + */ + protected $password = ''; + + /** + * Allowed users + * + * @access protected + * @var array + */ + protected $users = array(); + + /** + * $_SERVER + * + * @access protected + * @var array + */ + protected $serverVariable; + + /** + * ProcedureHandler object + * + * @access protected + * @var ProcedureHandler + */ + protected $procedureHandler; + + /** + * MiddlewareHandler object + * + * @access protected + * @var MiddlewareHandler + */ + protected $middlewareHandler; + + /** + * Response builder + * + * @access protected + * @var ResponseBuilder + */ + protected $responseBuilder; + + /** + * Response builder + * + * @access protected + * @var RequestParser + */ + protected $requestParser; + + /** + * + * Batch request parser + * + * @access protected + * @var BatchRequestParser + */ + protected $batchRequestParser; + + /** + * Constructor + * + * @access public + * @param string $request + * @param array $server + * @param ResponseBuilder $responseBuilder + * @param RequestParser $requestParser + * @param BatchRequestParser $batchRequestParser + * @param ProcedureHandler $procedureHandler + * @param MiddlewareHandler $middlewareHandler + */ + public function __construct( + $request = '', + array $server = array(), + ResponseBuilder $responseBuilder = null, + RequestParser $requestParser = null, + BatchRequestParser $batchRequestParser = null, + ProcedureHandler $procedureHandler = null, + MiddlewareHandler $middlewareHandler = null + ) { + if ($request !== '') { + $this->payload = json_decode($request, true); + } else { + $this->payload = json_decode(file_get_contents('php://input'), true); + } + + $this->serverVariable = $server ?: $_SERVER; + $this->responseBuilder = $responseBuilder ?: ResponseBuilder::create(); + $this->requestParser = $requestParser ?: RequestParser::create(); + $this->batchRequestParser = $batchRequestParser ?: BatchRequestParser::create(); + $this->procedureHandler = $procedureHandler ?: new ProcedureHandler(); + $this->middlewareHandler = $middlewareHandler ?: new MiddlewareHandler(); + } + + /** + * Define alternative authentication header + * + * @access public + * @param string $header Header name + * @return $this + */ + public function setAuthenticationHeader($header) + { + if (! empty($header)) { + $header = 'HTTP_'.str_replace('-', '_', strtoupper($header)); + $value = $this->getServerVariable($header); + + if (! empty($value)) { + list($this->username, $this->password) = explode(':', base64_decode($value)); + } + } + + return $this; + } + + /** + * Get ProcedureHandler + * + * @access public + * @return ProcedureHandler + */ + public function getProcedureHandler() + { + return $this->procedureHandler; + } + + /** + * Get MiddlewareHandler + * + * @access public + * @return MiddlewareHandler + */ + public function getMiddlewareHandler() + { + return $this->middlewareHandler; + } + + /** + * Get username + * + * @access public + * @return string + */ + public function getUsername() + { + return $this->username ?: $this->getServerVariable('PHP_AUTH_USER'); + } + + /** + * Get password + * + * @access public + * @return string + */ + public function getPassword() + { + return $this->password ?: $this->getServerVariable('PHP_AUTH_PW'); + } + + /** + * IP based client restrictions + * + * @access public + * @param array $hosts List of hosts + * @return $this + */ + public function allowHosts(array $hosts) + { + $this->hosts = $hosts; + return $this; + } + + /** + * HTTP Basic authentication + * + * @access public + * @param array $users Dictionary of username/password + * @return $this + */ + public function authentication(array $users) + { + $this->users = $users; + return $this; + } + + /** + * Register a new procedure + * + * @access public + * @deprecated Use $server->getProcedureHandler()->withCallback($procedure, $callback) + * @param string $procedure Procedure name + * @param closure $callback Callback + * @return $this + */ + public function register($procedure, Closure $callback) + { + $this->procedureHandler->withCallback($procedure, $callback); + return $this; + } + + /** + * Bind a procedure to a class + * + * @access public + * @deprecated Use $server->getProcedureHandler()->withClassAndMethod($procedure, $class, $method); + * @param string $procedure Procedure name + * @param mixed $class Class name or instance + * @param string $method Procedure name + * @return $this + */ + public function bind($procedure, $class, $method = '') + { + $this->procedureHandler->withClassAndMethod($procedure, $class, $method); + return $this; + } + + /** + * Bind a class instance + * + * @access public + * @deprecated Use $server->getProcedureHandler()->withObject($instance); + * @param mixed $instance Instance name + * @return $this + */ + public function attach($instance) + { + $this->procedureHandler->withObject($instance); + return $this; + } + + /** + * Exception classes that should not be relayed to the client + * + * @access public + * @param Exception|string $exception + * @return $this + */ + public function withLocalException($exception) + { + $this->localExceptions[] = $exception; + return $this; + } + + /** + * Parse incoming requests + * + * @access public + * @return string + */ + public function execute() + { + try { + JsonFormatValidator::validate($this->payload); + HostValidator::validate($this->hosts, $this->getServerVariable('REMOTE_ADDR')); + UserValidator::validate($this->users, $this->getUsername(), $this->getPassword()); + + $this->middlewareHandler + ->withUsername($this->getUsername()) + ->withPassword($this->getPassword()) + ; + + $response = $this->parseRequest(); + + } catch (Exception $e) { + $response = $this->handleExceptions($e); + } + + $this->responseBuilder->sendHeaders(); + return $response; + } + + /** + * Handle exceptions + * + * @access protected + * @param Exception $e + * @return string + * @throws Exception + */ + protected function handleExceptions(Exception $e) + { + foreach ($this->localExceptions as $exception) { + if ($e instanceof $exception) { + throw $e; + } + } + + return $this->responseBuilder->withException($e)->build(); + } + + /** + * Parse incoming request + * + * @access protected + * @return string + */ + protected function parseRequest() + { + if (BatchRequestParser::isBatchRequest($this->payload)) { + return $this->batchRequestParser + ->withPayload($this->payload) + ->withProcedureHandler($this->procedureHandler) + ->withMiddlewareHandler($this->middlewareHandler) + ->withLocalException($this->localExceptions) + ->parse(); + } + + return $this->requestParser + ->withPayload($this->payload) + ->withProcedureHandler($this->procedureHandler) + ->withMiddlewareHandler($this->middlewareHandler) + ->withLocalException($this->localExceptions) + ->parse(); + } + + /** + * Check existence and get value of server variable + * + * @access protected + * @param string $variable + * @return string|null + */ + protected function getServerVariable($variable) + { + return isset($this->serverVariable[$variable]) ? $this->serverVariable[$variable] : null; + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Validator/HostValidator.php b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/HostValidator.php new file mode 100644 index 00000000..7f8c0a04 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/HostValidator.php @@ -0,0 +1,30 @@ +<?php + +namespace JsonRPC\Validator; + +use JsonRPC\Exception\AccessDeniedException; + +/** + * Class HostValidator + * + * @package JsonRPC\Validator + * @author Frederic Guillot + */ +class HostValidator +{ + /** + * Validate + * + * @static + * @access public + * @param array $hosts + * @param string $remoteAddress + * @throws AccessDeniedException + */ + public static function validate(array $hosts, $remoteAddress) + { + if (! empty($hosts) && ! in_array($remoteAddress, $hosts)) { + throw new AccessDeniedException('Access Forbidden'); + } + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Validator/JsonEncodingValidator.php b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/JsonEncodingValidator.php new file mode 100644 index 00000000..0bbc4abd --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/JsonEncodingValidator.php @@ -0,0 +1,44 @@ +<?php + +namespace JsonRPC\Validator; + +use JsonRPC\Exception\ResponseEncodingFailureException; + +/** + * Class JsonEncodingValidator + * + * @package JsonRPC\Validator + * @author Frederic Guillot + */ +class JsonEncodingValidator +{ + public static function validate() + { + $jsonError = json_last_error(); + + if ($jsonError !== JSON_ERROR_NONE) { + switch ($jsonError) { + case JSON_ERROR_DEPTH: + $errorMessage = 'Maximum stack depth exceeded'; + break; + case JSON_ERROR_STATE_MISMATCH: + $errorMessage = 'Underflow or the modes mismatch'; + break; + case JSON_ERROR_CTRL_CHAR: + $errorMessage = 'Unexpected control character found'; + break; + case JSON_ERROR_SYNTAX: + $errorMessage = 'Syntax error, malformed JSON'; + break; + case JSON_ERROR_UTF8: + $errorMessage = 'Malformed UTF-8 characters, possibly incorrectly encoded'; + break; + default: + $errorMessage = 'Unknown error'; + break; + } + + throw new ResponseEncodingFailureException($errorMessage, $jsonError); + } + } +} diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Validator/JsonFormatValidator.php b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/JsonFormatValidator.php new file mode 100644 index 00000000..ca8e7a69 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/JsonFormatValidator.php @@ -0,0 +1,30 @@ +<?php + +namespace JsonRPC\Validator; + +use JsonRPC\Exception\InvalidJsonFormatException; + +/** + * Class JsonFormatValidator + * + * @package JsonRPC\Validator + * @author Frederic Guillot + */ +class JsonFormatValidator +{ + /** + * Validate + * + * @static + * @access public + * @param mixed $payload + * @throws InvalidJsonFormatException + */ + public static function validate($payload) + { + if (! is_array($payload)) { + throw new InvalidJsonFormatException('Malformed payload'); + } + } +} + diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Validator/RpcFormatValidator.php b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/RpcFormatValidator.php new file mode 100644 index 00000000..f253a5a1 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/RpcFormatValidator.php @@ -0,0 +1,35 @@ +<?php + +namespace JsonRPC\Validator; + +use JsonRPC\Exception\InvalidJsonRpcFormatException; + +/** + * Class RpcFormatValidator + * + * @package JsonRPC\Validator + * @author Frederic Guillot + */ +class RpcFormatValidator +{ + /** + * Validate + * + * @static + * @access public + * @param array $payload + * @throws InvalidJsonRpcFormatException + */ + public static function validate(array $payload) + { + if (! isset($payload['jsonrpc']) || + ! isset($payload['method']) || + ! is_string($payload['method']) || + $payload['jsonrpc'] !== '2.0' || + (isset($payload['params']) && ! is_array($payload['params']))) { + + throw new InvalidJsonRpcFormatException('Invalid JSON RPC payload'); + } + } +} + diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Validator/UserValidator.php b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/UserValidator.php new file mode 100644 index 00000000..4f889719 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/UserValidator.php @@ -0,0 +1,21 @@ +<?php + +namespace JsonRPC\Validator; + +use JsonRPC\Exception\AuthenticationFailureException; + +/** + * Class UserValidator + * + * @package JsonRPC\Validator + * @author Frederic Guillot + */ +class UserValidator +{ + public static function validate(array $users, $username, $password) + { + if (! empty($users) && (! isset($users[$username]) || $users[$username] !== $password)) { + throw new AuthenticationFailureException('Access not allowed'); + } + } +} diff --git a/vendor/fguillot/picodb/LICENSE b/vendor/fguillot/picodb/LICENSE new file mode 100644 index 00000000..6a362bc1 --- /dev/null +++ b/vendor/fguillot/picodb/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Frederic Guillot + +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. diff --git a/vendor/fguillot/picodb/lib/PicoDb/Builder/BaseBuilder.php b/vendor/fguillot/picodb/lib/PicoDb/Builder/BaseBuilder.php new file mode 100644 index 00000000..e075ae3c --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Builder/BaseBuilder.php @@ -0,0 +1,86 @@ +<?php + +namespace PicoDb\Builder; + +use PicoDb\Database; + +/** + * Class InsertBuilder + * + * @package PicoDb\Builder + * @author Frederic Guillot + */ +abstract class BaseBuilder +{ + /** + * @var Database + */ + protected $db; + + /** + * @var ConditionBuilder + */ + protected $conditionBuilder; + + /** + * @var string + */ + protected $table = ''; + + /** + * @var string[] + */ + protected $columns = array(); + + /** + * InsertBuilder constructor + * + * @param Database $db + * @param ConditionBuilder $condition + */ + public function __construct(Database $db, ConditionBuilder $condition) + { + $this->db = $db; + $this->conditionBuilder = $condition; + } + + /** + * Get object instance + * + * @static + * @access public + * @param Database $db + * @param ConditionBuilder $condition + * @return static + */ + public static function getInstance(Database $db, ConditionBuilder $condition) + { + return new static($db, $condition); + } + + /** + * Set table name + * + * @access public + * @param string $table + * @return $this + */ + public function withTable($table) + { + $this->table = $table; + return $this; + } + + /** + * Set columns name + * + * @access public + * @param string[] $columns + * @return $this + */ + public function withColumns(array $columns) + { + $this->columns = $columns; + return $this; + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Builder/ConditionBuilder.php b/vendor/fguillot/picodb/lib/PicoDb/Builder/ConditionBuilder.php new file mode 100644 index 00000000..b0465b6e --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Builder/ConditionBuilder.php @@ -0,0 +1,377 @@ +<?php + +namespace PicoDb\Builder; + +use PicoDb\Database; +use PicoDb\Table; + +/** + * Handle SQL conditions + * + * @package PicoDb\Builder + * @author Frederic Guillot + */ +class ConditionBuilder +{ + /** + * Database instance + * + * @access private + * @var Database + */ + private $db; + + /** + * Condition values + * + * @access private + * @var array + */ + private $values = array(); + + /** + * SQL AND conditions + * + * @access private + * @var string[] + */ + private $conditions = array(); + + /** + * SQL OR conditions + * + * @access private + * @var OrConditionBuilder[] + */ + private $orConditions = array(); + + /** + * SQL condition offset + * + * @access private + * @var int + */ + private $orConditionOffset = 0; + + /** + * Constructor + * + * @access public + * @param Database $db + */ + public function __construct(Database $db) + { + $this->db = $db; + } + + /** + * Build the SQL condition + * + * @access public + * @return string + */ + public function build() + { + return empty($this->conditions) ? '' : ' WHERE '.implode(' AND ', $this->conditions); + } + + /** + * Get condition values + * + * @access public + * @return array + */ + public function getValues() + { + return $this->values; + } + + /** + * Returns true if there is some conditions + * + * @access public + * @return boolean + */ + public function hasCondition() + { + return ! empty($this->conditions); + } + + /** + * Add custom condition + * + * @access public + * @param string $sql + */ + public function addCondition($sql) + { + if ($this->orConditionOffset > 0) { + $this->orConditions[$this->orConditionOffset]->withCondition($sql); + } + else { + $this->conditions[] = $sql; + } + } + + /** + * Start OR condition + * + * @access public + */ + public function beginOr() + { + $this->orConditionOffset++; + $this->orConditions[$this->orConditionOffset] = new OrConditionBuilder(); + } + + /** + * Close OR condition + * + * @access public + */ + public function closeOr() + { + $condition = $this->orConditions[$this->orConditionOffset]->build(); + $this->orConditionOffset--; + + if ($this->orConditionOffset > 0) { + $this->orConditions[$this->orConditionOffset]->withCondition($condition); + } else { + $this->conditions[] = $condition; + } + } + + /** + * Equal condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function eq($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' = ?'); + $this->values[] = $value; + } + + /** + * Not equal condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function neq($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' != ?'); + $this->values[] = $value; + } + + /** + * IN condition + * + * @access public + * @param string $column + * @param array $values + */ + public function in($column, array $values) + { + if (! empty($values)) { + $this->addCondition($this->db->escapeIdentifier($column).' IN ('.implode(', ', array_fill(0, count($values), '?')).')'); + $this->values = array_merge($this->values, $values); + } + } + + /** + * IN condition with a subquery + * + * @access public + * @param string $column + * @param Table $subquery + */ + public function inSubquery($column, Table $subquery) + { + $this->addCondition($this->db->escapeIdentifier($column).' IN ('.$subquery->buildSelectQuery().')'); + $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues()); + } + + /** + * NOT IN condition + * + * @access public + * @param string $column + * @param array $values + */ + public function notIn($column, array $values) + { + if (! empty($values)) { + $this->addCondition($this->db->escapeIdentifier($column).' NOT IN ('.implode(', ', array_fill(0, count($values), '?')).')'); + $this->values = array_merge($this->values, $values); + } + } + + /** + * NOT IN condition with a subquery + * + * @access public + * @param string $column + * @param Table $subquery + */ + public function notInSubquery($column, Table $subquery) + { + $this->addCondition($this->db->escapeIdentifier($column).' NOT IN ('.$subquery->buildSelectQuery().')'); + $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues()); + } + + /** + * LIKE condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function like($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' '.$this->db->getDriver()->getOperator('LIKE').' ?'); + $this->values[] = $value; + } + + /** + * ILIKE condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function ilike($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' '.$this->db->getDriver()->getOperator('ILIKE').' ?'); + $this->values[] = $value; + } + + /** + * Greater than condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function gt($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' > ?'); + $this->values[] = $value; + } + + /** + * Greater than condition with subquery + * + * @access public + * @param string $column + * @param Table $subquery + */ + public function gtSubquery($column, Table $subquery) + { + $this->addCondition($this->db->escapeIdentifier($column).' > ('.$subquery->buildSelectQuery().')'); + $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues()); + } + + /** + * Lower than condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function lt($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' < ?'); + $this->values[] = $value; + } + + /** + * Lower than condition with subquery + * + * @access public + * @param string $column + * @param Table $subquery + */ + public function ltSubquery($column, Table $subquery) + { + $this->addCondition($this->db->escapeIdentifier($column).' < ('.$subquery->buildSelectQuery().')'); + $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues()); + } + + /** + * Greater than or equals condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function gte($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' >= ?'); + $this->values[] = $value; + } + + /** + * Greater than or equal condition with subquery + * + * @access public + * @param string $column + * @param Table $subquery + */ + public function gteSubquery($column, Table $subquery) + { + $this->addCondition($this->db->escapeIdentifier($column).' >= ('.$subquery->buildSelectQuery().')'); + $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues()); + } + + /** + * Lower than or equals condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function lte($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' <= ?'); + $this->values[] = $value; + } + + /** + * Lower than or equal condition with subquery + * + * @access public + * @param string $column + * @param Table $subquery + */ + public function lteSubquery($column, Table $subquery) + { + $this->addCondition($this->db->escapeIdentifier($column).' <= ('.$subquery->buildSelectQuery().')'); + $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues()); + } + + /** + * IS NULL condition + * + * @access public + * @param string $column + */ + public function isNull($column) + { + $this->addCondition($this->db->escapeIdentifier($column).' IS NULL'); + } + + /** + * IS NOT NULL condition + * + * @access public + * @param string $column + */ + public function notNull($column) + { + $this->addCondition($this->db->escapeIdentifier($column).' IS NOT NULL'); + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Builder/InsertBuilder.php b/vendor/fguillot/picodb/lib/PicoDb/Builder/InsertBuilder.php new file mode 100644 index 00000000..9d06c405 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Builder/InsertBuilder.php @@ -0,0 +1,36 @@ +<?php + +namespace PicoDb\Builder; + +/** + * Class InsertBuilder + * + * @package PicoDb\Builder + * @author Frederic Guillot + */ +class InsertBuilder extends BaseBuilder +{ + /** + * Build SQL + * + * @access public + * @return string + */ + public function build() + { + $columns = array(); + $placeholders = array(); + + foreach ($this->columns as $column) { + $columns[] = $this->db->escapeIdentifier($column); + $placeholders[] = ':'.$column; + } + + return sprintf( + 'INSERT INTO %s (%s) VALUES (%s)', + $this->db->escapeIdentifier($this->table), + implode(', ', $columns), + implode(', ', $placeholders) + ); + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Builder/OrConditionBuilder.php b/vendor/fguillot/picodb/lib/PicoDb/Builder/OrConditionBuilder.php new file mode 100644 index 00000000..0defeaf4 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Builder/OrConditionBuilder.php @@ -0,0 +1,43 @@ +<?php + +namespace PicoDb\Builder; + +/** + * Class OrConditionBuilder + * + * @package PicoDb\Builder + * @author Frederic Guillot + */ +class OrConditionBuilder +{ + /** + * List of SQL conditions + * + * @access protected + * @var string[] + */ + protected $conditions = array(); + + /** + * Add new condition + * + * @access public + * @param string $condition + * @return $this + */ + public function withCondition($condition) { + $this->conditions[] = $condition; + return $this; + } + + /** + * Build SQL + * + * @access public + * @return string + */ + public function build() + { + return '('.implode(' OR ', $this->conditions).')'; + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Builder/UpdateBuilder.php b/vendor/fguillot/picodb/lib/PicoDb/Builder/UpdateBuilder.php new file mode 100644 index 00000000..300ea9b0 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Builder/UpdateBuilder.php @@ -0,0 +1,56 @@ +<?php + +namespace PicoDb\Builder; + +/** + * Class UpdateBuilder + * + * @package PicoDb\Builder + * @author Frederic Guillot + */ +class UpdateBuilder extends BaseBuilder +{ + /** + * @var string[] + */ + protected $sumColumns = array(); + + /** + * Set columns name + * + * @access public + * @param string[] $columns + * @return $this + */ + public function withSumColumns(array $columns) + { + $this->sumColumns = $columns; + return $this; + } + + /** + * Build SQL + * + * @access public + * @return string + */ + public function build() + { + $columns = array(); + + foreach ($this->columns as $column) { + $columns[] = $this->db->escapeIdentifier($column).'=?'; + } + + foreach ($this->sumColumns as $column) { + $columns[] = $this->db->escapeIdentifier($column).'='.$this->db->escapeIdentifier($column).' + ?'; + } + + return sprintf( + 'UPDATE %s SET %s %s', + $this->db->escapeIdentifier($this->table), + implode(', ', $columns), + $this->conditionBuilder->build() + ); + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Database.php b/vendor/fguillot/picodb/lib/PicoDb/Database.php new file mode 100644 index 00000000..22c9d2fb --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Database.php @@ -0,0 +1,370 @@ +<?php + +namespace PicoDb; + +use Closure; +use PDOException; +use LogicException; +use PicoDb\Driver\Mssql; +use PicoDb\Driver\Sqlite; +use PicoDb\Driver\Mysql; +use PicoDb\Driver\Postgres; + +/** + * Database + * + * @package PicoDb + * @author Frederic Guillot + */ +class Database +{ + /** + * Database instances + * + * @static + * @access private + * @var array + */ + private static $instances = array(); + + /** + * Statement object + * + * @access protected + * @var StatementHandler + */ + protected $statementHandler; + + /** + * Queries logs + * + * @access private + * @var array + */ + private $logs = array(); + + /** + * Driver instance + * + * @access private + */ + private $driver; + + /** + * Initialize the driver + * + * @access public + * @param array $settings + */ + public function __construct(array $settings = array()) + { + $this->driver = DriverFactory::getDriver($settings); + $this->statementHandler = new StatementHandler($this); + } + + /** + * Destructor + * + * @access public + */ + public function __destruct() + { + $this->closeConnection(); + } + + /** + * Register a new database instance + * + * @static + * @access public + * @param string $name Instance name + * @param Closure $callback Callback + */ + public static function setInstance($name, Closure $callback) + { + self::$instances[$name] = $callback; + } + + /** + * Get a database instance + * + * @static + * @access public + * @param string $name Instance name + * @return Database + */ + public static function getInstance($name) + { + if (! isset(self::$instances[$name])) { + throw new LogicException('No database instance created with that name'); + } + + if (is_callable(self::$instances[$name])) { + self::$instances[$name] = call_user_func(self::$instances[$name]); + } + + return self::$instances[$name]; + } + + /** + * Add a log message + * + * @access public + * @param mixed $message + * @return Database + */ + public function setLogMessage($message) + { + $this->logs[] = is_array($message) ? var_export($message, true) : $message; + return $this; + } + + /** + * Add many log messages + * + * @access public + * @param array $messages + * @return Database + */ + public function setLogMessages(array $messages) + { + foreach ($messages as $message) { + $this->setLogMessage($message); + } + + return $this; + } + + /** + * Get all queries logs + * + * @access public + * @return array + */ + public function getLogMessages() + { + return $this->logs; + } + + /** + * Get the PDO connection + * + * @access public + * @return \PDO + */ + public function getConnection() + { + return $this->driver->getConnection(); + } + + /** + * Get the Driver instance + * + * @access public + * @return Mssql|Sqlite|Postgres|Mysql + */ + public function getDriver() + { + return $this->driver; + } + + /** + * Get the last inserted id + * + * @access public + * @return integer + */ + public function getLastId() + { + return (int) $this->driver->getLastId(); + } + + /** + * Get statement object + * + * @access public + * @return StatementHandler + */ + public function getStatementHandler() + { + return $this->statementHandler; + } + + /** + * Release the PDO connection + * + * @access public + */ + public function closeConnection() + { + $this->driver->closeConnection(); + } + + /** + * Escape an identifier (column, table name...) + * + * @access public + * @param string $value Value + * @param string $table Table name + * @return string + */ + public function escapeIdentifier($value, $table = '') + { + // Do not escape custom query + if (strpos($value, '.') !== false || strpos($value, ' ') !== false) { + return $value; + } + + if (! empty($table)) { + return $this->driver->escape($table).'.'.$this->driver->escape($value); + } + + return $this->driver->escape($value); + } + + /** + * Escape an identifier list + * + * @access public + * @param array $identifiers List of identifiers + * @param string $table Table name + * @return string[] + */ + public function escapeIdentifierList(array $identifiers, $table = '') + { + foreach ($identifiers as $key => $value) { + $identifiers[$key] = $this->escapeIdentifier($value, $table); + } + + return $identifiers; + } + + /** + * Execute a prepared statement + * + * Note: returns false on duplicate keys instead of SQLException + * + * @access public + * @param string $sql SQL query + * @param array $values Values + * @return \PDOStatement|false + */ + public function execute($sql, array $values = array()) + { + return $this->statementHandler + ->withSql($sql) + ->withPositionalParams($values) + ->execute(); + } + + /** + * Run a transaction + * + * @access public + * @param Closure $callback Callback + * @return mixed + */ + public function transaction(Closure $callback) + { + try { + + $this->startTransaction(); + $result = $callback($this); + $this->closeTransaction(); + + return $result === null ? true : $result; + } catch (PDOException $e) { + return $this->statementHandler->handleSqlError($e); + } + } + + /** + * Begin a transaction + * + * @access public + */ + public function startTransaction() + { + if (! $this->getConnection()->inTransaction()) { + $this->getConnection()->beginTransaction(); + } + } + + /** + * Commit a transaction + * + * @access public + */ + public function closeTransaction() + { + if ($this->getConnection()->inTransaction()) { + $this->getConnection()->commit(); + } + } + + /** + * Rollback a transaction + * + * @access public + */ + public function cancelTransaction() + { + if ($this->getConnection()->inTransaction()) { + $this->getConnection()->rollBack(); + } + } + + /** + * Get a table object + * + * @access public + * @param string $table + * @return Table + */ + public function table($table) + { + return new Table($this, $table); + } + + /** + * Get a hashtable object + * + * @access public + * @param string $table + * @return Hashtable + */ + public function hashtable($table) + { + return new Hashtable($this, $table); + } + + /** + * Get a LOB object + * + * @access public + * @param string $table + * @return LargeObject + */ + public function largeObject($table) + { + return new LargeObject($this, $table); + } + + /** + * Get a schema object + * + * @access public + * @param string $namespace + * @return Schema + */ + public function schema($namespace = null) + { + $schema = new Schema($this); + + if ($namespace !== null) { + $schema->setNamespace($namespace); + } + + return $schema; + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Base.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Base.php new file mode 100644 index 00000000..790cd623 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Base.php @@ -0,0 +1,234 @@ +<?php + +namespace PicoDb\Driver; + +use PDO; +use LogicException; +use PDOException; + +/** + * Base Driver class + * + * @package PicoDb\Driver + * @author Frederic Guillot + */ +abstract class Base +{ + /** + * List of required settings options + * + * @access protected + * @var array + */ + protected $requiredAttributes = array(); + + /** + * PDO connection + * + * @access protected + * @var PDO + */ + protected $pdo = null; + + /** + * Create a new PDO connection + * + * @abstract + * @access public + * @param array $settings + */ + abstract public function createConnection(array $settings); + + /** + * Enable foreign keys + * + * @abstract + * @access public + */ + abstract public function enableForeignKeys(); + + /** + * Disable foreign keys + * + * @abstract + * @access public + */ + abstract public function disableForeignKeys(); + + /** + * Return true if the error code is a duplicate key + * + * @abstract + * @access public + * @param integer $code + * @return boolean + */ + abstract public function isDuplicateKeyError($code); + + /** + * Escape identifier + * + * @abstract + * @access public + * @param string $identifier + * @return string + */ + abstract public function escape($identifier); + + /** + * Get non standard operator + * + * @abstract + * @access public + * @param string $operator + * @return string + */ + abstract public function getOperator($operator); + + /** + * Get last inserted id + * + * @abstract + * @access public + * @return integer + */ + abstract public function getLastId(); + + /** + * Get current schema version + * + * @abstract + * @access public + * @return integer + */ + abstract public function getSchemaVersion(); + + /** + * Set current schema version + * + * @abstract + * @access public + * @param integer $version + */ + abstract public function setSchemaVersion($version); + + /** + * Constructor + * + * @access public + * @param array $settings + */ + public function __construct(array $settings) + { + foreach ($this->requiredAttributes as $attribute) { + if (! isset($settings[$attribute])) { + throw new LogicException('This configuration parameter is missing: "'.$attribute.'"'); + } + } + + $this->createConnection($settings); + $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } + + /** + * Get the PDO connection + * + * @access public + * @return PDO + */ + public function getConnection() + { + return $this->pdo; + } + + /** + * Release the PDO connection + * + * @access public + */ + public function closeConnection() + { + $this->pdo = null; + } + + /** + * Upsert for a key/value variable + * + * @access public + * @param string $table + * @param string $keyColumn + * @param string $valueColumn + * @param array $dictionary + * @return bool False on failure + */ + public function upsert($table, $keyColumn, $valueColumn, array $dictionary) + { + try { + $this->pdo->beginTransaction(); + + foreach ($dictionary as $key => $value) { + + $rq = $this->pdo->prepare('SELECT 1 FROM '.$this->escape($table).' WHERE '.$this->escape($keyColumn).'=?'); + $rq->execute(array($key)); + + if ($rq->fetchColumn()) { + $rq = $this->pdo->prepare('UPDATE '.$this->escape($table).' SET '.$this->escape($valueColumn).'=? WHERE '.$this->escape($keyColumn).'=?'); + $rq->execute(array($value, $key)); + } + else { + $rq = $this->pdo->prepare('INSERT INTO '.$this->escape($table).' ('.$this->escape($keyColumn).', '.$this->escape($valueColumn).') VALUES (?, ?)'); + $rq->execute(array($key, $value)); + } + } + + $this->pdo->commit(); + + return true; + } + catch (PDOException $e) { + $this->pdo->rollBack(); + return false; + } + } + + /** + * Run EXPLAIN command + * + * @access public + * @param string $sql + * @param array $values + * @return array + */ + public function explain($sql, array $values) + { + return $this->getConnection()->query('EXPLAIN '.$this->getSqlFromPreparedStatement($sql, $values))->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * Replace placeholder with values in prepared statement + * + * @access protected + * @param string $sql + * @param array $values + * @return string + */ + protected function getSqlFromPreparedStatement($sql, array $values) + { + foreach ($values as $value) { + $sql = substr_replace($sql, "'$value'", strpos($sql, '?'), 1); + } + + return $sql; + } + + /** + * Get database version + * + * @access public + * @return array + */ + public function getDatabaseVersion() + { + return $this->getConnection()->query('SELECT VERSION()')->fetchColumn(); + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Mssql.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Mssql.php new file mode 100644 index 00000000..83e75af2 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Mssql.php @@ -0,0 +1,178 @@ +<?php + +namespace PicoDb\Driver; + +use PDO; + +/** + * Microsoft SQL Server Driver + * + * @package PicoDb\Driver + * @author Algy Taylor <thomas.taylor@cmft.nhs.uk> + */ +class Mssql extends Base +{ + /** + * List of required settings options + * + * @access protected + * @var array + */ + protected $requiredAttributes = array( + 'hostname', + 'username', + 'password', + 'database', + ); + + /** + * Table to store the schema version + * + * @access private + * @var array + */ + private $schemaTable = 'schema_version'; + + /** + * Create a new PDO connection + * + * @access public + * @param array $settings + */ + public function createConnection(array $settings) + { + $dsn = 'sqlsrv:Server=' . $settings['hostname'] . ';Database=' . $settings['database']; + + if (! empty($settings['port'])) { + $dsn .= ';port=' . $settings['port']; + } + + $this->pdo = new PDO($dsn, $settings['username'], $settings['password']); + + if (isset($settings['schema_table'])) { + $this->schemaTable = $settings['schema_table']; + } + } + + /** + * Enable foreign keys + * + * @access public + */ + public function enableForeignKeys() + { + $this->pdo->exec('EXEC sp_MSforeachtable @command1="ALTER TABLE ? CHECK CONSTRAINT ALL"; GO;'); + } + + /** + * Disable foreign keys + * + * @access public + */ + public function disableForeignKeys() + { + $this->pdo->exec('EXEC sp_MSforeachtable @command1="ALTER TABLE ? NOCHECK CONSTRAINT ALL"; GO;'); + } + + /** + * Return true if the error code is a duplicate key + * + * @access public + * @param integer $code + * @return boolean + */ + public function isDuplicateKeyError($code) + { + return $code == 2601; + } + + /** + * Escape identifier + * + * https://msdn.microsoft.com/en-us/library/ms175874.aspx + * + * @access public + * @param string $identifier + * @return string + */ + public function escape($identifier) + { + return '['.$identifier.']'; + } + + /** + * Get non standard operator + * + * @access public + * @param string $operator + * @return string + */ + public function getOperator($operator) + { + if ($operator === 'LIKE' || $operator === 'ILIKE') { + return 'LIKE'; + } + + return ''; + } + + /** + * Get last inserted id + * + * @access public + * @return integer + */ + public function getLastId() + { + return $this->pdo->lastInsertId(); + } + + /** + * Get current schema version + * + * @access public + * @return integer + */ + public function getSchemaVersion() + { + $this->pdo->exec("CREATE TABLE IF NOT EXISTS [".$this->schemaTable."] ([version] INT DEFAULT '0')"); + + $rq = $this->pdo->prepare('SELECT [version] FROM ['.$this->schemaTable.']'); + $rq->execute(); + $result = $rq->fetchColumn(); + + if ($result !== false) { + return (int) $result; + } + else { + $this->pdo->exec('INSERT INTO ['.$this->schemaTable.'] VALUES(0)'); + } + + return 0; + } + + /** + * Set current schema version + * + * @access public + * @param integer $version + */ + public function setSchemaVersion($version) + { + $rq = $this->pdo->prepare('UPDATE ['.$this->schemaTable.'] SET [version]=?'); + $rq->execute(array($version)); + } + + /** + * Run EXPLAIN command + * + * @param string $sql + * @param array $values + * @return array + */ + public function explain($sql, array $values) + { + $this->getConnection()->exec('SET SHOWPLAN_ALL ON'); + return $this->getConnection()->query($this->getSqlFromPreparedStatement($sql, $values))->fetchAll(PDO::FETCH_ASSOC); + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Mysql.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Mysql.php new file mode 100644 index 00000000..4f3ca64e --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Mysql.php @@ -0,0 +1,252 @@ +<?php + +namespace PicoDb\Driver; + +use PDO; +use PDOException; + +/** + * Mysql Driver + * + * @package PicoDb\Driver + * @author Frederic Guillot + */ +class Mysql extends Base +{ + /** + * List of required settings options + * + * @access protected + * @var array + */ + protected $requiredAttributes = array( + 'hostname', + 'username', + 'password', + 'database', + ); + + /** + * Table to store the schema version + * + * @access private + * @var array + */ + private $schemaTable = 'schema_version'; + + /** + * Create a new PDO connection + * + * @access public + * @param array $settings + */ + public function createConnection(array $settings) + { + $this->pdo = new PDO( + $this->buildDsn($settings), + $settings['username'], + $settings['password'], + $this->buildOptions($settings) + ); + + if (isset($settings['schema_table'])) { + $this->schemaTable = $settings['schema_table']; + } + } + + /** + * Build connection DSN + * + * @access protected + * @param array $settings + * @return string + */ + protected function buildDsn(array $settings) + { + $charset = empty($settings['charset']) ? 'utf8' : $settings['charset']; + $dsn = 'mysql:host='.$settings['hostname'].';dbname='.$settings['database'].';charset='.$charset; + + if (! empty($settings['port'])) { + $dsn .= ';port='.$settings['port']; + } + + return $dsn; + } + + /** + * Build connection options + * + * @access protected + * @param array $settings + * @return array + */ + protected function buildOptions(array $settings) + { + $options = array( + PDO::MYSQL_ATTR_INIT_COMMAND => 'SET sql_mode = STRICT_ALL_TABLES', + ); + + if (! empty($settings['ssl_key'])) { + $options[PDO::MYSQL_ATTR_SSL_KEY] = $settings['ssl_key']; + } + + if (! empty($settings['ssl_cert'])) { + $options[PDO::MYSQL_ATTR_SSL_CERT] = $settings['ssl_cert']; + } + + if (! empty($settings['ssl_ca'])) { + $options[PDO::MYSQL_ATTR_SSL_CA] = $settings['ssl_ca']; + } + + return $options; + } + + /** + * Enable foreign keys + * + * @access public + */ + public function enableForeignKeys() + { + $this->pdo->exec('SET FOREIGN_KEY_CHECKS=1'); + } + + /** + * Disable foreign keys + * + * @access public + */ + public function disableForeignKeys() + { + $this->pdo->exec('SET FOREIGN_KEY_CHECKS=0'); + } + + /** + * Return true if the error code is a duplicate key + * + * @access public + * @param integer $code + * @return boolean + */ + public function isDuplicateKeyError($code) + { + return $code == 23000; + } + + /** + * Escape identifier + * + * @access public + * @param string $identifier + * @return string + */ + public function escape($identifier) + { + return '`'.$identifier.'`'; + } + + /** + * Get non standard operator + * + * @access public + * @param string $operator + * @return string + */ + public function getOperator($operator) + { + if ($operator === 'LIKE') { + return 'LIKE BINARY'; + } + else if ($operator === 'ILIKE') { + return 'LIKE'; + } + + return ''; + } + + /** + * Get last inserted id + * + * @access public + * @return integer + */ + public function getLastId() + { + return $this->pdo->lastInsertId(); + } + + /** + * Get current schema version + * + * @access public + * @return integer + */ + public function getSchemaVersion() + { + $this->pdo->exec("CREATE TABLE IF NOT EXISTS `".$this->schemaTable."` (`version` INT DEFAULT '0') ENGINE=InnoDB CHARSET=utf8"); + + $rq = $this->pdo->prepare('SELECT `version` FROM `'.$this->schemaTable.'`'); + $rq->execute(); + $result = $rq->fetchColumn(); + + if ($result !== false) { + return (int) $result; + } + else { + $this->pdo->exec('INSERT INTO `'.$this->schemaTable.'` VALUES(0)'); + } + + return 0; + } + + /** + * Set current schema version + * + * @access public + * @param integer $version + */ + public function setSchemaVersion($version) + { + $rq = $this->pdo->prepare('UPDATE `'.$this->schemaTable.'` SET `version`=?'); + $rq->execute(array($version)); + } + + /** + * Upsert for a key/value variable + * + * @access public + * @param string $table + * @param string $keyColumn + * @param string $valueColumn + * @param array $dictionary + * @return bool False on failure + */ + public function upsert($table, $keyColumn, $valueColumn, array $dictionary) + { + try { + + $sql = sprintf( + 'REPLACE INTO %s (%s, %s) VALUES %s', + $this->escape($table), + $this->escape($keyColumn), + $this->escape($valueColumn), + implode(', ', array_fill(0, count($dictionary), '(?, ?)')) + ); + + $values = array(); + + foreach ($dictionary as $key => $value) { + $values[] = $key; + $values[] = $value; + } + + $rq = $this->pdo->prepare($sql); + $rq->execute($values); + + return true; + } + catch (PDOException $e) { + return false; + } + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Postgres.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Postgres.php new file mode 100644 index 00000000..a494cdca --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Postgres.php @@ -0,0 +1,196 @@ +<?php + +namespace PicoDb\Driver; + +use PDO; +use PDOException; + +/** + * Postgres Driver + * + * @package PicoDb\Driver + * @author Frederic Guillot + */ +class Postgres extends Base +{ + /** + * List of required settings options + * + * @access protected + * @var array + */ + protected $requiredAttributes = array( + 'hostname', + 'username', + 'password', + 'database', + ); + + /** + * Table to store the schema version + * + * @access private + * @var array + */ + private $schemaTable = 'schema_version'; + + /** + * Create a new PDO connection + * + * @access public + * @param array $settings + */ + public function createConnection(array $settings) + { + $dsn = 'pgsql:host='.$settings['hostname'].';dbname='.$settings['database']; + + if (! empty($settings['port'])) { + $dsn .= ';port='.$settings['port']; + } + + $this->pdo = new PDO($dsn, $settings['username'], $settings['password']); + + if (isset($settings['schema_table'])) { + $this->schemaTable = $settings['schema_table']; + } + } + + /** + * Enable foreign keys + * + * @access public + */ + public function enableForeignKeys() + { + } + + /** + * Disable foreign keys + * + * @access public + */ + public function disableForeignKeys() + { + } + + /** + * Return true if the error code is a duplicate key + * + * @access public + * @param integer $code + * @return boolean + */ + public function isDuplicateKeyError($code) + { + return $code == 23505 || $code == 23503; + } + + /** + * Escape identifier + * + * @access public + * @param string $identifier + * @return string + */ + public function escape($identifier) + { + return '"'.$identifier.'"'; + } + + /** + * Get non standard operator + * + * @access public + * @param string $operator + * @return string + */ + public function getOperator($operator) + { + if ($operator === 'LIKE') { + return 'LIKE'; + } + else if ($operator === 'ILIKE') { + return 'ILIKE'; + } + + return ''; + } + + /** + * Get last inserted id + * + * @access public + * @return integer + */ + public function getLastId() + { + try { + $rq = $this->pdo->prepare('SELECT LASTVAL()'); + $rq->execute(); + + return $rq->fetchColumn(); + } + catch (PDOException $e) { + return 0; + } + } + + /** + * Get current schema version + * + * @access public + * @return integer + */ + public function getSchemaVersion() + { + $this->pdo->exec("CREATE TABLE IF NOT EXISTS ".$this->schemaTable." (version INTEGER DEFAULT 0)"); + + $rq = $this->pdo->prepare('SELECT "version" FROM "'.$this->schemaTable.'"'); + $rq->execute(); + $result = $rq->fetchColumn(); + + if ($result !== false) { + return (int) $result; + } + else { + $this->pdo->exec('INSERT INTO '.$this->schemaTable.' VALUES(0)'); + } + + return 0; + } + + /** + * Set current schema version + * + * @access public + * @param integer $version + */ + public function setSchemaVersion($version) + { + $rq = $this->pdo->prepare('UPDATE '.$this->schemaTable.' SET version=?'); + $rq->execute(array($version)); + } + + /** + * Run EXPLAIN command + * + * @param string $sql + * @param array $values + * @return array + */ + public function explain($sql, array $values) + { + return $this->getConnection()->query('EXPLAIN (FORMAT YAML) '.$this->getSqlFromPreparedStatement($sql, $values))->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * Get database version + * + * @access public + * @return array + */ + public function getDatabaseVersion() + { + return $this->getConnection()->query('SHOW server_version')->fetchColumn(); + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php new file mode 100644 index 00000000..ea39f007 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php @@ -0,0 +1,193 @@ +<?php + +namespace PicoDb\Driver; + +use PDO; +use PDOException; + +/** + * Sqlite Driver + * + * @package PicoDb\Driver + * @author Frederic Guillot + */ +class Sqlite extends Base +{ + /** + * List of required settings options + * + * @access protected + * @var array + */ + protected $requiredAttributes = array('filename'); + + /** + * Create a new PDO connection + * + * @access public + * @param array $settings + */ + public function createConnection(array $settings) + { + $this->pdo = new PDO('sqlite:'.$settings['filename']); + $this->enableForeignKeys(); + } + + /** + * Enable foreign keys + * + * @access public + */ + public function enableForeignKeys() + { + $this->pdo->exec('PRAGMA foreign_keys = ON'); + } + + /** + * Disable foreign keys + * + * @access public + */ + public function disableForeignKeys() + { + $this->pdo->exec('PRAGMA foreign_keys = OFF'); + } + + /** + * Return true if the error code is a duplicate key + * + * @access public + * @param integer $code + * @return boolean + */ + public function isDuplicateKeyError($code) + { + return $code == 23000; + } + + /** + * Escape identifier + * + * @access public + * @param string $identifier + * @return string + */ + public function escape($identifier) + { + return '"'.$identifier.'"'; + } + + /** + * Get non standard operator + * + * @access public + * @param string $operator + * @return string + */ + public function getOperator($operator) + { + if ($operator === 'LIKE' || $operator === 'ILIKE') { + return 'LIKE'; + } + + return ''; + } + + /** + * Get last inserted id + * + * @access public + * @return integer + */ + public function getLastId() + { + return $this->pdo->lastInsertId(); + } + + /** + * Get current schema version + * + * @access public + * @return integer + */ + public function getSchemaVersion() + { + $rq = $this->pdo->prepare('PRAGMA user_version'); + $rq->execute(); + + return (int) $rq->fetchColumn(); + } + + /** + * Set current schema version + * + * @access public + * @param integer $version + */ + public function setSchemaVersion($version) + { + $this->pdo->exec('PRAGMA user_version='.$version); + } + + /** + * Upsert for a key/value variable + * + * @access public + * @param string $table + * @param string $keyColumn + * @param string $valueColumn + * @param array $dictionary + * @return bool False on failure + */ + public function upsert($table, $keyColumn, $valueColumn, array $dictionary) + { + try { + $this->pdo->beginTransaction(); + + foreach ($dictionary as $key => $value) { + + $sql = sprintf( + 'INSERT OR REPLACE INTO %s (%s, %s) VALUES (?, ?)', + $this->escape($table), + $this->escape($keyColumn), + $this->escape($valueColumn) + ); + + $rq = $this->pdo->prepare($sql); + $rq->execute(array($key, $value)); + } + + $this->pdo->commit(); + + return true; + } + catch (PDOException $e) { + $this->pdo->rollBack(); + return false; + } + } + + /** + * Run EXPLAIN command + * + * @access public + * @param string $sql + * @param array $values + * @return array + */ + public function explain($sql, array $values) + { + return $this->getConnection()->query('EXPLAIN QUERY PLAN '.$this->getSqlFromPreparedStatement($sql, $values))->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * Get database version + * + * @access public + * @return array + */ + public function getDatabaseVersion() + { + return $this->getConnection()->query('SELECT sqlite_version()')->fetchColumn(); + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/DriverFactory.php b/vendor/fguillot/picodb/lib/PicoDb/DriverFactory.php new file mode 100644 index 00000000..13151ba7 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/DriverFactory.php @@ -0,0 +1,45 @@ +<?php + +namespace PicoDb; + +use LogicException; +use PicoDb\Driver\Mssql; +use PicoDb\Driver\Mysql; +use PicoDb\Driver\Postgres; +use PicoDb\Driver\Sqlite; + +/** + * Class DriverFactory + * + * @package PicoDb + * @author Frederic Guillot + */ +class DriverFactory +{ + /** + * Get database driver from settings or environment URL + * + * @access public + * @param array $settings + * @return Mssql|Mysql|Postgres|Sqlite + */ + public static function getDriver(array $settings) + { + if (! isset($settings['driver'])) { + throw new LogicException('You must define a database driver'); + } + + switch ($settings['driver']) { + case 'sqlite': + return new Sqlite($settings); + case 'mssql': + return new Mssql($settings); + case 'mysql': + return new Mysql($settings); + case 'postgres': + return new Postgres($settings); + default: + throw new LogicException('This database driver is not supported'); + } + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Hashtable.php b/vendor/fguillot/picodb/lib/PicoDb/Hashtable.php new file mode 100644 index 00000000..17afd0e6 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Hashtable.php @@ -0,0 +1,112 @@ +<?php + +namespace PicoDb; + +use PDO; + +/** + * HashTable (key/value) + * + * @package PicoDb + * @author Frederic Guillot + * @author Mathias Kresin + */ +class Hashtable extends Table +{ + /** + * Column for the key + * + * @access private + * @var string + */ + private $keyColumn = 'key'; + + /** + * Column for the value + * + * @access private + * @var string + */ + private $valueColumn = 'value'; + + /** + * Set the key column + * + * @access public + * @param string $column + * @return $this + */ + public function columnKey($column) + { + $this->keyColumn = $column; + return $this; + } + + /** + * Set the value column + * + * @access public + * @param string $column + * @return $this + */ + public function columnValue($column) + { + $this->valueColumn = $column; + return $this; + } + + /** + * Insert or update + * + * @access public + * @param array $hashmap + * @return boolean + */ + public function put(array $hashmap) + { + return $this->db->getDriver()->upsert($this->getName(), $this->keyColumn, $this->valueColumn, $hashmap); + } + + /** + * Hashmap result [ [column1 => column2], [], ...] + * + * @access public + * @return array + */ + public function get() + { + $hashmap = array(); + + // setup where condition + if (func_num_args() > 0) { + $this->in($this->keyColumn, func_get_args()); + } + + // setup to select columns in case that there are more than two + $this->columns($this->keyColumn, $this->valueColumn); + + $rq = $this->db->execute($this->buildSelectQuery(), $this->conditionBuilder->getValues()); + $rows = $rq->fetchAll(PDO::FETCH_NUM); + + foreach ($rows as $row) { + $hashmap[$row[0]] = $row[1]; + } + + return $hashmap; + } + + /** + * Shortcut method to get a hashmap result + * + * @access public + * @param string $key Key column + * @param string $value Value column + * @return array + */ + public function getAll($key, $value) + { + $this->keyColumn = $key; + $this->valueColumn = $value; + return $this->get(); + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/LargeObject.php b/vendor/fguillot/picodb/lib/PicoDb/LargeObject.php new file mode 100644 index 00000000..ba5e3b92 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/LargeObject.php @@ -0,0 +1,167 @@ +<?php + +namespace PicoDb; + +use PDO; +use PicoDb\Builder\InsertBuilder; +use PicoDb\Builder\UpdateBuilder; + +/** + * Handle Large Objects (LOBs) + * + * @package PicoDb + * @author Frederic Guillot + */ +class LargeObject extends Table +{ + /** + * Fetch large object as file descriptor + * + * This method is not compatible with Sqlite and Mysql (return a string instead of resource) + * + * @access public + * @param string $column + * @return resource + */ + public function findOneColumnAsStream($column) + { + $this->limit(1); + $this->columns($column); + + $rq = $this->db->getStatementHandler() + ->withSql($this->buildSelectQuery()) + ->withPositionalParams($this->conditionBuilder->getValues()) + ->execute(); + + $rq->bindColumn($column, $fd, PDO::PARAM_LOB); + $rq->fetch(PDO::FETCH_BOUND); + + return $fd; + } + + /** + * Fetch large object as string + * + * @access public + * @param string $column + * @return string + */ + public function findOneColumnAsString($column) + { + $fd = $this->findOneColumnAsStream($column); + + if (is_string($fd)) { + return $fd; + } + + return stream_get_contents($fd); + } + + /** + * Insert large object from stream + * + * @access public + * @param string $blobColumn + * @param resource|string $blobDescriptor + * @param array $data + * @return bool + */ + public function insertFromStream($blobColumn, &$blobDescriptor, array $data = array()) + { + $columns = array_merge(array($blobColumn), array_keys($data)); + $this->db->startTransaction(); + + $result = $this->db->getStatementHandler() + ->withSql(InsertBuilder::getInstance($this->db, $this->conditionBuilder) + ->withTable($this->name) + ->withColumns($columns) + ->build() + ) + ->withNamedParams($data) + ->withLobParam($blobColumn, $blobDescriptor) + ->execute(); + + $this->db->closeTransaction(); + + return $result !== false; + } + + /** + * Insert large object from file + * + * @access public + * @param string $blobColumn + * @param string $filename + * @param array $data + * @return bool + */ + public function insertFromFile($blobColumn, $filename, array $data = array()) + { + $fp = fopen($filename, 'rb'); + $result = $this->insertFromStream($blobColumn, $fp, $data); + fclose($fp); + return $result; + } + + /** + * Insert large object from string + * + * @access public + * @param string $blobColumn + * @param string $blobData + * @param array $data + * @return bool + */ + public function insertFromString($blobColumn, &$blobData, array $data = array()) + { + return $this->insertFromStream($blobColumn, $blobData, $data); + } + + /** + * Update large object from stream + * + * @access public + * @param string $blobColumn + * @param resource $blobDescriptor + * @param array $data + * @return bool + */ + public function updateFromStream($blobColumn, &$blobDescriptor, array $data = array()) + { + $values = array_merge(array_values($data), $this->conditionBuilder->getValues()); + $columns = array_merge(array($blobColumn), array_keys($data)); + + $this->db->startTransaction(); + + $result = $this->db->getStatementHandler() + ->withSql(UpdateBuilder::getInstance($this->db, $this->conditionBuilder) + ->withTable($this->name) + ->withColumns($columns) + ->build() + ) + ->withPositionalParams($values) + ->withLobParam($blobColumn, $blobDescriptor) + ->execute(); + + $this->db->closeTransaction(); + + return $result !== false; + } + + /** + * Update large object from file + * + * @access public + * @param string $blobColumn + * @param string $filename + * @param array $data + * @return bool + */ + public function updateFromFile($blobColumn, $filename, array $data = array()) + { + $fp = fopen($filename, 'r'); + $result = $this->updateFromStream($blobColumn, $fp, $data); + fclose($fp); + return $result; + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/SQLException.php b/vendor/fguillot/picodb/lib/PicoDb/SQLException.php new file mode 100644 index 00000000..7e570834 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/SQLException.php @@ -0,0 +1,15 @@ +<?php + +namespace PicoDb; + +use Exception; + +/** + * SQLException + * + * @package PicoDb + * @author Frederic Guillot + */ +class SQLException extends Exception +{ +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Schema.php b/vendor/fguillot/picodb/lib/PicoDb/Schema.php new file mode 100644 index 00000000..a0280368 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Schema.php @@ -0,0 +1,119 @@ +<?php + +namespace PicoDb; + +use PDOException; + +/** + * Schema migration class + * + * @package PicoDb + * @author Frederic Guillot + */ +class Schema +{ + /** + * Database instance + * + * @access protected + * @var Database + */ + protected $db = null; + + /** + * Schema namespace + * + * @access protected + * @var string + */ + protected $namespace = '\Schema'; + + /** + * Constructor + * + * @access public + * @param Database $db + */ + public function __construct(Database $db) + { + $this->db = $db; + } + + /** + * Set another namespace + * + * @access public + * @param string $namespace + * @return Schema + */ + public function setNamespace($namespace) + { + $this->namespace = $namespace; + return $this; + } + + /** + * Get schema namespace + * + * @access public + * @return string + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * Check the schema version and run the migrations + * + * @access public + * @param integer $last_version + * @return boolean + */ + public function check($last_version = 1) + { + $current_version = $this->db->getDriver()->getSchemaVersion(); + + if ($current_version < $last_version) { + return $this->migrateTo($current_version, $last_version); + } + + return true; + } + + /** + * Migrate the schema to one version to another + * + * @access public + * @param integer $current_version + * @param integer $next_version + * @return boolean + */ + public function migrateTo($current_version, $next_version) + { + try { + for ($i = $current_version + 1; $i <= $next_version; $i++) { + $this->db->startTransaction(); + $this->db->getDriver()->disableForeignKeys(); + + $function_name = $this->getNamespace().'\version_'.$i; + + if (function_exists($function_name)) { + $this->db->setLogMessage('Running migration '.$function_name); + call_user_func($function_name, $this->db->getConnection()); + } + + $this->db->getDriver()->setSchemaVersion($i); + $this->db->getDriver()->enableForeignKeys(); + $this->db->closeTransaction(); + } + } catch (PDOException $e) { + $this->db->setLogMessage($e->getMessage()); + $this->db->cancelTransaction(); + $this->db->getDriver()->enableForeignKeys(); + return false; + } + + return true; + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/StatementHandler.php b/vendor/fguillot/picodb/lib/PicoDb/StatementHandler.php new file mode 100644 index 00000000..a7021b36 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/StatementHandler.php @@ -0,0 +1,353 @@ +<?php + +namespace PicoDb; + +use PDO; +use PDOException; +use PDOStatement; + +/** + * Statement Handler + * + * @package PicoDb + * @author Frederic Guillot + */ +class StatementHandler +{ + /** + * Database instance + * + * @access protected + * @var Database + */ + protected $db = null; + + /** + * Flag to calculate query time + * + * @access protected + * @var boolean + */ + protected $stopwatch = false; + + /** + * Start time + * + * @access protected + * @var float + */ + protected $startTime = 0; + + /** + * Execution time of all queries + * + * @access protected + * @var float + */ + protected $executionTime = 0; + + /** + * Flag to log generated SQL queries + * + * @access protected + * @var boolean + */ + protected $logQueries = false; + + /** + * Run explain command on each query + * + * @access protected + * @var boolean + */ + protected $explain = false; + + /** + * Number of SQL queries executed + * + * @access protected + * @var integer + */ + protected $nbQueries = 0; + + /** + * SQL query + * + * @access protected + * @var string + */ + protected $sql = ''; + + /** + * Positional SQL parameters + * + * @access protected + * @var array + */ + protected $positionalParams = array(); + + /** + * Named SQL parameters + * + * @access protected + * @var array + */ + protected $namedParams = array(); + + /** + * Flag to use named params + * + * @access protected + * @var boolean + */ + protected $useNamedParams = false; + + /** + * LOB params + * + * @access protected + * @var array + */ + protected $lobParams = array(); + + /** + * Constructor + * + * @access public + * @param Database $db + */ + public function __construct(Database $db) + { + $this->db = $db; + } + + /** + * Enable query logging + * + * @access public + * @return $this + */ + public function withLogging() + { + $this->logQueries = true; + return $this; + } + + /** + * Record query execution time + * + * @access public + * @return $this + */ + public function withStopWatch() + { + $this->stopwatch = true; + return $this; + } + + /** + * Execute explain command on query + * + * @access public + * @return $this + */ + public function withExplain() + { + $this->explain = true; + return $this; + } + + /** + * Set SQL query + * + * @access public + * @param string $sql + * @return $this + */ + public function withSql($sql) + { + $this->sql = $sql; + return $this; + } + + /** + * Set positional parameters + * + * @access public + * @param array $params + * @return $this + */ + public function withPositionalParams(array $params) + { + $this->positionalParams = $params; + return $this; + } + + /** + * Set named parameters + * + * @access public + * @param array $params + * @return $this + */ + public function withNamedParams(array $params) + { + $this->namedParams = $params; + $this->useNamedParams = true; + return $this; + } + + /** + * Bind large object parameter + * + * @access public + * @param $name + * @param $fp + * @return $this + */ + public function withLobParam($name, &$fp) + { + $this->lobParams[$name] =& $fp; + return $this; + } + + /** + * Get number of queries executed + * + * @access public + * @return int + */ + public function getNbQueries() + { + return $this->nbQueries; + } + + /** + * Execute a prepared statement + * + * Note: returns false on duplicate keys instead of SQLException + * + * @access public + * @return PDOStatement|false + */ + public function execute() + { + try { + $this->beforeExecute(); + + $pdoStatement = $this->db->getConnection()->prepare($this->sql); + $this->bindParams($pdoStatement); + $pdoStatement->execute(); + + $this->afterExecute(); + return $pdoStatement; + } catch (PDOException $e) { + return $this->handleSqlError($e); + } + } + + /** + * Bind parameters to PDOStatement + * + * @access protected + * @param PDOStatement $pdoStatement + */ + protected function bindParams(PDOStatement $pdoStatement) + { + $i = 1; + + foreach ($this->lobParams as $name => $variable) { + if (! $this->useNamedParams) { + $parameter = $i; + $i++; + } else { + $parameter = $name; + } + + $pdoStatement->bindParam($parameter, $variable, PDO::PARAM_LOB); + } + + foreach ($this->positionalParams as $value) { + $pdoStatement->bindValue($i, $value, PDO::PARAM_STR); + $i++; + } + + foreach ($this->namedParams as $name => $value) { + $pdoStatement->bindValue($name, $value, PDO::PARAM_STR); + } + } + + /** + * Method executed before query execution + * + * @access protected + */ + protected function beforeExecute() + { + if ($this->logQueries) { + $this->db->setLogMessage($this->sql); + } + + if ($this->stopwatch) { + $this->startTime = microtime(true); + } + } + + /** + * Method executed after query execution + * + * @access protected + */ + protected function afterExecute() + { + if ($this->stopwatch) { + $duration = microtime(true) - $this->startTime; + $this->executionTime += $duration; + $this->db->setLogMessage('query_duration='.$duration); + $this->db->setLogMessage('total_execution_time='.$this->executionTime); + } + + if ($this->explain) { + $this->db->setLogMessages($this->db->getDriver()->explain($this->sql, $this->positionalParams)); + } + + $this->nbQueries++; + $this->cleanup(); + } + + /** + * Reset internal properties after execution + * The same object instance is used + * + * @access protected + */ + protected function cleanup() + { + $this->sql = ''; + $this->useNamedParams = false; + $this->positionalParams = array(); + $this->namedParams = array(); + $this->lobParams = array(); + } + + /** + * Handle PDOException + * + * @access public + * @param PDOException $e + * @return bool + * @throws SQLException + */ + public function handleSqlError(PDOException $e) + { + $this->cleanup(); + $this->db->cancelTransaction(); + $this->db->setLogMessage($e->getMessage()); + + if ($this->db->getDriver()->isDuplicateKeyError($e->getCode())) { + return false; + } + + throw new SQLException('SQL error'.($this->logQueries ? ': '.$e->getMessage() : '')); + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Table.php b/vendor/fguillot/picodb/lib/PicoDb/Table.php new file mode 100644 index 00000000..09eb928e --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Table.php @@ -0,0 +1,721 @@ +<?php + +namespace PicoDb; + +use PDO; +use Closure; +use PicoDb\Builder\ConditionBuilder; +use PicoDb\Builder\InsertBuilder; +use PicoDb\Builder\UpdateBuilder; + +/** + * Table + * + * @package PicoDb + * @author Frederic Guillot + * + * @method $this addCondition($sql) + * @method $this beginOr() + * @method $this closeOr() + * @method $this eq($column, $value) + * @method $this neq($column, $value) + * @method $this in($column, array $values) + * @method $this inSubquery($column, Table $subquery) + * @method $this notIn($column, array $values) + * @method $this notInSubquery($column, Table $subquery) + * @method $this like($column, $value) + * @method $this ilike($column, $value) + * @method $this gt($column, $value) + * @method $this gtSubquery($column, Table $subquery) + * @method $this lt($column, $value) + * @method $this ltSubquery($column, Table $subquery) + * @method $this gte($column, $value) + * @method $this gteSubquery($column, Table $subquery) + * @method $this lte($column, $value) + * @method $this lteSubquery($column, Table $subquery) + * @method $this isNull($column) + * @method $this notNull($column) + */ +class Table +{ + /** + * Sorting direction + * + * @access public + * @var string + */ + const SORT_ASC = 'ASC'; + const SORT_DESC = 'DESC'; + + /** + * Condition instance + * + * @access protected + * @var ConditionBuilder + */ + protected $conditionBuilder; + + /** + * Database instance + * + * @access protected + * @var Database + */ + protected $db; + + /** + * Table name + * + * @access protected + * @var string + */ + protected $name = ''; + + /** + * Columns list for SELECT query + * + * @access private + * @var array + */ + private $columns = array(); + + /** + * Columns to sum during update + * + * @access private + * @var array + */ + private $sumColumns = array(); + + /** + * SQL limit + * + * @access private + * @var string + */ + private $sqlLimit = ''; + + /** + * SQL offset + * + * @access private + * @var string + */ + private $sqlOffset = ''; + + /** + * SQL order + * + * @access private + * @var string + */ + private $sqlOrder = ''; + + /** + * SQL custom SELECT value + * + * @access private + * @var string + */ + private $sqlSelect = ''; + + /** + * SQL joins + * + * @access private + * @var array + */ + private $joins = array(); + + /** + * Use DISTINCT or not? + * + * @access private + * @var boolean + */ + private $distinct = false; + + /** + * Group by those columns + * + * @access private + * @var array + */ + private $groupBy = array(); + + /** + * Callback for result filtering + * + * @access private + * @var Closure + */ + private $callback = null; + + /** + * Constructor + * + * @access public + * @param Database $db + * @param string $name + */ + public function __construct(Database $db, $name) + { + $this->db = $db; + $this->name = $name; + $this->conditionBuilder = new ConditionBuilder($db); + } + + /** + * Return the table name + * + * @access public + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Return ConditionBuilder object + * + * @access public + * @return ConditionBuilder + */ + public function getConditionBuilder() + { + return $this->conditionBuilder; + } + + /** + * Insert or update + * + * @access public + * @param array $data + * @return boolean + */ + public function save(array $data) + { + return $this->conditionBuilder->hasCondition() ? $this->update($data) : $this->insert($data); + } + + /** + * Update + * + * @access public + * @param array $data + * @return boolean + */ + public function update(array $data = array()) + { + $values = array_merge(array_values($data), array_values($this->sumColumns), $this->conditionBuilder->getValues()); + $sql = UpdateBuilder::getInstance($this->db, $this->conditionBuilder) + ->withTable($this->name) + ->withColumns(array_keys($data)) + ->withSumColumns(array_keys($this->sumColumns)) + ->build(); + + return $this->db->execute($sql, $values) !== false; + } + + /** + * Insert + * + * @access public + * @param array $data + * @return boolean + */ + public function insert(array $data) + { + return $this->db->getStatementHandler() + ->withSql(InsertBuilder::getInstance($this->db, $this->conditionBuilder) + ->withTable($this->name) + ->withColumns(array_keys($data)) + ->build() + ) + ->withNamedParams($data) + ->execute() !== false; + } + + /** + * Insert a new row and return the ID of the primary key + * + * @access public + * @param array $data + * @return bool|int + */ + public function persist(array $data) + { + if ($this->insert($data)) { + return $this->db->getLastId(); + } + + return false; + } + + /** + * Remove + * + * @access public + * @return boolean + */ + public function remove() + { + $sql = sprintf( + 'DELETE FROM %s %s', + $this->db->escapeIdentifier($this->name), + $this->conditionBuilder->build() + ); + + $result = $this->db->execute($sql, $this->conditionBuilder->getValues()); + return $result->rowCount() > 0; + } + + /** + * Fetch all rows + * + * @access public + * @return array + */ + public function findAll() + { + $rq = $this->db->execute($this->buildSelectQuery(), $this->conditionBuilder->getValues()); + $results = $rq->fetchAll(PDO::FETCH_ASSOC); + + if (is_callable($this->callback) && ! empty($results)) { + return call_user_func($this->callback, $results); + } + + return $results; + } + + /** + * Find all with a single column + * + * @access public + * @param string $column + * @return mixed + */ + public function findAllByColumn($column) + { + $this->columns = array($column); + $rq = $this->db->execute($this->buildSelectQuery(), $this->conditionBuilder->getValues()); + + return $rq->fetchAll(PDO::FETCH_COLUMN, 0); + } + + /** + * Fetch one row + * + * @access public + * @return array|null + */ + public function findOne() + { + $this->limit(1); + $result = $this->findAll(); + + return isset($result[0]) ? $result[0] : null; + } + + /** + * Fetch one column, first row + * + * @access public + * @param string $column + * @return string + */ + public function findOneColumn($column) + { + $this->limit(1); + $this->columns = array($column); + + return $this->db->execute($this->buildSelectQuery(), $this->conditionBuilder->getValues())->fetchColumn(); + } + + /** + * Build a subquery with an alias + * + * @access public + * @param string $sql + * @param string $alias + * @return $this + */ + public function subquery($sql, $alias) + { + $this->columns[] = '('.$sql.') AS '.$this->db->escapeIdentifier($alias); + return $this; + } + + /** + * Exists + * + * @access public + * @return integer + */ + public function exists() + { + $sql = sprintf( + 'SELECT 1 FROM %s '.implode(' ', $this->joins).$this->conditionBuilder->build(), + $this->db->escapeIdentifier($this->name) + ); + + $rq = $this->db->execute($sql, $this->conditionBuilder->getValues()); + $result = $rq->fetchColumn(); + + return $result ? true : false; + } + + /** + * Count + * + * @access public + * @return integer + */ + public function count() + { + $sql = sprintf( + 'SELECT COUNT(*) FROM %s '.implode(' ', $this->joins).$this->conditionBuilder->build().$this->sqlOrder.$this->sqlLimit.$this->sqlOffset, + $this->db->escapeIdentifier($this->name) + ); + + $rq = $this->db->execute($sql, $this->conditionBuilder->getValues()); + $result = $rq->fetchColumn(); + + return $result ? (int) $result : 0; + } + + /** + * Sum + * + * @access public + * @param string $column + * @return float + */ + public function sum($column) + { + $sql = sprintf( + 'SELECT SUM(%s) FROM %s '.implode(' ', $this->joins).$this->conditionBuilder->build().$this->sqlOrder.$this->sqlLimit.$this->sqlOffset, + $this->db->escapeIdentifier($column), + $this->db->escapeIdentifier($this->name) + ); + + $rq = $this->db->execute($sql, $this->conditionBuilder->getValues()); + $result = $rq->fetchColumn(); + + return $result ? (float) $result : 0; + } + + /** + * Increment column value + * + * @access public + * @param string $column + * @param string $value + * @return boolean + */ + public function increment($column, $value) + { + $sql = sprintf( + 'UPDATE %s SET %s=%s+%d '.$this->conditionBuilder->build(), + $this->db->escapeIdentifier($this->name), + $this->db->escapeIdentifier($column), + $this->db->escapeIdentifier($column), + $value + ); + + return $this->db->execute($sql, $this->conditionBuilder->getValues()) !== false; + } + + /** + * Decrement column value + * + * @access public + * @param string $column + * @param string $value + * @return boolean + */ + public function decrement($column, $value) + { + $sql = sprintf( + 'UPDATE %s SET %s=%s-%d '.$this->conditionBuilder->build(), + $this->db->escapeIdentifier($this->name), + $this->db->escapeIdentifier($column), + $this->db->escapeIdentifier($column), + $value + ); + + return $this->db->execute($sql, $this->conditionBuilder->getValues()) !== false; + } + + /** + * Left join + * + * @access public + * @param string $table Join table + * @param string $foreign_column Foreign key on the join table + * @param string $local_column Local column + * @param string $local_table Local table + * @param string $alias Join table alias + * @return $this + */ + public function join($table, $foreign_column, $local_column, $local_table = '', $alias = '') + { + $this->joins[] = sprintf( + 'LEFT JOIN %s ON %s=%s', + $this->db->escapeIdentifier($table), + $this->db->escapeIdentifier($alias ?: $table).'.'.$this->db->escapeIdentifier($foreign_column), + $this->db->escapeIdentifier($local_table ?: $this->name).'.'.$this->db->escapeIdentifier($local_column) + ); + + return $this; + } + + /** + * Left join + * + * @access public + * @param string $table1 + * @param string $alias1 + * @param string $column1 + * @param string $table2 + * @param string $column2 + * @return $this + */ + public function left($table1, $alias1, $column1, $table2, $column2) + { + $this->joins[] = sprintf( + 'LEFT JOIN %s AS %s ON %s=%s', + $this->db->escapeIdentifier($table1), + $this->db->escapeIdentifier($alias1), + $this->db->escapeIdentifier($alias1).'.'.$this->db->escapeIdentifier($column1), + $this->db->escapeIdentifier($table2).'.'.$this->db->escapeIdentifier($column2) + ); + + return $this; + } + + /** + * Inner join + * + * @access public + * @param string $table1 + * @param string $alias1 + * @param string $column1 + * @param string $table2 + * @param string $column2 + * @return $this + */ + public function inner($table1, $alias1, $column1, $table2, $column2) + { + $this->joins[] = sprintf( + 'JOIN %s AS %s ON %s=%s', + $this->db->escapeIdentifier($table1), + $this->db->escapeIdentifier($alias1), + $this->db->escapeIdentifier($alias1).'.'.$this->db->escapeIdentifier($column1), + $this->db->escapeIdentifier($table2).'.'.$this->db->escapeIdentifier($column2) + ); + + return $this; + } + + /** + * Order by + * + * @access public + * @param string $column Column name + * @param string $order Direction ASC or DESC + * @return $this + */ + public function orderBy($column, $order = self::SORT_ASC) + { + $order = strtoupper($order); + $order = $order === self::SORT_ASC || $order === self::SORT_DESC ? $order : self::SORT_ASC; + + if ($this->sqlOrder === '') { + $this->sqlOrder = ' ORDER BY '.$this->db->escapeIdentifier($column).' '.$order; + } + else { + $this->sqlOrder .= ', '.$this->db->escapeIdentifier($column).' '.$order; + } + + return $this; + } + + /** + * Ascending sort + * + * @access public + * @param string $column + * @return $this + */ + public function asc($column) + { + $this->orderBy($column, self::SORT_ASC); + return $this; + } + + /** + * Descending sort + * + * @access public + * @param string $column + * @return $this + */ + public function desc($column) + { + $this->orderBy($column, self::SORT_DESC); + return $this; + } + + /** + * Limit + * + * @access public + * @param integer $value + * @return $this + */ + public function limit($value) + { + if (! is_null($value)) { + $this->sqlLimit = ' LIMIT '.(int) $value; + } + + return $this; + } + + /** + * Offset + * + * @access public + * @param integer $value + * @return $this + */ + public function offset($value) + { + if (! is_null($value)) { + $this->sqlOffset = ' OFFSET '.(int) $value; + } + + return $this; + } + + /** + * Group by + * + * @access public + * @return $this + */ + public function groupBy() + { + $this->groupBy = func_get_args(); + return $this; + } + + /** + * Custom select + * + * @access public + * @param string $select + * @return $this + */ + public function select($select) + { + $this->sqlSelect = $select; + return $this; + } + + /** + * Define the columns for the select + * + * @access public + * @return $this + */ + public function columns() + { + $this->columns = func_get_args(); + return $this; + } + + /** + * Sum column + * + * @access public + * @param string $column + * @param mixed $value + * @return $this + */ + public function sumColumn($column, $value) + { + $this->sumColumns[$column] = $value; + return $this; + } + + /** + * Distinct + * + * @access public + * @return $this + */ + public function distinct() + { + $this->columns = func_get_args(); + $this->distinct = true; + return $this; + } + + /** + * Add callback to alter the resultset + * + * @access public + * @param Closure|array $callback + * @return $this + */ + public function callback($callback) + { + $this->callback = $callback; + return $this; + } + + /** + * Build a select query + * + * @access public + * @return string + */ + public function buildSelectQuery() + { + if (empty($this->sqlSelect)) { + $this->columns = $this->db->escapeIdentifierList($this->columns); + $this->sqlSelect = ($this->distinct ? 'DISTINCT ' : '').(empty($this->columns) ? '*' : implode(', ', $this->columns)); + } + + $this->groupBy = $this->db->escapeIdentifierList($this->groupBy); + + return trim(sprintf( + 'SELECT %s FROM %s %s %s %s %s %s %s', + $this->sqlSelect, + $this->db->escapeIdentifier($this->name), + implode(' ', $this->joins), + $this->conditionBuilder->build(), + empty($this->groupBy) ? '' : 'GROUP BY '.implode(', ', $this->groupBy), + $this->sqlOrder, + $this->sqlLimit, + $this->sqlOffset + )); + } + + /** + * Magic method for sql conditions + * + * @access public + * @param string $name + * @param array $arguments + * @return $this + */ + public function __call($name, array $arguments) + { + call_user_func_array(array($this->conditionBuilder, $name), $arguments); + return $this; + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/UrlParser.php b/vendor/fguillot/picodb/lib/PicoDb/UrlParser.php new file mode 100644 index 00000000..d8fcaf00 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/UrlParser.php @@ -0,0 +1,93 @@ +<?php + +namespace PicoDb; + +/** + * Class UrlParser + * + * @package PicoDb + * @author Frederic Guillot + */ +class UrlParser +{ + /** + * URL + * + * @access private + * @var string + */ + private $url; + + /** + * Constructor + * + * @access public + * @param string $environmentVariable + */ + public function __construct($environmentVariable = 'DATABASE_URL') + { + $this->url = getenv($environmentVariable); + } + + /** + * Get object instance + * + * @access public + * @param string $environmentVariable + * @return static + */ + public static function getInstance($environmentVariable = 'DATABASE_URL') + { + return new static($environmentVariable); + } + + /** + * Return true if the variable is defined + * + * @access public + * @return bool + */ + public function isEnvironmentVariableDefined() + { + return ! empty($this->url); + } + + /** + * Get settings from URL + * + * @access public + * @param string $url + * @return array + */ + public function getSettings($url = '') + { + $url = $url ?: $this->url; + $components = parse_url($url); + + if ($components === false) { + return array(); + } + + return array( + 'driver' => $this->getUrlComponent($components, 'scheme'), + 'username' => $this->getUrlComponent($components, 'user'), + 'password' => $this->getUrlComponent($components, 'pass'), + 'hostname' => $this->getUrlComponent($components, 'host'), + 'port' => $this->getUrlComponent($components, 'port'), + 'database' => ltrim($this->getUrlComponent($components, 'path'), '/'), + ); + } + + /** + * Get URL component + * + * @access private + * @param array $components + * @param string $component + * @return mixed|null + */ + private function getUrlComponent(array $components, $component) + { + return ! empty($components[$component]) ? $components[$component] : null; + } +} diff --git a/vendor/fguillot/simple-queue/LICENSE b/vendor/fguillot/simple-queue/LICENSE new file mode 100644 index 00000000..a19d63a3 --- /dev/null +++ b/vendor/fguillot/simple-queue/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Frédéric Guillot + +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. diff --git a/vendor/fguillot/simple-queue/src/Adapter/AmqpQueueAdapter.php b/vendor/fguillot/simple-queue/src/Adapter/AmqpQueueAdapter.php new file mode 100644 index 00000000..379dd9b8 --- /dev/null +++ b/vendor/fguillot/simple-queue/src/Adapter/AmqpQueueAdapter.php @@ -0,0 +1,138 @@ +<?php + +namespace SimpleQueue\Adapter; + +use DateTime; +use PhpAmqpLib\Channel\AMQPChannel; +use PhpAmqpLib\Message\AMQPMessage; +use PhpAmqpLib\Wire\AMQPTable; +use SimpleQueue\Job; +use SimpleQueue\QueueAdapterInterface; + +/** + * Class AmqpQueueAdapter + * + * @package SimpleQueue\Adapter + */ +class AmqpQueueAdapter implements QueueAdapterInterface +{ + /** + * @var AMQPChannel + */ + protected $channel; + + /** + * @var string + */ + protected $exchange = ''; + + /** + * @var string + */ + protected $queue = ''; + + /** + * AmqpQueueAdapter constructor. + * + * @param AMQPChannel $channel + * @param string $queue + * @param string $exchange + */ + public function __construct(AMQPChannel $channel, $queue, $exchange) + { + $this->channel = $channel; + $this->exchange = $exchange; + $this->queue = $queue; + } + + /** + * Send a job + * + * @access public + * @param Job $job + * @return $this + */ + public function push(Job $job) + { + $message = new AMQPMessage($job->serialize(), array('content_type' => 'text/plain')); + $this->channel->basic_publish($message, $this->exchange); + return $this; + } + + /** + * Schedule a job in the future + * + * @access public + * @param Job $job + * @param DateTime $dateTime + * @return $this + */ + public function schedule(Job $job, DateTime $dateTime) + { + $now = new DateTime(); + $when = clone($dateTime); + $delay = $when->getTimestamp() - $now->getTimestamp(); + + $message = new AMQPMessage($job->serialize(), array('delivery_mode' => 2)); + $message->set('application_headers', new AMQPTable(array('x-delay' => $delay))); + + $this->channel->basic_publish($message, $this->exchange); + return $this; + } + + /** + * Wait and get job from a queue + * + * @access public + * @return Job|null + */ + public function pull() + { + $message = null; + + $this->channel->basic_consume($this->queue, 'test', false, false, false, false, function ($msg) use (&$message) { + $message = $msg; + $message->delivery_info['channel']->basic_cancel($message->delivery_info['consumer_tag']); + }); + + while (count($this->channel->callbacks)) { + $this->channel->wait(); + } + + if ($message === null) { + return null; + } + + $job = new Job(); + $job->setId($message->get('delivery_tag')); + $job->unserialize($message->getBody()); + + return $job; + } + + /** + * Acknowledge a job + * + * @access public + * @param Job $job + * @return $this + */ + public function completed(Job $job) + { + $this->channel->basic_ack($job->getId()); + return $this; + } + + /** + * Mark a job as failed + * + * @access public + * @param Job $job + * @return $this + */ + public function failed(Job $job) + { + $this->channel->basic_nack($job->getId()); + return $this; + } +} diff --git a/vendor/fguillot/simple-queue/src/Adapter/AwsSqsQueueAdapter.php b/vendor/fguillot/simple-queue/src/Adapter/AwsSqsQueueAdapter.php new file mode 100644 index 00000000..01377317 --- /dev/null +++ b/vendor/fguillot/simple-queue/src/Adapter/AwsSqsQueueAdapter.php @@ -0,0 +1,150 @@ +<?php + +namespace SimpleQueue\Adapter; + +use Aws\Sqs\SqsClient; +use SimpleQueue\Job; +use SimpleQueue\QueueAdapterInterface; +use DateTime; + +/** + * Class AwsSqsQueueAdapter + * + * @package SimpleQueue\Adapter + * @author George Webb <george@webb.uno> + */ +class AwsSqsQueueAdapter implements QueueAdapterInterface +{ + /** + * @var string + */ + private $queueName; + + /** + * @var SqsClient + */ + private $sqsClient; + + /** + * @var string + */ + private $sqsUrl; + + /** + * @var array + */ + private $config; + + /** + * AwsSqsQueueAdapter constructor. + * + * @param string $queueName The name of the SQS queue + * @param SqsClient $sqsClient An SQS client + * @param array $config Array of config values + */ + public function __construct($queueName, SqsClient $sqsClient, $config = array()) + { + $this->queueName = $queueName; + $this->sqsClient = $sqsClient; + $this->sqsUrl = $this->sqsClient->getQueueUrl(array('QueueName' => $this->queueName))->get('QueueUrl'); + $this->config = $config; + } + + /** + * Send a job + * + * @access public + * @param Job $job + * @return $this + */ + public function push(Job $job) + { + $this->sqsClient->sendMessage(array( + 'QueueUrl' => $this->sqsUrl, + 'MessageBody' => $job->serialize() + )); + return $this; + } + + /** + * Schedule a job in the future + * + * @access public + * @param Job $job + * @param DateTime $dateTime + * @return $this + */ + public function schedule(Job $job, DateTime $dateTime) + { + $now = new DateTime(); + $when = clone($dateTime); + $delay = $when->getTimestamp() - $now->getTimestamp(); + + $this->sqsClient->sendMessage(array( + 'QueueUrl' => $this->sqsUrl, + 'MessageBody' => $job->serialize(), + 'VisibilityTimeout' => $delay + )); + + return $this; + } + + /** + * Wait and get job from a queue + * + * @access public + * @return Job|null + */ + public function pull() + { + $result = $this->sqsClient->receiveMessage(array( + 'QueueUrl' => $this->sqsUrl, + 'WaitTimeSeconds' => empty($this->config['LongPollingTime']) ? 0 : (int) $this->config['LongPollingTime'] + )); + + if ($result['Messages'] == null) { + return null; + } + + $resultMessage = array_pop($result['Messages']); + + $job = new Job(); + $job->setId($resultMessage['ReceiptHandle']); + $job->unserialize($resultMessage['Body']); + + return $job; + } + + /** + * Acknowledge a job + * + * @access public + * @param Job $job + * @return $this + */ + public function completed(Job $job) + { + $this->sqsClient->deleteMessage(array( + 'QueueUrl' => $this->sqsUrl, + 'ReceiptHandle' => $job->getId() + )); + return $this; + } + + /** + * Mark a job as failed + * + * @access public + * @param Job $job + * @return $this + */ + public function failed(Job $job) + { + $this->sqsClient->changeMessageVisibility(array( + 'QueueUrl' => $this->sqsUrl, + 'ReceiptHandle' => $job->getId(), + 'VisibilityTimeout' => 0 + )); + return $this; + } +} diff --git a/vendor/fguillot/simple-queue/src/Adapter/BeanstalkQueueAdapter.php b/vendor/fguillot/simple-queue/src/Adapter/BeanstalkQueueAdapter.php new file mode 100644 index 00000000..407f60e2 --- /dev/null +++ b/vendor/fguillot/simple-queue/src/Adapter/BeanstalkQueueAdapter.php @@ -0,0 +1,120 @@ +<?php + +namespace SimpleQueue\Adapter; + +use DateTime; +use Pheanstalk\Job as BeanstalkJob; +use Pheanstalk\Pheanstalk; +use Pheanstalk\PheanstalkInterface; +use SimpleQueue\Job; +use SimpleQueue\QueueAdapterInterface; + +/** + * Class BeanstalkQueueAdapter + * + * @package SimpleQueue\Adapter + */ +class BeanstalkQueueAdapter implements QueueAdapterInterface +{ + /** + * @var PheanstalkInterface + */ + protected $beanstalk; + + /** + * @var string + */ + protected $queueName = ''; + + /** + * BeanstalkQueueAdapter constructor. + * + * @param PheanstalkInterface $beanstalk + * @param string $queueName + */ + public function __construct(PheanstalkInterface $beanstalk, $queueName) + { + $this->beanstalk = $beanstalk; + $this->queueName = $queueName; + } + + /** + * Send a job + * + * @access public + * @param Job $job + * @return $this + */ + public function push(Job $job) + { + $this->beanstalk->putInTube($this->queueName, $job->serialize()); + return $this; + } + + /** + * Schedule a job in the future + * + * @access public + * @param Job $job + * @param DateTime $dateTime + * @return $this + */ + public function schedule(Job $job, DateTime $dateTime) + { + $now = new DateTime(); + $when = clone($dateTime); + $delay = $when->getTimestamp() - $now->getTimestamp(); + + $this->beanstalk->putInTube($this->queueName, $job->serialize(), Pheanstalk::DEFAULT_PRIORITY, $delay); + return $this; + } + + /** + * Wait and get job from a queue + * + * @access public + * @return Job|null + */ + public function pull() + { + $beanstalkJob = $this->beanstalk->reserveFromTube($this->queueName); + + if ($beanstalkJob === false) { + return null; + } + + $job = new Job(); + $job->setId($beanstalkJob->getId()); + $job->unserialize($beanstalkJob->getData()); + + return $job; + } + + /** + * Acknowledge a job + * + * @access public + * @param Job $job + * @return $this + */ + public function completed(Job $job) + { + $beanstalkJob = new BeanstalkJob($job->getId(), $job->serialize()); + $this->beanstalk->delete($beanstalkJob); + return $this; + } + + /** + * Mark a job as failed + * + * @access public + * @param Job $job + * @return $this + */ + public function failed(Job $job) + { + $beanstalkJob = new BeanstalkJob($job->getId(), $job->serialize()); + $this->beanstalk->bury($beanstalkJob); + return $this; + } +} diff --git a/vendor/fguillot/simple-queue/src/Adapter/DisqueQueueAdapter.php b/vendor/fguillot/simple-queue/src/Adapter/DisqueQueueAdapter.php new file mode 100644 index 00000000..047658d7 --- /dev/null +++ b/vendor/fguillot/simple-queue/src/Adapter/DisqueQueueAdapter.php @@ -0,0 +1,109 @@ +<?php + +namespace SimpleQueue\Adapter; + +use DateTime; +use Disque\Client as DisqueClient; +use Disque\Queue\Job as DisqueJob; +use SimpleQueue\Job; +use SimpleQueue\QueueAdapterInterface; + +/** + * Class DisqueQueueAdapter + * + * @package SimpleQueue\Adapter + */ +class DisqueQueueAdapter implements QueueAdapterInterface +{ + /** + * @var DisqueClient + */ + protected $disque; + + /** + * @var string + */ + protected $queueName; + + /** + * DisqueQueueAdapter constructor. + * + * @param DisqueClient $disque + * @param string $queueName + */ + public function __construct(DisqueClient $disque, $queueName) + { + $this->disque = $disque; + $this->queueName = $queueName; + } + + /** + * Send a job + * + * @access public + * @param Job $job + * @return $this + */ + public function push(Job $job) + { + $this->disque->queue($this->queueName)->push(new DisqueJob($job->getBody())); + return $this; + } + + /** + * Schedule a job in the future + * + * @access public + * @param Job $job + * @param DateTime $dateTime + * @return $this + */ + public function schedule(Job $job, DateTime $dateTime) + { + $this->disque->queue($this->queueName)->schedule(new DisqueJob($job->serialize()), $dateTime); + return $this; + } + + /** + * Wait and get job from a queue + * + * @access public + * @return Job|null + */ + public function pull() + { + $disqueJob = $this->disque->queue($this->queueName)->pull(); + + if ($disqueJob === null) { + return null; + } + + return new Job($disqueJob->getBody(), $disqueJob->getId()); + } + + /** + * Acknowledge a job + * + * @access public + * @param Job $job + * @return $this + */ + public function completed(Job $job) + { + $this->disque->queue($this->queueName)->processed(new DisqueJob($job->getBody(), $job->getId())); + return $this; + } + + /** + * Mark a job as failed + * + * @access public + * @param Job $job + * @return $this + */ + public function failed(Job $job) + { + $this->disque->queue($this->queueName)->failed(new DisqueJob($job->getBody(), $job->getId())); + return $this; + } +} diff --git a/vendor/fguillot/simple-queue/src/Adapter/MemoryQueueAdapter.php b/vendor/fguillot/simple-queue/src/Adapter/MemoryQueueAdapter.php new file mode 100644 index 00000000..36375d5b --- /dev/null +++ b/vendor/fguillot/simple-queue/src/Adapter/MemoryQueueAdapter.php @@ -0,0 +1,100 @@ +<?php + +namespace SimpleQueue\Adapter; + +use DateTime; +use Exception; +use SimpleQueue\Exception\NotSupportedException; +use SimpleQueue\QueueAdapterInterface; +use SimpleQueue\Job; +use SplQueue; + +/** + * Class MemoryAdapter + * + * @package SimpleQueue\Adapter + */ +class MemoryQueueAdapter implements QueueAdapterInterface +{ + /** + * @var SplQueue + */ + protected $queue; + + /** + * MemoryAdapter constructor. + */ + public function __construct() + { + $this->queue = new SplQueue(); + } + + /** + * Send a job + * + * @access public + * @param Job $job + * @return $this + */ + public function push(Job $job) + { + $this->queue->enqueue($job->serialize()); + return $this; + } + + /** + * Schedule a job in the future + * + * @access public + * @param Job $job + * @param DateTime $dateTime + * @return bool + * @throws NotSupportedException + */ + public function schedule(Job $job, DateTime $dateTime) + { + throw new NotSupportedException('Job delay is not supported by MemoryQueue'); + } + + /** + * Wait and get job from a queue + * + * @access public + * @return Job|null + */ + public function pull() + { + try { + $job = new Job(); + $payload = $this->queue->dequeue(); + return $job->unserialize($payload); + } catch (Exception $e) { + return null; + } + } + + /** + * Acknowledge a job + * + * @access public + * @param Job $job + * @return $this + */ + public function completed(Job $job) + { + return $this; + } + + /** + * Mark a job as failed + * + * @access public + * @param Job $job + * @return $this + */ + public function failed(Job $job) + { + $this->queue->enqueue($job->serialize()); + return $this; + } +} diff --git a/vendor/fguillot/simple-queue/src/Exception/NotSupportedException.php b/vendor/fguillot/simple-queue/src/Exception/NotSupportedException.php new file mode 100644 index 00000000..36106659 --- /dev/null +++ b/vendor/fguillot/simple-queue/src/Exception/NotSupportedException.php @@ -0,0 +1,14 @@ +<?php + +namespace SimpleQueue\Exception; + +use Exception; + +/** + * Class NotSupportedException + * + * @package SimpleQueue\Exception + */ +class NotSupportedException extends Exception +{ +} diff --git a/vendor/fguillot/simple-queue/src/Job.php b/vendor/fguillot/simple-queue/src/Job.php new file mode 100644 index 00000000..799bbba8 --- /dev/null +++ b/vendor/fguillot/simple-queue/src/Job.php @@ -0,0 +1,98 @@ +<?php + +namespace SimpleQueue; + +/** + * Class Job + * + * @package SimpleQueue + */ +class Job +{ + protected $id; + protected $body; + + /** + * Job constructor. + * + * @param null $body + * @param null $id + */ + public function __construct($body = null, $id = null) + { + $this->body = $body; + $this->id = $id; + } + + /** + * Unserialize a payload + * + * @param string $payload + * @return $this + */ + public function unserialize($payload) + { + $this->body = json_decode($payload, true); + return $this; + } + + /** + * Serialize the body + * + * @return string + */ + public function serialize() + { + return json_encode($this->body); + } + + /** + * Set body + * + * @param mixed $body + * @return Job + */ + public function setBody($body) + { + $this->body = $body; + return $this; + } + + /** + * Get body + * + * @return mixed + */ + public function getBody() + { + return $this->body; + } + + /** + * Set job ID + * + * @param mixed $jobId + * @return Job + */ + public function setId($jobId) + { + $this->id = $jobId; + return $this; + } + + /** + * Get job ID + * @return mixed + */ + public function getId() + { + return $this->id; + } + + /** + * Execute job + */ + public function execute() + { + } +} diff --git a/vendor/fguillot/simple-queue/src/Queue.php b/vendor/fguillot/simple-queue/src/Queue.php new file mode 100644 index 00000000..a88b55cb --- /dev/null +++ b/vendor/fguillot/simple-queue/src/Queue.php @@ -0,0 +1,92 @@ +<?php + +namespace SimpleQueue; + +use DateTime; + +/** + * Class Queue + * + * @package SimpleQueue + */ +class Queue implements QueueAdapterInterface +{ + /** + * @var QueueAdapterInterface + */ + protected $queueAdapter; + + /** + * Queue constructor. + * + * @param QueueAdapterInterface $queueAdapter + */ + public function __construct(QueueAdapterInterface $queueAdapter) + { + $this->queueAdapter = $queueAdapter; + } + + /** + * Send a job + * + * @access public + * @param Job $job + * @return $this + */ + public function push(Job $job) + { + $this->queueAdapter->push($job); + return $this; + } + + /** + * Schedule a job in the future + * + * @access public + * @param Job $job + * @param DateTime $dateTime + * @return $this + */ + public function schedule(Job $job, DateTime $dateTime) + { + $this->queueAdapter->schedule($job, $dateTime); + return $this; + } + + /** + * Wait and get job from a queue + * + * @access public + * @return Job|null + */ + public function pull() + { + return $this->queueAdapter->pull(); + } + + /** + * Acknowledge a job + * + * @access public + * @param Job $job + * @return $this + */ + public function completed(Job $job) + { + $this->queueAdapter->completed($job); + return $this; + } + + /** + * Mark a job as failed + * + * @access public + * @param Job $job + * @return $this + */ + public function failed(Job $job) + { + $this->queueAdapter->failed($job); + return $this; + } +} diff --git a/vendor/fguillot/simple-queue/src/QueueAdapterInterface.php b/vendor/fguillot/simple-queue/src/QueueAdapterInterface.php new file mode 100644 index 00000000..9bda3070 --- /dev/null +++ b/vendor/fguillot/simple-queue/src/QueueAdapterInterface.php @@ -0,0 +1,58 @@ +<?php + +namespace SimpleQueue; + +use DateTime; + +/** + * Interface AdapterInterface + * + * @package SimpleQueue\Adapter + */ +interface QueueAdapterInterface +{ + /** + * Send a job + * + * @access public + * @param Job $job + * @return $this + */ + public function push(Job $job); + + /** + * Schedule a job in the future + * + * @access public + * @param Job $job + * @param DateTime $dateTime + * @return $this + */ + public function schedule(Job $job, DateTime $dateTime); + + /** + * Wait and get job from a queue + * + * @access public + * @return Job|null + */ + public function pull(); + + /** + * Acknowledge a job + * + * @access public + * @param Job $job + * @return $this + */ + public function completed(Job $job); + + /** + * Mark a job as failed + * + * @access public + * @param Job $job + * @return $this + */ + public function failed(Job $job); +} diff --git a/vendor/fguillot/simple-validator/LICENSE b/vendor/fguillot/simple-validator/LICENSE new file mode 100644 index 00000000..6a362bc1 --- /dev/null +++ b/vendor/fguillot/simple-validator/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Frederic Guillot + +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. diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validator.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validator.php new file mode 100644 index 00000000..30015dc6 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validator.php @@ -0,0 +1,44 @@ +<?php + +namespace SimpleValidator; + +class Validator +{ + private $data = array(); + private $errors = array(); + private $validators = array(); + + public function __construct(array $data, array $validators) + { + $this->data = $data; + $this->validators = $validators; + } + + public function execute() + { + $result = true; + + foreach ($this->validators as $validator) { + if (! $validator->execute($this->data)) { + $this->addError($validator->getField(), $validator->getErrorMessage()); + $result = false; + } + } + + return $result; + } + + public function addError($field, $message) + { + if (! isset($this->errors[$field])) { + $this->errors[$field] = array(); + } + + $this->errors[$field][] = $message; + } + + public function getErrors() + { + return $this->errors; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Alpha.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Alpha.php new file mode 100644 index 00000000..c29ba481 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Alpha.php @@ -0,0 +1,15 @@ +<?php + +namespace SimpleValidator\Validators; + +class Alpha extends Base +{ + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + return ctype_alpha($data[$this->field]); + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/AlphaNumeric.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/AlphaNumeric.php new file mode 100644 index 00000000..8d5000b4 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/AlphaNumeric.php @@ -0,0 +1,15 @@ +<?php + +namespace SimpleValidator\Validators; + +class AlphaNumeric extends Base +{ + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + return ctype_alnum($data[$this->field]); + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Base.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Base.php new file mode 100644 index 00000000..8157ae50 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Base.php @@ -0,0 +1,37 @@ +<?php + +namespace SimpleValidator\Validators; + +abstract class Base +{ + protected $field = ''; + protected $error_message = ''; + protected $data = array(); + + abstract public function execute(array $data); + + public function __construct($field, $error_message) + { + $this->field = $field; + $this->error_message = $error_message; + } + + public function getErrorMessage() + { + return $this->error_message; + } + + public function getField() + { + if (is_array($this->field)) { + return $this->field[0]; + } + + return $this->field; + } + + public function isFieldNotEmpty(array $data) + { + return isset($data[$this->field]) && $data[$this->field] !== ''; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Date.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Date.php new file mode 100644 index 00000000..4ec4b7fd --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Date.php @@ -0,0 +1,45 @@ +<?php + +namespace SimpleValidator\Validators; + +use DateTime; + +class Date extends Base +{ + private $formats = array(); + + public function __construct($field, $error_message, array $formats) + { + parent::__construct($field, $error_message); + $this->formats = $formats; + } + + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + foreach ($this->formats as $format) { + if ($this->isValidDate($data[$this->field], $format)) { + return true; + } + } + + return false; + } + + return true; + } + + public function isValidDate($value, $format) + { + $date = DateTime::createFromFormat($format, $value); + + if ($date !== false) { + $errors = DateTime::getLastErrors(); + if ($errors['error_count'] === 0 && $errors['warning_count'] === 0) { + return $date->getTimestamp() > 0; + } + } + + return false; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Email.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Email.php new file mode 100644 index 00000000..f3977042 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Email.php @@ -0,0 +1,67 @@ +<?php + +namespace SimpleValidator\Validators; + +class Email extends Base +{ + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + + // I use the same validation method as Firefox + // http://hg.mozilla.org/mozilla-central/file/cf5da681d577/content/html/content/src/nsHTMLInputElement.cpp#l3967 + + $value = $data[$this->field]; + $length = strlen($value); + + // If the email address begins with a '@' or ends with a '.', + // we know it's invalid. + if ($value[0] === '@' || $value[$length - 1] === '.') { + + return false; + } + + // Check the username + for ($i = 0; $i < $length && $value[$i] !== '@'; ++$i) { + + $c = $value[$i]; + + if (! (ctype_alnum($c) || $c === '.' || $c === '!' || $c === '#' || $c === '$' || + $c === '%' || $c === '&' || $c === '\'' || $c === '*' || $c === '+' || + $c === '-' || $c === '/' || $c === '=' || $c === '?' || $c === '^' || + $c === '_' || $c === '`' || $c === '{' || $c === '|' || $c === '}' || + $c === '~')) { + + return false; + } + } + + // There is no domain name (or it's one-character long), + // that's not a valid email address. + if (++$i >= $length) return false; + if (($i + 1) === $length) return false; + + // The domain name can't begin with a dot. + if ($value[$i] === '.') return false; + + // Parsing the domain name. + for (; $i < $length; ++$i) { + + $c = $value[$i]; + + if ($c === '.') { + + // A dot can't follow a dot. + if ($value[$i - 1] === '.') return false; + } + elseif (! (ctype_alnum($c) || $c === '-')) { + + // The domain characters have to be in this list to be valid. + return false; + } + } + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Equals.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Equals.php new file mode 100644 index 00000000..6b69dd80 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Equals.php @@ -0,0 +1,27 @@ +<?php + +namespace SimpleValidator\Validators; + +class Equals extends Base +{ + private $field2; + + public function __construct($field1, $field2, $error_message) + { + parent::__construct($field1, $error_message); + $this->field2 = $field2; + } + + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + if (! isset($data[$this->field2])) { + return false; + } + + return $data[$this->field] === $data[$this->field2]; + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Exists.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Exists.php new file mode 100644 index 00000000..1998e673 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Exists.php @@ -0,0 +1,38 @@ +<?php + +namespace SimpleValidator\Validators; + +use PDO; + +class Exists extends Base +{ + private $pdo; + private $key; + private $table; + + public function __construct($field, $error_message, PDO $pdo, $table, $key = '') + { + parent::__construct($field, $error_message); + + $this->pdo = $pdo; + $this->table = $table; + $this->key = $key; + } + + + public function execute(array $data) + { + if (! $this->isFieldNotEmpty($data)) { + return true; + } + + if ($this->key === '') { + $this->key = $this->field; + } + + $rq = $this->pdo->prepare('SELECT 1 FROM '.$this->table.' WHERE '.$this->key.'=?'); + $rq->execute(array($data[$this->field])); + + return $rq->fetchColumn() == 1; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/GreaterThan.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/GreaterThan.php new file mode 100644 index 00000000..6e560319 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/GreaterThan.php @@ -0,0 +1,23 @@ +<?php + +namespace SimpleValidator\Validators; + +class GreaterThan extends Base +{ + private $min; + + public function __construct($field, $error_message, $min) + { + parent::__construct($field, $error_message); + $this->min = $min; + } + + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + return $data[$this->field] > $this->min; + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/InArray.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/InArray.php new file mode 100644 index 00000000..f2f8c134 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/InArray.php @@ -0,0 +1,23 @@ +<?php + +namespace SimpleValidator\Validators; + +class InArray extends Base +{ + protected $array; + + public function __construct($field, array $array, $error_message) + { + parent::__construct($field, $error_message); + $this->array = $array; + } + + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + return in_array($data[$this->field], $this->array); + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Integer.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Integer.php new file mode 100644 index 00000000..5afdc1e0 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Integer.php @@ -0,0 +1,25 @@ +<?php + +namespace SimpleValidator\Validators; + +class Integer extends Base +{ + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + if (is_string($data[$this->field])) { + + if ($data[$this->field][0] === '-') { + return ctype_digit(substr($data[$this->field], 1)); + } + + return ctype_digit($data[$this->field]); + } + else { + return is_int($data[$this->field]); + } + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Ip.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Ip.php new file mode 100644 index 00000000..754f4f3e --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Ip.php @@ -0,0 +1,15 @@ +<?php + +namespace SimpleValidator\Validators; + +class Ip extends Base +{ + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + return filter_var($data[$this->field], FILTER_VALIDATE_IP) !== false; + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Length.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Length.php new file mode 100644 index 00000000..7ef241c4 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Length.php @@ -0,0 +1,26 @@ +<?php + +namespace SimpleValidator\Validators; + +class Length extends Base +{ + private $min; + private $max; + + public function __construct($field, $error_message, $min, $max) + { + parent::__construct($field, $error_message); + $this->min = $min; + $this->max = $max; + } + + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + $length = mb_strlen($data[$this->field], 'UTF-8'); + return $length >= $this->min && $length <= $this->max; + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MaxLength.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MaxLength.php new file mode 100644 index 00000000..6c4e7771 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MaxLength.php @@ -0,0 +1,24 @@ +<?php + +namespace SimpleValidator\Validators; + +class MaxLength extends Base +{ + private $max; + + public function __construct($field, $error_message, $max) + { + parent::__construct($field, $error_message); + $this->max = $max; + } + + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + $length = mb_strlen($data[$this->field], 'UTF-8'); + return $length <= $this->max; + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MinLength.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MinLength.php new file mode 100644 index 00000000..0ac4217a --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MinLength.php @@ -0,0 +1,24 @@ +<?php + +namespace SimpleValidator\Validators; + +class MinLength extends Base +{ + private $min; + + public function __construct($field, $error_message, $min) + { + parent::__construct($field, $error_message); + $this->min = $min; + } + + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + $length = mb_strlen($data[$this->field], 'UTF-8'); + return $length >= $this->min; + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEmpty.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEmpty.php new file mode 100644 index 00000000..bbb14b5b --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEmpty.php @@ -0,0 +1,15 @@ +<?php + +namespace SimpleValidator\Validators; + +class NotEmpty extends Base +{ + public function execute(array $data) + { + if (array_key_exists($this->field, $data)) { + return $data[$this->field] !== null && $data[$this->field] !== ''; + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEquals.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEquals.php new file mode 100644 index 00000000..d1d949ea --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEquals.php @@ -0,0 +1,28 @@ +<?php + +namespace SimpleValidator\Validators; + +class NotEquals extends Base +{ + private $field2; + + public function __construct($field1, $field2, $error_message) + { + parent::__construct($field1, $error_message); + $this->field2 = $field2; + } + + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + + if (! isset($data[$this->field2])) { + return true; + } + + return $data[$this->field] !== $data[$this->field2]; + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotInArray.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotInArray.php new file mode 100644 index 00000000..98974c9c --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotInArray.php @@ -0,0 +1,15 @@ +<?php + +namespace SimpleValidator\Validators; + +class NotInArray extends InArray +{ + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + return ! in_array($data[$this->field], $this->array); + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Numeric.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Numeric.php new file mode 100644 index 00000000..31226866 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Numeric.php @@ -0,0 +1,15 @@ +<?php + +namespace SimpleValidator\Validators; + +class Numeric extends Base +{ + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + return is_numeric($data[$this->field]); + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Range.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Range.php new file mode 100644 index 00000000..065b2b9d --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Range.php @@ -0,0 +1,33 @@ +<?php + +namespace SimpleValidator\Validators; + +class Range extends Base +{ + private $min; + private $max; + + public function __construct($field, $error_message, $min, $max) + { + parent::__construct($field, $error_message); + + $this->min = $min; + $this->max = $max; + } + + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + + if (! is_numeric($data[$this->field])) { + return false; + } + + if ($data[$this->field] < $this->min || $data[$this->field] > $this->max) { + return false; + } + } + + return true; + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Required.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Required.php new file mode 100644 index 00000000..f5e65616 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Required.php @@ -0,0 +1,11 @@ +<?php + +namespace SimpleValidator\Validators; + +class Required extends Base +{ + public function execute(array $data) + { + return $this->isFieldNotEmpty($data); + } +} diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Unique.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Unique.php new file mode 100644 index 00000000..00caeb54 --- /dev/null +++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Unique.php @@ -0,0 +1,48 @@ +<?php + +namespace SimpleValidator\Validators; + +use PDO; + +class Unique extends Base +{ + private $pdo; + private $primary_key; + private $table; + + public function __construct($field, $error_message, PDO $pdo, $table, $primary_key = 'id') + { + parent::__construct($field, $error_message); + + $this->pdo = $pdo; + $this->primary_key = $primary_key; + $this->table = $table; + } + + public function execute(array $data) + { + if ($this->isFieldNotEmpty($data)) { + if (! isset($data[$this->primary_key])) { + $rq = $this->pdo->prepare('SELECT 1 FROM '.$this->table.' WHERE '.$this->field.'=?'); + $rq->execute(array($data[$this->field])); + } + else { + + $rq = $this->pdo->prepare( + 'SELECT 1 FROM '.$this->table.' + WHERE '.$this->field.'=? AND '.$this->primary_key.' != ?' + ); + + $rq->execute(array($data[$this->field], $data[$this->primary_key])); + } + + $result = $rq->fetchColumn(); + + if ($result == 1) { // Postgresql returns an integer but other database returns a string '1' + return false; + } + } + + return true; + } +} diff --git a/vendor/fguillot/simpleLogger/LICENSE b/vendor/fguillot/simpleLogger/LICENSE new file mode 100644 index 00000000..6a362bc1 --- /dev/null +++ b/vendor/fguillot/simpleLogger/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Frederic Guillot + +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. diff --git a/vendor/fguillot/simpleLogger/src/SimpleLogger/Base.php b/vendor/fguillot/simpleLogger/src/SimpleLogger/Base.php new file mode 100644 index 00000000..c662a1a3 --- /dev/null +++ b/vendor/fguillot/simpleLogger/src/SimpleLogger/Base.php @@ -0,0 +1,89 @@ +<?php + +namespace SimpleLogger; + +use Psr\Log\AbstractLogger; +use Psr\Log\LogLevel; + +/** + * Base class for loggers + * + * @package SimpleLogger + * @author Frédéric Guillot + */ +abstract class Base extends AbstractLogger +{ + /** + * Minimum log level for the logger + * + * @access private + * @var string + */ + private $level = LogLevel::DEBUG; + + /** + * Set minimum log level + * + * @access public + * @param string $level + */ + public function setLevel($level) + { + $this->level = $level; + } + + /** + * Get minimum log level + * + * @access public + * @return string + */ + public function getLevel() + { + return $this->level; + } + + /** + * Dump to log a variable (by example an array) + * + * @param mixed $variable + */ + public function dump($variable) + { + $this->log(LogLevel::DEBUG, var_export($variable, true)); + } + + /** + * Interpolates context values into the message placeholders. + * + * @access protected + * @param string $message + * @param array $context + * @return string + */ + protected function interpolate($message, array $context = array()) + { + // build a replacement array with braces around the context keys + $replace = array(); + + foreach ($context as $key => $val) { + $replace['{' . $key . '}'] = $val; + } + + // interpolate replacement values into the message and return + return strtr($message, $replace); + } + + /** + * Format log message + * + * @param mixed $level + * @param string $message + * @param array $context + * @return string + */ + protected function formatMessage($level, $message, array $context = array()) + { + return '['.date('Y-m-d H:i:s').'] ['.$level.'] '.$this->interpolate($message, $context).PHP_EOL; + } +} diff --git a/vendor/fguillot/simpleLogger/src/SimpleLogger/File.php b/vendor/fguillot/simpleLogger/src/SimpleLogger/File.php new file mode 100644 index 00000000..be3bfa85 --- /dev/null +++ b/vendor/fguillot/simpleLogger/src/SimpleLogger/File.php @@ -0,0 +1,48 @@ +<?php + +namespace SimpleLogger; + +use RuntimeException; + +/** + * File logger + * + * @package SimpleLogger + * @author Frédéric Guillot + */ +class File extends Base +{ + /** + * Filename + * + * @access protected + * @var string + */ + protected $filename = ''; + + /** + * Setup logger configuration + * + * @param string $filename Output file + */ + public function __construct($filename) + { + $this->filename = $filename; + } + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + */ + public function log($level, $message, array $context = array()) + { + $line = $this->formatMessage($level, $message, $context); + + if (file_put_contents($this->filename, $line, FILE_APPEND | LOCK_EX) === false) { + throw new RuntimeException('Unable to write to the log file.'); + } + } +} diff --git a/vendor/fguillot/simpleLogger/src/SimpleLogger/Logger.php b/vendor/fguillot/simpleLogger/src/SimpleLogger/Logger.php new file mode 100644 index 00000000..dc340cde --- /dev/null +++ b/vendor/fguillot/simpleLogger/src/SimpleLogger/Logger.php @@ -0,0 +1,94 @@ +<?php + +namespace SimpleLogger; + +use Psr\Log\AbstractLogger; +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; + +/** + * Handler for multiple loggers + * + * @package SimpleLogger + * @author Frédéric Guillot + */ +class Logger extends AbstractLogger implements LoggerAwareInterface +{ + /** + * Logger instances + * + * @access private + */ + private $loggers = array(); + + /** + * Get level priority + * + * @param mixed $level + * @return integer + */ + public function getLevelPriority($level) + { + switch ($level) { + case LogLevel::EMERGENCY: + return 600; + case LogLevel::ALERT: + return 550; + case LogLevel::CRITICAL: + return 500; + case LogLevel::ERROR: + return 400; + case LogLevel::WARNING: + return 300; + case LogLevel::NOTICE: + return 250; + case LogLevel::INFO: + return 200; + } + + return 100; + } + + /** + * Sets a logger instance on the object + * + * @param LoggerInterface $logger + * @return null + */ + public function setLogger(LoggerInterface $logger) + { + $this->loggers[] = $logger; + } + + /** + * Proxy method to the real loggers + * + * @param mixed $level + * @param string $message + * @param array $context + * @return null + */ + public function log($level, $message, array $context = array()) + { + foreach ($this->loggers as $logger) { + if ($this->getLevelPriority($level) >= $this->getLevelPriority($logger->getLevel())) { + $logger->log($level, $message, $context); + } + } + } + + /** + * Dump variables for debugging + * + * @param mixed $variable + */ + public function dump($variable) + { + foreach ($this->loggers as $logger) { + if ($this->getLevelPriority(LogLevel::DEBUG) >= $this->getLevelPriority($logger->getLevel())) { + $logger->dump($variable); + } + } + } +} diff --git a/vendor/fguillot/simpleLogger/src/SimpleLogger/Stderr.php b/vendor/fguillot/simpleLogger/src/SimpleLogger/Stderr.php new file mode 100644 index 00000000..2573177e --- /dev/null +++ b/vendor/fguillot/simpleLogger/src/SimpleLogger/Stderr.php @@ -0,0 +1,25 @@ +<?php + +namespace SimpleLogger; + +/** + * Stderr logger + * + * @package SimpleLogger + * @author Frédéric Guillot + */ +class Stderr extends Base +{ + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * @return null + */ + public function log($level, $message, array $context = array()) + { + file_put_contents('php://stderr', $this->formatMessage($level, $message, $context), FILE_APPEND); + } +} diff --git a/vendor/fguillot/simpleLogger/src/SimpleLogger/Stdout.php b/vendor/fguillot/simpleLogger/src/SimpleLogger/Stdout.php new file mode 100644 index 00000000..82d181b2 --- /dev/null +++ b/vendor/fguillot/simpleLogger/src/SimpleLogger/Stdout.php @@ -0,0 +1,25 @@ +<?php + +namespace SimpleLogger; + +/** + * Stdout logger + * + * @package SimpleLogger + * @author Frédéric Guillot + */ +class Stdout extends Base +{ + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * @return null + */ + public function log($level, $message, array $context = array()) + { + file_put_contents('php://stdout', $this->formatMessage($level, $message, $context), FILE_APPEND); + } +} diff --git a/vendor/fguillot/simpleLogger/src/SimpleLogger/Syslog.php b/vendor/fguillot/simpleLogger/src/SimpleLogger/Syslog.php new file mode 100644 index 00000000..c4e26a7a --- /dev/null +++ b/vendor/fguillot/simpleLogger/src/SimpleLogger/Syslog.php @@ -0,0 +1,72 @@ +<?php + +namespace SimpleLogger; + +use RuntimeException; +use Psr\Log\LogLevel; + +/** + * Syslog Logger + * + * @package SimpleLogger + * @author Frédéric Guillot + */ +class Syslog extends Base +{ + /** + * Setup Syslog configuration + * + * @param string $ident Application name + * @param int $facility See http://php.net/manual/en/function.openlog.php + */ + public function __construct($ident = 'PHP', $facility = LOG_USER) + { + if (! openlog($ident, LOG_ODELAY | LOG_PID, $facility)) { + throw new RuntimeException('Unable to connect to syslog.'); + } + } + + /** + * Get syslog priority according to Psr\LogLevel + * + * @param mixed $level + * @return integer + */ + public function getSyslogPriority($level) + { + switch ($level) { + case LogLevel::EMERGENCY: + return LOG_EMERG; + case LogLevel::ALERT: + return LOG_ALERT; + case LogLevel::CRITICAL: + return LOG_CRIT; + case LogLevel::ERROR: + return LOG_ERR; + case LogLevel::WARNING: + return LOG_WARNING; + case LogLevel::NOTICE: + return LOG_NOTICE; + case LogLevel::INFO: + return LOG_INFO; + } + + return LOG_DEBUG; + } + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * @return null + */ + public function log($level, $message, array $context = array()) + { + $syslogPriority = $this->getSyslogPriority($level); + $syslogMessage = $this->interpolate($message, $context); + + syslog($syslogPriority, $syslogMessage); + } +} diff --git a/vendor/gregwar/captcha/.gitignore b/vendor/gregwar/captcha/.gitignore new file mode 100644 index 00000000..5a275919 --- /dev/null +++ b/vendor/gregwar/captcha/.gitignore @@ -0,0 +1,3 @@ +demo/*.jpg +demo/*.pgm +demo/temp/ diff --git a/vendor/gregwar/captcha/.travis.yml b/vendor/gregwar/captcha/.travis.yml new file mode 100644 index 00000000..9c4b7049 --- /dev/null +++ b/vendor/gregwar/captcha/.travis.yml @@ -0,0 +1,12 @@ +language: php + +php: + - 5.3.3 + - 5.3 + - 5.4 + - 5.5 + - 5.6 + +script: + - composer install + - phpunit diff --git a/vendor/gregwar/captcha/CaptchaBuilder.php b/vendor/gregwar/captcha/CaptchaBuilder.php new file mode 100644 index 00000000..bc6173fe --- /dev/null +++ b/vendor/gregwar/captcha/CaptchaBuilder.php @@ -0,0 +1,720 @@ +<?php + +namespace Gregwar\Captcha; + +use \Exception; + +/** + * Builds a new captcha image + * Uses the fingerprint parameter, if one is passed, to generate the same image + * + * @author Gregwar <g.passault@gmail.com> + * @author Jeremy Livingston <jeremy.j.livingston@gmail.com> + */ +class CaptchaBuilder implements CaptchaBuilderInterface +{ + /** + * @var array + */ + protected $fingerprint = array(); + + /** + * @var bool + */ + protected $useFingerprint = false; + + /** + * @var array + */ + protected $textColor = null; + + /** + * @var array + */ + protected $backgroundColor = null; + + /** + * @var array + */ + protected $backgroundImages = array(); + + /** + * @var resource + */ + protected $contents = null; + + /** + * @var string + */ + protected $phrase = null; + + /** + * @var PhraseBuilderInterface + */ + protected $builder; + + /** + * @var bool + */ + protected $distortion = true; + + /** + * The maximum number of lines to draw in front of + * the image. null - use default algorithm + */ + protected $maxFrontLines = null; + + /** + * The maximum number of lines to draw behind + * the image. null - use default algorithm + */ + protected $maxBehindLines = null; + + /** + * The maximum angle of char + */ + protected $maxAngle = 8; + + /** + * The maximum offset of char + */ + protected $maxOffset = 5; + + /** + * Is the interpolation enabled ? + * + * @var bool + */ + protected $interpolation = true; + + /** + * Ignore all effects + * + * @var bool + */ + protected $ignoreAllEffects = false; + + /** + * Allowed image types for the background images + * + * @var array + */ + protected $allowedBackgroundImageTypes = array('image/png', 'image/jpeg', 'image/gif'); + + /** + * The image contents + */ + public function getContents() + { + return $this->contents; + } + + /** + * Enable/Disables the interpolation + * + * @param $interpolate bool True to enable, false to disable + * + * @return CaptchaBuilder + */ + public function setInterpolation($interpolate = true) + { + $this->interpolation = $interpolate; + + return $this; + } + + /** + * Temporary dir, for OCR check + */ + public $tempDir = 'temp/'; + + public function __construct($phrase = null, PhraseBuilderInterface $builder = null) + { + if ($builder === null) { + $this->builder = new PhraseBuilder; + } else { + $this->builder = $builder; + } + + if ($phrase === null) { + $phrase = $this->builder->build(); + } + + $this->phrase = $phrase; + } + + /** + * Setting the phrase + */ + public function setPhrase($phrase) + { + $this->phrase = (string) $phrase; + } + + /** + * Enables/disable distortion + */ + public function setDistortion($distortion) + { + $this->distortion = (bool) $distortion; + + return $this; + } + + public function setMaxBehindLines($maxBehindLines) + { + $this->maxBehindLines = $maxBehindLines; + + return $this; + } + + public function setMaxFrontLines($maxFrontLines) + { + $this->maxFrontLines = $maxFrontLines; + + return $this; + } + + public function setMaxAngle($maxAngle) + { + $this->maxAngle = $maxAngle; + + return $this; + } + + public function setMaxOffset($maxOffset) + { + $this->maxOffset = $maxOffset; + + return $this; + } + + /** + * Gets the captcha phrase + */ + public function getPhrase() + { + return $this->phrase; + } + + /** + * Returns true if the given phrase is good + */ + public function testPhrase($phrase) + { + return ($this->builder->niceize($phrase) == $this->builder->niceize($this->getPhrase())); + } + + /** + * Instantiation + */ + public static function create($phrase = null) + { + return new self($phrase); + } + + /** + * Sets the text color to use + */ + public function setTextColor($r, $g, $b) + { + $this->textColor = array($r, $g, $b); + + return $this; + } + + /** + * Sets the background color to use + */ + public function setBackgroundColor($r, $g, $b) + { + $this->backgroundColor = array($r, $g, $b); + + return $this; + } + + /** + * Sets the ignoreAllEffects value + * + * @param bool $ignoreAllEffects + * @return CaptchaBuilder + */ + public function setIgnoreAllEffects($ignoreAllEffects) + { + $this->ignoreAllEffects = $ignoreAllEffects; + + return $this; + } + + /** + * Sets the list of background images to use (one image is randomly selected) + */ + public function setBackgroundImages(array $backgroundImages) + { + $this->backgroundImages = $backgroundImages; + + return $this; + } + + /** + * Draw lines over the image + */ + protected function drawLine($image, $width, $height, $tcol = null) + { + if ($tcol === null) { + $tcol = imagecolorallocate($image, $this->rand(100, 255), $this->rand(100, 255), $this->rand(100, 255)); + } + + if ($this->rand(0, 1)) { // Horizontal + $Xa = $this->rand(0, $width/2); + $Ya = $this->rand(0, $height); + $Xb = $this->rand($width/2, $width); + $Yb = $this->rand(0, $height); + } else { // Vertical + $Xa = $this->rand(0, $width); + $Ya = $this->rand(0, $height/2); + $Xb = $this->rand(0, $width); + $Yb = $this->rand($height/2, $height); + } + imagesetthickness($image, $this->rand(1, 3)); + imageline($image, $Xa, $Ya, $Xb, $Yb, $tcol); + } + + /** + * Apply some post effects + */ + protected function postEffect($image) + { + if (!function_exists('imagefilter')) { + return; + } + + if ($this->backgroundColor != null || $this->textColor != null) { + return; + } + + // Negate ? + if ($this->rand(0, 1) == 0) { + imagefilter($image, IMG_FILTER_NEGATE); + } + + // Edge ? + if ($this->rand(0, 10) == 0) { + imagefilter($image, IMG_FILTER_EDGEDETECT); + } + + // Contrast + imagefilter($image, IMG_FILTER_CONTRAST, $this->rand(-50, 10)); + + // Colorize + if ($this->rand(0, 5) == 0) { + imagefilter($image, IMG_FILTER_COLORIZE, $this->rand(-80, 50), $this->rand(-80, 50), $this->rand(-80, 50)); + } + } + + /** + * Writes the phrase on the image + */ + protected function writePhrase($image, $phrase, $font, $width, $height) + { + $length = strlen($phrase); + if ($length === 0) { + return imagecolorallocate($image, 0, 0, 0); + } + + // Gets the text size and start position + $size = $width / $length - $this->rand(0, 3) - 1; + $box = imagettfbbox($size, 0, $font, $phrase); + $textWidth = $box[2] - $box[0]; + $textHeight = $box[1] - $box[7]; + $x = ($width - $textWidth) / 2; + $y = ($height - $textHeight) / 2 + $size; + + if (!count($this->textColor)) { + $textColor = array($this->rand(0, 150), $this->rand(0, 150), $this->rand(0, 150)); + } else { + $textColor = $this->textColor; + } + $col = imagecolorallocate($image, $textColor[0], $textColor[1], $textColor[2]); + + // Write the letters one by one, with random angle + for ($i=0; $i<$length; $i++) { + $box = imagettfbbox($size, 0, $font, $phrase[$i]); + $w = $box[2] - $box[0]; + $angle = $this->rand(-$this->maxAngle, $this->maxAngle); + $offset = $this->rand(-$this->maxOffset, $this->maxOffset); + imagettftext($image, $size, $angle, $x, $y + $offset, $col, $font, $phrase[$i]); + $x += $w; + } + + return $col; + } + + /** + * Try to read the code against an OCR + */ + public function isOCRReadable() + { + if (!is_dir($this->tempDir)) { + @mkdir($this->tempDir, 0755, true); + } + + $tempj = $this->tempDir . uniqid('captcha', true) . '.jpg'; + $tempp = $this->tempDir . uniqid('captcha', true) . '.pgm'; + + $this->save($tempj); + shell_exec("convert $tempj $tempp"); + $value = trim(strtolower(shell_exec("ocrad $tempp"))); + + @unlink($tempj); + @unlink($tempp); + + return $this->testPhrase($value); + } + + /** + * Builds while the code is readable against an OCR + */ + public function buildAgainstOCR($width = 150, $height = 40, $font = null, $fingerprint = null) + { + do { + $this->build($width, $height, $font, $fingerprint); + } while ($this->isOCRReadable()); + } + + /** + * Generate the image + */ + public function build($width = 150, $height = 40, $font = null, $fingerprint = null) + { + if (null !== $fingerprint) { + $this->fingerprint = $fingerprint; + $this->useFingerprint = true; + } else { + $this->fingerprint = array(); + $this->useFingerprint = false; + } + + if ($font === null) { + $font = __DIR__ . '/Font/captcha'.$this->rand(0, 5).'.ttf'; + } + + if (empty($this->backgroundImages)) { + // if background images list is not set, use a color fill as a background + $image = imagecreatetruecolor($width, $height); + if ($this->backgroundColor == null) { + $bg = imagecolorallocate($image, $this->rand(200, 255), $this->rand(200, 255), $this->rand(200, 255)); + } else { + $color = $this->backgroundColor; + $bg = imagecolorallocate($image, $color[0], $color[1], $color[2]); + } + $this->background = $bg; + imagefill($image, 0, 0, $bg); + } else { + // use a random background image + $randomBackgroundImage = $this->backgroundImages[rand(0, count($this->backgroundImages)-1)]; + + $imageType = $this->validateBackgroundImage($randomBackgroundImage); + + $image = $this->createBackgroundImageFromType($randomBackgroundImage, $imageType); + } + + // Apply effects + if (!$this->ignoreAllEffects) { + $square = $width * $height; + $effects = $this->rand($square/3000, $square/2000); + + // set the maximum number of lines to draw in front of the text + if ($this->maxBehindLines != null && $this->maxBehindLines > 0) { + $effects = min($this->maxBehindLines, $effects); + } + + if ($this->maxBehindLines !== 0) { + for ($e = 0; $e < $effects; $e++) { + $this->drawLine($image, $width, $height); + } + } + } + + // Write CAPTCHA text + $color = $this->writePhrase($image, $this->phrase, $font, $width, $height); + + // Apply effects + if (!$this->ignoreAllEffects) { + $square = $width * $height; + $effects = $this->rand($square/3000, $square/2000); + + // set the maximum number of lines to draw in front of the text + if ($this->maxFrontLines != null && $this->maxFrontLines > 0) { + $effects = min($this->maxFrontLines, $effects); + } + + if ($this->maxFrontLines !== 0) { + for ($e = 0; $e < $effects; $e++) { + $this->drawLine($image, $width, $height, $color); + } + } + } + + // Distort the image + if ($this->distortion && !$this->ignoreAllEffects) { + $image = $this->distort($image, $width, $height, $bg); + } + + // Post effects + if (!$this->ignoreAllEffects) { + $this->postEffect($image); + } + + $this->contents = $image; + + return $this; + } + + /** + * Distorts the image + */ + public function distort($image, $width, $height, $bg) + { + $contents = imagecreatetruecolor($width, $height); + $X = $this->rand(0, $width); + $Y = $this->rand(0, $height); + $phase = $this->rand(0, 10); + $scale = 1.1 + $this->rand(0, 10000) / 30000; + for ($x = 0; $x < $width; $x++) { + for ($y = 0; $y < $height; $y++) { + $Vx = $x - $X; + $Vy = $y - $Y; + $Vn = sqrt($Vx * $Vx + $Vy * $Vy); + + if ($Vn != 0) { + $Vn2 = $Vn + 4 * sin($Vn / 30); + $nX = $X + ($Vx * $Vn2 / $Vn); + $nY = $Y + ($Vy * $Vn2 / $Vn); + } else { + $nX = $X; + $nY = $Y; + } + $nY = $nY + $scale * sin($phase + $nX * 0.2); + + if ($this->interpolation) { + $p = $this->interpolate( + $nX - floor($nX), + $nY - floor($nY), + $this->getCol($image, floor($nX), floor($nY), $bg), + $this->getCol($image, ceil($nX), floor($nY), $bg), + $this->getCol($image, floor($nX), ceil($nY), $bg), + $this->getCol($image, ceil($nX), ceil($nY), $bg) + ); + } else { + $p = $this->getCol($image, round($nX), round($nY), $bg); + } + + if ($p == 0) { + $p = $bg; + } + + imagesetpixel($contents, $x, $y, $p); + } + } + + return $contents; + } + + /** + * Saves the Captcha to a jpeg file + */ + public function save($filename, $quality = 90) + { + imagejpeg($this->contents, $filename, $quality); + } + + /** + * Gets the image GD + */ + public function getGd() + { + return $this->contents; + } + + /** + * Gets the image contents + */ + public function get($quality = 90) + { + ob_start(); + $this->output($quality); + + return ob_get_clean(); + } + + /** + * Gets the HTML inline base64 + */ + public function inline($quality = 90) + { + return 'data:image/jpeg;base64,' . base64_encode($this->get($quality)); + } + + /** + * Outputs the image + */ + public function output($quality = 90) + { + imagejpeg($this->contents, null, $quality); + } + + /** + * @return array + */ + public function getFingerprint() + { + return $this->fingerprint; + } + + /** + * Returns a random number or the next number in the + * fingerprint + */ + protected function rand($min, $max) + { + if (!is_array($this->fingerprint)) { + $this->fingerprint = array(); + } + + if ($this->useFingerprint) { + $value = current($this->fingerprint); + next($this->fingerprint); + } else { + $value = mt_rand($min, $max); + $this->fingerprint[] = $value; + } + + return $value; + } + + /** + * @param $x + * @param $y + * @param $nw + * @param $ne + * @param $sw + * @param $se + * + * @return int + */ + protected function interpolate($x, $y, $nw, $ne, $sw, $se) + { + list($r0, $g0, $b0) = $this->getRGB($nw); + list($r1, $g1, $b1) = $this->getRGB($ne); + list($r2, $g2, $b2) = $this->getRGB($sw); + list($r3, $g3, $b3) = $this->getRGB($se); + + $cx = 1.0 - $x; + $cy = 1.0 - $y; + + $m0 = $cx * $r0 + $x * $r1; + $m1 = $cx * $r2 + $x * $r3; + $r = (int) ($cy * $m0 + $y * $m1); + + $m0 = $cx * $g0 + $x * $g1; + $m1 = $cx * $g2 + $x * $g3; + $g = (int) ($cy * $m0 + $y * $m1); + + $m0 = $cx * $b0 + $x * $b1; + $m1 = $cx * $b2 + $x * $b3; + $b = (int) ($cy * $m0 + $y * $m1); + + return ($r << 16) | ($g << 8) | $b; + } + + /** + * @param $image + * @param $x + * @param $y + * + * @return int + */ + protected function getCol($image, $x, $y, $background) + { + $L = imagesx($image); + $H = imagesy($image); + if ($x < 0 || $x >= $L || $y < 0 || $y >= $H) { + return $background; + } + + return imagecolorat($image, $x, $y); + } + + /** + * @param $col + * + * @return array + */ + protected function getRGB($col) + { + return array( + (int) ($col >> 16) & 0xff, + (int) ($col >> 8) & 0xff, + (int) ($col) & 0xff, + ); + } + + /** + * Validate the background image path. Return the image type if valid + * + * @param string $backgroundImage + * @return string + * @throws Exception + */ + protected function validateBackgroundImage($backgroundImage) + { + // check if file exists + if (!file_exists($backgroundImage)) { + $backgroundImageExploded = explode('/', $backgroundImage); + $imageFileName = count($backgroundImageExploded) > 1? $backgroundImageExploded[count($backgroundImageExploded)-1] : $backgroundImage; + + throw new Exception('Invalid background image: ' . $imageFileName); + } + + // check image type + $finfo = finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension + $imageType = finfo_file($finfo, $backgroundImage); + finfo_close($finfo); + + if (!in_array ($imageType, $this->allowedBackgroundImageTypes)) { + throw new Exception('Invalid background image type! Allowed types are: ' . join(', ', $this->allowedBackgroundImageTypes)); + } + + return $imageType; + } + + /** + * Create background image from type + * + * @param string $backgroundImage + * @param string $imageType + * @return resource + * @throws Exception + */ + protected function createBackgroundImageFromType($backgroundImage, $imageType) + { + switch ($imageType) { + case 'image/jpeg': + $image = imagecreatefromjpeg($backgroundImage); + break; + case 'image/png': + $image = imagecreatefrompng($backgroundImage); + break; + case 'image/gif': + $image = imagecreatefromgif($backgroundImage); + break; + + default: + throw new Exception('Not supported file type for background image!'); + break; + } + + return $image; + } +} diff --git a/vendor/gregwar/captcha/CaptchaBuilderInterface.php b/vendor/gregwar/captcha/CaptchaBuilderInterface.php new file mode 100644 index 00000000..bdebf38f --- /dev/null +++ b/vendor/gregwar/captcha/CaptchaBuilderInterface.php @@ -0,0 +1,30 @@ +<?php + +namespace Gregwar\Captcha; + +/** + * A Captcha builder + */ +interface CaptchaBuilderInterface +{ + /** + * Builds the code + */ + public function build($width, $height, $font, $fingerprint); + + /** + * Saves the code to a file + */ + public function save($filename, $quality); + + /** + * Gets the image contents + */ + public function get($quality); + + /** + * Outputs the image + */ + public function output($quality); +} + diff --git a/vendor/gregwar/captcha/Font/captcha0.ttf b/vendor/gregwar/captcha/Font/captcha0.ttf Binary files differnew file mode 100644 index 00000000..139f0b43 --- /dev/null +++ b/vendor/gregwar/captcha/Font/captcha0.ttf diff --git a/vendor/gregwar/captcha/Font/captcha1.ttf b/vendor/gregwar/captcha/Font/captcha1.ttf Binary files differnew file mode 100644 index 00000000..bb1abf6b --- /dev/null +++ b/vendor/gregwar/captcha/Font/captcha1.ttf diff --git a/vendor/gregwar/captcha/Font/captcha2.ttf b/vendor/gregwar/captcha/Font/captcha2.ttf Binary files differnew file mode 100644 index 00000000..8d1f31e8 --- /dev/null +++ b/vendor/gregwar/captcha/Font/captcha2.ttf diff --git a/vendor/gregwar/captcha/Font/captcha3.ttf b/vendor/gregwar/captcha/Font/captcha3.ttf Binary files differnew file mode 100644 index 00000000..d232902c --- /dev/null +++ b/vendor/gregwar/captcha/Font/captcha3.ttf diff --git a/vendor/gregwar/captcha/Font/captcha4.ttf b/vendor/gregwar/captcha/Font/captcha4.ttf Binary files differnew file mode 100644 index 00000000..ab154440 --- /dev/null +++ b/vendor/gregwar/captcha/Font/captcha4.ttf diff --git a/vendor/gregwar/captcha/Font/captcha5.ttf b/vendor/gregwar/captcha/Font/captcha5.ttf Binary files differnew file mode 100644 index 00000000..f7cd9986 --- /dev/null +++ b/vendor/gregwar/captcha/Font/captcha5.ttf diff --git a/vendor/gregwar/captcha/ImageFileHandler.php b/vendor/gregwar/captcha/ImageFileHandler.php new file mode 100644 index 00000000..6b6bdb87 --- /dev/null +++ b/vendor/gregwar/captcha/ImageFileHandler.php @@ -0,0 +1,106 @@ +<?php + +namespace Gregwar\Captcha; + +use Symfony\Component\Finder\Finder; + +/** + * Handles actions related to captcha image files including saving and garbage collection + * + * @author Gregwar <g.passault@gmail.com> + * @author Jeremy Livingston <jeremy@quizzle.com> + */ +class ImageFileHandler +{ + /** + * Name of folder for captcha images + * @var string + */ + protected $imageFolder; + + /** + * Absolute path to public web folder + * @var string + */ + protected $webPath; + + /** + * Frequency of garbage collection in fractions of 1 + * @var int + */ + protected $gcFreq; + + /** + * Maximum age of images in minutes + * @var int + */ + protected $expiration; + + /** + * @param $imageFolder + * @param $webPath + * @param $gcFreq + * @param $expiration + */ + public function __construct($imageFolder, $webPath, $gcFreq, $expiration) + { + $this->imageFolder = $imageFolder; + $this->webPath = $webPath; + $this->gcFreq = $gcFreq; + $this->expiration = $expiration; + } + + /** + * Saves the provided image content as a file + * + * @param string $contents + * + * @return string + */ + public function saveAsFile($contents) + { + $this->createFolderIfMissing(); + + $filename = md5(uniqid()) . '.jpg'; + $filePath = $this->webPath . '/' . $this->imageFolder . '/' . $filename; + imagejpeg($contents, $filePath, 15); + + return '/' . $this->imageFolder . '/' . $filename; + } + + /** + * Randomly runs garbage collection on the image directory + * + * @return bool + */ + public function collectGarbage() + { + if (!mt_rand(1, $this->gcFreq) == 1) { + return false; + } + + $this->createFolderIfMissing(); + + $finder = new Finder(); + $criteria = sprintf('<= now - %s minutes', $this->expiration); + $finder->in($this->webPath . '/' . $this->imageFolder) + ->date($criteria); + + foreach($finder->files() as $file) { + unlink($file->getPathname()); + } + + return true; + } + + /** + * Creates the folder if it doesn't exist + */ + protected function createFolderIfMissing() + { + if (!file_exists($this->webPath . '/' . $this->imageFolder)) { + mkdir($this->webPath . '/' . $this->imageFolder, 0755); + } + } +} + diff --git a/vendor/gregwar/captcha/LICENSE b/vendor/gregwar/captcha/LICENSE new file mode 100644 index 00000000..7db6ad8e --- /dev/null +++ b/vendor/gregwar/captcha/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) <2012-2015> Grégoire Passault + +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. diff --git a/vendor/gregwar/captcha/PhraseBuilder.php b/vendor/gregwar/captcha/PhraseBuilder.php new file mode 100644 index 00000000..b94bd61c --- /dev/null +++ b/vendor/gregwar/captcha/PhraseBuilder.php @@ -0,0 +1,34 @@ +<?php + +namespace Gregwar\Captcha; + +/** + * Generates random phrase + * + * @author Gregwar <g.passault@gmail.com> + */ +class PhraseBuilder implements PhraseBuilderInterface +{ + /** + * Generates random phrase of given length with given charset + */ + public function build($length = 5, $charset = 'abcdefghijklmnpqrstuvwxyz123456789') + { + $phrase = ''; + $chars = str_split($charset); + + for ($i = 0; $i < $length; $i++) { + $phrase .= $chars[array_rand($chars)]; + } + + return $phrase; + } + + /** + * "Niceize" a code + */ + public function niceize($str) + { + return strtr(strtolower($str), '01', 'ol'); + } +} diff --git a/vendor/gregwar/captcha/PhraseBuilderInterface.php b/vendor/gregwar/captcha/PhraseBuilderInterface.php new file mode 100644 index 00000000..0a4f5361 --- /dev/null +++ b/vendor/gregwar/captcha/PhraseBuilderInterface.php @@ -0,0 +1,21 @@ +<?php + +namespace Gregwar\Captcha; + +/** + * Interface for the PhraseBuilder + * + * @author Gregwar <g.passault@gmail.com> + */ +interface PhraseBuilderInterface +{ + /** + * Generates random phrase of given length with given charset + */ + public function build($length, $charset); + + /** + * "Niceize" a code + */ + public function niceize($str); +} diff --git a/vendor/gregwar/captcha/README.md b/vendor/gregwar/captcha/README.md new file mode 100644 index 00000000..9f6c17a9 --- /dev/null +++ b/vendor/gregwar/captcha/README.md @@ -0,0 +1,108 @@ +Captcha +======= + + + +Installation +============ + +With composer : + +``` json +{ + ... + "require": { + "gregwar/captcha": "1.*" + } +} +``` + +Usage +===== + +You can create a captcha with the `CaptchaBuilder` : + +```php +<?php + +use Gregwar\Captcha\CaptchaBuilder; + +$builder = new CaptchaBuilder; +$builder->build(); +``` + +You can then save it to a file : + +```php +<?php + +$builder->save('out.jpg'); +``` + +Or output it directly : + +```php +<?php + +header('Content-type: image/jpeg'); +$builder->output(); +``` + +Or inline it directly in the HTML page: + +```php +<img src="<?php echo $builder->inline(); ?>" /> +``` + +You'll be able to get the code and compare it with a user input : + +```php +<?php + +// Example: storing the phrase in the session to test for the user +// input later +$_SESSION['phrase'] = $builder->getPhrase(); +``` + +You can compare the phrase with user input: +```php +if($builder->testPhrase($userInput)) { + // instructions if user phrase is good +} +else { + // user phrase is wrong +} +``` + +API +=== + +You can use theses functions : + +* **__construct($phrase = null)**, constructs the builder with the given phrase, if the phrase is null, a random one will be generated +* **getPhrase()**, allow you to get the phrase contents +* **setDistortion($distortion)**, enable or disable the distortion, call it before `build()` +* **isOCRReadable()**, returns `true` if the OCR can be read using the `ocrad` software, you'll need to have shell_exec enabled, imagemagick and ocrad installed +* **buildAgainstOCR($width = 150, $height = 40, $font = null)**, builds a code until it is not readable by `ocrad` +* **build($width = 150, $height = 40, $font = null)**, builds a code with the given $width, $height and $font. By default, a random font will be used from the library +* **save($filename, $quality = 80)**, saves the captcha into a jpeg in the $filename, with the given quality +* **get($quality = 80)**, returns the jpeg data +* **output($quality = 80)**, directly outputs the jpeg code to a browser +* **setBackgroundColor($r, $g, $b)**, sets the background color to force it (this will disable many effects and is not recommended) +* **setBackgroundImages(array($imagepath1, $imagePath2))**, Sets custom background images to be used as captcha background. It is recommended to disable image effects when passing custom images for background (ignore_all_effects). A random image is selected from the list passed, the full paths to the image files must be passed. +* **setInterpolation($interpolate)**, enable or disable the interpolation (enabled by default), disabling it will be quicker but the images will look uglier +* **setIgnoreAllEffects($ignoreAllEffects)**, disable all effects on the captcha image. Recommended to use when passing custom background images for the captcha. +* **testPhrase($phrase)**, returns true if the given phrase is good +* **setMaxBehindLines($lines)**, sets the maximum number of lines behind the code +* **setMaxFrontLines($lines)**, sets the maximum number of lines on the front of the code + +Symfony 2 Bundle +================ + +You can have a look at the following repository to enjoy the Symfony 2 bundle packaging this captcha generator : +https://github.com/Gregwar/CaptchaBundle + +License +======= + +This library is under MIT license, have a look to the `LICENSE` file diff --git a/vendor/gregwar/captcha/autoload.php b/vendor/gregwar/captcha/autoload.php new file mode 100644 index 00000000..8b3fa392 --- /dev/null +++ b/vendor/gregwar/captcha/autoload.php @@ -0,0 +1,16 @@ +<?php + +/** +* Registers an autoload for all the classes in Gregwar\Captcha +*/ +spl_autoload_register(function ($className) { + $namespace = 'Gregwar\\Captcha'; + + if (strpos($className, $namespace) === 0) { + $className = str_replace($namespace, '', $className); + $fileName = __DIR__ . '/' . str_replace('\\', '/', $className) . '.php'; + if (file_exists($fileName)) { + require($fileName); + } + } +}); diff --git a/vendor/gregwar/captcha/composer.json b/vendor/gregwar/captcha/composer.json new file mode 100644 index 00000000..ef79423b --- /dev/null +++ b/vendor/gregwar/captcha/composer.json @@ -0,0 +1,28 @@ +{ + "name": "gregwar/captcha", + "type": "captcha", + "description": "Captcha generator", + "keywords": ["captcha", "spam", "bot"], + "homepage": "https://github.com/Gregwar/Captcha", + "license": "MIT", + "authors": [ + { + "name": "Grégoire Passault", + "email": "g.passault@gmail.com", + "homepage": "http://www.gregwar.com/" + }, + { + "name": "Jeremy Livingston", + "email": "jeremy.j.livingston@gmail.com" + } + ], + "require": { + "php": ">=5.3.0", + "ext-gd": "*" + }, + "autoload": { + "psr-4": { + "Gregwar\\Captcha\\": "/" + } + } +} diff --git a/vendor/gregwar/captcha/demo/demo.php b/vendor/gregwar/captcha/demo/demo.php new file mode 100644 index 00000000..7852745f --- /dev/null +++ b/vendor/gregwar/captcha/demo/demo.php @@ -0,0 +1,14 @@ +<?php + +include(__DIR__.'/../CaptchaBuilderInterface.php'); +include(__DIR__.'/../PhraseBuilderInterface.php'); +include(__DIR__.'/../CaptchaBuilder.php'); +include(__DIR__.'/../PhraseBuilder.php'); + +use Gregwar\Captcha\CaptchaBuilder; + +$captcha = new CaptchaBuilder; +$captcha + ->build() + ->save('out.jpg') +; diff --git a/vendor/gregwar/captcha/demo/fingerprint.php b/vendor/gregwar/captcha/demo/fingerprint.php new file mode 100644 index 00000000..ce30d991 --- /dev/null +++ b/vendor/gregwar/captcha/demo/fingerprint.php @@ -0,0 +1,15 @@ +<?php + +include(__DIR__.'/../CaptchaBuilderInterface.php'); +include(__DIR__.'/../PhraseBuilderInterface.php'); +include(__DIR__.'/../CaptchaBuilder.php'); +include(__DIR__.'/../PhraseBuilder.php'); + +use Gregwar\Captcha\CaptchaBuilder; + +echo count(CaptchaBuilder::create() + ->build() + ->getFingerprint() +); + +echo "\n"; diff --git a/vendor/gregwar/captcha/demo/index.php b/vendor/gregwar/captcha/demo/index.php new file mode 100644 index 00000000..e543883b --- /dev/null +++ b/vendor/gregwar/captcha/demo/index.php @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<body> + <html> + <meta charset="utf-8" /> + </html> + <body> + <h1>Captchas gallery</h1> + <?php for ($x=0; $x<8; $x++) { ?> + <?php for ($y=0; $y<5; $y++) { ?> + <img src="output.php?n=<?php echo 5*$x+$y; ?>" /> + <?php } ?> + <br /> + <?php } ?> + </body> +</body> diff --git a/vendor/gregwar/captcha/demo/ocr.php b/vendor/gregwar/captcha/demo/ocr.php new file mode 100644 index 00000000..3d745f65 --- /dev/null +++ b/vendor/gregwar/captcha/demo/ocr.php @@ -0,0 +1,42 @@ +<?php + +include(__DIR__.'/../CaptchaBuilderInterface.php'); +include(__DIR__.'/../PhraseBuilderInterface.php'); +include(__DIR__.'/../CaptchaBuilder.php'); +include(__DIR__.'/../PhraseBuilder.php'); + +use Gregwar\Captcha\CaptchaBuilder; + +/** + * Generates 1000 captchas and try to read their code with the + * ocrad OCR + */ + +$tests = 10000; +$passed = 0; + +shell_exec('rm passed*.jpg'); + +for ($i=0; $i<$tests; $i++) { + echo "Captcha $i/$tests... "; + + $captcha = new CaptchaBuilder; + + $captcha + ->setDistortion(false) + ->build() + ; + + if ($captcha->isOCRReadable()) { + $passed++; + $captcha->save("passed$passed.jpg"); + echo "passed at ocr... "; + } else { + echo "failed... "; + } + + echo "pass rate: ".round(100*$passed/($i+1),2)."%\n"; +} + +echo "\n"; +echo "Over, $passed/$tests readed with OCR\n"; diff --git a/vendor/gregwar/captcha/demo/output.php b/vendor/gregwar/captcha/demo/output.php new file mode 100644 index 00000000..2a4f3303 --- /dev/null +++ b/vendor/gregwar/captcha/demo/output.php @@ -0,0 +1,15 @@ +<?php + +include(__DIR__.'/../CaptchaBuilderInterface.php'); +include(__DIR__.'/../PhraseBuilderInterface.php'); +include(__DIR__.'/../CaptchaBuilder.php'); +include(__DIR__.'/../PhraseBuilder.php'); + +use Gregwar\Captcha\CaptchaBuilder; + +header('Content-type: image/jpeg'); + +CaptchaBuilder::create() + ->build() + ->output() +; diff --git a/vendor/miniflux/picofeed/LICENSE b/vendor/miniflux/picofeed/LICENSE new file mode 100644 index 00000000..6a362bc1 --- /dev/null +++ b/vendor/miniflux/picofeed/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Frederic Guillot + +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. diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Base.php b/vendor/miniflux/picofeed/lib/PicoFeed/Base.php new file mode 100644 index 00000000..41a6f8f0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Base.php @@ -0,0 +1,38 @@ +<?php + +namespace PicoFeed; + +use PicoFeed\Config\Config; +use PicoFeed\Logging\Logger; + +/** + * Base class + * + * @package PicoFeed + * @author Frederic Guillot + */ +abstract class Base +{ + /** + * Config class instance + * + * @access protected + * @var \PicoFeed\Config\Config + */ + protected $config; + + /** + * Constructor. + * + * @param \PicoFeed\Config\Config $config Config class instance + */ + public function __construct(Config $config = null) + { + $this->config = $config ?: new Config(); + Logger::setTimezone($this->config->getTimezone()); + } + + public function setConfig(Config $config) { + $this->config = $config; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/Client.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Client.php new file mode 100644 index 00000000..0548d5c6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Client.php @@ -0,0 +1,719 @@ +<?php + +namespace PicoFeed\Client; + +use DateTime; +use Exception; +use LogicException; +use PicoFeed\Logging\Logger; +use PicoFeed\Config\Config; + +/** + * Client class. + * + * @author Frederic Guillot + */ +abstract class Client +{ + /** + * Flag that say if the resource have been modified. + * + * @var bool + */ + private $is_modified = true; + + /** + * HTTP Content-Type. + * + * @var string + */ + private $content_type = ''; + + /** + * HTTP encoding. + * + * @var string + */ + private $encoding = ''; + + /** + * HTTP request headers. + * + * @var array + */ + protected $request_headers = array(); + + /** + * HTTP Etag header. + * + * @var string + */ + protected $etag = ''; + + /** + * HTTP Last-Modified header. + * + * @var string + */ + protected $last_modified = ''; + + /** + * Expiration DateTime + * + * @var DateTime + */ + protected $expiration = null; + + /** + * Proxy hostname. + * + * @var string + */ + protected $proxy_hostname = ''; + + /** + * Proxy port. + * + * @var int + */ + protected $proxy_port = 3128; + + /** + * Proxy username. + * + * @var string + */ + protected $proxy_username = ''; + + /** + * Proxy password. + * + * @var string + */ + protected $proxy_password = ''; + + /** + * Basic auth username. + * + * @var string + */ + protected $username = ''; + + /** + * Basic auth password. + * + * @var string + */ + protected $password = ''; + + /** + * Client connection timeout. + * + * @var int + */ + protected $timeout = 10; + + /** + * User-agent. + * + * @var string + */ + protected $user_agent = 'PicoFeed (https://github.com/miniflux/picoFeed)'; + + /** + * Real URL used (can be changed after a HTTP redirect). + * + * @var string + */ + protected $url = ''; + + /** + * Page/Feed content. + * + * @var string + */ + protected $content = ''; + + /** + * Number maximum of HTTP redirections to avoid infinite loops. + * + * @var int + */ + protected $max_redirects = 5; + + /** + * Maximum size of the HTTP body response. + * + * @var int + */ + protected $max_body_size = 2097152; // 2MB + + /** + * HTTP response status code. + * + * @var int + */ + protected $status_code = 0; + + /** + * Enables direct passthrough to requesting client. + * + * @var bool + */ + protected $passthrough = false; + + /** + * Do the HTTP request. + * + * @abstract + * + * @return array + */ + abstract public function doRequest(); + + /** + * Get client instance: curl or stream driver. + * + * @static + * + * @return \PicoFeed\Client\Client + */ + public static function getInstance() + { + if (function_exists('curl_init')) { + return new Curl(); + } elseif (ini_get('allow_url_fopen')) { + return new Stream(); + } + + throw new LogicException('You must have "allow_url_fopen=1" or curl extension installed'); + } + + /** + * Add HTTP Header to the request. + * + * @param array $headers + */ + public function setHeaders($headers) + { + $this->request_headers = $headers; + } + + /** + * Perform the HTTP request. + * + * @param string $url URL + * + * @return Client + */ + public function execute($url = '') + { + if ($url !== '') { + $this->url = $url; + } + + Logger::setMessage(get_called_class().' Fetch URL: '.$this->url); + Logger::setMessage(get_called_class().' Etag provided: '.$this->etag); + Logger::setMessage(get_called_class().' Last-Modified provided: '.$this->last_modified); + + $response = $this->doRequest(); + + $this->status_code = $response['status']; + $this->handleNotModifiedResponse($response); + $this->handleErrorResponse($response); + $this->handleNormalResponse($response); + + $this->expiration = $this->parseExpiration($response['headers']); + Logger::setMessage(get_called_class().' Expiration: '.$this->expiration->format(DATE_ISO8601)); + + return $this; + } + + /** + * Handle not modified response. + * + * @param array $response Client response + */ + protected function handleNotModifiedResponse(array $response) + { + if ($response['status'] == 304) { + $this->is_modified = false; + } elseif ($response['status'] == 200) { + $this->is_modified = $this->hasBeenModified($response, $this->etag, $this->last_modified); + $this->etag = $this->getHeader($response, 'ETag'); + $this->last_modified = $this->getHeader($response, 'Last-Modified'); + } + + if ($this->is_modified === false) { + Logger::setMessage(get_called_class().' Resource not modified'); + } + } + + /** + * Handle Http Error codes + * + * @param array $response Client response + * @throws ForbiddenException + * @throws InvalidUrlException + * @throws UnauthorizedException + */ + protected function handleErrorResponse(array $response) + { + $status = $response['status']; + if ($status == 401) { + throw new UnauthorizedException('Wrong or missing credentials'); + } else if ($status == 403) { + throw new ForbiddenException('Not allowed to access resource'); + } else if ($status == 404) { + throw new InvalidUrlException('Resource not found'); + } + } + + /** + * Handle normal response. + * + * @param array $response Client response + */ + protected function handleNormalResponse(array $response) + { + if ($response['status'] == 200) { + $this->content = $response['body']; + $this->content_type = $this->findContentType($response); + $this->encoding = $this->findCharset(); + } + } + + /** + * Check if a request has been modified according to the parameters. + * + * @param array $response + * @param string $etag + * @param string $lastModified + * + * @return bool + */ + private function hasBeenModified($response, $etag, $lastModified) + { + $headers = array( + 'Etag' => $etag, + 'Last-Modified' => $lastModified, + ); + + // Compare the values for each header that is present + $presentCacheHeaderCount = 0; + foreach ($headers as $key => $value) { + if (isset($response['headers'][$key])) { + if ($response['headers'][$key] !== $value) { + return true; + } + ++$presentCacheHeaderCount; + } + } + + // If at least one header is present and the values match, the response + // was not modified + if ($presentCacheHeaderCount > 0) { + return false; + } + + return true; + } + + /** + * Find content type from response headers. + * + * @param array $response Client response + * + * @return string + */ + public function findContentType(array $response) + { + return strtolower($this->getHeader($response, 'Content-Type')); + } + + /** + * Find charset from response headers. + * + * @return string + */ + public function findCharset() + { + $result = explode('charset=', $this->content_type); + + return isset($result[1]) ? $result[1] : ''; + } + + /** + * Get header value from a client response. + * + * @param array $response Client response + * @param string $header Header name + * + * @return string + */ + public function getHeader(array $response, $header) + { + return isset($response['headers'][$header]) ? $response['headers'][$header] : ''; + } + + /** + * Set the Last-Modified HTTP header. + * + * @param string $last_modified Header value + * + * @return \PicoFeed\Client\Client + */ + public function setLastModified($last_modified) + { + $this->last_modified = $last_modified; + + return $this; + } + + /** + * Get the value of the Last-Modified HTTP header. + * + * @return string + */ + public function getLastModified() + { + return $this->last_modified; + } + + /** + * Set the value of the Etag HTTP header. + * + * @param string $etag Etag HTTP header value + * + * @return \PicoFeed\Client\Client + */ + public function setEtag($etag) + { + $this->etag = $etag; + + return $this; + } + + /** + * Get the Etag HTTP header value. + * + * @return string + */ + public function getEtag() + { + return $this->etag; + } + + /** + * Get the final url value. + * + * @return string + */ + public function getUrl() + { + return $this->url; + } + + /** + * Set the url. + * + * @param $url + * @return string + */ + public function setUrl($url) + { + $this->url = $url; + return $this; + } + + /** + * Get the HTTP response status code. + * + * @return int + */ + public function getStatusCode() + { + return $this->status_code; + } + + /** + * Get the body of the HTTP response. + * + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * Get the content type value from HTTP headers. + * + * @return string + */ + public function getContentType() + { + return $this->content_type; + } + + /** + * Get the encoding value from HTTP headers. + * + * @return string + */ + public function getEncoding() + { + return $this->encoding; + } + + /** + * Return true if the remote resource has changed. + * + * @return bool + */ + public function isModified() + { + return $this->is_modified; + } + + /** + * return true if passthrough mode is enabled. + * + * @return bool + */ + public function isPassthroughEnabled() + { + return $this->passthrough; + } + + /** + * Set connection timeout. + * + * @param int $timeout Connection timeout + * + * @return \PicoFeed\Client\Client + */ + public function setTimeout($timeout) + { + $this->timeout = $timeout ?: $this->timeout; + + return $this; + } + + /** + * Set a custom user agent. + * + * @param string $user_agent User Agent + * + * @return \PicoFeed\Client\Client + */ + public function setUserAgent($user_agent) + { + $this->user_agent = $user_agent ?: $this->user_agent; + + return $this; + } + + /** + * Set the maximum number of HTTP redirections. + * + * @param int $max Maximum + * + * @return \PicoFeed\Client\Client + */ + public function setMaxRedirections($max) + { + $this->max_redirects = $max ?: $this->max_redirects; + + return $this; + } + + /** + * Set the maximum size of the HTTP body. + * + * @param int $max Maximum + * + * @return \PicoFeed\Client\Client + */ + public function setMaxBodySize($max) + { + $this->max_body_size = $max ?: $this->max_body_size; + + return $this; + } + + /** + * Set the proxy hostname. + * + * @param string $hostname Proxy hostname + * + * @return \PicoFeed\Client\Client + */ + public function setProxyHostname($hostname) + { + $this->proxy_hostname = $hostname ?: $this->proxy_hostname; + + return $this; + } + + /** + * Set the proxy port. + * + * @param int $port Proxy port + * + * @return \PicoFeed\Client\Client + */ + public function setProxyPort($port) + { + $this->proxy_port = $port ?: $this->proxy_port; + + return $this; + } + + /** + * Set the proxy username. + * + * @param string $username Proxy username + * + * @return \PicoFeed\Client\Client + */ + public function setProxyUsername($username) + { + $this->proxy_username = $username ?: $this->proxy_username; + + return $this; + } + + /** + * Set the proxy password. + * + * @param string $password Password + * + * @return \PicoFeed\Client\Client + */ + public function setProxyPassword($password) + { + $this->proxy_password = $password ?: $this->proxy_password; + + return $this; + } + + /** + * Set the username. + * + * @param string $username Basic Auth username + * + * @return \PicoFeed\Client\Client + */ + public function setUsername($username) + { + $this->username = $username ?: $this->username; + + return $this; + } + + /** + * Set the password. + * + * @param string $password Basic Auth Password + * + * @return \PicoFeed\Client\Client + */ + public function setPassword($password) + { + $this->password = $password ?: $this->password; + + return $this; + } + + /** + * Enable the passthrough mode. + * + * @return \PicoFeed\Client\Client + */ + public function enablePassthroughMode() + { + $this->passthrough = true; + + return $this; + } + + /** + * Disable the passthrough mode. + * + * @return \PicoFeed\Client\Client + */ + public function disablePassthroughMode() + { + $this->passthrough = false; + + return $this; + } + + /** + * Set config object. + * + * @param \PicoFeed\Config\Config $config Config instance + * + * @return \PicoFeed\Client\Client + */ + public function setConfig(Config $config) + { + if ($config !== null) { + $this->setTimeout($config->getClientTimeout()); + $this->setUserAgent($config->getClientUserAgent()); + $this->setMaxRedirections($config->getMaxRedirections()); + $this->setMaxBodySize($config->getMaxBodySize()); + $this->setProxyHostname($config->getProxyHostname()); + $this->setProxyPort($config->getProxyPort()); + $this->setProxyUsername($config->getProxyUsername()); + $this->setProxyPassword($config->getProxyPassword()); + } + + return $this; + } + + /** + * Return true if the HTTP status code is a redirection + * + * @access protected + * @param integer $code + * @return boolean + */ + public function isRedirection($code) + { + return $code == 301 || $code == 302 || $code == 303 || $code == 307; + } + + public function parseExpiration(HttpHeaders $headers) + { + try { + + if (isset($headers['Cache-Control'])) { + if (preg_match('/s-maxage=(\d+)/', $headers['Cache-Control'], $matches)) { + return new DateTime('+' . $matches[1] . ' seconds'); + } else if (preg_match('/max-age=(\d+)/', $headers['Cache-Control'], $matches)) { + return new DateTime('+' . $matches[1] . ' seconds'); + } + } + + if (! empty($headers['Expires'])) { + return new DateTime($headers['Expires']); + } + } catch (Exception $e) { + Logger::setMessage('Unable to parse expiration date: '.$e->getMessage()); + } + + return new DateTime(); + } + + /** + * Get expiration date time from "Expires" or "Cache-Control" headers + * + * @return DateTime + */ + public function getExpiration() + { + return $this->expiration ?: new DateTime(); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/ClientException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/ClientException.php new file mode 100644 index 00000000..b3a95c9f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/ClientException.php @@ -0,0 +1,14 @@ +<?php + +namespace PicoFeed\Client; + +use PicoFeed\PicoFeedException; + +/** + * ClientException Exception. + * + * @author Frederic Guillot + */ +abstract class ClientException extends PicoFeedException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/Curl.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Curl.php new file mode 100644 index 00000000..f4a65782 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Curl.php @@ -0,0 +1,402 @@ +<?php + +namespace PicoFeed\Client; + +use PicoFeed\Logging\Logger; + +/** + * cURL HTTP client. + * + * @author Frederic Guillot + */ +class Curl extends Client +{ + protected $nbRedirects = 0; + + /** + * HTTP response body. + * + * @var string + */ + private $body = ''; + + /** + * Body size. + * + * @var int + */ + private $body_length = 0; + + /** + * HTTP response headers. + * + * @var array + */ + private $response_headers = array(); + + /** + * Counter on the number of header received. + * + * @var int + */ + private $response_headers_count = 0; + + /** + * cURL callback to read the HTTP body. + * + * If the function return -1, curl stop to read the HTTP response + * + * @param resource $ch cURL handler + * @param string $buffer Chunk of data + * + * @return int Length of the buffer + */ + public function readBody($ch, $buffer) + { + $length = strlen($buffer); + $this->body_length += $length; + + if ($this->body_length > $this->max_body_size) { + return -1; + } + + $this->body .= $buffer; + + return $length; + } + + /** + * cURL callback to read HTTP headers. + * + * @param resource $ch cURL handler + * @param string $buffer Header line + * + * @return int Length of the buffer + */ + public function readHeaders($ch, $buffer) + { + $length = strlen($buffer); + + if ($buffer === "\r\n" || $buffer === "\n") { + ++$this->response_headers_count; + } else { + if (!isset($this->response_headers[$this->response_headers_count])) { + $this->response_headers[$this->response_headers_count] = ''; + } + + $this->response_headers[$this->response_headers_count] .= $buffer; + } + + return $length; + } + + /** + * cURL callback to passthrough the HTTP body to the client. + * + * If the function return -1, curl stop to read the HTTP response + * + * @param resource $ch cURL handler + * @param string $buffer Chunk of data + * + * @return int Length of the buffer + */ + public function passthroughBody($ch, $buffer) + { + // do it only at the beginning of a transmission + if ($this->body_length === 0) { + list($status, $headers) = HttpHeaders::parse(explode("\n", $this->response_headers[$this->response_headers_count - 1])); + + if ($this->isRedirection($status)) { + return $this->handleRedirection($headers['Location']); + } + + // Do not work with PHP-FPM + if (strpos(PHP_SAPI, 'cgi') !== false) { + header(':', true, $status); + } + + if (isset($headers['Content-Type'])) { + header('Content-Type:' .$headers['Content-Type']); + } + } + + $length = strlen($buffer); + $this->body_length += $length; + + echo $buffer; + + return $length; + } + + /** + * Prepare HTTP headers. + * + * @return string[] + */ + private function prepareHeaders() + { + $headers = array( + 'Connection: close', + ); + + if ($this->etag) { + $headers[] = 'If-None-Match: '.$this->etag; + $headers[] = 'A-IM: feed'; + } + + if ($this->last_modified) { + $headers[] = 'If-Modified-Since: '.$this->last_modified; + } + + $headers = array_merge($headers, $this->request_headers); + + return $headers; + } + + /** + * Prepare curl proxy context. + * + * @param resource $ch + * + * @return resource $ch + */ + private function prepareProxyContext($ch) + { + if ($this->proxy_hostname) { + Logger::setMessage(get_called_class().' Proxy: '.$this->proxy_hostname.':'.$this->proxy_port); + + curl_setopt($ch, CURLOPT_PROXYPORT, $this->proxy_port); + curl_setopt($ch, CURLOPT_PROXYTYPE, 'HTTP'); + curl_setopt($ch, CURLOPT_PROXY, $this->proxy_hostname); + + if ($this->proxy_username) { + Logger::setMessage(get_called_class().' Proxy credentials: Yes'); + curl_setopt($ch, CURLOPT_PROXYUSERPWD, $this->proxy_username.':'.$this->proxy_password); + } else { + Logger::setMessage(get_called_class().' Proxy credentials: No'); + } + } + + return $ch; + } + + /** + * Prepare curl auth context. + * + * @param resource $ch + * + * @return resource $ch + */ + private function prepareAuthContext($ch) + { + if ($this->username && $this->password) { + curl_setopt($ch, CURLOPT_USERPWD, $this->username.':'.$this->password); + } + + return $ch; + } + + /** + * Set write/header functions. + * + * @param resource $ch + * + * @return resource $ch + */ + private function prepareDownloadMode($ch) + { + $this->body = ''; + $this->response_headers = array(); + $this->response_headers_count = 0; + $write_function = 'readBody'; + $header_function = 'readHeaders'; + + if ($this->isPassthroughEnabled()) { + $write_function = 'passthroughBody'; + } + + curl_setopt($ch, CURLOPT_WRITEFUNCTION, array($this, $write_function)); + curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, $header_function)); + + return $ch; + } + + /** + * Prepare curl context. + * + * @return resource + */ + private function prepareContext() + { + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $this->url); + curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->timeout); + curl_setopt($ch, CURLOPT_USERAGENT, $this->user_agent); + curl_setopt($ch, CURLOPT_HTTPHEADER, $this->prepareHeaders()); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); + curl_setopt($ch, CURLOPT_ENCODING, ''); + curl_setopt($ch, CURLOPT_COOKIEJAR, 'php://memory'); + curl_setopt($ch, CURLOPT_COOKIEFILE, 'php://memory'); + + // Disable SSLv3 by enforcing TLSv1.x for curl >= 7.34.0 and < 7.39.0. + // Versions prior to 7.34 and at least when compiled against openssl + // interpret this parameter as "limit to TLSv1.0" which fails for sites + // which enforce TLS 1.1+. + // Starting with curl 7.39.0 SSLv3 is disabled by default. + $version = curl_version(); + if ($version['version_number'] >= 467456 && $version['version_number'] < 468736) { + curl_setopt($ch, CURLOPT_SSLVERSION, 1); + } + + $ch = $this->prepareDownloadMode($ch); + $ch = $this->prepareProxyContext($ch); + $ch = $this->prepareAuthContext($ch); + + return $ch; + } + + /** + * Execute curl context. + */ + private function executeContext() + { + $ch = $this->prepareContext(); + curl_exec($ch); + + Logger::setMessage(get_called_class().' cURL total time: '.curl_getinfo($ch, CURLINFO_TOTAL_TIME)); + Logger::setMessage(get_called_class().' cURL dns lookup time: '.curl_getinfo($ch, CURLINFO_NAMELOOKUP_TIME)); + Logger::setMessage(get_called_class().' cURL connect time: '.curl_getinfo($ch, CURLINFO_CONNECT_TIME)); + Logger::setMessage(get_called_class().' cURL speed download: '.curl_getinfo($ch, CURLINFO_SPEED_DOWNLOAD)); + Logger::setMessage(get_called_class().' cURL effective url: '.curl_getinfo($ch, CURLINFO_EFFECTIVE_URL)); + + $curl_errno = curl_errno($ch); + + if ($curl_errno) { + Logger::setMessage(get_called_class().' cURL error: '.curl_error($ch)); + curl_close($ch); + + $this->handleError($curl_errno); + } + + // Update the url if there where redirects + $this->url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); + + curl_close($ch); + } + + /** + * Do the HTTP request. + * + * @return array HTTP response ['body' => ..., 'status' => ..., 'headers' => ...] + */ + public function doRequest() + { + $this->executeContext(); + + list($status, $headers) = HttpHeaders::parse(explode("\n", $this->response_headers[$this->response_headers_count - 1])); + + if ($this->isRedirection($status)) { + if (empty($headers['Location'])) { + $status = 200; + } else { + return $this->handleRedirection($headers['Location']); + } + } + + return array( + 'status' => $status, + 'body' => $this->body, + 'headers' => $headers, + ); + } + + /** + * Handle HTTP redirects + * + * @param string $location Redirected URL + * @return array + * @throws MaxRedirectException + */ + private function handleRedirection($location) + { + $result = array(); + $this->url = Url::resolve($location, $this->url); + $this->body = ''; + $this->body_length = 0; + $this->response_headers = array(); + $this->response_headers_count = 0; + + while (true) { + $this->nbRedirects++; + + if ($this->nbRedirects >= $this->max_redirects) { + throw new MaxRedirectException('Maximum number of redirections reached'); + } + + $result = $this->doRequest(); + + if ($this->isRedirection($result['status'])) { + $this->url = Url::resolve($result['headers']['Location'], $this->url); + $this->body = ''; + $this->body_length = 0; + $this->response_headers = array(); + $this->response_headers_count = 0; + } else { + break; + } + } + + return $result; + } + + /** + * Handle cURL errors (throw individual exceptions). + * + * We don't use constants because they are not necessary always available + * (depends of the version of libcurl linked to php) + * + * @see http://curl.haxx.se/libcurl/c/libcurl-errors.html + * + * @param int $errno cURL error code + * @throws InvalidCertificateException + * @throws InvalidUrlException + * @throws MaxRedirectException + * @throws MaxSizeException + * @throws TimeoutException + */ + private function handleError($errno) + { + switch ($errno) { + case 78: // CURLE_REMOTE_FILE_NOT_FOUND + throw new InvalidUrlException('Resource not found', $errno); + case 6: // CURLE_COULDNT_RESOLVE_HOST + throw new InvalidUrlException('Unable to resolve hostname', $errno); + case 7: // CURLE_COULDNT_CONNECT + throw new InvalidUrlException('Unable to connect to the remote host', $errno); + case 23: // CURLE_WRITE_ERROR + throw new MaxSizeException('Maximum response size exceeded', $errno); + case 28: // CURLE_OPERATION_TIMEDOUT + throw new TimeoutException('Operation timeout', $errno); + case 35: // CURLE_SSL_CONNECT_ERROR + case 51: // CURLE_PEER_FAILED_VERIFICATION + case 58: // CURLE_SSL_CERTPROBLEM + case 60: // CURLE_SSL_CACERT + case 59: // CURLE_SSL_CIPHER + case 64: // CURLE_USE_SSL_FAILED + case 66: // CURLE_SSL_ENGINE_INITFAILED + case 77: // CURLE_SSL_CACERT_BADFILE + case 83: // CURLE_SSL_ISSUER_ERROR + $msg = 'Invalid SSL certificate caused by CURL error number ' . $errno; + throw new InvalidCertificateException($msg, $errno); + case 47: // CURLE_TOO_MANY_REDIRECTS + throw new MaxRedirectException('Maximum number of redirections reached', $errno); + case 63: // CURLE_FILESIZE_EXCEEDED + throw new MaxSizeException('Maximum response size exceeded', $errno); + default: + throw new InvalidUrlException('Unable to fetch the URL', $errno); + } + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/ForbiddenException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/ForbiddenException.php new file mode 100644 index 00000000..c226e95a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/ForbiddenException.php @@ -0,0 +1,10 @@ +<?php + +namespace PicoFeed\Client; + +/** + * @author Bernhard Posselt + */ +class ForbiddenException extends ClientException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/HttpHeaders.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/HttpHeaders.php new file mode 100644 index 00000000..34b81399 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/HttpHeaders.php @@ -0,0 +1,79 @@ +<?php + +namespace PicoFeed\Client; + +use ArrayAccess; +use PicoFeed\Logging\Logger; + +/** + * Class to handle HTTP headers case insensitivity. + * + * @author Bernhard Posselt + * @author Frederic Guillot + */ +class HttpHeaders implements ArrayAccess +{ + private $headers = array(); + + public function __construct(array $headers) + { + foreach ($headers as $key => $value) { + $this->headers[strtolower($key)] = $value; + } + } + + public function offsetGet($offset) + { + return $this->offsetExists($offset) ? $this->headers[strtolower($offset)] : ''; + } + + public function offsetSet($offset, $value) + { + $this->headers[strtolower($offset)] = $value; + } + + public function offsetExists($offset) + { + return isset($this->headers[strtolower($offset)]); + } + + public function offsetUnset($offset) + { + unset($this->headers[strtolower($offset)]); + } + + /** + * Parse HTTP headers. + * + * @static + * + * @param array $lines List of headers + * + * @return array + */ + public static function parse(array $lines) + { + $status = 0; + $headers = array(); + + foreach ($lines as $line) { + if (strpos($line, 'HTTP/1') === 0) { + $headers = array(); + $status = (int) substr($line, 9, 3); + } elseif (strpos($line, ': ') !== false) { + list($name, $value) = explode(': ', $line); + if ($value) { + $headers[trim($name)] = trim($value); + } + } + } + + Logger::setMessage(get_called_class().' HTTP status code: '.$status); + + foreach ($headers as $name => $value) { + Logger::setMessage(get_called_class().' HTTP header: '.$name.' => '.$value); + } + + return array($status, new self($headers)); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php new file mode 100644 index 00000000..8d25d7e4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php @@ -0,0 +1,12 @@ +<?php + +namespace PicoFeed\Client; + +/** + * InvalidCertificateException Exception. + * + * @author Frederic Guillot + */ +class InvalidCertificateException extends ClientException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidUrlException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidUrlException.php new file mode 100644 index 00000000..15534d98 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidUrlException.php @@ -0,0 +1,12 @@ +<?php + +namespace PicoFeed\Client; + +/** + * InvalidUrlException Exception. + * + * @author Frederic Guillot + */ +class InvalidUrlException extends ClientException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxRedirectException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxRedirectException.php new file mode 100644 index 00000000..0a221af6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxRedirectException.php @@ -0,0 +1,12 @@ +<?php + +namespace PicoFeed\Client; + +/** + * MaxRedirectException Exception. + * + * @author Frederic Guillot + */ +class MaxRedirectException extends ClientException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxSizeException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxSizeException.php new file mode 100644 index 00000000..201b22a6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxSizeException.php @@ -0,0 +1,12 @@ +<?php + +namespace PicoFeed\Client; + +/** + * MaxSizeException Exception. + * + * @author Frederic Guillot + */ +class MaxSizeException extends ClientException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/Stream.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Stream.php new file mode 100644 index 00000000..2e91d472 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Stream.php @@ -0,0 +1,205 @@ +<?php + +namespace PicoFeed\Client; + +use PicoFeed\Logging\Logger; + +/** + * Stream context HTTP client. + * + * @author Frederic Guillot + */ +class Stream extends Client +{ + /** + * Prepare HTTP headers. + * + * @return string[] + */ + private function prepareHeaders() + { + $headers = array( + 'Connection: close', + 'User-Agent: '.$this->user_agent, + ); + + // disable compression in passthrough mode. It could result in double + // compressed content which isn't decodeable by browsers + if (function_exists('gzdecode') && !$this->isPassthroughEnabled()) { + $headers[] = 'Accept-Encoding: gzip'; + } + + if ($this->etag) { + $headers[] = 'If-None-Match: '.$this->etag; + $headers[] = 'A-IM: feed'; + } + + if ($this->last_modified) { + $headers[] = 'If-Modified-Since: '.$this->last_modified; + } + + if ($this->proxy_username) { + $headers[] = 'Proxy-Authorization: Basic '.base64_encode($this->proxy_username.':'.$this->proxy_password); + } + + if ($this->username && $this->password) { + $headers[] = 'Authorization: Basic '.base64_encode($this->username.':'.$this->password); + } + + $headers = array_merge($headers, $this->request_headers); + + return $headers; + } + + /** + * Construct the final URL from location headers. + * + * @param array $headers List of HTTP response header + */ + private function setEffectiveUrl($headers) + { + foreach ($headers as $header) { + if (stripos($header, 'Location') === 0) { + list(, $value) = explode(': ', $header); + + $this->url = Url::resolve($value, $this->url); + } + } + } + + /** + * Prepare stream context. + * + * @return array + */ + private function prepareContext() + { + $context = array( + 'http' => array( + 'method' => 'GET', + 'protocol_version' => 1.1, + 'timeout' => $this->timeout, + 'max_redirects' => $this->max_redirects, + ), + ); + + if ($this->proxy_hostname) { + Logger::setMessage(get_called_class().' Proxy: '.$this->proxy_hostname.':'.$this->proxy_port); + + $context['http']['proxy'] = 'tcp://'.$this->proxy_hostname.':'.$this->proxy_port; + $context['http']['request_fulluri'] = true; + + if ($this->proxy_username) { + Logger::setMessage(get_called_class().' Proxy credentials: Yes'); + } else { + Logger::setMessage(get_called_class().' Proxy credentials: No'); + } + } + + $context['http']['header'] = implode("\r\n", $this->prepareHeaders()); + + return $context; + } + + /** + * Do the HTTP request. + * + * @return array HTTP response ['body' => ..., 'status' => ..., 'headers' => ...] + * @throws InvalidUrlException + * @throws MaxSizeException + * @throws TimeoutException + */ + public function doRequest() + { + $body = ''; + + // Create context + $context = stream_context_create($this->prepareContext()); + + // Make HTTP request + $stream = @fopen($this->url, 'r', false, $context); + if (!is_resource($stream)) { + throw new InvalidUrlException('Unable to establish a connection'); + } + + // Get HTTP headers response + $metadata = stream_get_meta_data($stream); + list($status, $headers) = HttpHeaders::parse($metadata['wrapper_data']); + + if ($this->isPassthroughEnabled()) { + header(':', true, $status); + + if (isset($headers['Content-Type'])) { + header('Content-Type: '.$headers['Content-Type']); + } + + fpassthru($stream); + } else { + // Get the entire body until the max size + $body = stream_get_contents($stream, $this->max_body_size + 1); + + // If the body size is too large abort everything + if (strlen($body) > $this->max_body_size) { + throw new MaxSizeException('Content size too large'); + } + + if ($metadata['timed_out']) { + throw new TimeoutException('Operation timeout'); + } + } + + fclose($stream); + + $this->setEffectiveUrl($metadata['wrapper_data']); + + return array( + 'status' => $status, + 'body' => $this->decodeBody($body, $headers), + 'headers' => $headers, + ); + } + + /** + * Decode body response according to the HTTP headers. + * + * @param string $body Raw body + * @param HttpHeaders $headers HTTP headers + * + * @return string + */ + public function decodeBody($body, HttpHeaders $headers) + { + if (isset($headers['Transfer-Encoding']) && $headers['Transfer-Encoding'] === 'chunked') { + $body = $this->decodeChunked($body); + } + + if (isset($headers['Content-Encoding']) && $headers['Content-Encoding'] === 'gzip') { + $body = gzdecode($body); + } + + return $body; + } + + /** + * Decode a chunked body. + * + * @param string $str Raw body + * + * @return string Decoded body + */ + public function decodeChunked($str) + { + for ($result = ''; !empty($str); $str = trim($str)) { + + // Get the chunk length + $pos = strpos($str, "\r\n"); + $len = hexdec(substr($str, 0, $pos)); + + // Append the chunk to the result + $result .= substr($str, $pos + 2, $len); + $str = substr($str, $pos + 2 + $len); + } + + return $result; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/TimeoutException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/TimeoutException.php new file mode 100644 index 00000000..da98da12 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/TimeoutException.php @@ -0,0 +1,12 @@ +<?php + +namespace PicoFeed\Client; + +/** + * TimeoutException Exception. + * + * @author Frederic Guillot + */ +class TimeoutException extends ClientException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/UnauthorizedException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/UnauthorizedException.php new file mode 100644 index 00000000..81898b99 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/UnauthorizedException.php @@ -0,0 +1,10 @@ +<?php + +namespace PicoFeed\Client; + +/** + * @author Bernhard Posselt + */ +class UnauthorizedException extends ClientException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/Url.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Url.php new file mode 100644 index 00000000..a9337988 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Url.php @@ -0,0 +1,290 @@ +<?php + +namespace PicoFeed\Client; + +/** + * URL class. + * + * @author Frederic Guillot + */ +class Url +{ + /** + * URL. + * + * @var string + */ + private $url = ''; + + /** + * URL components. + * + * @var array + */ + private $components = array(); + + /** + * Constructor. + * + * @param string $url URL + */ + public function __construct($url) + { + $this->url = $url; + $this->components = parse_url($url) ?: array(); + + // Issue with PHP < 5.4.7 and protocol relative url + if (version_compare(PHP_VERSION, '5.4.7', '<') && $this->isProtocolRelative()) { + $pos = strpos($this->components['path'], '/', 2); + + if ($pos === false) { + $pos = strlen($this->components['path']); + } + + $this->components['host'] = substr($this->components['path'], 2, $pos - 2); + $this->components['path'] = substr($this->components['path'], $pos); + } + } + + /** + * Shortcut method to get an absolute url from relative url. + * + * @static + * + * @param mixed $item_url Unknown url (can be relative or not) + * @param mixed $website_url Website url + * + * @return string + */ + public static function resolve($item_url, $website_url) + { + $link = is_string($item_url) ? new self($item_url) : $item_url; + $website = is_string($website_url) ? new self($website_url) : $website_url; + + if ($link->isRelativeUrl()) { + if ($link->isRelativePath()) { + return $link->getAbsoluteUrl($website->getBaseUrl($website->getBasePath())); + } + + return $link->getAbsoluteUrl($website->getBaseUrl()); + } elseif ($link->isProtocolRelative()) { + $link->setScheme($website->getScheme()); + } + + return $link->getAbsoluteUrl(); + } + + /** + * Shortcut method to get a base url. + * + * @static + * + * @param string $url + * + * @return string + */ + public static function base($url) + { + $link = new self($url); + + return $link->getBaseUrl(); + } + + /** + * Get the base URL. + * + * @param string $suffix Add a suffix to the url + * + * @return string + */ + public function getBaseUrl($suffix = '') + { + return $this->hasHost() ? $this->getScheme('://').$this->getHost().$this->getPort(':').$suffix : ''; + } + + /** + * Get the absolute URL. + * + * @param string $base_url Use this url as base url + * + * @return string + */ + public function getAbsoluteUrl($base_url = '') + { + if ($base_url) { + $base = new self($base_url); + $url = $base->getAbsoluteUrl().substr($this->getFullPath(), 1); + } else { + $url = $this->hasHost() ? $this->getBaseUrl().$this->getFullPath() : ''; + } + + return $url; + } + + /** + * Return true if the url is relative. + * + * @return bool + */ + public function isRelativeUrl() + { + return !$this->hasScheme() && !$this->isProtocolRelative(); + } + + /** + * Return true if the path is relative. + * + * @return bool + */ + public function isRelativePath() + { + $path = $this->getPath(); + + return empty($path) || $path{0} + !== '/'; + } + + /** + * Filters the path of a URI. + * + * Imported from Guzzle library: https://github.com/guzzle/psr7/blob/master/src/Uri.php#L568-L582 + * + * @param $path + * + * @return string + */ + public function filterPath($path, $charUnreserved = 'a-zA-Z0-9_\-\.~', $charSubDelims = '!\$&\'\(\)\*\+,;=') + { + return preg_replace_callback( + '/(?:[^'.$charUnreserved.$charSubDelims.':@\/%]+|%(?![A-Fa-f0-9]{2}))/', + function (array $matches) { return rawurlencode($matches[0]); }, + $path + ); + } + + /** + * Get the path. + * + * @return string + */ + public function getPath() + { + return $this->filterPath(empty($this->components['path']) ? '' : $this->components['path']); + } + + /** + * Get the base path. + * + * @return string + */ + public function getBasePath() + { + $current_path = $this->getPath(); + + $path = $this->isRelativePath() ? '/' : ''; + $path .= substr($current_path, -1) === '/' ? $current_path : dirname($current_path); + + return preg_replace('/\\\\\/|\/\//', '/', $path.'/'); + } + + /** + * Get the full path (path + querystring + fragment). + * + * @return string + */ + public function getFullPath() + { + $path = $this->isRelativePath() ? '/' : ''; + $path .= $this->getPath(); + $path .= empty($this->components['query']) ? '' : '?'.$this->components['query']; + $path .= empty($this->components['fragment']) ? '' : '#'.$this->components['fragment']; + + return $path; + } + + /** + * Get the hostname. + * + * @return string + */ + public function getHost() + { + return empty($this->components['host']) ? '' : $this->components['host']; + } + + /** + * Return true if the url has a hostname. + * + * @return bool + */ + public function hasHost() + { + return !empty($this->components['host']); + } + + /** + * Get the scheme. + * + * @param string $suffix Suffix to add when there is a scheme + * + * @return string + */ + public function getScheme($suffix = '') + { + return ($this->hasScheme() ? $this->components['scheme'] : 'http').$suffix; + } + + /** + * Set the scheme. + * + * @param string $scheme Set a scheme + * + * @return string + */ + public function setScheme($scheme) + { + $this->components['scheme'] = $scheme; + } + + /** + * Return true if the url has a scheme. + * + * @return bool + */ + public function hasScheme() + { + return !empty($this->components['scheme']); + } + + /** + * Get the port. + * + * @param string $prefix Prefix to add when there is a port + * + * @return string + */ + public function getPort($prefix = '') + { + return $this->hasPort() ? $prefix.$this->components['port'] : ''; + } + + /** + * Return true if the url has a port. + * + * @return bool + */ + public function hasPort() + { + return !empty($this->components['port']); + } + + /** + * Return true if the url is protocol relative (start with //). + * + * @return bool + */ + public function isProtocolRelative() + { + return strpos($this->url, '//') === 0; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Encoding/Encoding.php b/vendor/miniflux/picofeed/lib/PicoFeed/Encoding/Encoding.php new file mode 100644 index 00000000..fa0917e8 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Encoding/Encoding.php @@ -0,0 +1,33 @@ +<?php + +namespace PicoFeed\Encoding; + +/** + * Encoding class. + */ +class Encoding +{ + public static function convert($input, $encoding) + { + if ($encoding === 'utf-8' || $encoding === '') { + return $input; + } + + // suppress all notices since it isn't possible to silence only the + // notice "Wrong charset, conversion from $in_encoding to $out_encoding is not allowed" + set_error_handler(function () {}, E_NOTICE); + + // convert input to utf-8 and strip invalid characters + $value = iconv($encoding, 'UTF-8//IGNORE', $input); + + // stop silencing of notices + restore_error_handler(); + + // return input if something went wrong, maybe it's usable anyway + if ($value === false) { + return $input; + } + + return $value; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Attribute.php b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Attribute.php new file mode 100644 index 00000000..f0021532 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Attribute.php @@ -0,0 +1,700 @@ +<?php + +namespace PicoFeed\Filter; + +use PicoFeed\Client\Url; + +/** + * Attribute Filter class. + * + * @author Frederic Guillot + */ +class Attribute +{ + /** + * Image proxy url. + * + * @var string + */ + private $image_proxy_url = ''; + + /** + * Image proxy callback. + * + * @var \Closure|null + */ + private $image_proxy_callback = null; + + /** + * limits the image proxy usage to this protocol. + * + * @var string + */ + private $image_proxy_limit_protocol = ''; + + /** + * Tags and attribute whitelist. + * + * @var array + */ + private $attribute_whitelist = array( + 'audio' => array('controls', 'src'), + 'video' => array('poster', 'controls', 'height', 'width', 'src'), + 'source' => array('src', 'type'), + 'dt' => array(), + 'dd' => array(), + 'dl' => array(), + 'table' => array(), + 'caption' => array(), + 'tr' => array(), + 'th' => array(), + 'td' => array(), + 'tbody' => array(), + 'thead' => array(), + 'h1' => array(), + 'h2' => array(), + 'h3' => array(), + 'h4' => array(), + 'h5' => array(), + 'h6' => array(), + 'strong' => array(), + 'em' => array(), + 'code' => array(), + 'pre' => array(), + 'blockquote' => array(), + 'p' => array(), + 'ul' => array(), + 'li' => array(), + 'ol' => array(), + 'br' => array(), + 'del' => array(), + 'a' => array('href'), + 'img' => array('src', 'title', 'alt'), + 'figure' => array(), + 'figcaption' => array(), + 'cite' => array(), + 'time' => array('datetime'), + 'abbr' => array('title'), + 'iframe' => array('width', 'height', 'frameborder', 'src', 'allowfullscreen'), + 'q' => array('cite'), + ); + + /** + * Scheme whitelist. + * + * For a complete list go to http://en.wikipedia.org/wiki/URI_scheme + * + * @var array + */ + private $scheme_whitelist = array( + 'bitcoin:', + 'callto:', + 'ed2k://', + 'facetime://', + 'feed:', + 'ftp://', + 'geo:', + 'git://', + 'http://', + 'https://', + 'irc://', + 'irc6://', + 'ircs://', + 'jabber:', + 'magnet:', + 'mailto:', + 'nntp://', + 'rtmp://', + 'sftp://', + 'sip:', + 'sips:', + 'skype:', + 'smb://', + 'sms:', + 'spotify:', + 'ssh:', + 'steam:', + 'svn://', + 'tel:', + ); + + /** + * Iframe source whitelist, everything else is ignored. + * + * @var array + */ + private $iframe_whitelist = array( + 'http://www.youtube.com', + 'https://www.youtube.com', + 'http://player.vimeo.com', + 'https://player.vimeo.com', + 'http://www.dailymotion.com', + 'https://www.dailymotion.com', + 'http://vk.com', + 'https://vk.com', + ); + + /** + * Blacklisted resources. + * + * @var array + */ + private $media_blacklist = array( + 'api.flattr.com', + 'feeds.feedburner.com', + 'share.feedsportal.com', + 'da.feedsportal.com', + 'rc.feedsportal.com', + 'rss.feedsportal.com', + 'res.feedsportal.com', + 'res1.feedsportal.com', + 'res2.feedsportal.com', + 'res3.feedsportal.com', + 'pi.feedsportal.com', + 'rss.nytimes.com', + 'feeds.wordpress.com', + 'stats.wordpress.com', + 'rss.cnn.com', + 'twitter.com/home?status=', + 'twitter.com/share', + 'twitter_icon_large.png', + 'www.facebook.com/sharer.php', + 'facebook_icon_large.png', + 'plus.google.com/share', + 'www.gstatic.com/images/icons/gplus-16.png', + 'www.gstatic.com/images/icons/gplus-32.png', + 'www.gstatic.com/images/icons/gplus-64.png', + ); + + /** + * Attributes used for external resources. + * + * @var array + */ + private $media_attributes = array( + 'src', + 'href', + 'poster', + ); + + /** + * Attributes that must be integer. + * + * @var array + */ + private $integer_attributes = array( + 'width', + 'height', + 'frameborder', + ); + + /** + * Mandatory attributes for specified tags. + * + * @var array + */ + private $required_attributes = array( + 'a' => array('href'), + 'img' => array('src'), + 'iframe' => array('src'), + 'audio' => array('src'), + 'source' => array('src'), + ); + + /** + * Add attributes to specified tags. + * + * @var array + */ + private $add_attributes = array( + 'a' => array('rel' => 'noreferrer', 'target' => '_blank'), + 'video' => array('controls' => 'true'), + ); + + /** + * List of filters to apply. + * + * @var array + */ + private $filters = array( + 'filterAllowedAttribute', + 'filterIntegerAttribute', + 'rewriteAbsoluteUrl', + 'filterIframeAttribute', + 'filterBlacklistResourceAttribute', + 'filterProtocolUrlAttribute', + 'rewriteImageProxyUrl', + 'secureIframeSrc', + 'removeYouTubeAutoplay', + ); + + /** + * Add attributes to specified tags. + * + * @var \PicoFeed\Client\Url + */ + private $website; + + /** + * Constructor. + * + * @param \PicoFeed\Client\Url $website Website url instance + */ + public function __construct(Url $website) + { + $this->website = $website; + } + + /** + * Apply filters to the attributes list. + * + * @param string $tag Tag name + * @param array $attributes Attributes dictionary + * + * @return array Filtered attributes + */ + public function filter($tag, array $attributes) + { + foreach ($attributes as $attribute => &$value) { + foreach ($this->filters as $filter) { + if (!$this->$filter($tag, $attribute, $value)) { + unset($attributes[$attribute]); + break; + } + } + } + + return $attributes; + } + + /** + * Return true if the value is allowed (remove not allowed attributes). + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function filterAllowedAttribute($tag, $attribute, $value) + { + return isset($this->attribute_whitelist[$tag]) && in_array($attribute, $this->attribute_whitelist[$tag]); + } + + /** + * Return true if the value is not integer (remove attributes that should have an integer value). + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function filterIntegerAttribute($tag, $attribute, $value) + { + if (in_array($attribute, $this->integer_attributes)) { + return ctype_digit($value); + } + + return true; + } + + /** + * Return true if the iframe source is allowed (remove not allowed iframe). + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function filterIframeAttribute($tag, $attribute, $value) + { + if ($tag === 'iframe' && $attribute === 'src') { + foreach ($this->iframe_whitelist as $url) { + if (strpos($value, $url) === 0) { + return true; + } + } + + return false; + } + + return true; + } + + /** + * Return true if the resource is not blacklisted (remove blacklisted resource attributes). + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function filterBlacklistResourceAttribute($tag, $attribute, $value) + { + if ($this->isResource($attribute) && $this->isBlacklistedMedia($value)) { + return false; + } + + return true; + } + + /** + * Convert all relative links to absolute url. + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function rewriteAbsoluteUrl($tag, $attribute, &$value) + { + if ($this->isResource($attribute)) { + $value = Url::resolve($value, $this->website); + } + + return true; + } + + /** + * Turns iframes' src attribute from http to https to prevent + * mixed active content. + * + * @param string $tag Tag name + * @param array $attribute Atttributes name + * @param string $value Attribute value + * + * @return bool + */ + public function secureIframeSrc($tag, $attribute, &$value) + { + if ($tag === 'iframe' && $attribute === 'src' && strpos($value, 'http://') === 0) { + $value = substr_replace($value, 's', 4, 0); + } + + return true; + } + + /** + * Removes YouTube autoplay from iframes. + * + * @param string $tag Tag name + * @param array $attribute Atttributes name + * @param string $value Attribute value + * + * @return bool + */ + public function removeYouTubeAutoplay($tag, $attribute, &$value) + { + $regex = '%^(https://(?:www\.)?youtube.com/.*\?.*autoplay=)(1)(.*)%i'; + if ($tag === 'iframe' && $attribute === 'src' && preg_match($regex, $value)) { + $value = preg_replace($regex, '${1}0$3', $value); + } + + return true; + } + + /** + * Rewrite image url to use with a proxy. + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function rewriteImageProxyUrl($tag, $attribute, &$value) + { + if ($tag === 'img' && $attribute === 'src' + && !($this->image_proxy_limit_protocol !== '' && stripos($value, $this->image_proxy_limit_protocol.':') !== 0)) { + if ($this->image_proxy_url) { + $value = sprintf($this->image_proxy_url, rawurlencode($value)); + } elseif (is_callable($this->image_proxy_callback)) { + $value = call_user_func($this->image_proxy_callback, $value); + } + } + + return true; + } + + /** + * Return true if the scheme is authorized. + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function filterProtocolUrlAttribute($tag, $attribute, $value) + { + if ($this->isResource($attribute) && !$this->isAllowedProtocol($value)) { + return false; + } + + return true; + } + + /** + * Automatically add/override some attributes for specific tags. + * + * @param string $tag Tag name + * @param array $attributes Attributes list + * + * @return array + */ + public function addAttributes($tag, array $attributes) + { + if (isset($this->add_attributes[$tag])) { + $attributes += $this->add_attributes[$tag]; + } + + return $attributes; + } + + /** + * Return true if all required attributes are present. + * + * @param string $tag Tag name + * @param array $attributes Attributes list + * + * @return bool + */ + public function hasRequiredAttributes($tag, array $attributes) + { + if (isset($this->required_attributes[$tag])) { + foreach ($this->required_attributes[$tag] as $attribute) { + if (!isset($attributes[$attribute])) { + return false; + } + } + } + + return true; + } + + /** + * Check if an attribute name is an external resource. + * + * @param string $attribute Attribute name + * + * @return bool + */ + public function isResource($attribute) + { + return in_array($attribute, $this->media_attributes); + } + + /** + * Detect if the protocol is allowed or not. + * + * @param string $value Attribute value + * + * @return bool + */ + public function isAllowedProtocol($value) + { + foreach ($this->scheme_whitelist as $protocol) { + if (strpos($value, $protocol) === 0) { + return true; + } + } + + return false; + } + + /** + * Detect if an url is blacklisted. + * + * @param string $resource Attribute value (URL) + * + * @return bool + */ + public function isBlacklistedMedia($resource) + { + foreach ($this->media_blacklist as $name) { + if (strpos($resource, $name) !== false) { + return true; + } + } + + return false; + } + + /** + * Convert the attribute list to html. + * + * @param array $attributes Attributes + * + * @return string + */ + public function toHtml(array $attributes) + { + $html = array(); + + foreach ($attributes as $attribute => $value) { + $html[] = sprintf('%s="%s"', $attribute, Filter::escape($value)); + } + + return implode(' ', $html); + } + + /** + * Set whitelisted tags and attributes for each tag. + * + * @param array $values List of tags: ['video' => ['src', 'cover'], 'img' => ['src']] + * + * @return Attribute + */ + public function setWhitelistedAttributes(array $values) + { + $this->attribute_whitelist = $values ?: $this->attribute_whitelist; + + return $this; + } + + /** + * Set scheme whitelist. + * + * @param array $values List of scheme: ['http://', 'ftp://'] + * + * @return Attribute + */ + public function setSchemeWhitelist(array $values) + { + $this->scheme_whitelist = $values ?: $this->scheme_whitelist; + + return $this; + } + + /** + * Set media attributes (used to load external resources). + * + * @param array $values List of values: ['src', 'href'] + * + * @return Attribute + */ + public function setMediaAttributes(array $values) + { + $this->media_attributes = $values ?: $this->media_attributes; + + return $this; + } + + /** + * Set blacklisted external resources. + * + * @param array $values List of tags: ['http://google.com/', '...'] + * + * @return Attribute + */ + public function setMediaBlacklist(array $values) + { + $this->media_blacklist = $values ?: $this->media_blacklist; + + return $this; + } + + /** + * Set mandatory attributes for whitelisted tags. + * + * @param array $values List of tags: ['img' => 'src'] + * + * @return Attribute + */ + public function setRequiredAttributes(array $values) + { + $this->required_attributes = $values ?: $this->required_attributes; + + return $this; + } + + /** + * Set attributes to automatically to specific tags. + * + * @param array $values List of tags: ['a' => 'target="_blank"'] + * + * @return Attribute + */ + public function setAttributeOverrides(array $values) + { + $this->add_attributes = $values ?: $this->add_attributes; + + return $this; + } + + /** + * Set attributes that must be an integer. + * + * @param array $values List of tags: ['width', 'height'] + * + * @return Attribute + */ + public function setIntegerAttributes(array $values) + { + $this->integer_attributes = $values ?: $this->integer_attributes; + + return $this; + } + + /** + * Set allowed iframe resources. + * + * @param array $values List of tags: ['http://www.youtube.com'] + * + * @return Attribute + */ + public function setIframeWhitelist(array $values) + { + $this->iframe_whitelist = $values ?: $this->iframe_whitelist; + + return $this; + } + + /** + * Set image proxy URL. + * + * The original image url will be urlencoded + * + * @param string $url Proxy URL + * + * @return Attribute + */ + public function setImageProxyUrl($url) + { + $this->image_proxy_url = $url ?: $this->image_proxy_url; + + return $this; + } + + /** + * Set image proxy callback. + * + * @param \Closure $callback + * + * @return Attribute + */ + public function setImageProxyCallback($callback) + { + $this->image_proxy_callback = $callback ?: $this->image_proxy_callback; + + return $this; + } + + /** + * Set image proxy protocol restriction. + * + * @param string $value + * + * @return Attribute + */ + public function setImageProxyProtocol($value) + { + $this->image_proxy_limit_protocol = $value ?: $this->image_proxy_limit_protocol; + + return $this; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Filter.php b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Filter.php new file mode 100644 index 00000000..bae2aff0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Filter.php @@ -0,0 +1,155 @@ +<?php + +namespace PicoFeed\Filter; + +/** + * Filter class. + * + * @author Frederic Guillot + */ +class Filter +{ + /** + * Get the Html filter instance. + * + * @static + * + * @param string $html HTML content + * @param string $website Site URL (used to build absolute URL) + * + * @return Html + */ + public static function html($html, $website) + { + $filter = new Html($html, $website); + + return $filter; + } + + /** + * Escape HTML content. + * + * @static + * + * @return string + */ + public static function escape($content) + { + return htmlspecialchars($content, ENT_QUOTES, 'UTF-8', false); + } + + /** + * Remove HTML tags. + * + * @param string $data Input data + * + * @return string + */ + public function removeHTMLTags($data) + { + return preg_replace('~<(?:!DOCTYPE|/?(?:html|head|body))[^>]*>\s*~i', '', $data); + } + + /** + * Remove the XML tag from a document. + * + * @static + * + * @param string $data Input data + * + * @return string + */ + public static function stripXmlTag($data) + { + if (strpos($data, '<?xml') !== false) { + $data = ltrim(substr($data, strpos($data, '?>') + 2)); + } + + do { + $pos = strpos($data, '<?xml-stylesheet '); + + if ($pos !== false) { + $data = ltrim(substr($data, strpos($data, '?>') + 2)); + } + } while ($pos !== false && $pos < 200); + + return $data; + } + + /** + * Strip head tag from the HTML content. + * + * @static + * + * @param string $data Input data + * + * @return string + */ + public static function stripHeadTags($data) + { + return preg_replace('@<head[^>]*?>.*?</head>@siu', '', $data); + } + + /** + * Trim whitespace from the begining, the end and inside a string and don't break utf-8 string. + * + * @static + * + * @param string $value Raw data + * + * @return string Normalized data + */ + public static function stripWhiteSpace($value) + { + $value = str_replace("\r", ' ', $value); + $value = str_replace("\t", ' ', $value); + $value = str_replace("\n", ' ', $value); + // $value = preg_replace('/\s+/', ' ', $value); <= break utf-8 + return trim($value); + } + + /** + * Fixes before XML parsing. + * + * @static + * + * @param string $data Raw data + * + * @return string Normalized data + */ + public static function normalizeData($data) + { + $entities = array( + '/(&#)(\d+);/m', // decimal encoded + '/(&#x)([a-f0-9]+);/mi', // hex encoded + ); + + // strip invalid XML 1.0 characters which are encoded as entities + $data = preg_replace_callback($entities, function ($matches) { + $code_point = $matches[2]; + + // convert hex entity to decimal + if (strtolower($matches[1]) === '&#x') { + $code_point = hexdec($code_point); + } + + $code_point = (int) $code_point; + + // replace invalid characters + if ($code_point < 9 + || ($code_point > 10 && $code_point < 13) + || ($code_point > 13 && $code_point < 32) + || ($code_point > 55295 && $code_point < 57344) + || ($code_point > 65533 && $code_point < 65536) + || $code_point > 1114111 + ) { + return ''; + }; + + return $matches[0]; + }, $data); + + // strip every utf-8 character than isn't in the range of valid XML 1.0 characters + return (string) preg_replace('/[^\x{0009}\x{000A}\x{000D}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]/u', '', $data); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Html.php b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Html.php new file mode 100644 index 00000000..0ccc192f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Html.php @@ -0,0 +1,243 @@ +<?php + +namespace PicoFeed\Filter; + +use PicoFeed\Config\Config; +use PicoFeed\Client\Url; +use PicoFeed\Scraper\RuleLoader; +use PicoFeed\Parser\XmlParser; + +/** + * HTML Filter class. + * + * @author Frederic Guillot + */ +class Html +{ + /** + * Config object. + * + * @var \PicoFeed\Config\Config + */ + private $config; + + /** + * Unfiltered XML data. + * + * @var string + */ + private $input = ''; + + /** + * Filtered XML data. + * + * @var string + */ + private $output = ''; + + /** + * List of empty tags. + * + * @var array + */ + private $empty_tags = array(); + + /** + * Empty flag. + * + * @var bool + */ + private $empty = true; + + /** + * Tag instance. + * + * @var \PicoFeed\Filter\Tag + */ + public $tag = ''; + + /** + * Attribute instance. + * + * @var \PicoFeed\Filter\Attribute + */ + public $attribute = ''; + + /** + * The website to filter. + * + * @var string + */ + private $website; + + /** + * Initialize the filter, all inputs data must be encoded in UTF-8 before. + * + * @param string $html HTML content + * @param string $website Site URL (used to build absolute URL) + */ + public function __construct($html, $website) + { + $this->config = new Config(); + $this->input = XmlParser::htmlToXml($html); + $this->output = ''; + $this->tag = new Tag($this->config); + $this->website = $website; + $this->attribute = new Attribute(new Url($website)); + } + + /** + * Set config object. + * + * @param \PicoFeed\Config\Config $config Config instance + * + * @return \PicoFeed\Filter\Html + */ + public function setConfig($config) + { + $this->config = $config; + + if ($this->config !== null) { + $this->attribute->setImageProxyCallback($this->config->getFilterImageProxyCallback()); + $this->attribute->setImageProxyUrl($this->config->getFilterImageProxyUrl()); + $this->attribute->setImageProxyProtocol($this->config->getFilterImageProxyProtocol()); + $this->attribute->setIframeWhitelist($this->config->getFilterIframeWhitelist(array())); + $this->attribute->setIntegerAttributes($this->config->getFilterIntegerAttributes(array())); + $this->attribute->setAttributeOverrides($this->config->getFilterAttributeOverrides(array())); + $this->attribute->setRequiredAttributes($this->config->getFilterRequiredAttributes(array())); + $this->attribute->setMediaBlacklist($this->config->getFilterMediaBlacklist(array())); + $this->attribute->setMediaAttributes($this->config->getFilterMediaAttributes(array())); + $this->attribute->setSchemeWhitelist($this->config->getFilterSchemeWhitelist(array())); + $this->attribute->setWhitelistedAttributes($this->config->getFilterWhitelistedTags(array())); + $this->tag->setWhitelistedTags(array_keys($this->config->getFilterWhitelistedTags(array()))); + } + + return $this; + } + + /** + * Run tags/attributes filtering. + * + * @return string + */ + public function execute() + { + $this->preFilter(); + + $parser = xml_parser_create(); + + xml_set_object($parser, $this); + xml_set_element_handler($parser, 'startTag', 'endTag'); + xml_set_character_data_handler($parser, 'dataTag'); + xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false); + xml_parse($parser, $this->input, true); + xml_parser_free($parser); + + $this->postFilter(); + + return $this->output; + } + + /** + * Called before XML parsing. + */ + public function preFilter() + { + $this->input = $this->tag->removeBlacklistedTags($this->input); + } + + /** + * Called after XML parsing. + */ + public function postFilter() + { + $this->output = $this->tag->removeEmptyTags($this->output); + $this->output = $this->filterRules($this->output); + $this->output = $this->tag->removeMultipleBreakTags($this->output); + $this->output = trim($this->output); + } + + /** + * Called after XML parsing. + * + * @param string $content the content that should be filtered + */ + public function filterRules($content) + { + // the constructor should require a config, then this if can be removed + if ($this->config === null) { + $config = new Config(); + } else { + $config = $this->config; + } + + $loader = new RuleLoader($config); + $rules = $loader->getRules($this->website); + + $url = new Url($this->website); + $sub_url = $url->getFullPath(); + + if (isset($rules['filter'])) { + foreach ($rules['filter'] as $pattern => $rule) { + if (preg_match($pattern, $sub_url)) { + foreach ($rule as $search => $replace) { + $content = preg_replace($search, $replace, $content); + } + } + } + } + + return $content; + } + + /** + * Parse opening tag. + * + * @param resource $parser XML parser + * @param string $tag Tag name + * @param array $attributes Tag attributes + */ + public function startTag($parser, $tag, array $attributes) + { + $this->empty = true; + + if ($this->tag->isAllowed($tag, $attributes)) { + $attributes = $this->attribute->filter($tag, $attributes); + + if ($this->attribute->hasRequiredAttributes($tag, $attributes)) { + $attributes = $this->attribute->addAttributes($tag, $attributes); + + $this->output .= $this->tag->openHtmlTag($tag, $this->attribute->toHtml($attributes)); + $this->empty = false; + } + } + + $this->empty_tags[] = $this->empty; + } + + /** + * Parse closing tag. + * + * @param resource $parser XML parser + * @param string $tag Tag name + */ + public function endTag($parser, $tag) + { + if (!array_pop($this->empty_tags) && $this->tag->isAllowedTag($tag)) { + $this->output .= $this->tag->closeHtmlTag($tag); + } + } + + /** + * Parse tag content. + * + * @param resource $parser XML parser + * @param string $content Tag content + */ + public function dataTag($parser, $content) + { + // Replace with normal space + $content = str_replace("\xc2\xa0", ' ', $content); + $this->output .= Filter::escape($content); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Tag.php b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Tag.php new file mode 100644 index 00000000..84a298a7 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Tag.php @@ -0,0 +1,218 @@ +<?php + +namespace PicoFeed\Filter; + +use DOMXPath; +use PicoFeed\Base; +use PicoFeed\Parser\XmlParser; + +/** + * Tag Filter class. + * + * @author Frederic Guillot + */ +class Tag extends Base +{ + /** + * Tags blacklist (Xpath expressions). + * + * @var array + */ + private $tag_blacklist = array( + '//script', + '//style', + ); + + /** + * Tags whitelist. + * + * @var array + */ + private $tag_whitelist = array( + 'audio', + 'video', + 'source', + 'dt', + 'dd', + 'dl', + 'table', + 'caption', + 'tr', + 'th', + 'td', + 'tbody', + 'thead', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'strong', + 'em', + 'code', + 'pre', + 'blockquote', + 'p', + 'ul', + 'li', + 'ol', + 'br', + 'del', + 'a', + 'img', + 'figure', + 'figcaption', + 'cite', + 'time', + 'abbr', + 'iframe', + 'q', + 'sup', + 'sub', + ); + + /** + * Check if the tag is allowed and is not a pixel tracker. + * + * @param string $tag Tag name + * @param array $attributes Attributes dictionary + * + * @return bool + */ + public function isAllowed($tag, array $attributes) + { + return $this->isAllowedTag($tag) && !$this->isPixelTracker($tag, $attributes); + } + + /** + * Return the HTML opening tag. + * + * @param string $tag Tag name + * @param string $attributes Attributes converted in html + * + * @return string + */ + public function openHtmlTag($tag, $attributes = '') + { + return '<'.$tag.(empty($attributes) ? '' : ' '.$attributes).($this->isSelfClosingTag($tag) ? '/>' : '>'); + } + + /** + * Return the HTML closing tag. + * + * @param string $tag Tag name + * + * @return string + */ + public function closeHtmlTag($tag) + { + return $this->isSelfClosingTag($tag) ? '' : '</'.$tag.'>'; + } + + /** + * Return true is the tag is self-closing. + * + * @param string $tag Tag name + * + * @return bool + */ + public function isSelfClosingTag($tag) + { + return $tag === 'br' || $tag === 'img'; + } + + /** + * Check if a tag is on the whitelist. + * + * @param string $tag Tag name + * + * @return bool + */ + public function isAllowedTag($tag) + { + return in_array($tag, array_merge( + $this->tag_whitelist, + array_keys($this->config->getFilterWhitelistedTags(array())) + )); + } + + /** + * Detect if an image tag is a pixel tracker. + * + * @param string $tag Tag name + * @param array $attributes Tag attributes + * + * @return bool + */ + public function isPixelTracker($tag, array $attributes) + { + return $tag === 'img' && + isset($attributes['height']) && isset($attributes['width']) && + $attributes['height'] == 1 && $attributes['width'] == 1; + } + + /** + * Remove script tags. + * + * @param string $data Input data + * + * @return string + */ + public function removeBlacklistedTags($data) + { + $dom = XmlParser::getDomDocument($data); + + if ($dom === false) { + return ''; + } + + $xpath = new DOMXpath($dom); + + $nodes = $xpath->query(implode(' | ', $this->tag_blacklist)); + + foreach ($nodes as $node) { + $node->parentNode->removeChild($node); + } + + return $dom->saveXML(); + } + + /** + * Remove empty tags. + * + * @param string $data Input data + * + * @return string + */ + public function removeEmptyTags($data) + { + return preg_replace('/<([^<\/>]*)>([\s]*?|(?R))<\/\1>/imsU', '', $data); + } + + /** + * Replace <br/><br/> by only one. + * + * @param string $data Input data + * + * @return string + */ + public function removeMultipleBreakTags($data) + { + return preg_replace("/(<br\s*\/?>\s*)+/", '<br/>', $data); + } + + /** + * Set whitelisted tags adn attributes for each tag. + * + * @param array $values List of tags: ['video' => ['src', 'cover'], 'img' => ['src']] + * + * @return Tag + */ + public function setWhitelistedTags(array $values) + { + $this->tag_whitelist = $values ?: $this->tag_whitelist; + + return $this; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Generator/ContentGeneratorInterface.php b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/ContentGeneratorInterface.php new file mode 100644 index 00000000..5c2f205c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/ContentGeneratorInterface.php @@ -0,0 +1,23 @@ +<?php + +namespace PicoFeed\Generator; + +use PicoFeed\Parser\Item; + +/** + * Content Generator Interface + * + * @package PicoFeed\Generator + * @author Frederic Guillot + */ +interface ContentGeneratorInterface +{ + /** + * Execute Content Generator + * + * @access public + * @param Item $item + * @return boolean + */ + public function execute(Item $item); +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Generator/FileContentGenerator.php b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/FileContentGenerator.php new file mode 100644 index 00000000..03f37e16 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/FileContentGenerator.php @@ -0,0 +1,36 @@ +<?php + +namespace PicoFeed\Generator; + +use PicoFeed\Base; +use PicoFeed\Parser\Item; + +/** + * File Content Generator + * + * @package PicoFeed\Generator + * @author Frederic Guillot + */ +class FileContentGenerator extends Base implements ContentGeneratorInterface +{ + private $extensions = array('pdf'); + + /** + * Execute Content Generator + * + * @access public + * @param Item $item + * @return boolean + */ + public function execute(Item $item) + { + foreach ($this->extensions as $extension) { + if (substr($item->getUrl(), - strlen($extension)) === $extension) { + $item->setContent('<a href="'.$item->getUrl().'" target="_blank">'.$item->getUrl().'</a>'); + return true; + } + } + + return false; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Generator/YoutubeContentGenerator.php b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/YoutubeContentGenerator.php new file mode 100644 index 00000000..198090d4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/YoutubeContentGenerator.php @@ -0,0 +1,67 @@ +<?php + +namespace PicoFeed\Generator; + +use PicoFeed\Base; +use PicoFeed\Parser\Item; + +/** + * Youtube Content Generator + * + * @package PicoFeed\Generator + * @author Frederic Guillot + */ +class YoutubeContentGenerator extends Base implements ContentGeneratorInterface +{ + /** + * Execute Content Generator + * + * @access public + * @param Item $item + * @return boolean + */ + public function execute(Item $item) + { + if ($item->hasNamespace('yt')) { + return $this->generateHtmlFromXml($item); + } + + return $this->generateHtmlFromUrl($item); + } + + /** + * Generate HTML + * + * @access public + * @param Item $item + * @return boolean + */ + private function generateHtmlFromXml(Item $item) + { + $videoId = $item->getTag('yt:videoId'); + + if (! empty($videoId)) { + $item->setContent('<iframe width="560" height="315" src="//www.youtube.com/embed/'.$videoId[0].'" frameborder="0"></iframe>'); + return true; + } + + return false; + } + + /** + * Generate HTML from item URL + * + * @access public + * @param Item $item + * @return bool + */ + public function generateHtmlFromUrl(Item $item) + { + if (preg_match('/youtube\.com\/watch\?v=(.*)/', $item->getUrl(), $matches)) { + $item->setContent('<iframe width="560" height="315" src="//www.youtube.com/embed/'.$matches[1].'" frameborder="0"></iframe>'); + return true; + } + + return false; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Logging/Logger.php b/vendor/miniflux/picofeed/lib/PicoFeed/Logging/Logger.php new file mode 100644 index 00000000..caec463f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Logging/Logger.php @@ -0,0 +1,114 @@ +<?php + +namespace PicoFeed\Logging; + +use DateTime; +use DateTimeZone; + +/** + * Logging class. + * + * @author Frederic Guillot + */ +class Logger +{ + /** + * List of messages. + * + * @static + * + * @var array + */ + private static $messages = array(); + + /** + * Default timezone. + * + * @static + * + * @var string + */ + private static $timezone = 'UTC'; + + /** + * Enable or disable logging. + * + * @static + * + * @var bool + */ + public static $enable = false; + + /** + * Enable logging. + * + * @static + */ + public static function enable() + { + self::$enable = true; + } + + /** + * Add a new message. + * + * @static + * + * @param string $message Message + */ + public static function setMessage($message) + { + if (self::$enable) { + $date = new DateTime('now', new DateTimeZone(self::$timezone)); + self::$messages[] = '['.$date->format('Y-m-d H:i:s').'] '.$message; + } + } + + /** + * Get all logged messages. + * + * @static + * + * @return array + */ + public static function getMessages() + { + return self::$messages; + } + + /** + * Remove all logged messages. + * + * @static + */ + public static function deleteMessages() + { + self::$messages = array(); + } + + /** + * Set a different timezone. + * + * @static + * + * @see http://php.net/manual/en/timezones.php + * + * @param string $timezone Timezone + */ + public static function setTimeZone($timezone) + { + self::$timezone = $timezone ?: self::$timezone; + } + + /** + * Get all messages serialized into a string. + * + * @static + * + * @return string + */ + public static function toString() + { + return implode(PHP_EOL, self::$messages).PHP_EOL; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Atom.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Atom.php new file mode 100644 index 00000000..1c570a08 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Atom.php @@ -0,0 +1,382 @@ +<?php + +namespace PicoFeed\Parser; + +use SimpleXMLElement; +use PicoFeed\Filter\Filter; +use PicoFeed\Client\Url; + +/** + * Atom parser. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +class Atom extends Parser +{ + /** + * Supported namespaces. + */ + protected $namespaces = array( + 'atom' => 'http://www.w3.org/2005/Atom', + ); + + /** + * Get the path to the items XML tree. + * + * @param SimpleXMLElement $xml Feed xml + * + * @return SimpleXMLElement + */ + public function getItemsTree(SimpleXMLElement $xml) + { + return XmlParser::getXPathResult($xml, 'atom:entry', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'entry'); + } + + /** + * Find the feed url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedUrl(SimpleXMLElement $xml, Feed $feed) + { + $feed->setFeedUrl($this->getUrl($xml, 'self')); + } + + /** + * Find the site url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findSiteUrl(SimpleXMLElement $xml, Feed $feed) + { + $feed->setSiteUrl($this->getUrl($xml, 'alternate', true)); + } + + /** + * Find the feed description. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedDescription(SimpleXMLElement $xml, Feed $feed) + { + $description = XmlParser::getXPathResult($xml, 'atom:subtitle', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'subtitle'); + + $feed->setDescription(XmlParser::getValue($description)); + } + + /** + * Find the feed logo url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedLogo(SimpleXMLElement $xml, Feed $feed) + { + $logo = XmlParser::getXPathResult($xml, 'atom:logo', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'logo'); + + $feed->setLogo(XmlParser::getValue($logo)); + } + + /** + * Find the feed icon. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedIcon(SimpleXMLElement $xml, Feed $feed) + { + $icon = XmlParser::getXPathResult($xml, 'atom:icon', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'icon'); + + $feed->setIcon(XmlParser::getValue($icon)); + } + + /** + * Find the feed title. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedTitle(SimpleXMLElement $xml, Feed $feed) + { + $title = XmlParser::getXPathResult($xml, 'atom:title', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'title'); + + $feed->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($title)) ?: $feed->getSiteUrl()); + } + + /** + * Find the feed language. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedLanguage(SimpleXMLElement $xml, Feed $feed) + { + $language = XmlParser::getXPathResult($xml, '*[not(self::atom:entry)]/@xml:lang', $this->namespaces) + ?: XmlParser::getXPathResult($xml, '@xml:lang'); + + $feed->setLanguage(XmlParser::getValue($language)); + } + + /** + * Find the feed id. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedId(SimpleXMLElement $xml, Feed $feed) + { + $id = XmlParser::getXPathResult($xml, 'atom:id', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'id'); + + $feed->setId(XmlParser::getValue($id)); + } + + /** + * Find the feed date. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedDate(SimpleXMLElement $xml, Feed $feed) + { + $updated = XmlParser::getXPathResult($xml, 'atom:updated', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'updated'); + + $feed->setDate($this->getDateParser()->getDateTime(XmlParser::getValue($updated))); + } + + /** + * Find the item published date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemPublishedDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $date = XmlParser::getXPathResult($entry, 'atom:published', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'published'); + + $item->setPublishedDate(!empty($date) ? $this->getDateParser()->getDateTime((string) current($date)) : null); + } + + /** + * Find the item updated date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemUpdatedDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $date = XmlParser::getXPathResult($entry, 'atom:updated', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'updated'); + + $item->setUpdatedDate(!empty($date) ? $this->getDateParser()->getDateTime((string) current($date)) : null); + } + + /** + * Find the item title. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + */ + public function findItemTitle(SimpleXMLElement $entry, Item $item) + { + $title = XmlParser::getXPathResult($entry, 'atom:title', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'title'); + + $item->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($title)) ?: $item->getUrl()); + } + + /** + * Find the item author. + * + * @param SimpleXMLElement $xml Feed + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemAuthor(SimpleXMLElement $xml, SimpleXMLElement $entry, Item $item) + { + $author = XmlParser::getXPathResult($entry, 'atom:author/atom:name', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'author/name') + ?: XmlParser::getXPathResult($xml, 'atom:author/atom:name', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'author/name'); + + $item->setAuthor(XmlParser::getValue($author)); + } + + /** + * Find the item content. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemContent(SimpleXMLElement $entry, Item $item) + { + $item->setContent($this->getContent($entry)); + } + + /** + * Find the item URL. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemUrl(SimpleXMLElement $entry, Item $item) + { + $item->setUrl($this->getUrl($entry, 'alternate', true)); + } + + /** + * Genereate the item id. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemId(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $id = XmlParser::getXPathResult($entry, 'atom:id', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'id'); + + if (!empty($id)) { + $item->setId($this->generateId(XmlParser::getValue($id))); + } else { + $item->setId($this->generateId( + $item->getTitle(), $item->getUrl(), $item->getContent() + )); + } + } + + /** + * Find the item enclosure. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemEnclosure(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $enclosure = $this->findLink($entry, 'enclosure'); + + if ($enclosure) { + $item->setEnclosureUrl(Url::resolve((string) $enclosure['href'], $feed->getSiteUrl())); + $item->setEnclosureType((string) $enclosure['type']); + } + } + + /** + * Find the item language. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemLanguage(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $language = XmlParser::getXPathResult($entry, './/@xml:lang'); + $item->setLanguage(XmlParser::getValue($language) ?: $feed->getLanguage()); + } + + /** + * Find the item categories. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemCategories(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $categories = XmlParser::getXPathResult($entry, 'atom:category/@term', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'category/@term'); + $item->setCategoriesFromXml($categories); + } + + /** + * Get the URL from a link tag. + * + * @param SimpleXMLElement $xml XML tag + * @param string $rel Link relationship: alternate, enclosure, related, self, via + * + * @return string + */ + private function getUrl(SimpleXMLElement $xml, $rel, $fallback = false) + { + $link = $this->findLink($xml, $rel); + + if ($link) { + return (string) $link['href']; + } + + if ($fallback) { + $link = $this->findLink($xml, ''); + return $link ? (string) $link['href'] : ''; + } + + return ''; + } + + /** + * Get a link tag that match a relationship. + * + * @param SimpleXMLElement $xml XML tag + * @param string $rel Link relationship: alternate, enclosure, related, self, via + * + * @return SimpleXMLElement|null + */ + private function findLink(SimpleXMLElement $xml, $rel) + { + $links = XmlParser::getXPathResult($xml, 'atom:link', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'link'); + + foreach ($links as $link) { + if ($rel === (string) $link['rel']) { + return $link; + } + } + + return null; + } + + /** + * Get the entry content. + * + * @param SimpleXMLElement $entry XML Entry + * + * @return string + */ + private function getContent(SimpleXMLElement $entry) + { + $content = current( + XmlParser::getXPathResult($entry, 'atom:content', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'content') + ); + + if (!empty($content) && count($content->children())) { + $xml_string = ''; + + foreach ($content->children() as $child) { + $xml_string .= $child->asXML(); + } + + return $xml_string; + } elseif (trim((string) $content) !== '') { + return (string) $content; + } + + $summary = XmlParser::getXPathResult($entry, 'atom:summary', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'summary'); + + return (string) current($summary); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/DateParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/DateParser.php new file mode 100644 index 00000000..0e5b80e3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/DateParser.php @@ -0,0 +1,128 @@ +<?php + +namespace PicoFeed\Parser; + +use DateTime; +use DateTimeZone; +use PicoFeed\Base; + +/** + * Date Parser. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +class DateParser extends Base +{ + /** + * Timezone used to parse feed dates. + * + * @access private + * @var string + */ + private $timezone = 'UTC'; + + /** + * Supported formats [ 'format' => length ]. + * + * @var array + */ + public $formats = array( + DATE_ATOM => null, + DATE_RSS => null, + DATE_COOKIE => null, + DATE_ISO8601 => null, + DATE_RFC822 => null, + DATE_RFC850 => null, + DATE_RFC1036 => null, + DATE_RFC1123 => null, + DATE_RFC2822 => null, + DATE_RFC3339 => null, + 'l, d M Y H:i:s' => null, + 'D, d M Y H:i:s' => 25, + 'D, d M Y h:i:s' => 25, + 'D M d Y H:i:s' => 24, + 'j M Y H:i:s' => 20, + 'Y-m-d H:i:s' => 19, + 'Y-m-d\TH:i:s' => 19, + 'd/m/Y H:i:s' => 19, + 'D, d M Y' => 16, + 'Y-m-d' => 10, + 'd-m-Y' => 10, + 'm-d-Y' => 10, + 'd.m.Y' => 10, + 'm.d.Y' => 10, + 'd/m/Y' => 10, + 'm/d/Y' => 10, + ); + + /** + * Try to parse all date format for broken feeds. + * + * @param string $value Original date format + * + * @return DateTime + */ + public function getDateTime($value) + { + $value = trim($value); + + foreach ($this->formats as $format => $length) { + $truncated_value = $value; + if ($length !== null) { + $truncated_value = substr($truncated_value, 0, $length); + } + + $date = $this->getValidDate($format, $truncated_value); + if ($date !== false) { + return $date; + } + } + + return $this->getCurrentDateTime(); + } + + /** + * Get a valid date from a given format. + * + * @param string $format Date format + * @param string $value Original date value + * + * @return DateTime|bool + */ + public function getValidDate($format, $value) + { + $date = DateTime::createFromFormat($format, $value, $this->getTimeZone()); + + if ($date !== false) { + $errors = DateTime::getLastErrors(); + + if ($errors['error_count'] === 0 && $errors['warning_count'] === 0) { + return $date; + } + } + + return false; + } + + /** + * Get the current datetime. + * + * @return DateTime + */ + public function getCurrentDateTime() + { + return new DateTime('now', $this->getTimeZone()); + } + + /** + * Get DateTimeZone instance + * + * @access public + * @return DateTimeZone + */ + public function getTimeZone() + { + return new DateTimeZone($this->config->getTimezone() ?: $this->timezone); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Feed.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Feed.php new file mode 100644 index 00000000..a56e71c3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Feed.php @@ -0,0 +1,315 @@ +<?php + +namespace PicoFeed\Parser; + +/** + * Feed. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +class Feed +{ + /** + * Feed items. + * + * @var Item[] + */ + public $items = array(); + + /** + * Feed id. + * + * @var string + */ + public $id = ''; + + /** + * Feed title. + * + * @var string + */ + public $title = ''; + + /** + * Feed description. + * + * @var string + */ + public $description = ''; + + /** + * Feed url. + * + * @var string + */ + public $feedUrl = ''; + + /** + * Site url. + * + * @var string + */ + public $siteUrl = ''; + + /** + * Feed date. + * + * @var \DateTime + */ + public $date = null; + + /** + * Feed language. + * + * @var string + */ + public $language = ''; + + /** + * Feed logo URL. + * + * @var string + */ + public $logo = ''; + + /** + * Feed icon URL. + * + * @var string + */ + public $icon = ''; + + /** + * Return feed information. + */ + public function __toString() + { + $output = ''; + + foreach (array('id', 'title', 'feedUrl', 'siteUrl', 'language', 'description', 'logo') as $property) { + $output .= 'Feed::'.$property.' = '.$this->$property.PHP_EOL; + } + + $output .= 'Feed::date = '.$this->date->format(DATE_RFC822).PHP_EOL; + $output .= 'Feed::isRTL() = '.($this->isRTL() ? 'true' : 'false').PHP_EOL; + $output .= 'Feed::items = '.count($this->items).' items'.PHP_EOL; + + foreach ($this->items as $item) { + $output .= '----'.PHP_EOL; + $output .= $item; + } + + return $output; + } + + /** + * Get title. + */ + public function getTitle() + { + return $this->title; + } + + /** + * Get description. + */ + public function getDescription() + { + return $this->description; + } + + /** + * Get the logo url. + */ + public function getLogo() + { + return $this->logo; + } + + /** + * Get the icon url. + */ + public function getIcon() + { + return $this->icon; + } + + /** + * Get feed url. + */ + public function getFeedUrl() + { + return $this->feedUrl; + } + + /** + * Get site url. + */ + public function getSiteUrl() + { + return $this->siteUrl; + } + + /** + * Get date. + */ + public function getDate() + { + return $this->date; + } + + /** + * Get language. + */ + public function getLanguage() + { + return $this->language; + } + + /** + * Get id. + */ + public function getId() + { + return $this->id; + } + + /** + * Get feed items. + */ + public function getItems() + { + return $this->items; + } + + /** + * Return true if the feed is "Right to Left". + * + * @return bool + */ + public function isRTL() + { + return Parser::isLanguageRTL($this->language); + } + + /** + * Set feed items. + * + * @param Item[] $items + * @return Feed + */ + public function setItems(array $items) + { + $this->items = $items; + return $this; + } + + /** + * Set feed id. + * + * @param string $id + * @return Feed + */ + public function setId($id) + { + $this->id = $id; + return $this; + } + + /** + * Set feed title. + * + * @param string $title + * @return Feed + */ + public function setTitle($title) + { + $this->title = $title; + return $this; + } + + /** + * Set feed description. + * + * @param string $description + * @return Feed + */ + public function setDescription($description) + { + $this->description = $description; + return $this; + } + + /** + * Set feed url. + * + * @param string $feedUrl + * @return Feed + */ + public function setFeedUrl($feedUrl) + { + $this->feedUrl = $feedUrl; + return $this; + } + + /** + * Set feed website url. + * + * @param string $siteUrl + * @return Feed + */ + public function setSiteUrl($siteUrl) + { + $this->siteUrl = $siteUrl; + return $this; + } + + /** + * Set feed date. + * + * @param \DateTime $date + * @return Feed + */ + public function setDate($date) + { + $this->date = $date; + return $this; + } + + /** + * Set feed language. + * + * @param string $language + * @return Feed + */ + public function setLanguage($language) + { + $this->language = $language; + return $this; + } + + /** + * Set feed logo. + * + * @param string $logo + * @return Feed + */ + public function setLogo($logo) + { + $this->logo = $logo; + return $this; + } + + /** + * Set feed icon. + * + * @param string $icon + * @return Feed + */ + public function setIcon($icon) + { + $this->icon = $icon; + return $this; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Item.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Item.php new file mode 100644 index 00000000..f9581941 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Item.php @@ -0,0 +1,534 @@ +<?php + +namespace PicoFeed\Parser; + +/** + * Feed Item. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +class Item +{ + /** + * List of known RTL languages. + * + * @var string[] + */ + public $rtl = array( + 'ar', // Arabic (ar-**) + 'fa', // Farsi (fa-**) + 'ur', // Urdu (ur-**) + 'ps', // Pashtu (ps-**) + 'syr', // Syriac (syr-**) + 'dv', // Divehi (dv-**) + 'he', // Hebrew (he-**) + 'yi', // Yiddish (yi-**) + ); + + /** + * Item id. + * + * @var string + */ + public $id = ''; + + /** + * Item title. + * + * @var string + */ + public $title = ''; + + /** + * Item url. + * + * @var string + */ + public $url = ''; + + /** + * Item author. + * + * @var string + */ + public $author = ''; + + /** + * Item date. + * + * @var \DateTime + */ + public $date = null; + + /** + * Item published date. + * + * @var \DateTime + */ + public $publishedDate = null; + + /** + * Item updated date. + * + * @var \DateTime + */ + public $updatedDate = null; + + /** + * Item content. + * + * @var string + */ + public $content = ''; + + /** + * Item enclosure url. + * + * @var string + */ + public $enclosureUrl = ''; + + /** + * Item enclusure type. + * + * @var string + */ + public $enclosureType = ''; + + /** + * Item language. + * + * @var string + */ + public $language = ''; + + /** + * Item categories. + * + * @var array + */ + public $categories = array(); + + /** + * Raw XML. + * + * @var \SimpleXMLElement + */ + public $xml; + + /** + * List of namespaces. + * + * @var array + */ + public $namespaces = array(); + + /** + * Check if a XML namespace exists + * + * @access public + * @param string $namespace + * @return bool + */ + public function hasNamespace($namespace) + { + return array_key_exists($namespace, $this->namespaces); + } + + /** + * Get specific XML tag or attribute value. + * + * @param string $tag Tag name (examples: guid, media:content) + * @param string $attribute Tag attribute + * + * @return array|false Tag values or error + */ + public function getTag($tag, $attribute = '') + { + if ($attribute !== '') { + $attribute = '/@'.$attribute; + } + + $query = './/'.$tag.$attribute; + $elements = XmlParser::getXPathResult($this->xml, $query, $this->namespaces); + + if ($elements === false) { // xPath error + return false; + } + + return array_map(function ($element) { return (string) $element;}, $elements); + } + + /** + * Return item information. + * + * @return string + */ + public function __toString() + { + $output = ''; + + foreach (array('id', 'title', 'url', 'language', 'author', 'enclosureUrl', 'enclosureType') as $property) { + $output .= 'Item::'.$property.' = '.$this->$property.PHP_EOL; + } + + $publishedDate = $this->publishedDate != null ? $this->publishedDate->format(DATE_RFC822) : null; + $updatedDate = $this->updatedDate != null ? $this->updatedDate->format(DATE_RFC822) : null; + + $categoryString = $this->categories != null ? implode(',', $this->categories) : null; + + $output .= 'Item::date = '.$this->date->format(DATE_RFC822).PHP_EOL; + $output .= 'Item::publishedDate = '.$publishedDate.PHP_EOL; + $output .= 'Item::updatedDate = '.$updatedDate.PHP_EOL; + $output .= 'Item::isRTL() = '.($this->isRTL() ? 'true' : 'false').PHP_EOL; + $output .= 'Item::categories = ['.$categoryString.']'.PHP_EOL; + $output .= 'Item::content = '.strlen($this->content).' bytes'.PHP_EOL; + + return $output; + } + + /** + * Get title. + * + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * Get URL + * + * @access public + * @return string + */ + public function getUrl() + { + return $this->url; + } + + /** + * Set URL + * + * @access public + * @param string $url + * @return Item + */ + public function setUrl($url) + { + $this->url = $url; + return $this; + } + + /** + * Get id. + * + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * Get date. + * + * @return \DateTime + */ + public function getDate() + { + return $this->date; + } + + /** + * Get published date. + * + * @return \DateTime + */ + public function getPublishedDate() + { + return $this->publishedDate; + } + + /** + * Get updated date. + * + * @return \DateTime + */ + public function getUpdatedDate() + { + return $this->updatedDate; + } + + /** + * Get content. + * + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * Set content + * + * @access public + * @param string $value + * @return Item + */ + public function setContent($value) + { + $this->content = $value; + return $this; + } + + /** + * Get enclosure url. + * + * @return string + */ + public function getEnclosureUrl() + { + return $this->enclosureUrl; + } + + /** + * Get enclosure type. + * + * @return string + */ + public function getEnclosureType() + { + return $this->enclosureType; + } + + /** + * Get language. + * + * @return string + */ + public function getLanguage() + { + return $this->language; + } + + /** + * Get categories. + * + * @return string + */ + public function getCategories() + { + return $this->categories; + } + + /** + * Get author. + * + * @return string + */ + public function getAuthor() + { + return $this->author; + } + + /** + * Return true if the item is "Right to Left". + * + * @return bool + */ + public function isRTL() + { + return Parser::isLanguageRTL($this->language); + } + + /** + * Set item id. + * + * @param string $id + * @return Item + */ + public function setId($id) + { + $this->id = $id; + return $this; + } + + /** + * Set item title. + * + * @param string $title + * @return Item + */ + public function setTitle($title) + { + $this->title = $title; + return $this; + } + + /** + * Set author. + * + * @param string $author + * @return Item + */ + public function setAuthor($author) + { + $this->author = $author; + return $this; + } + + /** + * Set item date. + * + * @param \DateTime $date + * @return Item + */ + public function setDate($date) + { + $this->date = $date; + return $this; + } + + /** + * Set item published date. + * + * @param \DateTime $publishedDate + * @return Item + */ + public function setPublishedDate($publishedDate) + { + $this->publishedDate = $publishedDate; + return $this; + } + + /** + * Set item updated date. + * + * @param \DateTime $updatedDate + * @return Item + */ + public function setUpdatedDate($updatedDate) + { + $this->updatedDate = $updatedDate; + return $this; + } + + /** + * Set enclosure url. + * + * @param string $enclosureUrl + * @return Item + */ + public function setEnclosureUrl($enclosureUrl) + { + $this->enclosureUrl = $enclosureUrl; + return $this; + } + + /** + * Set enclosure type. + * + * @param string $enclosureType + * @return Item + */ + public function setEnclosureType($enclosureType) + { + $this->enclosureType = $enclosureType; + return $this; + } + + /** + * Set item language. + * + * @param string $language + * @return Item + */ + public function setLanguage($language) + { + $this->language = $language; + return $this; + } + + /** + * Set item categories. + * + * @param array $categories + * @return Item + */ + public function setCategories($categories) + { + $this->categories = $categories; + return $this; + } + + /** + * Set item categories from xml. + * + * @param SimpleXMLElement[] $categories + * @return Item + */ + public function setCategoriesFromXml($categories) + { + if ($categories !== false) { + $this->setCategories( + array_map( + function ($element) { + return trim((string) $element); + }, + $categories + ) + ); + } else { + $categories = array(); + } + return $this; + } + + /** + * Set raw XML. + * + * @param \SimpleXMLElement $xml + * @return Item + */ + public function setXml($xml) + { + $this->xml = $xml; + return $this; + } + + /** + * Get raw XML. + * + * @return \SimpleXMLElement + */ + public function getXml() + { + return $this->xml; + } + + /** + * Set XML namespaces. + * + * @param array $namespaces + * @return Item + */ + public function setNamespaces($namespaces) + { + $this->namespaces = $namespaces; + return $this; + } + + /** + * Get XML namespaces. + * + * @return array + */ + public function getNamespaces() + { + return $this->namespaces; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php new file mode 100644 index 00000000..efaf0ff1 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php @@ -0,0 +1,13 @@ +<?php + +namespace PicoFeed\Parser; + +/** + * MalformedXmlException Exception. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +class MalformedXmlException extends ParserException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Parser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Parser.php new file mode 100644 index 00000000..3ee99f59 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Parser.php @@ -0,0 +1,404 @@ +<?php + +namespace PicoFeed\Parser; + +use PicoFeed\Processor\ContentFilterProcessor; +use PicoFeed\Processor\ContentGeneratorProcessor; +use PicoFeed\Processor\ItemPostProcessor; +use PicoFeed\Processor\ScraperProcessor; +use SimpleXMLElement; +use PicoFeed\Client\Url; +use PicoFeed\Encoding\Encoding; +use PicoFeed\Filter\Filter; +use PicoFeed\Logging\Logger; + +/** + * Base parser class. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +abstract class Parser implements ParserInterface +{ + /** + * Config object. + * + * @var \PicoFeed\Config\Config + */ + private $config; + + /** + * DateParser object. + * + * @var \PicoFeed\Parser\DateParser + */ + private $dateParser; + + /** + * Hash algorithm used to generate item id, any value supported by PHP, see hash_algos(). + * + * @var string + */ + private $hash_algo = 'sha256'; + + /** + * Feed content (XML data). + * + * @var string + */ + protected $content = ''; + + /** + * Fallback url. + * + * @var string + */ + protected $fallback_url = ''; + + /** + * XML namespaces supported by parser. + * + * @var array + */ + protected $namespaces = array(); + + /** + * XML namespaces used in document. + * + * @var array + */ + protected $used_namespaces = array(); + + /** + * Item Post Processor instance + * + * @access private + * @var ItemPostProcessor + */ + private $itemPostProcessor; + + /** + * Constructor. + * + * @param string $content Feed content + * @param string $http_encoding HTTP encoding (headers) + * @param string $fallback_url Fallback url when the feed provide relative or broken url + */ + public function __construct($content, $http_encoding = '', $fallback_url = '') + { + $this->fallback_url = $fallback_url; + $xml_encoding = XmlParser::getEncodingFromXmlTag($content); + + // Strip XML tag to avoid multiple encoding/decoding in the next XML processing + $this->content = Filter::stripXmlTag($content); + + // Encode everything in UTF-8 + Logger::setMessage(get_called_class().': HTTP Encoding "'.$http_encoding.'" ; XML Encoding "'.$xml_encoding.'"'); + $this->content = Encoding::convert($this->content, $xml_encoding ?: $http_encoding); + + $this->itemPostProcessor = new ItemPostProcessor($this->config); + $this->itemPostProcessor->register(new ContentGeneratorProcessor($this->config)); + $this->itemPostProcessor->register(new ContentFilterProcessor($this->config)); + } + + /** + * Parse the document. + * + * @return \PicoFeed\Parser\Feed + */ + public function execute() + { + Logger::setMessage(get_called_class().': begin parsing'); + + $xml = XmlParser::getSimpleXml($this->content); + + if ($xml === false) { + Logger::setMessage(get_called_class().': Applying XML workarounds'); + $this->content = Filter::normalizeData($this->content); + $xml = XmlParser::getSimpleXml($this->content); + + if ($xml === false) { + Logger::setMessage(get_called_class().': XML parsing error'); + Logger::setMessage(XmlParser::getErrors()); + throw new MalformedXmlException('XML parsing error'); + } + } + + $this->used_namespaces = $xml->getNamespaces(true); + $xml = $this->registerSupportedNamespaces($xml); + + $feed = new Feed(); + + $this->findFeedUrl($xml, $feed); + $this->checkFeedUrl($feed); + + $this->findSiteUrl($xml, $feed); + $this->checkSiteUrl($feed); + + $this->findFeedTitle($xml, $feed); + $this->findFeedDescription($xml, $feed); + $this->findFeedLanguage($xml, $feed); + $this->findFeedId($xml, $feed); + $this->findFeedDate($xml, $feed); + $this->findFeedLogo($xml, $feed); + $this->findFeedIcon($xml, $feed); + + foreach ($this->getItemsTree($xml) as $entry) { + $entry = $this->registerSupportedNamespaces($entry); + + $item = new Item(); + $item->xml = $entry; + $item->namespaces = $this->used_namespaces; + + $this->findItemAuthor($xml, $entry, $item); + + $this->findItemUrl($entry, $item); + $this->checkItemUrl($feed, $item); + + $this->findItemTitle($entry, $item); + $this->findItemContent($entry, $item); + + // Id generation can use the item url/title/content (order is important) + $this->findItemId($entry, $item, $feed); + $this->findItemDate($entry, $item, $feed); + $this->findItemEnclosure($entry, $item, $feed); + $this->findItemLanguage($entry, $item, $feed); + $this->findItemCategories($entry, $item, $feed); + + $this->itemPostProcessor->execute($feed, $item); + $feed->items[] = $item; + } + + Logger::setMessage(get_called_class().PHP_EOL.$feed); + + return $feed; + } + + /** + * Check if the feed url is correct. + * + * @param Feed $feed Feed object + */ + public function checkFeedUrl(Feed $feed) + { + if ($feed->getFeedUrl() === '') { + $feed->feedUrl = $this->fallback_url; + } else { + $feed->feedUrl = Url::resolve($feed->getFeedUrl(), $this->fallback_url); + } + } + + /** + * Check if the site url is correct. + * + * @param Feed $feed Feed object + */ + public function checkSiteUrl(Feed $feed) + { + if ($feed->getSiteUrl() === '') { + $feed->siteUrl = Url::base($feed->getFeedUrl()); + } else { + $feed->siteUrl = Url::resolve($feed->getSiteUrl(), $this->fallback_url); + } + } + + /** + * Check if the item url is correct. + * + * @param Feed $feed Feed object + * @param Item $item Item object + */ + public function checkItemUrl(Feed $feed, Item $item) + { + $item->url = Url::resolve($item->getUrl(), $feed->getSiteUrl()); + } + + /** + * Find the item date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $this->findItemPublishedDate($entry, $item, $feed); + $this->findItemUpdatedDate($entry, $item, $feed); + + if ($item->getPublishedDate() === null) { + // Use the updated date if available, otherwise use the feed date + $item->setPublishedDate($item->getUpdatedDate() ?: $feed->getDate()); + } + + if ($item->getUpdatedDate() === null) { + // Use the published date as fallback + $item->setUpdatedDate($item->getPublishedDate()); + } + + // Use the most recent of published and updated dates + $item->setDate(max($item->getPublishedDate(), $item->getUpdatedDate())); + } + + /** + * Get Item Post Processor instance + * + * @access public + * @return ItemPostProcessor + */ + public function getItemPostProcessor() + { + return $this->itemPostProcessor; + } + + /** + * Get DateParser instance + * + * @access public + * @return DateParser + */ + public function getDateParser() + { + if ($this->dateParser === null) { + $this->dateParser = new DateParser($this->config); + } + + return $this->dateParser; + } + + /** + * Generate a unique id for an entry (hash all arguments). + * + * @return string + */ + public function generateId() + { + return hash($this->hash_algo, implode(func_get_args())); + } + + /** + * Return true if the given language is "Right to Left". + * + * @static + * + * @param string $language Language: fr-FR, en-US + * + * @return bool + */ + public static function isLanguageRTL($language) + { + $language = strtolower($language); + + $rtl_languages = array( + 'ar', // Arabic (ar-**) + 'fa', // Farsi (fa-**) + 'ur', // Urdu (ur-**) + 'ps', // Pashtu (ps-**) + 'syr', // Syriac (syr-**) + 'dv', // Divehi (dv-**) + 'he', // Hebrew (he-**) + 'yi', // Yiddish (yi-**) + ); + + foreach ($rtl_languages as $prefix) { + if (strpos($language, $prefix) === 0) { + return true; + } + } + + return false; + } + + /** + * Set Hash algorithm used for id generation. + * + * @param string $algo Algorithm name + * @return \PicoFeed\Parser\Parser + */ + public function setHashAlgo($algo) + { + $this->hash_algo = $algo ?: $this->hash_algo; + return $this; + } + + /** + * Set config object. + * + * @param \PicoFeed\Config\Config $config Config instance + * + * @return \PicoFeed\Parser\Parser + */ + public function setConfig($config) + { + $this->config = $config; + $this->itemPostProcessor->setConfig($config); + return $this; + } + + /** + * Enable the content grabber. + * + * @return \PicoFeed\Parser\Parser + */ + public function disableContentFiltering() + { + $this->itemPostProcessor->unregister('PicoFeed\Processor\ContentFilterProcessor'); + return $this; + } + + /** + * Enable the content grabber. + * + * @param bool $needsRuleFile true if only pages with rule files should be + * scraped + * @param null|\Closure $scraperCallback Callback function that gets called for each + * scraper execution + * + * @return \PicoFeed\Parser\Parser + */ + public function enableContentGrabber($needsRuleFile = false, $scraperCallback = null) + { + $processor = new ScraperProcessor($this->config); + + if ($needsRuleFile) { + $processor->getScraper()->disableCandidateParser(); + } + + if ($scraperCallback !== null) { + $processor->setExecutionCallback($scraperCallback); + } + + $this->itemPostProcessor->register($processor); + return $this; + } + + /** + * Set ignored URLs for the content grabber. + * + * @param array $urls URLs + * + * @return \PicoFeed\Parser\Parser + */ + public function setGrabberIgnoreUrls(array $urls) + { + $this->itemPostProcessor->getProcessor('PicoFeed\Processor\ScraperProcessor')->ignoreUrls($urls); + return $this; + } + + /** + * Register all supported namespaces to be used within an xpath query. + * + * @param SimpleXMLElement $xml Feed xml + * + * @return SimpleXMLElement + */ + public function registerSupportedNamespaces(SimpleXMLElement $xml) + { + foreach ($this->namespaces as $prefix => $ns) { + $xml->registerXPathNamespace($prefix, $ns); + } + + return $xml; + } + + +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserException.php new file mode 100644 index 00000000..b5fbb699 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserException.php @@ -0,0 +1,15 @@ +<?php + +namespace PicoFeed\Parser; + +use PicoFeed\PicoFeedException; + +/** + * ParserException Exception. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +abstract class ParserException extends PicoFeedException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserInterface.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserInterface.php new file mode 100644 index 00000000..8d6be085 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserInterface.php @@ -0,0 +1,182 @@ +<?php + +namespace PicoFeed\Parser; + +use SimpleXMLElement; + +/** + * Interface ParserInterface + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +interface ParserInterface +{ + /** + * Find the feed url. + * + * @param SimpleXMLElement $xml Feed xml + * @param Feed $feed Feed object + */ + public function findFeedUrl(SimpleXMLElement $xml, Feed $feed); + + /** + * Find the site url. + * + * @param SimpleXMLElement $xml Feed xml + * @param Feed $feed Feed object + */ + public function findSiteUrl(SimpleXMLElement $xml, Feed $feed); + + /** + * Find the feed title. + * + * @param SimpleXMLElement $xml Feed xml + * @param Feed $feed Feed object + */ + public function findFeedTitle(SimpleXMLElement $xml, Feed $feed); + + /** + * Find the feed description. + * + * @param SimpleXMLElement $xml Feed xml + * @param Feed $feed Feed object + */ + public function findFeedDescription(SimpleXMLElement $xml, Feed $feed); + + /** + * Find the feed language. + * + * @param SimpleXMLElement $xml Feed xml + * @param Feed $feed Feed object + */ + public function findFeedLanguage(SimpleXMLElement $xml, Feed $feed); + + /** + * Find the feed id. + * + * @param SimpleXMLElement $xml Feed xml + * @param Feed $feed Feed object + */ + public function findFeedId(SimpleXMLElement $xml, Feed $feed); + + /** + * Find the feed date. + * + * @param SimpleXMLElement $xml Feed xml + * @param Feed $feed Feed object + */ + public function findFeedDate(SimpleXMLElement $xml, Feed $feed); + + /** + * Find the feed logo url. + * + * @param SimpleXMLElement $xml Feed xml + * @param Feed $feed Feed object + */ + public function findFeedLogo(SimpleXMLElement $xml, Feed $feed); + + /** + * Find the feed icon. + * + * @param SimpleXMLElement $xml Feed xml + * @param Feed $feed Feed object + */ + public function findFeedIcon(SimpleXMLElement $xml, Feed $feed); + + /** + * Get the path to the items XML tree. + * + * @param SimpleXMLElement $xml Feed xml + * + * @return SimpleXMLElement + */ + public function getItemsTree(SimpleXMLElement $xml); + + /** + * Find the item author. + * + * @param SimpleXMLElement $xml Feed + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + */ + public function findItemAuthor(SimpleXMLElement $xml, SimpleXMLElement $entry, Item $item); + + /** + * Find the item URL. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + */ + public function findItemUrl(SimpleXMLElement $entry, Item $item); + + /** + * Find the item title. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + */ + public function findItemTitle(SimpleXMLElement $entry, Item $item); + + /** + * Genereate the item id. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemId(SimpleXMLElement $entry, Item $item, Feed $feed); + + /** + * Find the item published date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemPublishedDate(SimpleXMLElement $entry, Item $item, Feed $feed); + + /** + * Find the item updated date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemUpdatedDate(SimpleXMLElement $entry, Item $item, Feed $feed); + + /** + * Find the item content. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + */ + public function findItemContent(SimpleXMLElement $entry, Item $item); + + /** + * Find the item enclosure. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemEnclosure(SimpleXMLElement $entry, Item $item, Feed $feed); + + /** + * Find the item language. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemLanguage(SimpleXMLElement $entry, Item $item, Feed $feed); + + /** + * Find the item categories. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemCategories(SimpleXMLElement $entry, Item $item, Feed $feed); +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss10.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss10.php new file mode 100644 index 00000000..bb9bf424 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss10.php @@ -0,0 +1,306 @@ +<?php + +namespace PicoFeed\Parser; + +use SimpleXMLElement; +use PicoFeed\Filter\Filter; + +/** + * RSS 1.0 parser. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +class Rss10 extends Parser +{ + /** + * Supported namespaces. + */ + protected $namespaces = array( + 'rss' => 'http://purl.org/rss/1.0/', + 'dc' => 'http://purl.org/dc/elements/1.1/', + 'content' => 'http://purl.org/rss/1.0/modules/content/', + 'feedburner' => 'http://rssnamespace.org/feedburner/ext/1.0', + ); + + /** + * Get the path to the items XML tree. + * + * @param SimpleXMLElement $xml Feed xml + * + * @return SimpleXMLElement + */ + public function getItemsTree(SimpleXMLElement $xml) + { + return XmlParser::getXPathResult($xml, 'rss:item', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'item') + ?: $xml->item; + } + + /** + * Find the feed url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedUrl(SimpleXMLElement $xml, Feed $feed) + { + $feed->setFeedUrl(''); + } + + /** + * Find the site url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findSiteUrl(SimpleXMLElement $xml, Feed $feed) + { + $value = XmlParser::getXPathResult($xml, 'rss:channel/rss:link', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/link') + ?: $xml->channel->link; + + $feed->setSiteUrl(XmlParser::getValue($value)); + } + + /** + * Find the feed description. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedDescription(SimpleXMLElement $xml, Feed $feed) + { + $description = XmlParser::getXPathResult($xml, 'rss:channel/rss:description', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/description') + ?: $xml->channel->description; + + $feed->setDescription(XmlParser::getValue($description)); + } + + /** + * Find the feed logo url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedLogo(SimpleXMLElement $xml, Feed $feed) + { + $logo = XmlParser::getXPathResult($xml, 'rss:image/rss:url', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'image/url'); + + $feed->setLogo(XmlParser::getValue($logo)); + } + + /** + * Find the feed icon. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedIcon(SimpleXMLElement $xml, Feed $feed) + { + $feed->setIcon(''); + } + + /** + * Find the feed title. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedTitle(SimpleXMLElement $xml, Feed $feed) + { + $title = XmlParser::getXPathResult($xml, 'rss:channel/rss:title', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/title') + ?: $xml->channel->title; + + $feed->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($title)) ?: $feed->getSiteUrl()); + } + + /** + * Find the feed language. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedLanguage(SimpleXMLElement $xml, Feed $feed) + { + $language = XmlParser::getXPathResult($xml, 'rss:channel/dc:language', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/dc:language', $this->namespaces); + + $feed->setLanguage(XmlParser::getValue($language)); + } + + /** + * Find the feed id. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedId(SimpleXMLElement $xml, Feed $feed) + { + $feed->setId($feed->getFeedUrl() ?: $feed->getSiteUrl()); + } + + /** + * Find the feed date. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedDate(SimpleXMLElement $xml, Feed $feed) + { + $date = XmlParser::getXPathResult($xml, 'rss:channel/dc:date', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/dc:date', $this->namespaces); + + $feed->setDate($this->getDateParser()->getDateTime(XmlParser::getValue($date))); + } + + /** + * Find the item published date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemPublishedDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $date = XmlParser::getXPathResult($entry, 'dc:date', $this->namespaces); + + $item->setPublishedDate(!empty($date) ? $this->getDateParser()->getDateTime(XmlParser::getValue($date)) : null); + } + + /** + * Find the item updated date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemUpdatedDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + if ($item->publishedDate === null) { + $this->findItemPublishedDate($entry, $item, $feed); + } + $item->setUpdatedDate($item->getPublishedDate()); // No updated date in RSS 1.0 specifications + } + + /** + * Find the item title. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemTitle(SimpleXMLElement $entry, Item $item) + { + $title = XmlParser::getXPathResult($entry, 'rss:title', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'title') + ?: $entry->title; + + $item->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($title)) ?: $item->getUrl()); + } + + /** + * Find the item author. + * + * @param SimpleXMLElement $xml Feed + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemAuthor(SimpleXMLElement $xml, SimpleXMLElement $entry, Item $item) + { + $author = XmlParser::getXPathResult($entry, 'dc:creator', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'rss:channel/dc:creator', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/dc:creator', $this->namespaces); + + $item->setAuthor(XmlParser::getValue($author)); + } + + /** + * Find the item content. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemContent(SimpleXMLElement $entry, Item $item) + { + $content = XmlParser::getXPathResult($entry, 'content:encoded', $this->namespaces); + + if (XmlParser::getValue($content) === '') { + $content = XmlParser::getXPathResult($entry, 'rss:description', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'description') + ?: $entry->description; + } + + $item->setContent(XmlParser::getValue($content)); + } + + /** + * Find the item URL. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemUrl(SimpleXMLElement $entry, Item $item) + { + $link = XmlParser::getXPathResult($entry, 'feedburner:origLink', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'rss:link', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'link') + ?: $entry->link; + + $item->setUrl(XmlParser::getValue($link)); + } + + /** + * Genereate the item id. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemId(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $item->setId($this->generateId( + $item->getTitle(), $item->getUrl(), $item->getContent() + )); + } + + /** + * Find the item enclosure. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemEnclosure(SimpleXMLElement $entry, Item $item, Feed $feed) + { + } + + /** + * Find the item language. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemLanguage(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $language = XmlParser::getXPathResult($entry, 'dc:language', $this->namespaces); + + $item->setLanguage(XmlParser::getValue($language) ?: $feed->getLanguage()); + } + + /** + * Find the item categories. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemCategories(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $categories = XmlParser::getXPathResult($entry, 'dc:subject', $this->namespaces); + $item->setCategoriesFromXml($categories); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss20.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss20.php new file mode 100644 index 00000000..1dd3bf8c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss20.php @@ -0,0 +1,319 @@ +<?php + +namespace PicoFeed\Parser; + +use SimpleXMLElement; +use PicoFeed\Filter\Filter; +use PicoFeed\Client\Url; + +/** + * RSS 2.0 Parser. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +class Rss20 extends Parser +{ + /** + * Supported namespaces. + */ + protected $namespaces = array( + 'dc' => 'http://purl.org/dc/elements/1.1/', + 'content' => 'http://purl.org/rss/1.0/modules/content/', + 'feedburner' => 'http://rssnamespace.org/feedburner/ext/1.0', + 'atom' => 'http://www.w3.org/2005/Atom', + ); + + /** + * Get the path to the items XML tree. + * + * @param SimpleXMLElement $xml Feed xml + * + * @return SimpleXMLElement + */ + public function getItemsTree(SimpleXMLElement $xml) + { + return XmlParser::getXPathResult($xml, 'channel/item'); + } + + /** + * Find the feed url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedUrl(SimpleXMLElement $xml, Feed $feed) + { + $feed->setFeedUrl(''); + } + + /** + * Find the site url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findSiteUrl(SimpleXMLElement $xml, Feed $feed) + { + $value = XmlParser::getXPathResult($xml, 'channel/link'); + $feed->setSiteUrl(XmlParser::getValue($value)); + } + + /** + * Find the feed description. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedDescription(SimpleXMLElement $xml, Feed $feed) + { + $value = XmlParser::getXPathResult($xml, 'channel/description'); + $feed->setDescription(XmlParser::getValue($value)); + } + + /** + * Find the feed logo url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedLogo(SimpleXMLElement $xml, Feed $feed) + { + $value = XmlParser::getXPathResult($xml, 'channel/image/url'); + $feed->setLogo(XmlParser::getValue($value)); + } + + /** + * Find the feed icon. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedIcon(SimpleXMLElement $xml, Feed $feed) + { + $feed->setIcon(''); + } + + /** + * Find the feed title. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedTitle(SimpleXMLElement $xml, Feed $feed) + { + $title = XmlParser::getXPathResult($xml, 'channel/title'); + $feed->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($title)) ?: $feed->getSiteUrl()); + } + + /** + * Find the feed language. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedLanguage(SimpleXMLElement $xml, Feed $feed) + { + $value = XmlParser::getXPathResult($xml, 'channel/language'); + $feed->setLanguage(XmlParser::getValue($value)); + } + + /** + * Find the feed id. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedId(SimpleXMLElement $xml, Feed $feed) + { + $feed->setId($feed->getFeedUrl() ?: $feed->getSiteUrl()); + } + + /** + * Find the feed date. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedDate(SimpleXMLElement $xml, Feed $feed) + { + $publish_date = XmlParser::getXPathResult($xml, 'channel/pubDate'); + $update_date = XmlParser::getXPathResult($xml, 'channel/lastBuildDate'); + + $published = !empty($publish_date) ? $this->getDateParser()->getDateTime(XmlParser::getValue($publish_date)) : null; + $updated = !empty($update_date) ? $this->getDateParser()->getDateTime(XmlParser::getValue($update_date)) : null; + + if ($published === null && $updated === null) { + $feed->setDate($this->getDateParser()->getCurrentDateTime()); // We use the current date if there is no date for the feed + } elseif ($published !== null && $updated !== null) { + $feed->setDate(max($published, $updated)); // We use the most recent date between published and updated + } else { + $feed->setDate($updated ?: $published); + } + } + + /** + * Find the item published date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemPublishedDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $date = XmlParser::getXPathResult($entry, 'pubDate'); + + $item->setPublishedDate(!empty($date) ? $this->getDateParser()->getDateTime(XmlParser::getValue($date)) : null); + } + + /** + * Find the item updated date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemUpdatedDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + if ($item->publishedDate === null) { + $this->findItemPublishedDate($entry, $item, $feed); + } + $item->setUpdatedDate($item->getPublishedDate()); // No updated date in RSS 2.0 specifications + } + + /** + * Find the item title. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemTitle(SimpleXMLElement $entry, Item $item) + { + $value = XmlParser::getXPathResult($entry, 'title'); + $item->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($value)) ?: $item->getUrl()); + } + + /** + * Find the item author. + * + * @param SimpleXMLElement $xml Feed + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemAuthor(SimpleXMLElement $xml, SimpleXMLElement $entry, Item $item) + { + $value = XmlParser::getXPathResult($entry, 'dc:creator', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'author') + ?: XmlParser::getXPathResult($xml, 'channel/dc:creator', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/managingEditor'); + + $item->setAuthor(XmlParser::getValue($value)); + } + + /** + * Find the item content. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemContent(SimpleXMLElement $entry, Item $item) + { + $content = XmlParser::getXPathResult($entry, 'content:encoded', $this->namespaces); + + if (XmlParser::getValue($content) === '') { + $content = XmlParser::getXPathResult($entry, 'description'); + } + + $item->setContent(XmlParser::getValue($content)); + } + + /** + * Find the item URL. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemUrl(SimpleXMLElement $entry, Item $item) + { + $link = XmlParser::getXPathResult($entry, 'feedburner:origLink', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'link') + ?: XmlParser::getXPathResult($entry, 'atom:link/@href', $this->namespaces); + + if (!empty($link)) { + $item->setUrl(XmlParser::getValue($link)); + } else { + $link = XmlParser::getXPathResult($entry, 'guid'); + $link = XmlParser::getValue($link); + + if (filter_var($link, FILTER_VALIDATE_URL) !== false) { + $item->setUrl($link); + } + } + } + + /** + * Genereate the item id. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemId(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $id = XmlParser::getValue(XmlParser::getXPathResult($entry, 'guid')); + + if ($id) { + $item->setId($this->generateId($id)); + } else { + $item->setId($this->generateId( + $item->getTitle(), $item->getUrl(), $item->getContent() + )); + } + } + + /** + * Find the item enclosure. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemEnclosure(SimpleXMLElement $entry, Item $item, Feed $feed) + { + if (isset($entry->enclosure)) { + $type = XmlParser::getXPathResult($entry, 'enclosure/@type'); + $url = XmlParser::getXPathResult($entry, 'feedburner:origEnclosureLink', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'enclosure/@url'); + + $item->setEnclosureUrl(Url::resolve(XmlParser::getValue($url), $feed->getSiteUrl())); + $item->setEnclosureType(XmlParser::getValue($type)); + } + } + + /** + * Find the item language. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemLanguage(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $language = XmlParser::getXPathResult($entry, 'dc:language', $this->namespaces); + $item->setLanguage(XmlParser::getValue($language) ?: $feed->getLanguage()); + } + + /** + * Find the item categories. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemCategories(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $categories = XmlParser::getXPathResult($entry, 'category'); + $item->setCategoriesFromXml($categories); + } + +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss91.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss91.php new file mode 100644 index 00000000..058fca1b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss91.php @@ -0,0 +1,13 @@ +<?php + +namespace PicoFeed\Parser; + +/** + * RSS 0.91 Parser. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +class Rss91 extends Rss20 +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss92.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss92.php new file mode 100644 index 00000000..e3df3c8b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss92.php @@ -0,0 +1,13 @@ +<?php + +namespace PicoFeed\Parser; + +/** + * RSS 0.92 Parser. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +class Rss92 extends Rss20 +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlEntityException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlEntityException.php new file mode 100644 index 00000000..1188217d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlEntityException.php @@ -0,0 +1,13 @@ +<?php + +namespace PicoFeed\Parser; + +/** + * XmlEntityException Exception. + * + * @package PicoFeed\Parser + * @author Bernhard Posselt + */ +class XmlEntityException extends MalformedXmlException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlParser.php new file mode 100644 index 00000000..ea42949f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlParser.php @@ -0,0 +1,246 @@ +<?php + +namespace PicoFeed\Parser; + +use DOMDocument; +use SimpleXMLElement; +use ZendXml\Exception\RuntimeException; +use ZendXml\Security; + +/** + * XML parser class. + * + * Checks for XML eXternal Entity (XXE) and XML Entity Expansion (XEE) attacks on XML documents + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +class XmlParser +{ + /** + * Get a SimpleXmlElement instance or return false. + * + * @static + * @param string $input XML content + * @return mixed + */ + public static function getSimpleXml($input) + { + return self::scan($input); + } + + /** + * Get a DomDocument instance or return false. + * + * @static + * @param string $input XML content + * @return DOMDocument + */ + public static function getDomDocument($input) + { + if (empty($input)) { + return false; + } + + $dom = self::scan($input, new DOMDocument()); + + // The document is empty, there is probably some parsing errors + if ($dom && $dom->childNodes->length === 0) { + return false; + } + + return $dom; + } + + /** + * Small wrapper around ZendXml to turn their exceptions into PicoFeed exceptions + * + * @static + * @access private + * @param string $input + * @param DOMDocument $dom + * @throws XmlEntityException + * @return SimpleXMLElement|DomDocument|boolean + */ + private static function scan($input, $dom = null) + { + try { + return Security::scan($input, $dom); + } catch(RuntimeException $e) { + throw new XmlEntityException($e->getMessage()); + } + } + + /** + * Load HTML document by using a DomDocument instance or return false on failure. + * + * @static + * @access public + * @param string $input XML content + * @return DOMDocument + */ + public static function getHtmlDocument($input) + { + $dom = new DomDocument(); + + if (empty($input)) { + return $dom; + } + + libxml_use_internal_errors(true); + + if (version_compare(PHP_VERSION, '5.4.0', '>=')) { + $dom->loadHTML($input, LIBXML_NONET); + } else { + $dom->loadHTML($input); + } + + return $dom; + } + + /** + * Convert a HTML document to XML. + * + * @static + * @access public + * @param string $html HTML document + * @return string + */ + public static function htmlToXml($html) + { + $dom = self::getHtmlDocument('<?xml version="1.0" encoding="UTF-8">'.$html); + return $dom->saveXML($dom->getElementsByTagName('body')->item(0)); + } + + /** + * Get XML parser errors. + * + * @static + * @access public + * @return string + */ + public static function getErrors() + { + $errors = array(); + + foreach (libxml_get_errors() as $error) { + $errors[] = sprintf('XML error: %s (Line: %d - Column: %d - Code: %d)', + $error->message, + $error->line, + $error->column, + $error->code + ); + } + + return implode(', ', $errors); + } + + /** + * Get the encoding from a xml tag. + * + * @static + * @access public + * @param string $data Input data + * @return string + */ + public static function getEncodingFromXmlTag($data) + { + $encoding = ''; + + if (strpos($data, '<?xml') !== false) { + $data = substr($data, 0, strrpos($data, '?>')); + $data = str_replace("'", '"', $data); + + $p1 = strpos($data, 'encoding='); + $p2 = strpos($data, '"', $p1 + 10); + + if ($p1 !== false && $p2 !== false) { + $encoding = substr($data, $p1 + 10, $p2 - $p1 - 10); + $encoding = strtolower($encoding); + } + } + + return $encoding; + } + + /** + * Get the charset from a meta tag. + * + * @static + * @access public + * @param string $data Input data + * @return string + */ + public static function getEncodingFromMetaTag($data) + { + $encoding = ''; + + if (preg_match('/<meta.*?charset\s*=\s*["\']?\s*([^"\'\s\/>;]+)/i', $data, $match) === 1) { + $encoding = strtolower($match[1]); + } + + return $encoding; + } + + /** + * Rewrite XPath query to use namespace-uri and local-name derived from prefix. + * + * @static + * @access public + * @param string $query XPath query + * @param array $ns Prefix to namespace URI mapping + * @return string + */ + public static function replaceXPathPrefixWithNamespaceURI($query, array $ns) + { + return preg_replace_callback('/([A-Z0-9]+):([A-Z0-9]+)/iu', function ($matches) use ($ns) { + // don't try to map the special prefix XML + if (strtolower($matches[1]) === 'xml') { + return $matches[0]; + } + + return '*[namespace-uri()="'.$ns[$matches[1]].'" and local-name()="'.$matches[2].'"]'; + }, + $query); + } + + /** + * Get the result elements of a XPath query. + * + * @static + * @access public + * @param SimpleXMLElement $xml XML element + * @param string $query XPath query + * @param array $ns Prefix to namespace URI mapping + * @return SimpleXMLElement[] + */ + public static function getXPathResult(SimpleXMLElement $xml, $query, array $ns = array()) + { + if (!empty($ns)) { + $query = static::replaceXPathPrefixWithNamespaceURI($query, $ns); + } + + return $xml->xpath($query); + } + + /** + * Get the first Xpath result or SimpleXMLElement value + * + * @static + * @access public + * @param mixed $value + * @return string + */ + public static function getValue($value) + { + $result = ''; + + if (is_array($value) && count($value) > 0) { + $result = (string) $value[0]; + } elseif (is_a($value, 'SimpleXMLElement')) { + return $result = (string) $value; + } + + return trim($result); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/PicoFeedException.php b/vendor/miniflux/picofeed/lib/PicoFeed/PicoFeedException.php new file mode 100644 index 00000000..2de9e4b7 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/PicoFeedException.php @@ -0,0 +1,14 @@ +<?php + +namespace PicoFeed; + +use Exception; + +/** + * PicoFeedException Exception. + * + * @author Frederic Guillot + */ +abstract class PicoFeedException extends Exception +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentFilterProcessor.php b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentFilterProcessor.php new file mode 100644 index 00000000..9b7ddcce --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentFilterProcessor.php @@ -0,0 +1,37 @@ +<?php + +namespace PicoFeed\Processor; + +use PicoFeed\Base; +use PicoFeed\Filter\Filter; +use PicoFeed\Logging\Logger; +use PicoFeed\Parser\Feed; +use PicoFeed\Parser\Item; + +/** + * Item Content Filter + * + * @package PicoFeed\Processor + * @author Frederic Guillot + */ +class ContentFilterProcessor extends Base implements ItemProcessorInterface +{ + /** + * Execute Item Processor + * + * @access public + * @param Feed $feed + * @param Item $item + * @return bool + */ + public function execute(Feed $feed, Item $item) + { + if ($this->config->getContentFiltering(true)) { + $filter = Filter::html($item->getContent(), $feed->getSiteUrl()); + $filter->setConfig($this->config); + $item->setContent($filter->execute()); + } else { + Logger::setMessage(get_called_class().': Content filtering disabled'); + } + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentGeneratorProcessor.php b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentGeneratorProcessor.php new file mode 100644 index 00000000..49adf9cc --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentGeneratorProcessor.php @@ -0,0 +1,49 @@ +<?php + +namespace PicoFeed\Processor; + +use PicoFeed\Base; +use PicoFeed\Parser\Feed; +use PicoFeed\Parser\Item; + +/** + * Item Content Generator + * + * @package PicoFeed\Processor + * @author Frederic Guillot + */ +class ContentGeneratorProcessor extends Base implements ItemProcessorInterface +{ + /** + * List of generators + * + * @access protected + * @var array + */ + protected $generators = array( + 'youtube', + 'file', + ); + + /** + * Execute Item Processor + * + * @access public + * @param Feed $feed + * @param Item $item + * @return bool + */ + public function execute(Feed $feed, Item $item) + { + foreach ($this->generators as $generator) { + $className = '\PicoFeed\Generator\\'.ucfirst($generator).'ContentGenerator'; + $object = new $className($this->config); + + if ($object->execute($item)) { + return true; + } + } + + return false; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemPostProcessor.php b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemPostProcessor.php new file mode 100644 index 00000000..d2787677 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemPostProcessor.php @@ -0,0 +1,106 @@ +<?php + +namespace PicoFeed\Processor; + +use PicoFeed\Base; +use PicoFeed\Parser\Feed; +use PicoFeed\Parser\Item; +use PicoFeed\Config\Config; + +/** + * Item Post Processor + * + * @package PicoFeed\Processor + * @author Frederic Guillot + */ +class ItemPostProcessor extends Base +{ + /** + * List of processors + * + * @access private + * @var array + */ + private $processors = array(); + + /** + * Execute all processors + * + * @access public + * @param Feed $feed + * @param Item $item + * @return bool + */ + public function execute(Feed $feed, Item $item) + { + foreach ($this->processors as $processor) { + if ($processor->execute($feed, $item)) { + return true; + } + } + + return false; + } + + /** + * Register a new Item post-processor + * + * @access public + * @param ItemProcessorInterface $processor + * @return ItemPostProcessor + */ + public function register(ItemProcessorInterface $processor) + { + $this->processors[get_class($processor)] = $processor; + return $this; + } + + /** + * Remove Processor instance + * + * @access public + * @param string $class + * @return ItemPostProcessor + */ + public function unregister($class) + { + if (isset($this->processors[$class])) { + unset($this->processors[$class]); + } + + return $this; + } + + /** + * Checks wheather a specific processor is registered or not + * + * @access public + * @param string $class + * @return bool + */ + public function hasProcessor($class) + { + return isset($this->processors[$class]); + } + + /** + * Get Processor instance + * + * @access public + * @param string $class + * @return ItemProcessorInterface|null + */ + public function getProcessor($class) + { + return isset($this->processors[$class]) ? $this->processors[$class] : null; + } + + public function setConfig(Config $config) + { + foreach ($this->processors as $processor) { + $processor->setConfig($config); + } + + return false; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemProcessorInterface.php b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemProcessorInterface.php new file mode 100644 index 00000000..5d532262 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemProcessorInterface.php @@ -0,0 +1,25 @@ +<?php + +namespace PicoFeed\Processor; + +use PicoFeed\Parser\Feed; +use PicoFeed\Parser\Item; + +/** + * Item Processor Interface + * + * @package PicoFeed\Processor + * @author Frederic Guillot + */ +interface ItemProcessorInterface +{ + /** + * Execute Item Processor + * + * @access public + * @param Feed $feed + * @param Item $item + * @return bool + */ + public function execute(Feed $feed, Item $item); +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ScraperProcessor.php b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ScraperProcessor.php new file mode 100644 index 00000000..0c467af0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ScraperProcessor.php @@ -0,0 +1,96 @@ +<?php + +namespace PicoFeed\Processor; + +use Closure; +use PicoFeed\Base; +use PicoFeed\Parser\Feed; +use PicoFeed\Parser\Item; +use PicoFeed\Scraper\Scraper; + +/** + * Scraper Processor + * + * @package PicoFeed\Processor + * @author Frederic Guillot + */ +class ScraperProcessor extends Base implements ItemProcessorInterface +{ + private $ignoredUrls = array(); + private $scraper; + + /** + * Callback function for each scraper execution + * + * @var Closure + */ + private $executionCallback; + + /** + * Add a new execution callback + * + * @access public + * @param Closure $executionCallback + * @return $this + */ + public function setExecutionCallback(Closure $executionCallback) + { + $this->executionCallback = $executionCallback; + return $this; + } + + /** + * Execute Item Processor + * + * @access public + * @param Feed $feed + * @param Item $item + * @return bool + */ + public function execute(Feed $feed, Item $item) + { + if (!in_array($item->getUrl(), $this->ignoredUrls)) { + $scraper = $this->getScraper(); + $scraper->setUrl($item->getUrl()); + $scraper->execute(); + + if ($this->executionCallback && is_callable($this->executionCallback)) { + call_user_func($this->executionCallback, $feed, $item, $scraper); + } + + if ($scraper->hasRelevantContent()) { + $item->setContent($scraper->getFilteredContent()); + } + } + + return false; + } + + /** + * Ignore list of URLs + * + * @access public + * @param array $urls + * @return $this + */ + public function ignoreUrls(array $urls) + { + $this->ignoredUrls = $urls; + return $this; + } + + /** + * Returns Scraper instance + * + * @access public + * @return Scraper + */ + public function getScraper() + { + if ($this->scraper === null) { + $this->scraper = new Scraper($this->config); + } + + return $this->scraper; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Favicon.php b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Favicon.php new file mode 100644 index 00000000..d4ca07db --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Favicon.php @@ -0,0 +1,186 @@ +<?php + +namespace PicoFeed\Reader; + +use DOMXPath; +use PicoFeed\Base; +use PicoFeed\Client\Client; +use PicoFeed\Client\ClientException; +use PicoFeed\Client\Url; +use PicoFeed\Logging\Logger; +use PicoFeed\Parser\XmlParser; + +/** + * Favicon class. + * + * https://en.wikipedia.org/wiki/Favicon + * + * @author Frederic Guillot + */ +class Favicon extends Base +{ + /** + * Valid types for favicon (supported by browsers). + * + * @var array + */ + private $types = array( + 'image/png', + 'image/gif', + 'image/x-icon', + 'image/jpeg', + 'image/jpg', + 'image/svg+xml', + ); + + /** + * Icon binary content. + * + * @var string + */ + private $content = ''; + + /** + * Icon content type. + * + * @var string + */ + private $content_type = ''; + + /** + * Get the icon file content (available only after the download). + * + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * Get the icon file type (available only after the download). + * + * @return string + */ + public function getType() + { + foreach ($this->types as $type) { + if (strpos($this->content_type, $type) === 0) { + return $type; + } + } + + return 'image/x-icon'; + } + + /** + * Get data URI (http://en.wikipedia.org/wiki/Data_URI_scheme). + * + * @return string + */ + public function getDataUri() + { + if (empty($this->content)) { + return ''; + } + + return sprintf( + 'data:%s;base64,%s', + $this->getType(), + base64_encode($this->content) + ); + } + + /** + * Download and check if a resource exists. + * + * @param string $url URL + * @return \PicoFeed\Client\Client Client instance + */ + public function download($url) + { + $client = Client::getInstance(); + $client->setConfig($this->config); + + Logger::setMessage(get_called_class().' Download => '.$url); + + try { + $client->execute($url); + } catch (ClientException $e) { + Logger::setMessage(get_called_class().' Download Failed => '.$e->getMessage()); + } + + return $client; + } + + /** + * Check if a remote file exists. + * + * @param string $url URL + * @return bool + */ + public function exists($url) + { + return $this->download($url)->getContent() !== ''; + } + + /** + * Get the icon link for a website. + * + * @param string $website_link URL + * @param string $favicon_link optional URL + * @return string + */ + public function find($website_link, $favicon_link = '') + { + $website = new Url($website_link); + + if ($favicon_link !== '') { + $icons = array($favicon_link); + } else { + $icons = $this->extract($this->download($website->getBaseUrl('/'))->getContent()); + $icons[] = $website->getBaseUrl('/favicon.ico'); + } + + foreach ($icons as $icon_link) { + $icon_link = Url::resolve($icon_link, $website); + $resource = $this->download($icon_link); + $this->content = $resource->getContent(); + $this->content_type = $resource->getContentType(); + + if ($this->content !== '') { + return $icon_link; + } elseif ($favicon_link !== '') { + return $this->find($website_link); + } + } + + return ''; + } + + /** + * Extract the icon links from the HTML. + * + * @param string $html HTML + * @return array + */ + public function extract($html) + { + $icons = array(); + + if (empty($html)) { + return $icons; + } + + $dom = XmlParser::getHtmlDocument($html); + + $xpath = new DOMXpath($dom); + $elements = $xpath->query('//link[@rel="icon" or @rel="shortcut icon" or @rel="Shortcut Icon" or @rel="icon shortcut"]'); + + for ($i = 0; $i < $elements->length; ++$i) { + $icons[] = $elements->item($i)->getAttribute('href'); + } + + return $icons; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Reader.php b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Reader.php new file mode 100644 index 00000000..769ffe93 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Reader.php @@ -0,0 +1,190 @@ +<?php + +namespace PicoFeed\Reader; + +use DOMXPath; +use PicoFeed\Base; +use PicoFeed\Client\Client; +use PicoFeed\Client\Url; +use PicoFeed\Logging\Logger; +use PicoFeed\Parser\XmlParser; + +/** + * Reader class. + * + * @author Frederic Guillot + */ +class Reader extends Base +{ + /** + * Feed formats for detection. + * + * @var array + */ + private $formats = array( + 'Atom' => '//feed', + 'Rss20' => '//rss[@version="2.0"]', + 'Rss92' => '//rss[@version="0.92"]', + 'Rss91' => '//rss[@version="0.91"]', + 'Rss10' => '//rdf', + ); + + /** + * Download a feed (no discovery). + * + * @param string $url Feed url + * @param string $last_modified Last modified HTTP header + * @param string $etag Etag HTTP header + * @param string $username HTTP basic auth username + * @param string $password HTTP basic auth password + * + * @return \PicoFeed\Client\Client + */ + public function download($url, $last_modified = '', $etag = '', $username = '', $password = '') + { + $url = $this->prependScheme($url); + + return Client::getInstance() + ->setConfig($this->config) + ->setLastModified($last_modified) + ->setEtag($etag) + ->setUsername($username) + ->setPassword($password) + ->execute($url); + } + + /** + * Discover and download a feed. + * + * @param string $url Feed or website url + * @param string $last_modified Last modified HTTP header + * @param string $etag Etag HTTP header + * @param string $username HTTP basic auth username + * @param string $password HTTP basic auth password + * @return Client + * @throws SubscriptionNotFoundException + */ + public function discover($url, $last_modified = '', $etag = '', $username = '', $password = '') + { + $client = $this->download($url, $last_modified, $etag, $username, $password); + + // It's already a feed or the feed was not modified + if (!$client->isModified() || $this->detectFormat($client->getContent())) { + return $client; + } + + // Try to find a subscription + $links = $this->find($client->getUrl(), $client->getContent()); + + if (empty($links)) { + throw new SubscriptionNotFoundException('Unable to find a subscription'); + } + + return $this->download($links[0], $last_modified, $etag, $username, $password); + } + + /** + * Find feed urls inside a HTML document. + * + * @param string $url Website url + * @param string $html HTML content + * + * @return array List of feed links + */ + public function find($url, $html) + { + Logger::setMessage(get_called_class().': Try to discover subscriptions'); + + $dom = XmlParser::getHtmlDocument($html); + $xpath = new DOMXPath($dom); + $links = array(); + + $queries = array( + '//link[@type="application/rss+xml"]', + '//link[@type="application/atom+xml"]', + ); + + foreach ($queries as $query) { + $nodes = $xpath->query($query); + + foreach ($nodes as $node) { + $link = $node->getAttribute('href'); + + if (!empty($link)) { + $feedUrl = new Url($link); + $siteUrl = new Url($url); + + $links[] = $feedUrl->getAbsoluteUrl($feedUrl->isRelativeUrl() ? $siteUrl->getBaseUrl() : ''); + } + } + } + + Logger::setMessage(get_called_class().': '.implode(', ', $links)); + + return $links; + } + + /** + * Get a parser instance. + * + * @param string $url Site url + * @param string $content Feed content + * @param string $encoding HTTP encoding + * + * @return \PicoFeed\Parser\Parser + */ + public function getParser($url, $content, $encoding) + { + $format = $this->detectFormat($content); + + if (empty($format)) { + throw new UnsupportedFeedFormatException('Unable to detect feed format'); + } + + $className = '\PicoFeed\Parser\\'.$format; + + $parser = new $className($content, $encoding, $url); + $parser->setHashAlgo($this->config->getParserHashAlgo()); + $parser->setConfig($this->config); + + return $parser; + } + + /** + * Detect the feed format. + * + * @param string $content Feed content + * + * @return string + */ + public function detectFormat($content) + { + $dom = XmlParser::getHtmlDocument($content); + $xpath = new DOMXPath($dom); + + foreach ($this->formats as $parser_name => $query) { + $nodes = $xpath->query($query); + + if ($nodes->length === 1) { + return $parser_name; + } + } + + return ''; + } + + /** + * Add the prefix "http://" if the end-user just enter a domain name. + * + * @param string $url Url + * @retunr string + */ + public function prependScheme($url) + { + if (!preg_match('%^https?://%', $url)) { + $url = 'http://'.$url; + } + + return $url; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Reader/ReaderException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/ReaderException.php new file mode 100644 index 00000000..4f03dbe0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/ReaderException.php @@ -0,0 +1,14 @@ +<?php + +namespace PicoFeed\Reader; + +use PicoFeed\PicoFeedException; + +/** + * ReaderException Exception. + * + * @author Frederic Guillot + */ +abstract class ReaderException extends PicoFeedException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Reader/SubscriptionNotFoundException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/SubscriptionNotFoundException.php new file mode 100644 index 00000000..dd77847e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/SubscriptionNotFoundException.php @@ -0,0 +1,12 @@ +<?php + +namespace PicoFeed\Reader; + +/** + * SubscriptionNotFoundException Exception. + * + * @author Frederic Guillot + */ +class SubscriptionNotFoundException extends ReaderException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Reader/UnsupportedFeedFormatException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/UnsupportedFeedFormatException.php new file mode 100644 index 00000000..4aa8d87f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/UnsupportedFeedFormatException.php @@ -0,0 +1,12 @@ +<?php + +namespace PicoFeed\Reader; + +/** + * UnsupportedFeedFormatException Exception. + * + * @author Frederic Guillot + */ +class UnsupportedFeedFormatException extends ReaderException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blog.lemonde.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blog.lemonde.fr.php new file mode 100644 index 00000000..773616c4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blog.lemonde.fr.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://combat.blog.lemonde.fr/2013/08/31/teddy-riner-le-rookie-devenu-rambo/#xtor=RSS-3208', + 'body' => array( + '//div[@class="entry-content"]', + ), + 'strip' => array( + '//*[contains(@class, "fb-like") or contains(@class, "social")]' + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blogs.nytimes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blogs.nytimes.com.php new file mode 100644 index 00000000..ee641b09 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blogs.nytimes.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'title' => '//header/h1', + 'test_url' => 'http://bits.blogs.nytimes.com/2012/01/16/wikipedia-plans-to-go-dark-on-wednesday-to-protest-sopa/', + 'body' => array( + '//div[@class="postContent"]', + ), + 'strip' => array( + '//*[@class="shareToolsBox"]', + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.igen.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.igen.fr.php new file mode 100644 index 00000000..f2028f4e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.igen.fr.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.igen.fr/ailleurs/2014/05/nvidia-va-delaisser-les-smartphones-grand-public-86031', + 'body' => array( + '//div[contains(@class, "field-name-body")]' + ), + 'strip' => array( + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.nytimes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.nytimes.com.php new file mode 100644 index 00000000..ed27bb5c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.nytimes.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.nytimes.com/2011/05/15/world/middleeast/15prince.html', + 'body' => array( + '//div[@class="articleBody"]', + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.over-blog.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.over-blog.com.php new file mode 100644 index 00000000..cc5d83c7 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.over-blog.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://eliascarpe.over-blog.com/2015/12/re-upload-projets-d-avenir.html', + 'body' => array( + '//div[contains(concat(" ", normalize-space(@class), " "), " ob-section ")]', + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.phoronix.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.phoronix.com.php new file mode 100644 index 00000000..66713f71 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.phoronix.com.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.phoronix.com/scan.php?page=article&item=amazon_ec2_bare&num=1', + 'body' => array( + '//div[@class="content"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.slate.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.slate.com.php new file mode 100644 index 00000000..a795bca3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.slate.com.php @@ -0,0 +1,20 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.slate.com/articles/business/moneybox/2013/08/microsoft_ceo_steve_ballmer_retires_a_firsthand_account_of_the_company_s.html', + 'body' => array( + '//div[@class="sl-art-body"]', + ), + 'strip' => array( + '//*[contains(@class, "social") or contains(@class, "comments") or contains(@class, "sl-article-floatin-tools") or contains(@class, "sl-art-pag")]', + '//*[@id="mys_slate_logged_in"]', + '//*[@id="sl_article_tools_myslate_bottom"]', + '//*[@id="mys_myslate"]', + '//*[@class="sl-viral-container"]', + '//*[@class="sl-art-creds-cntr"]', + '//*[@class="sl-art-ad-midflex"]', + ) + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.theguardian.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.theguardian.com.php new file mode 100644 index 00000000..e0d6f3fd --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.theguardian.com.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.theguardian.com/sustainable-business/2015/feb/02/2015-hyper-transparency-global-business', + 'body' => array( + '//div[contains(@class, "content__main-column--article")]', + ), + 'strip' => array( + '//div[contains(@class, "meta-container")]', + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wikipedia.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wikipedia.org.php new file mode 100644 index 00000000..7b8f76e5 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wikipedia.org.php @@ -0,0 +1,29 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://en.wikipedia.org/wiki/Grace_Hopper', + 'body' => array( + '//div[@id="bodyContent"]', + ), + 'strip' => array( + "//div[@id='toc']", + "//div[@id='catlinks']", + "//div[@id='jump-to-nav']", + "//div[@class='thumbcaption']//div[@class='magnify']", + "//table[@class='navbox']", + "//table[contains(@class, 'infobox')]", + "//div[@class='dablink']", + "//div[@id='contentSub']", + "//div[@id='siteSub']", + "//table[@id='persondata']", + "//table[contains(@class, 'metadata')]", + "//*[contains(@class, 'noprint')]", + "//*[contains(@class, 'printfooter')]", + "//*[contains(@class, 'editsection')]", + "//*[contains(@class, 'error')]", + "//span[@title='pronunciation:']", + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wired.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wired.com.php new file mode 100644 index 00000000..952b09ac --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wired.com.php @@ -0,0 +1,44 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.wired.com/gamelife/2013/09/ouya-free-the-games/', + 'body' => array( + '//div[@data-js="gallerySlides"]', + '//div[starts-with(@class,"post")]', + ), + 'strip' => array( + '//h1', + '//nav', + '//button', + '//figure[starts-with(@class,"rad-slide")]', + '//figure[starts-with(@class,"end-slate")]', + '//div[contains(@class,"mobile-")]', + '//div[starts-with(@class,"mob-gallery-launcher")]', + '//div[contains(@id,"mobile-")]', + '//span[contains(@class,"slide-count")]', + '//div[contains(@class,"show-ipad")]', + '//img[contains(@id,"-hero-bg")]', + '//div[@data-js="overlayWrap"]', + '//ul[contains(@class,"metadata")]', + '//div[@class="opening center"]', + '//p[contains(@class="byline-mob"]', + '//div[@id="o-gallery"]', + '//div[starts-with(@class,"sm-col")]', + '//div[contains(@class,"pad-b-huge")]', + '//a[contains(@class,"visually-hidden")]', + '//*[@class="social"]', + '//i', + '//div[@data-js="mobGalleryAd"]', + '//div[contains(@class,"footer")]', + '//div[contains(@data-js,"fader")]', + '//div[@id="sharing"]', + '//div[contains(@id,"related")]', + '//div[@id="most-pop"]', + '//ul[@id="article-tags"]', + '//style', + '//section[contains(@class,"footer")]' + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wsj.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wsj.com.php new file mode 100644 index 00000000..f6e6cc12 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wsj.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://online.wsj.com/article/SB10001424127887324108204579023143974408428.html', + 'body' => array( + '//div[@class="articlePage"]', + ), + 'strip' => array( + '//*[@id="articleThumbnail_2"]', + '//*[@class="socialByline"]', + ) + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/01net.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/01net.com.php new file mode 100644 index 00000000..6d144f05 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/01net.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.01net.com/editorial/624550/twitter-rachete-madbits-un-specialiste-francais-de-lanalyse-dimages/', + 'body' => array( + '//div[@class="article_ventre_box"]', + ), + 'strip' => array( + '//link', + '//*[contains(@class, "article_navigation")]', + '//h1', + '//*[contains(@class, "article_toolbarMain")]', + '//*[contains(@class, "article_imagehaute_box")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/abstrusegoose.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/abstrusegoose.com.php new file mode 100644 index 00000000..752d0413 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/abstrusegoose.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%alt="(.+)" title="(.+)" */>%' => '/><br/>$1<br/>$2', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/adventuregamers.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/adventuregamers.com.php new file mode 100644 index 00000000..98d384e6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/adventuregamers.com.php @@ -0,0 +1,23 @@ +<?php +return array( + 'grabber' => array( + '%^/news.*%' => array( + 'test_url' => 'http://www.adventuregamers.com/news/view/31079', + 'body' => array( + '//div[@class="bodytext"]', + ) + ), + '%^/videos.*%' => array( + 'test_url' => 'http://www.adventuregamers.com/videos/view/31056', + 'body' => array( + '//iframe', + ) + ), + '%^/articles.*%' => array( + 'test_url' => 'http://www.adventuregamers.com/articles/view/31049', + 'body' => array( + '//div[@class="cleft"]', + ) + ) + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/alainonline.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/alainonline.net.php new file mode 100644 index 00000000..f440b234 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/alainonline.net.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.alainonline.net/news_details.php?lang=arabic&sid=18907', + 'body' => array( + '//div[@class="news_details"]', + ), + 'strip' => array( + '//div[@class="news_details"]/div/div[last()]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/aljazeera.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/aljazeera.com.php new file mode 100644 index 00000000..c02eb219 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/aljazeera.com.php @@ -0,0 +1,25 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.aljazeera.com/news/2015/09/xi-jinping-seattle-china-150922230118373.html', + 'body' => array( + '//article[@id="main-story"]', + ), + 'strip' => array( + '//script', + '//header', + '//ul', + '//section[contains(@class,"profile")]', + '//a[@target="_self"]', + '//div[contains(@id,"_2")]', + '//div[contains(@id,"_3")]', + '//img[@class="viewMode"]', + '//table[contains(@class,"in-article-item")]', + '//div[@data-embed-type="Brightcove"]', + '//div[@class="QuoteContainer"]', + '//div[@class="BottomByLine"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allafrica.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allafrica.com.php new file mode 100644 index 00000000..e8a506d4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allafrica.com.php @@ -0,0 +1,20 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.aljazeera.com/news/2015/09/xi-jinping-seattle-china-150922230118373.html', + 'body' => array( + '//div[@class="story-body"]', + ), + 'strip' => array( + '//p[@class="kindofstory"]', + '//cite[@class="byline"]', + '//div[@class="useful-top"]', + '//div[contains(@class,"related-topics")]', + '//links', + '//sharebar', + '//related-topics', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allgemeine-zeitung.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allgemeine-zeitung.de.php new file mode 100644 index 00000000..8ede99b1 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allgemeine-zeitung.de.php @@ -0,0 +1,23 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.allgemeine-zeitung.de/lokales/polizei/mainz-gonsenheim-unbekannte-rauben-esso-tankstelle-in-kurt-schumacher-strasse-aus_14913147.htm', + 'body' => array( + '//div[contains(@class, "article")][1]', + ), + 'strip' => array( + '//read/h1', + '//*[@id="t-map"]', + '//*[contains(@class, "modules")]', + '//*[contains(@class, "adsense")]', + '//*[contains(@class, "linkbox")]', + '//*[contains(@class, "info")]', + '//*[@class="skip"]', + '//*[@class="funcs"]', + '//span[@class="nd address"]', + '//a[contains(@href, "abo-und-services")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/amazingsuperpowers.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/amazingsuperpowers.com.php new file mode 100644 index 00000000..3214c62a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/amazingsuperpowers.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/anythingcomic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/anythingcomic.com.php new file mode 100644 index 00000000..51247f76 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/anythingcomic.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array( + '//img[@id="comic_image"]', + '//div[@class="comment-wrapper"][position()=1]', + ), + 'strip' => array(), + 'test_url' => 'http://www.anythingcomic.com/comics/2108929/stress-free/', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ap.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ap.org.php new file mode 100644 index 00000000..5bb2bb6c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ap.org.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://hosted.ap.org/dynamic/stories/A/AS_CHINA_GAO_ZHISHENG?SITE=AP&SECTION=HOME&TEMPLATE=DEFAULT', + 'body' => array( + '//img[@class="ap-smallphoto-img"]', + '//span[@class="entry-content"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/areadvd.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/areadvd.de.php new file mode 100644 index 00000000..fc569220 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/areadvd.de.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.areadvd.de/news/daily-deals-angebote-bei-lautsprecher-teufel-3/', + 'body' => array('//div[contains(@class,"entry")]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/arstechnica.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/arstechnica.com.php new file mode 100644 index 00000000..55e01ce3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/arstechnica.com.php @@ -0,0 +1,25 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://arstechnica.com/tech-policy/2015/09/judge-warners-2m-happy-birthday-copyright-is-bogus/', + 'body' => array( + '//article', + ), + 'strip' => array( + '//h4[@class="post-upperdek"]', + '//h1', + '//ul[@class="lSPager lSGallery"]', + '//div[@class="lSAction"]', + '//section[@class="post-meta"]', + '//figcaption', + '//aside', + '//div[@class="gallery-image-credit"]', + '//section[@class="article-author"]', + '//*[contains(@id,"social-")]', + '//div[contains(@id,"footer")]', + ), + ), + ), +); + diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/awkwardzombie.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/awkwardzombie.com.php new file mode 100644 index 00000000..5ab70514 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/awkwardzombie.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%/index.php.*comic=.*%' => array( + 'test_url' => 'http://www.awkwardzombie.com/index.php?comic=041315', + 'body' => array('//*[@id="comic"]/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/backchannel.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/backchannel.com.php new file mode 100644 index 00000000..bc5932a2 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/backchannel.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://medium.com/lessons-learned/917b8b63ae3e', + 'body' => array( + '//div[contains(@class,"section-inner")]', + ), + 'strip' => array( + '//div[contains(@class,"metabar")]', + '//img[contains(@class,"thumbnail")]', + '//h1', + '//blockquote', + '//p[contains(@class,"graf-after--h4")]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bangkokpost.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bangkokpost.com.php new file mode 100644 index 00000000..165515bb --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bangkokpost.com.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.bangkokpost.com/news/politics/704204/new-us-ambassador-arrives-in-bangkok', + 'body' => array( + '//article/div[@class="articleContents"]', + ), + 'strip' => array( + '//h2', + '//h4', + '//div[@class="text-size"]', + '//div[@class="relate-story"]', + '//div[@class="text-ads"]', + '//ul', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bgr.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bgr.com.php new file mode 100644 index 00000000..7507a2f1 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bgr.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://bgr.com/2015/09/27/iphone-6s-waterproof-testing/', + 'body' => array( + '//img[contains(@class,"img")]', + '//div[@class="text-column"]', + ), + 'strip' => array( + '//strong', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigfootjustice.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigfootjustice.com.php new file mode 100644 index 00000000..d06ed124 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigfootjustice.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%-150x150%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigpicture.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigpicture.ru.php new file mode 100644 index 00000000..55c40894 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigpicture.ru.php @@ -0,0 +1,31 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://bigpicture.ru/?p=556658', + 'body' => array( + '//div[@class="article container"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//h1', + '//*[@class="wp-smiley"]', + '//div[@class="ipmd"]', + '//div[@class="tags"]', + '//div[@class="social-button"]', + '//div[@class="bottom-share"]', + '//div[@class="raccoonbox"]', + '//div[@class="yndadvert"]', + '//div[@class="we-recommend"]', + '//div[@class="relap-bigpicture_ru-wrapper"]', + '//div[@id="mmail"]', + '//div[@id="mobile-ads-cut"]', + '//div[@id="liquidstorm-alt-html"]', + '//div[contains(@class, "post-tags")]', + '//*[contains(text(),"Смотрите также")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bizjournals.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bizjournals.com.php new file mode 100644 index 00000000..d1cc3da9 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bizjournals.com.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.bizjournals.com/milwaukee/news/2015/09/30/bucks-will-hike-prices-on-best-seats-at-new-arena.html', + 'body' => array( + '//figure/div/a/img', + '//p[@class="content__segment"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/biztimes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/biztimes.com.php new file mode 100644 index 00000000..d21aa98c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/biztimes.com.php @@ -0,0 +1,22 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://www.biztimes.com/2017/02/10/settlement-would-revive-fowler-lake-condo-project-in-oconomowoc/', + 'body' => array( + '//h2/span[@class="subhead"]', + '//div[contains(@class,"article-content")]', + ), + 'strip' => array( + '//script', + '//div[contains(@class,"mobile-article-content")]', + '//div[contains(@class,"sharedaddy")]', + '//div[contains(@class,"author-details")]', + '//div[@class="row ad"]', + '//div[contains(@class,"relatedposts")]', + '//div[@class="col-lg-12"]', + '//div[contains(@class,"widget")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bleepingcomputer.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bleepingcomputer.com.php new file mode 100644 index 00000000..7b740600 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bleepingcomputer.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://www.bleepingcomputer.com/news/google/chromes-sandbox-feature-infringes-on-three-patents-so-google-must-now-pay-20m/', + 'body' => array( + '//div[@class="article_section"]', + ), + 'strip' => array( + '//*[@itemprop="headline"]', + '//div[@class="cz-news-story-title-section"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.fefe.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.fefe.de.php new file mode 100644 index 00000000..39c88ae4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.fefe.de.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://blog.fefe.de/?ts=ad706a73', + 'body' => array( + '/html/body/ul', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.mapillary.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.mapillary.com.php new file mode 100644 index 00000000..ce016510 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.mapillary.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://blog.mapillary.com/update/2015/08/26/traffic-sign-updates.html', + 'body' => array( + '//div[contains(@class, "blog-post__content")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/brewers.mlb.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/brewers.mlb.com.php new file mode 100644 index 00000000..be406faf --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/brewers.mlb.com.php @@ -0,0 +1,22 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://m.brewers.mlb.com/news/article/161364798', + 'body' => array( + '//article[contains(@class,"article")]', + ), + 'strip' => array( + '//div[contains(@class,"ad-slot")]', + '//h1', + '//span[@class="timestamp"]', + '//div[contains(@class,"contributor-bottom")]', + '//div[contains(@class,"video")]', + '//ul[contains(@class,"social")]', + '//p[@class="tagline"]', + '//div[contains(@class,"social")]', + '//div[@class="button-wrap"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buenosairesherald.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buenosairesherald.com.php new file mode 100644 index 00000000..4e73e79f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buenosairesherald.com.php @@ -0,0 +1,17 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.buenosairesherald.com/article/199344/manzur-named-next-governor-of-tucum%C3%A1n', + 'body' => array( + '//div[@style="float:none"]', + ), + 'strip' => array( + '//div[contains(@class, "bz_alias_short_desc_container"]', + '//td[@id="bz_show_bug_column_1"]', + '//table[@id="attachment_table"]', + '//table[@class="bz_comment_table"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bunicomic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bunicomic.com.php new file mode 100644 index 00000000..ad83e436 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bunicomic.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.bunicomic.com/comic/buni-623/', + 'body' => array( + '//div[@class="comic-table"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buttersafe.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buttersafe.com.php new file mode 100644 index 00000000..1f313cd0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buttersafe.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://buttersafe.com/2015/04/21/the-incredible-flexible-man/', + 'body' => array( + '//div[@id="comic"]', + '//div[@class="post-comic"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cad-comic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cad-comic.com.php new file mode 100644 index 00000000..a631c97f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cad-comic.com.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%/cad/.+%' => array( + 'test_url' => 'http://www.cad-comic.com/cad/20150417', + 'body' => array( + '//*[@id="content"]/img', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chaoslife.findchaos.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chaoslife.findchaos.com.php new file mode 100644 index 00000000..ea6191e8 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chaoslife.findchaos.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://chaoslife.findchaos.com/pets-in-the-wild', + 'body' => array('//div[@id="comic"]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chinafile.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chinafile.com.php new file mode 100644 index 00000000..450117b3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chinafile.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.chinafile.com/books/shanghai-faithful?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+chinafile%2FAll+%28ChinaFile%29', + 'body' => array( + '//div[contains(@class,"pane-featured-photo-panel-pane-1")]', + '//div[contains(@class,"video-above-fold")]', + '//div[@class="sc-media"]', + '//div[contains(@class,"field-name-body")]', + ), + 'strip' => array( + '//div[contains(@class,"cboxes")]', + '//div[contains(@class,"l-middle")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cliquerefresh.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cliquerefresh.com.php new file mode 100644 index 00000000..9dcc7e54 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cliquerefresh.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%/comic.*%' => array( + 'test_url' => 'http://cliquerefresh.com/comic/078-stating-the-obvious/', + 'body' => array('//div[@class="comicImg"]/img | //div[@class="comicImg"]/a/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cnet.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cnet.com.php new file mode 100644 index 00000000..60767a53 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cnet.com.php @@ -0,0 +1,37 @@ +<?php +return array( + 'grabber' => array( + '%^/products.*%' => array( + 'test_url' => 'http://www.cnet.com/products/fibaro-flood-sensor/#ftag=CADf328eec', + 'body' => array( + '//li[contains(@class,"slide first"] || //figure[contains(@class,(promoFigure))]', + '//div[@class="quickInfo"]', + '//div[@class="col-6 ratings"]', + '//div[@id="editorReview"]', + ), + 'strip' => array( + '//script', + '//a[@class="clickToEnlarge"]', + '//div[@section="topSharebar"]', + '//div[contains(@class,"related")]', + '//div[contains(@class,"ad-")]', + '//div[@section="shortcodeGallery"]', + ), + ), + '%.*%' => array( + 'test_url' => 'http://cnet.com.feedsportal.com/c/34938/f/645093/s/4a340866/sc/28/l/0L0Scnet0N0Cnews0Cman0Eclaims0Eonline0Epsychic0Emade0Ehim0Ebuy0E10Emillion0Epowerball0Ewinning0Eticket0C0Tftag0FCAD590Aa51e/story01.htm', + 'body' => array( + '//p[@itemprop="description"]', + '//div[@itemprop="articleBody"]', + ), + 'strip' => array( + '//script', + '//a[@class="clickToEnlarge"]', + '//div[@section="topSharebar"]', + '//div[contains(@class,"related")]', + '//div[contains(@class,"ad-")]', + '//div[@section="shortcodeGallery"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/consomac.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/consomac.fr.php new file mode 100644 index 00000000..9209f9cb --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/consomac.fr.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://consomac.fr/news-2430-l-iphone-6-toujours-un-secret-bien-garde.html', + 'body' => array( + '//div[contains(@id, "newscontent")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cowbirdsinlove.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cowbirdsinlove.com.php new file mode 100644 index 00000000..3214c62a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cowbirdsinlove.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/crash.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/crash.net.php new file mode 100644 index 00000000..9d9b35a6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/crash.net.php @@ -0,0 +1,28 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.crash.net/motogp/interview/247550/1/exclusive-andrea-dovizioso-interview.html', + 'body' => array( + '//div[@id="content"]', + ), + 'strip' => array( + '//script', + '//style', + '//*[@title="Social Networking"]', + '//*[@class="crash-ad2"]', + '//*[@class="clearfix"]', + '//*[@class="crash-ad2"]', + '//*[contains(@id, "divCB"]', + '//*[@class="pnlComment"]', + '//*[@class="comments-tabs"]', + '//*[contains(@class, "ad-twocol"]', + '//*[@class="stories-list"]', + '//*[contains(@class, "btn")]', + '//*[@class="content"]', + '//h3', + ), + ), + ), +); + diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/csmonitor.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/csmonitor.com.php new file mode 100644 index 00000000..481e4b09 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/csmonitor.com.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.csmonitor.com/USA/Politics/2015/0925/John-Boehner-steps-down-Self-sacrificing-but-will-it-lead-to-better-government', + 'body' => array( + '//h2[@id="summary"]', + '//div[@class="flex-video youtube"]', + '//div[contains(@class,"eza-body")]', + ), + 'strip' => array( + '//span[@id="breadcrumb"]', + '//div[@id="byline-wrapper"]', + '//div[@class="injection"]', + '//*[contains(@class,"promo_link")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyjs.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyjs.com.php new file mode 100644 index 00000000..20eb1d75 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyjs.com.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://dailyjs.com/2014/08/07/p5js/', + 'body' => array( + '//div[@id="post"]', + ), + 'strip' => array( + '//h2[@class="post"]', + '//div[@class="meta"]', + '//*[contains(@class, "addthis_toolbox")]', + '//*[contains(@class, "addthis_default_style")]', + '//*[@class="navigation small"]', + '//*[@id="related"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyreporter.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyreporter.com.php new file mode 100644 index 00000000..db3fc0e1 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyreporter.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://dailyreporter.com/2016/01/09/us-supreme-court-case-could-weaken-government-workers-unions/', + 'body' => array( + '//div[contains(@class, "entry-content")]', + ), + 'strip' => array( + '//div[@class="dmcss_login_form"]', + '//*[contains(@class, "sharedaddy")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailytech.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailytech.com.php new file mode 100644 index 00000000..5d1df4a9 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailytech.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.dailytech.com/Apples+First+Fixes+to+iOS+9+Land+w+iOS++901+Release/article37495.htm', + 'body' => array( + '//div[@class="NewsBodyImage"]', + '//span[@id="lblSummary"]', + '//span[@id="lblBody"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/degroupnews.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/degroupnews.com.php new file mode 100644 index 00000000..91f5c56e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/degroupnews.com.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.degroupnews.com/medias/vodsvod/amazon-concurrence-la-chromecast-de-google-avec-fire-tv-stick', + 'body' => array( + '//div[@class="contenu"]', + ), + 'strip' => array( + '//div[contains(@class, "a2a")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/derstandard.at.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/derstandard.at.php new file mode 100644 index 00000000..7e95a51f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/derstandard.at.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://derstandard.at/2000010267354/The-Witcher-3-Hohe-Hardware-Anforderungen-fuer-PC-Spieler?ref=rss', + 'body' => array( + '//div[@class="copytext"]', + '//ul[@id="media-list"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dilbert.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dilbert.com.php new file mode 100644 index 00000000..b8e9b3d6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dilbert.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array( + '//img[@class="img-responsive img-comic"]', + ), + 'test_url' => 'http://dilbert.com/strip/2016-01-28', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/discovermagazine.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/discovermagazine.com.php new file mode 100644 index 00000000..ae0dfe71 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/discovermagazine.com.php @@ -0,0 +1,26 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://blogs.discovermagazine.com/neuroskeptic/2017/01/25/publishers-jeffrey-beall/', + 'body' => array( + '//div[@class="contentWell"]', + ), + 'strip' => array( + '//h1', + '//div[@class="breadcrumbs"]', + '//div[@class="mobile"]', + '//div[@class="fromIssue"]', + '//div[contains(@class,"belowDeck")]', + '//div[@class="meta"]', + '//div[@class="shareIcons"]', + '//div[@class="categories"]', + '//div[@class="navigation"]', + '//div[@class="heading"]', + '//div[contains(@id,"-ad")]', + '//div[@class="relatedArticles"]', + '//div[@id="disqus_thread"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/distrowatch.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/distrowatch.com.php new file mode 100644 index 00000000..aefc8f81 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/distrowatch.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://distrowatch.com/?newsid=08355', + 'body' => array( + '//td[@class="NewsText"][1]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dozodomo.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dozodomo.com.php new file mode 100644 index 00000000..e1166957 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dozodomo.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://dozodomo.com/bento/2014/03/04/lart-des-maki-de-takayo-kiyota/', + 'body' => array( + '//div[@class="joke"]', + '//div[@class="story-cover"]', + '//div[@class="story-content"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/drawingboardcomic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/drawingboardcomic.com.php new file mode 100644 index 00000000..cd30f2e0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/drawingboardcomic.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array('//img[@id="comicimage"]'), + 'strip' => array(), + 'test_url' => 'http://drawingboardcomic.com/index.php?comic=208', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/e-w-e.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/e-w-e.ru.php new file mode 100644 index 00000000..8139cc9a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/e-w-e.ru.php @@ -0,0 +1,22 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://e-w-e.ru/16-prekrasnyx-izobretenij-zhenshhin/', + 'body' => array( + '//div[contains(@class, "post_text")]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//*[@class="views_post"]', + '//*[@class="adman_mobile"]', + '//*[@class="adman_desctop"]', + '//*[contains(@rel, "nofollow")]', + '//*[contains(@class, "wp-smiley")]', + '//*[contains(text(),"ИÑточник:")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/economist.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/economist.com.php new file mode 100644 index 00000000..522032fd --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/economist.com.php @@ -0,0 +1,25 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.economist.com/blogs/buttonwood/2017/02/mixed-signals?fsrc=rss', + 'body' => array( + '//article', + ), + 'strip' => array( + '//span[@class="blog-post__siblings-list-header "]', + '//h1', + '//aside', + '//div[@class="blog-post__asideable-wrapper"]', + '//div[@class="share_inline_header"]', + '//div[@id="column-right"]', + '//div[contains(@class,"blog-post__siblings-list-aside")]', + '//div[@class="video-player__wrapper"]', + '//div[@class="blog-post__bottom-panel"]', + '//div[contains(@class,"latest-updates-panel__container")]', + '//div[contains(@class,"blog-post__asideable-content")]', + '//div[@aria-label="Advertisement"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/encyclopedie.naheulbeuk.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/encyclopedie.naheulbeuk.com.php new file mode 100644 index 00000000..19bcbdef --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/encyclopedie.naheulbeuk.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://encyclopedie.naheulbeuk.com/article.php3?id_article=352', + 'body' => array( + '//td//h1[@class="titre-texte"]', + '//td//div[@class="surtitre"]', + '//td//div[@class="texte"]', + ), + ) + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/endlessorigami.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/endlessorigami.com.php new file mode 100644 index 00000000..d06ed124 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/endlessorigami.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%-150x150%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/engadget.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/engadget.com.php new file mode 100644 index 00000000..cf9e4485 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/engadget.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.engadget.com/2015/04/20/dark-matter-discovery/?ncid=rss_truncated', + 'body' => array('//div[@id="page_body"]/div[@class="container@m-"]'), + 'strip' => array('//aside[@role="banner"]'), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/escapistmagazine.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/escapistmagazine.com.php new file mode 100644 index 00000000..e86b59cb --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/escapistmagazine.com.php @@ -0,0 +1,45 @@ +<?php +return array( + 'grabber' => array( + '%/articles/view/comicsandcosplay/comics/critical-miss.*%' => array( + 'body' => array('//*[@class="body"]/span/img | //div[@class="folder_nav_links"]/following::p'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/critical-miss/13776-Critical-Miss-on-Framerates?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/comicsandcosplay/comics/namegame.*%' => array( + 'body' => array('//*[@class="body"]/span/p/img[@height != "120"]'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/namegame/9759-Leaving-the-Nest?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/comicsandcosplay/comics/stolen-pixels.*%' => array( + 'body' => array('//*[@class="body"]/span/p[2]/img'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/stolen-pixels/8866-Stolen-Pixels-258-Where-the-Boys-Are?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/comicsandcosplay/comics/bumhugparade.*%' => array( + 'body' => array('//*[@class="body"]/span/p[2]/img'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/bumhugparade/8262-Bumhug-Parade-13?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/comicsandcosplay.*/comics/escapistradiotheater%' => array( + 'body' => array('//*[@class="body"]/span/p[2]/img'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/escapistradiotheater/8265-The-Escapist-Radio-Theater-13?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/comicsandcosplay/comics/paused.*%' => array( + 'body' => array('//*[@class="body"]/span/p[2]/img | //*[@class="body"]/span/div/img'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/paused/8263-Paused-16?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/comicsandcosplay/comics/fraughtwithperil.*%' => array( + 'body' => array('//*[@class="body"]'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/fraughtwithperil/12166-The-Escapist-Presents-Escapist-Comics-Critical-Miss-B-lyeh-Fhlop?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/video-games/columns/.*%' => array( + 'body' => array('//*[@id="article_content"]'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/video-games/columns/experienced-points/13971-What-50-Shades-and-Batman-Have-in-Common.2', + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/espn.go.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/espn.go.com.php new file mode 100644 index 00000000..76a20f74 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/espn.go.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://espn.go.com/nfl/story/_/id/13388208/jason-whitlock-chip-kelly-controversy', + 'body' => array( + '//p', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/exocomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/exocomics.com.php new file mode 100644 index 00000000..5adc59f8 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/exocomics.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array('//a[@class="comic"]/img'), + 'strip' => array(), + 'test_url' => 'http://www.exocomics.com/379', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/explosm.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/explosm.net.php new file mode 100644 index 00000000..3fdf02c0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/explosm.net.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://explosm.net/comics/3803/', + 'body' => array( + '//div[@id="comic-container"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/extrafabulouscomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/extrafabulouscomics.com.php new file mode 100644 index 00000000..12697ccb --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/extrafabulouscomics.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%-150x150%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/factroom.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/factroom.ru.php new file mode 100644 index 00000000..a572061d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/factroom.ru.php @@ -0,0 +1,27 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.factroom.ru/life/20-facts-about-oil', + 'body' => array( + '//div[@class="post"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//h1', + '//div[@id="yandex_ad2"]', + '//*[@class="jp-relatedposts"]', + '//div[contains(@class, "likely-desktop")]', + '//div[contains(@class, "likely-mobile")]', + '//p[last()]', + '//div[contains(@class, "facebook")]', + '//div[contains(@class, "desktop-underpost-direct")]', + '//div[contains(@class, "source-box")]', + '//div[contains(@class, "under-likely-desktop")]', + '//div[contains(@class, "mobile-down-post")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcodesign.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcodesign.com.php new file mode 100644 index 00000000..74e70a86 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcodesign.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.fastcodesign.com/3026548/exposure/peek-inside-the-worlds-forbidden-subway-tunnels', + 'body' => array( + '//article[contains(@class, "body prose")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcoexist.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcoexist.com.php new file mode 100644 index 00000000..6916f280 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcoexist.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.fastcoexist.com/3026114/take-a-seat-on-this-gates-funded-future-toilet-that-will-change-how-we-think-about-poop', + 'body' => array( + '//article[contains(@class, "body prose")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcompany.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcompany.com.php new file mode 100644 index 00000000..e0869a29 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcompany.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.fastcompany.com/3026712/fast-feed/elon-musk-an-apple-tesla-merger-is-very-unlikely', + 'body' => array( + '//article[contains(@class, "body prose")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ffworld.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ffworld.com.php new file mode 100644 index 00000000..20a47b2d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ffworld.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.ffworld.com/?rub=news&page=voir&id=2709', + 'body' => array( + '//div[@class="news_body"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/foreignpolicy.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/foreignpolicy.com.php new file mode 100644 index 00000000..3cbcddc4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/foreignpolicy.com.php @@ -0,0 +1,21 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://foreignpolicy.com/2016/01/09/networking-giant-pulls-nsa-linked-code-exploited-by-hackers/', + 'body' => array( + '//article', + ), + 'strip' => array( + '//div[@id="post-category"]', + '//div[@id="desktop-right"]', + '//h1', + '//section[@class="article-meta"]', + '//div[@class="side-panel-wrapper"]', + '//*[contains(@class, "share-")]', + '//*[contains(@id, "taboola-")]', + '//div[@class="comments"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fossbytes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fossbytes.com.php new file mode 100644 index 00000000..6ce47256 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fossbytes.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://fossbytes.com/fbi-hacked-1000-computers-to-shut-down-largest-child-pornography-site-on-the-dark-web/', + 'body' => array( + '//div[@class="entry-inner"]', + ), + 'strip' => array( + '//*[@class="at-above-post addthis_default_style addthis_toolbox at-wordpress-hide"]', + '//*[@class="at-below-post addthis_default_style addthis_toolbox at-wordpress-hide"]', + '//*[@class="at-below-post-recommended addthis_default_style addthis_toolbox at-wordpress-hide"]', + '//*[@class="code-block code-block-12 ai-desktop"]', + '//*[@class="code-block code-block-13 ai-tablet-phone"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fototelegraf.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fototelegraf.ru.php new file mode 100644 index 00000000..ca2f85aa --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fototelegraf.ru.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://fototelegraf.ru/?p=348232', + 'body' => array( + '//div[@class="post-content"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//div[@class="imageButtonsBlock"]', + '//div[@class="adOnPostBtwImg"]', + '//div[contains(@class, "post-tags")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fowllanguagecomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fowllanguagecomics.com.php new file mode 100644 index 00000000..3f62f071 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fowllanguagecomics.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array('//*[@id="comic"] | //*[@class="post-image"]'), + 'strip' => array(), + 'test_url' => 'http://www.fowllanguagecomics.com/comic/working-out/', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geek.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geek.com.php new file mode 100644 index 00000000..d9ccecc2 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geek.com.php @@ -0,0 +1,17 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.geek.com/news/the-11-best-ways-to-eat-eggs-1634076/', + 'body' => array( + '//div[@class="articleinfo"]/figure', + '//div[@class="articleinfo"]/article', + '//span[@class="by"]', + ), + 'strip' => array( + '//span[@class="red"]', + '//div[@class="on-target"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geektimes.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geektimes.ru.php new file mode 100644 index 00000000..19541386 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geektimes.ru.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://geektimes.ru/post/289151/', + 'body' => array( + "//div[contains(concat(' ',normalize-space(@class),' '),' content ')]" + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gerbilwithajetpack.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gerbilwithajetpack.com.php new file mode 100644 index 00000000..44013b3b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gerbilwithajetpack.com.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array( + '//div[@id="comic-1"]', + '//div[@class="entry"]', + ), + 'test_url' => 'http://gerbilwithajetpack.com/passing-the-digital-buck/', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/giantitp.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/giantitp.com.php new file mode 100644 index 00000000..d9c3ae5d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/giantitp.com.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%/comics/oots.*%' => array( + 'test_url' => 'http://www.giantitp.com/comics/oots0989.html', + 'body' => array( + '//td[@align="center"]/img', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/github.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/github.com.php new file mode 100644 index 00000000..726634f9 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/github.com.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://github.com/audreyr/favicon-cheat-sheet', + 'body' => array( + '//article[contains(@class, "entry-content")]', + ), + 'strip' => array( + '//h1', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gocomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gocomics.com.php new file mode 100644 index 00000000..32960f0e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gocomics.com.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.gocomics.com/pearlsbeforeswine/2015/05/30', + 'body' => array( + '//div[1]/p[1]/a[1]/img', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/golem.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/golem.de.php new file mode 100644 index 00000000..84224830 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/golem.de.php @@ -0,0 +1,20 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.golem.de/news/breko-telekom-verzoegert-gezielt-den-vectoring-ausbau-1311-102974.html', + 'body' => array( + '//header[@class="cluster-header"]', + '//header[@class="paged-cluster-header"]', + '//div[@class="formatted"]', + ), + 'next_page' => array( + '//a[@id="atoc_next"]' + ), + 'strip' => array( + '//header[@class="cluster-header"]/a', + '//div[@id="iqadtile4"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gorabbit.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gorabbit.ru.php new file mode 100644 index 00000000..4e432481 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gorabbit.ru.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://gorabbit.ru/article/10-oshchushcheniy-za-rulem-kogda-tolko-poluchil-voditelskie-prava', + 'body' => array( + '//div[@class="detail_text"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//div[@class="socials"]', + '//div[@id="cr_1"]', + '//div[@class="related_items"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/habrahabr.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/habrahabr.ru.php new file mode 100644 index 00000000..3f1ec165 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/habrahabr.ru.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://habrahabr.ru/company/pentestit/blog/328606/', + 'body' => array( + "//div[contains(concat(' ',normalize-space(@class),' '),' content ')]" + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/happletea.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/happletea.com.php new file mode 100644 index 00000000..75b0b83d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/happletea.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array( + '//div[@id="comic"]', + '//div[@class="entry"]', + ), + 'strip' => array('//div[@class="ssba"]'), + 'test_url' => 'http://www.happletea.com/comic/mans-best-friend/', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hardware.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hardware.fr.php new file mode 100644 index 00000000..56aec4f4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hardware.fr.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%^/news.*%' => array( + 'test_url' => 'http://www.hardware.fr/news/14760/intel-lance-nouveaux-ssd-nand-3d.html', + 'body' => array( + '//div[@class="content_actualite"]/div[@class="md"]', + ) + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/heise.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/heise.de.php new file mode 100644 index 00000000..2055b3bc --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/heise.de.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.heise.de/security/meldung/BND-300-Millionen-Euro-fuer-Fruehwarnsystem-gegen-Cyber-Attacken-2192237.html', + 'body' => array( + '//div[@class="meldung_wrapper"]', + '//div[@class="artikel_content"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hotshowlife.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hotshowlife.com.php new file mode 100644 index 00000000..faf01f3d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hotshowlife.com.php @@ -0,0 +1,23 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://hotshowlife.com/top-10-chempionov-produktov-po-szhiganiyu-kalorij/', + 'body' => array( + '//div[@class="entry-content"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//div[@class="ads2"]', + '//div[@class="mistape_caption"]', + '//div[contains(@class, "et_social_media_hidden")]', + '//div[contains(@class, "et_social_inline_bottom")]', + '//div[contains(@class, "avatar")]', + '//ul[contains(@class, "entry-tags")]', + '//div[contains(@class, "entry-meta")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/huffingtonpost.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/huffingtonpost.com.php new file mode 100644 index 00000000..b52b07b5 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/huffingtonpost.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.huffingtonpost.com/2014/02/20/centscere-social-media-syracuse_n_4823848.html', + 'body' => array( + '//article[@class="content")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/imogenquest.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/imogenquest.net.php new file mode 100644 index 00000000..3214c62a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/imogenquest.net.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/indiehaven.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/indiehaven.com.php new file mode 100644 index 00000000..a40ce694 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/indiehaven.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://indiehaven.com/no-mans-sky-is-a-solo-space-adventure-and-im-ok-with-that/', + 'body' => array( + '//section[contains(@class, "entry-content")]', + ) + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ing.dk.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ing.dk.php new file mode 100644 index 00000000..5a021a08 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ing.dk.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://ing.dk/artikel/smart-husisolering-og-styring-skal-mindske-japans-energikrise-164517', + 'body' => array( + '//section[contains(@class, "teaser")]', + '//section[contains(@class, "body")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/invisiblebread.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/invisiblebread.com.php new file mode 100644 index 00000000..90f87597 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/invisiblebread.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%(<img.+(\\d{4}-\\d{2}-\\d{2}).+/>)%' => '$1<img src="http://invisiblebread.com/eps/$2-extrapanel.png"/>', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ir.amd.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ir.amd.com.php new file mode 100644 index 00000000..af99fe99 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ir.amd.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array('//span[@class="ccbnTxt"]'), + 'strip' => array(), + 'test_url' => 'http://ir.amd.com/phoenix.zhtml?c=74093&p=RssLanding&cat=news&id=2055819', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantimes.co.jp.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantimes.co.jp.php new file mode 100644 index 00000000..9959441d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantimes.co.jp.php @@ -0,0 +1,21 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.japantimes.co.jp/news/2015/09/27/world/social-issues-world/pope-meets-sex-abuse-victims-philadelphia-promises-accountability/', + 'body' => array( + '//article[@role="main"]', + ), + 'strip' => array( + '//script', + '//header', + '//div[contains(@class, "meta")]', + '//div[@class="clearfix"]', + '//div[@class="OUTBRAIN"]', + '//ul[@id="content_footer_menu"]', + '//div[@class="article_footer_ad"]', + '//div[@id="disqus_thread"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantoday.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantoday.com.php new file mode 100644 index 00000000..22485d69 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantoday.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.japantoday.com/category/politics/view/japan-u-s-to-sign-new-base-environment-pact', + 'body' => array( + '//div[@id="article_container"]', + ), + 'strip' => array( + '//h2', + '//div[@id="article_info"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/journaldugeek.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/journaldugeek.com.php new file mode 100644 index 00000000..876b2698 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/journaldugeek.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www./2014/05/20/le-playstation-now-arrive-en-beta-fermee-aux-etats-unis/', + 'body' => array( + '//div[@class="post-content"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/jsonline.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/jsonline.com.php new file mode 100644 index 00000000..5895256e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/jsonline.com.php @@ -0,0 +1,37 @@ +<?php +return array( + 'grabber' => array( + '%.%/picture-gallery/%' => array( + 'test_url' => 'http://www.jsonline.com/picture-gallery/news/local/milwaukee/2017/02/22/photos-aclu-sues-milwaukee-police-over-profiling-stop-and-frisk/98250836/', + 'body' => array( + '//div[@class="priority-asset-gallery galleries standalone hasendslate"]', + ), + 'strip' => array( + '//div[@class="buy-photo-btn"]', + '//div[@class="gallery-thumbs thumbs pag-thumbs")]', + ), + ), + '%.*%' => array( + 'test_url' => 'http://www.jsonline.com/news/usandworld/as-many-as-a-million-expected-for-popes-last-mass-in-us-b99585180z1-329688131.html', + 'body' => array( + '//div[@itemprop="articleBody"]', + ), + 'strip' => array( + '//h1', + '//iframe', + '//span[@class="mycapture-small-btn mycapture-btn-with-text mycapture-expandable-photo-btn-small js-mycapture-btn-small"]', + '//div[@class="close-wrap"]', + '//div[contains(@class,"ui-video-wrapper")]', + '//div[contains(@class,"media-mob")]', + '//div[contains(@class,"left-mob")]', + '//div[contains(@class,"nerdbox")]', + '//p/span', + '//div[contains(@class,"oembed-asset")]', + '//*[contains(@class,"share")]', + '//div[contains(@class,"gallery-asset")]', + '//div[contains(@class,"oembed-asset")]', + '//div[@class="article-print-url"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/justcoolidea.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/justcoolidea.ru.php new file mode 100644 index 00000000..089ff29c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/justcoolidea.ru.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://justcoolidea.ru/idealnyj-sad-samodelnye-proekty-dlya-berezhlivogo-domovladeltsa/', + 'body' => array( + '//section[@class="entry-content"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//*[contains(@class, "essb_links")]', + '//*[contains(@rel, "nofollow")]', + '//*[contains(@class, "ads")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kanpai.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kanpai.fr.php new file mode 100644 index 00000000..c3a1abc4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kanpai.fr.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.kanpai.fr/japon/comment-donner-lheure-en-japonais.html', + 'body' => array( + '//div[@class="single-left"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/karriere.jobfinder.dk.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/karriere.jobfinder.dk.php new file mode 100644 index 00000000..25d6dfa3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/karriere.jobfinder.dk.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://karriere.jobfinder.dk/artikel/dansk-professor-skal-lede-smart-grid-forskning-20-millioner-dollars-763', + 'body' => array( + '//section[contains(@class, "teaser")]', + '//section[contains(@class, "body")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kodi.tv.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kodi.tv.php new file mode 100644 index 00000000..439fc907 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kodi.tv.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://kodi.tv/article/andwere-baaaaack', + 'body' => array( + '//div[@class="l-region--content"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreaherald.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreaherald.com.php new file mode 100644 index 00000000..96510560 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreaherald.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.koreaherald.com/view.php?ud=20150926000018', + 'body' => array( + '//div[@id="articleText"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreatimes.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreatimes.php new file mode 100644 index 00000000..f274b4a9 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreatimes.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.koreatimes.co.kr/www/news/nation/2015/12/116_192409.html', + 'body' => array( + '//div[@id="p"]', + ), + 'strip' => array( + '//div[@id="webtalks_btn_listenDiv"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lastplacecomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lastplacecomics.com.php new file mode 100644 index 00000000..12697ccb --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lastplacecomics.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%-150x150%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/legorafi.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/legorafi.fr.php new file mode 100644 index 00000000..e6aae46b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/legorafi.fr.php @@ -0,0 +1,22 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => array( + 'http://www.legorafi.fr/2016/12/16/gorafi-magazine-bravo-vous-avez-bientot-presque-survecu-a-2016/', + 'http://www.legorafi.fr/2016/12/15/manuel-valls-promet-quune-fois-elu-il-debarrassera-la-france-de-manuel-valls/', + ), + 'body' => array( + '//section[@id="banner_magazine"]', + '//figure[@class="main_picture"]', + '//div[@class="content"]', + ), + 'strip' => array( + '//figcaption', + '//div[@class="sharebox"]', + '//div[@class="tags"]', + '//section[@class="taboola_article"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lejapon.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lejapon.fr.php new file mode 100644 index 00000000..8f2b2932 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lejapon.fr.php @@ -0,0 +1,17 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://lejapon.fr/guide-voyage-japon/5223/tokyo-sous-la-neige.htm', + 'body' => array( + '//div[@class="entry"]', + ), + 'strip' => array( + '//*[contains(@class, "addthis_toolbox")]', + '//*[contains(@class, "addthis_default_style")]', + '//*[@class="navigation small"]', + '//*[@id="related"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lesjoiesducode.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lesjoiesducode.fr.php new file mode 100644 index 00000000..369206ab --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lesjoiesducode.fr.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://lesjoiesducode.fr/post/75576211207/quand-lappli-ne-fonctionne-plus-sans-aucune-raison', + 'body' => array( + '//div[@class="blog-post-content"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lfg.co.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lfg.co.php new file mode 100644 index 00000000..d978a5fc --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lfg.co.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.lfg.co/page/871/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+LookingForGroup+%28Looking+For+Group%29&utm_content=FeedBurner', + 'body' => array( + '//*[@id="comic"]/img | //*[@class="content"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.com.php new file mode 100644 index 00000000..b9a69338 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://lifehacker.com/bring-water-bottle-caps-into-concerts-to-protect-your-d-1269334973', + 'body' => array( + '//div[contains(@class, "row")/img', + '//div[contains(@class, "content-column")]', + ), + 'strip' => array( + '//*[contains(@class, "meta")]', + '//span[contains(@class, "icon")]', + '//h1', + '//aside', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.ru.php new file mode 100644 index 00000000..bc140f67 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.ru.php @@ -0,0 +1,22 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://lifehacker.ru/2016/03/03/polymail/', + 'body' => array( + '//div[@class="post-content"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//*[@class="wp-thumbnail-caption"]', + '//*[contains(@class, "social-likes")]', + '//*[@class="jp-relatedposts"]', + '//*[contains(@class, "wpappbox")]', + '//*[contains(@class, "icon__image")]', + '//div[@id="hypercomments_widget"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.php new file mode 100644 index 00000000..2520d0d0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.linux.org/threads/lua-the-scripting-interpreter.8352/', + 'body' => array( + '//div[@class="messageContent"]', + ), + 'strip' => array( + '//aside', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.ru.php new file mode 100644 index 00000000..7fa02497 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.ru.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.linux.org/threads/lua-the-scripting-interpreter.8352/', + 'body' => array( + '//div[@itemprop="articleBody"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linuxinsider.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linuxinsider.com.php new file mode 100644 index 00000000..4e0a4cc1 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linuxinsider.com.php @@ -0,0 +1,20 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.linuxinsider.com/story/82526.html?rss=1', + 'body' => array( + '//div[@id="story"]', + ), + 'strip' => array( + '//script', + '//h1', + '//div[@id="story-toolbox1"]', + '//div[@id="story-byline"]', + '//div[@id="story"]/p', + '//div[@class="story-advertisement"]', + '//iframe', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lists.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lists.php new file mode 100644 index 00000000..c7051a20 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lists.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://lists.freebsd.org/pipermail/freebsd-announce/2013-September/001504.html', + 'body' => array( + '//pre', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loadingartist.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loadingartist.com.php new file mode 100644 index 00000000..d06ed124 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loadingartist.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%-150x150%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loldwell.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loldwell.com.php new file mode 100644 index 00000000..d358e156 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loldwell.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://loldwell.com/?comic=food-math-101', + 'body' => array('//*[@id="comic"]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lukesurl.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lukesurl.com.php new file mode 100644 index 00000000..816233dd --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lukesurl.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array('//div[@id="comic"]//img'), + 'strip' => array(), + 'test_url' => 'http://www.lukesurl.com/archives/comic/665-3-of-clubs', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/macg.co.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/macg.co.php new file mode 100644 index 00000000..bbe6dbcd --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/macg.co.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.macg.co//logiciels/2014/05/feedly-sameliore-un-petit-peu-sur-mac-82205', + 'body' => array( + '//div[contains(@class, "field-name-body")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marc.info.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marc.info.php new file mode 100644 index 00000000..5f582a6d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marc.info.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://marc.info/?l=openbsd-misc&m=141987113202061&w=2', + 'body' => array( + '//pre', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marriedtothesea.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marriedtothesea.com.php new file mode 100644 index 00000000..469640df --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marriedtothesea.com.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.marriedtothesea.com/index.php?date=052915', + 'body' => array( + '//div[@align]/a/img', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marycagle.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marycagle.com.php new file mode 100644 index 00000000..b8665e35 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marycagle.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array( + '//img[@id="cc-comic"]', + '//div[@class="cc-newsbody"]', + ), + 'strip' => array(), + 'test_url' => 'http://www.marycagle.com/letsspeakenglish/74-grim-reality/', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/maximumble.thebookofbiff.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/maximumble.thebookofbiff.com.php new file mode 100644 index 00000000..88800546 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/maximumble.thebookofbiff.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://maximumble.thebookofbiff.com/2015/04/20/1084-change/', + 'body' => array('//div[@id="comic"]/div/a/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/medium.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/medium.com.php new file mode 100644 index 00000000..e20860e0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/medium.com.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://medium.com/lessons-learned/917b8b63ae3e', + 'body' => array( + '//div[@class="section-content"]', + ), + 'strip' => array( + '//div[contains(@class,"metabar")]', + '//img[contains(@class,"thumbnail")]', + '//h1', + '//blockquote', + '//div[@class="aspectRatioPlaceholder-fill"]', + '//footer' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mercworks.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mercworks.net.php new file mode 100644 index 00000000..c7a27dea --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mercworks.net.php @@ -0,0 +1,17 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array('//div[@id="comic"]', + '//div[contains(@class,"entry-content")]', + ), + 'strip' => array(), + 'test_url' => 'http://mercworks.net/comicland/healthy-choice/', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/metronieuws.nl.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/metronieuws.nl.php new file mode 100644 index 00000000..5011169f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/metronieuws.nl.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.metronieuws.nl/sport/2015/04/broer-fellaini-zorgde-bijna-voor-paniek-bij-mourinho', + 'body' => array('//div[contains(@class,"article-top")]/div[contains(@class,"image-component")] | //div[@class="article-full-width"]/div[1]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/milwaukeenns.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/milwaukeenns.php new file mode 100644 index 00000000..ddb29a56 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/milwaukeenns.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://milwaukeenns.org/2016/01/08/united-way-grant-enables-sdc-to-restore-free-tax-assistance-program/', + 'body' => array( + '//div[@class="pf-content"]', + ), + 'strip' => array( + '//div[@class="printfriendly"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mokepon.smackjeeves.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mokepon.smackjeeves.com.php new file mode 100644 index 00000000..1ddcd407 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mokepon.smackjeeves.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://mokepon.smackjeeves.com/comics/2120096/chapter-9-page-68/', + 'body' => array('//*[@id="comic_area_inner"]/img | //*[@id="comic_area_inner"]/a/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monandroid.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monandroid.com.php new file mode 100644 index 00000000..f87560e2 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monandroid.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.monandroid.com/blog/tutoriel-avance-activer-le-stockage-fusionne-sur-android-6-marshamallow-t12.html', + 'body' => array( + '//div[@class="blog-post-body"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monwindows.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monwindows.com.php new file mode 100644 index 00000000..b2b24d74 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monwindows.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.monwindows.com/tout-savoir-sur-le-centre-d-action-de-windows-phone-8-1-t40574.html', + 'body' => array( + '//div[@class="blog-post-body"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/moya-planeta.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/moya-planeta.ru.php new file mode 100644 index 00000000..dd842844 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/moya-planeta.ru.php @@ -0,0 +1,21 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.moya-planeta.ru/travel/view/chto_yaponcu_horosho_russkomu_ne_ponyat_20432/', + 'body' => array( + '//div[@class="full_object"]', + ), + 'strip' => array( + '//div[@class="full_object_panel object_panel"]', + '//div[@class="full_object_panel_geo object_panel"]', + '//div[@class="full_object_title"]', + '//div[@class="full_object_social_likes"]', + '//div[@class="full_object_planeta_likes"]', + '//div[@class="full_object_go2comments"]', + '//div[@id="yandex_ad_R-163191-3"]', + '//div[@class="full_object_shop_article_recommend"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mrlovenstein.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mrlovenstein.com.php new file mode 100644 index 00000000..b971091f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mrlovenstein.com.php @@ -0,0 +1,9 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%alt="(.+)" */>%' => '/><br/>$1', + '%\.png%' => '_rollover.png', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/muckrock.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/muckrock.com.php new file mode 100644 index 00000000..9e354a36 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/muckrock.com.php @@ -0,0 +1,20 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://www.muckrock.com/news/archives/2016/jan/13/5-concerns-private-prisons/', + 'body' => array( + '//div[@class="content"]', + ), + 'strip' => array( + '//div[@class="newsletter-widget"]', + '//div[@class="contributors"]', + '//time', + '//h1', + '//div[@class="secondary"]', + '//aside', + '//div[@class="articles__related"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mynorthshorenow.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mynorthshorenow.com.php new file mode 100644 index 00000000..b6309157 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mynorthshorenow.com.php @@ -0,0 +1,27 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.mynorthshorenow.com/story/news/local/fox-point/2017/04/04/fox-point-building-board-approves-dunwood-commons-project/99875570/', + 'body' => array( + '//div[@itemprop="articleBody"]', + ), + 'strip' => array( + '//h1', + '//iframe', + '//span[@class="mycapture-small-btn mycapture-btn-with-text mycapture-expandable-photo-btn-small js-mycapture-btn-small"]', + '//div[@class="close-wrap"]', + '//div[contains(@class,"ui-video-wrapper")]', + '//div[contains(@class,"media-mob")]', + '//div[contains(@class,"left-mob")]', + '//div[contains(@class,"nerdbox")]', + '//p/span', + '//div[contains(@class,"oembed-asset")]', + '//*[contains(@class,"share")]', + '//div[contains(@class,"gallery-asset")]', + '//div[contains(@class,"oembed-asset")]', + '//div[@class="article-print-url"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nakedCapitalism.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nakedCapitalism.php new file mode 100644 index 00000000..ec2d5fd5 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nakedCapitalism.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://feedproxy.google.com/~r/NakedCapitalism/~3/JOBxEHxN8ZI/mark-blyth-liberalism-undermined-democracy-failure-democratic-party.html', + 'body' => array( + '//div[@class="pf-content"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nasa.gov.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nasa.gov.php new file mode 100644 index 00000000..c6692d07 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nasa.gov.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://www.nasa.gov/image-feature/jpl/pia20514/coy-dione', + 'body' => array( + '//div[@class="article-body"]', + ), + 'strip' => array( + '//div[@class="title-bar"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nat-geo.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nat-geo.ru.php new file mode 100644 index 00000000..1a42d997 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nat-geo.ru.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.nat-geo.ru/fact/868093-knidos-antichnyy-naukograd/', + 'body' => array( + '//div[@class="article-inner-text"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nationaljournal.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nationaljournal.com.php new file mode 100644 index 00000000..5e612bef --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nationaljournal.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.nationaljournal.com/s/354962/south-carolina-evangelicals-outstrip-establishment?mref=home_top_main', + 'body' => array( + '//div[@class="section-body"]', + ), + 'strip' => array( + '//*[contains(@class, "-related")]', + '//*[contains(@class, "social")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nature.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nature.com.php new file mode 100644 index 00000000..6b9e87f4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nature.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.nature.com/doifinder/10.1038/nature.2015.18340', + 'body' => array( + '//div[contains(@class,"main-content")]', + ), + 'strip' => array(), + ), + ), +); + diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nba.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nba.com.php new file mode 100644 index 00000000..c8ea926f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nba.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.nba.com/2015/news/09/25/knicks-jackson-to-spend-more-time-around-coaching-staff.ap/index.html?rss=true', + 'body' => array( + '//div[@class="paragraphs"]', + ), + 'strip' => array( + '//div[@id="nbaArticleSocialWrapper_bot"]', + '//h5', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nedroid.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nedroid.com.php new file mode 100644 index 00000000..3214c62a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nedroid.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/networkworld.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/networkworld.com.php new file mode 100644 index 00000000..18524354 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/networkworld.com.php @@ -0,0 +1,20 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.networkworld.com/article/3020585/security/the-incident-response-fab-five.html', + 'body' => array( + '//figure/img[@class="hero-img"]', + '//section[@class="deck"]', + '//div[@itemprop="articleBody"] | //div[@itemprop="reviewBody"]', + '//div[@class="carousel-inside-crop"]', + ), + 'strip' => array( + '//script', + '//aside', + '//div[@class="credit"]', + '//div[@class="view-large"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/neustadt-ticker.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/neustadt-ticker.de.php new file mode 100644 index 00000000..e0c0d19d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/neustadt-ticker.de.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.neustadt-ticker.de/41302/alltag/kultur/demo-auf-der-boehmischen', + 'body' => array( + '//div[@class="entry-content"]', + ), + 'strip' => array( + '//*[contains(@class, "sharedaddy")]', + '//*[contains(@class, "yarpp-related")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nextinpact.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nextinpact.com.php new file mode 100644 index 00000000..29dd9d6b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nextinpact.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.nextinpact.com/news/101122-3d-nand-intel-lance-six-nouvelles-gammes-ssd-pour-tous-usages.htm', + 'body' => array( + '//div[@class="container_article"]', + ), + 'strip' => array( + '//div[@class="infos_article"]', + '//div[@id="actu_auteur"]', + '//div[@id="soutenir_journaliste"]', + '//section[@id="bandeau_abonnez_vous"]', + '//br' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/niceteethcomic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/niceteethcomic.com.php new file mode 100644 index 00000000..f41e4438 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/niceteethcomic.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%/archives.*%' => array( + 'test_url' => 'http://niceteethcomic.com/archives/page119/', + 'body' => array('//*[@class="comicpane"]/a/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nichtlustig.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nichtlustig.de.php new file mode 100644 index 00000000..4d083f98 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nichtlustig.de.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%.*static.nichtlustig.de/comics/full/(\\d+).*%s' => '<img src="http://static.nichtlustig.de/comics/full/$1.jpg" />', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/oglaf.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/oglaf.com.php new file mode 100644 index 00000000..8b2b5b65 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/oglaf.com.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array( + '//img[@id="strip"]', + '//a/div[@id="nx"]/..', + ), + 'strip' => array(), + 'test_url' => 'http://oglaf.com/slodging/', + ), + ), + 'filter' => array( + '%.*%' => array( + '%alt="(.+)" title="(.+)" */>%' => '/><br/>$1<br/>$2<br/>', + '%</a>%' => 'Next page</a>', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onhax.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onhax.net.php new file mode 100644 index 00000000..213849d8 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onhax.net.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://onhax.net/process-lasso-8-9-1-4-pro-key-portable-is-here-latest', + 'body' => array( + '//div[@class="postcontent"]', + ), + 'strip' => array( + '//*[@class="sharedaddy sd-sharing-enabled"]', + '//*[@class="yarpp-related"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onmilwaukee.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onmilwaukee.php new file mode 100644 index 00000000..f66ac4b8 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onmilwaukee.php @@ -0,0 +1,24 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://onmilwaukee.com/movies/articles/downerspelunking.html', + 'body' => array( + '//article[contains(@class, "show")]', + ), + 'strip' => array( + '//h1', + '//div[contains(@class,"-ad")]', + '//div[contains(@class,"_ad")]', + '//div[@id="pub_wrapper"]', + '//div[contains(@class,"share_tools")]', + '//div[@class="clearfix"]', + '//div[contains(@class,"image_control")]', + '//section[@class="ribboned"]', + '//div[contains(@class,"sidebar")]', + '//aside[@class="article_tag_list"]', + '//section[contains(@id,"more_posts")]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openculture.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openculture.com.php new file mode 100644 index 00000000..84f2beed --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openculture.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.openculture.com/2017/03/are-we-living-inside-a-computer-simulation-watch-the-simulation-argument.html', + 'body' => array( + '//div[@class="entry"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opennet.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opennet.ru.php new file mode 100644 index 00000000..1fb7722b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opennet.ru.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://www.opennet.ru/opennews/art.shtml?num=46549', + 'body' => array( + '//*[@id="r_memo"]', + ), + 'strip' => array( + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openrightsgroup.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openrightsgroup.org.php new file mode 100644 index 00000000..94139a72 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openrightsgroup.org.php @@ -0,0 +1,20 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://www.openrightsgroup.org/blog/2014/3-days-to-go-till-orgcon2014', + 'body' => array( + '//div[contains(@class, "content")]/div', + ), + 'strip' => array( + '//h2[1]', + '//div[@class="info"]', + '//div[@class="tags"]', + '//div[@class="comments"]', + '//div[@class="breadcrumbs"]', + '//h1[@class="pageTitle"]', + '//p[@class="bookmarkThis"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opensource.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opensource.com.php new file mode 100644 index 00000000..60f3577b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opensource.com.php @@ -0,0 +1,22 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://opensource.com/life/15/10/how-internet-things-will-change-way-we-think', + 'body' => array( + '//div[@id="article-template"]', + ), + 'strip' => array( + '//div[contains(@class,"os-article__sidebar")]', + '//div[@class="panel-pane pane-node-title"]', + '//div[@class="panel-pane pane-os-article-byline"]', + '//ul', + '//div[contains(@class,"-license")]', + '//div[contains(@class,"-tags")]', + '//div[@class="panel-pane pane-os-article-byline"]', + '//div[@class="os-article__content-below"]', + '//div[@id="comments"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/optipess.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/optipess.com.php new file mode 100644 index 00000000..3214c62a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/optipess.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/osnews.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/osnews.com.php new file mode 100644 index 00000000..1d1396c8 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/osnews.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://osnews.com/story/28863/Google_said_to_be_under_US_antitrust_scrutiny_over_Android', + 'body' => array( + '//div[@class="newscontent1"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pastebin.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pastebin.com.php new file mode 100644 index 00000000..b20bf41c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pastebin.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://pastebin.com/ed1pP9Ak', + 'body' => array( + '//div[@class="text"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/peebleslab.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/peebleslab.com.php new file mode 100644 index 00000000..ce4891d1 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/peebleslab.com.php @@ -0,0 +1,9 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + // the extra space is required to strip the title cleanly + '%title="(.+) " */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/penny-arcade.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/penny-arcade.com.php new file mode 100644 index 00000000..dd39983b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/penny-arcade.com.php @@ -0,0 +1,21 @@ +<?php +return array( + 'grabber' => array( + '%/news/.*%' => array( + 'test_url' => 'http://penny-arcade.com/news/post/2015/04/15/101-part-two', + 'body' => array( + '//*[@class="postBody"]/*', + ), + 'strip' => array( + ), + ), + '%/comic/.*%' => array( + 'test_url' => 'http://penny-arcade.com/comic/2015/04/15', + 'body' => array( + '//*[@id="comicFrame"]/a/img', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pixelbeat.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pixelbeat.org.php new file mode 100644 index 00000000..fa9052ea --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pixelbeat.org.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.pixelbeat.org/programming/sigpipe_handling.html#1425573246', + 'body' => array( + '//div[@class="contentText"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/plus.google.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/plus.google.com.php new file mode 100644 index 00000000..5e48a6cf --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/plus.google.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://plus.google.com/+LarryPage/posts/Lh8SKC6sED1', + 'body' => array( + '//div[@role="article"]/div[contains(@class, "eE")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/popstrip.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/popstrip.com.php new file mode 100644 index 00000000..801a2814 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/popstrip.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%(<img.+/s/[^"]+/)(.+)%' => '$1$2$1bonus.png"/>', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publicpolicyforum.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publicpolicyforum.org.php new file mode 100644 index 00000000..5dc8be88 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publicpolicyforum.org.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://publicpolicyforum.org/blog/going-extra-mile', + 'body' => array( + '//div[contains(@class,"field-name-post-date")]', + '//div[contains(@class,"field-name-body")]', + ), + 'strip' => array( + '//img[@src="http://publicpolicyforum.org/sites/default/files/logo3.jpg"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publy.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publy.ru.php new file mode 100644 index 00000000..bcfeeb99 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publy.ru.php @@ -0,0 +1,24 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.publy.ru/post/19988', + 'body' => array( + '//div[@class="singlepost"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//*[@class="featured"]', + '//*[@class="toc_white no_bullets"]', + '//*[@class="toc_title"]', + '//*[@class="pba"]', + '//*[@class="comments"]', + '//*[contains(@class, "g-single")]', + '//*[@class="ts-fab-wrapper"]', + '//*[contains(@class, "wp_rp_wrap")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/putaindecode.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/putaindecode.fr.php new file mode 100644 index 00000000..9fa5568c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/putaindecode.fr.php @@ -0,0 +1,16 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://putaindecode.fr/posts/js/etat-lieux-js-modulaire-front/', + 'body' => array( + '//*[@class="putainde-Post-md"]', + ), + 'strip' => array( + '//*[contains(@class, "inlineimg")]', + '//*[contains(@class, "comment-respond")]', + '//header', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/recode.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/recode.net.php new file mode 100644 index 00000000..343cd12f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/recode.net.php @@ -0,0 +1,20 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://recode.net/2015/09/26/big-tech-rolls-out-red-carpet-for-indian-prime-minister-lobbies-behind-closed-doors/', + 'body' => array( + '//img[contains(@class,"attachment-large")]', + '//div[contains(@class,"postarea")]', + '//li[@class,"author"]', + ), + 'strip' => array( + '//script', + '//div[contains(@class,"sharedaddy")]', + '//div[@class="post-send-off"]', + '//div[@class="large-12 columns"]', + '//div[contains(@class,"inner-related-article")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/retractionwatch.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/retractionwatch.com.php new file mode 100644 index 00000000..b97c73ed --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/retractionwatch.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://retractionwatch.com/2015/11/12/psychologist-jens-forster-settles-case-by-agreeing-to-2-retractions/', + 'body' => array( + '//*[@class="main"]', + '//*[@class="entry-content"]', + ), + 'strip' => array( + '//*[contains(@class, "sharedaddy")]', + '//*[contains(@class, "jp-relatedposts")]', + '//p[@class="p1"]', + ) + ) + ) +); + diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rockpapershotgun.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rockpapershotgun.com.php new file mode 100644 index 00000000..71110786 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rockpapershotgun.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://www.rockpapershotgun.com/2016/08/26/the-divisions-expansions-delayed-to-improve-the-game/', + 'body' => array( + '//div[@class="entry"]', + ) + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rue89.nouvelobs.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rue89.nouvelobs.com.php new file mode 100644 index 00000000..cb9116a3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rue89.nouvelobs.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://rue89.feedsportal.com/c/33822/f/608948/s/30999fa0/sc/24/l/0L0Srue890N0C20A130C0A80C30A0Cfaisait0Eboris0Eboillon0Eex0Esarko0Eboy0E350A0E0A0A0A0Eeuros0Egare0Enord0E245315/story01.htm', + 'body' => array( + '//*[@id="article"]/div[contains(@class, "content")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rugbyrama.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rugbyrama.fr.php new file mode 100644 index 00000000..9915c234 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rugbyrama.fr.php @@ -0,0 +1,20 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.rugbyrama.fr/rugby/top-14/2015-2016/top-14-hayman-coupe-du-monde-finale-2012-lutte.-voici-levan-chilachava-toulon_sto5283863/story.shtml', + 'body' => array( + '//div[@class="storyfull__content"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//*[@class="share-buttons"]', + '//*[@class="ad"]', + '//*[@class="hide-desktop"]', + '//*[@id="tracking_img"]', + ) + ) + ) +);
\ No newline at end of file diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/satwcomic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/satwcomic.com.php new file mode 100644 index 00000000..d63fc11c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/satwcomic.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://satwcomic.com/day-at-the-beach', + 'body' => array( + '//div[@class="container"]/center/a/img', + '//span[@itemprop="articleBody"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/scrumalliance.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/scrumalliance.org.php new file mode 100644 index 00000000..7835fd9e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/scrumalliance.org.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://www.scrumalliance.org/community/articles/2015/march/an-introduction-to-agile-project-intake?feed=articles', + 'body' => array( + '//div[@class="article_content"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/securityfocus.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/securityfocus.com.php new file mode 100644 index 00000000..01045148 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/securityfocus.com.php @@ -0,0 +1,17 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.securityfocus.com/archive/1/540139', + 'body' => array( + '//div[@id="vulnerability"]', + '//div[@class="comments_reply"]', + ), + 'strip' => array( + '//span[@class="title"]', + '//div[@id="logo_new"]', + '//div[@id="bannerAd"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sentfromthemoon.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sentfromthemoon.com.php new file mode 100644 index 00000000..f4354179 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sentfromthemoon.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array( + '//div[@class="comicpane"]/a/img', + '//div[@class="entry"]', + ), + 'strip' => array(), + 'test_url' => 'http://sentfromthemoon.com/archives/1417', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sitepoint.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sitepoint.com.php new file mode 100644 index 00000000..ab0eb7d4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sitepoint.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.sitepoint.com/creating-hello-world-app-swift/', + 'body' => array( + '//section[@class="article_body"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/slashdot.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/slashdot.org.php new file mode 100644 index 00000000..89ced8b6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/slashdot.org.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://science.slashdot.org/story/15/04/20/0528253/pull-top-can-tabs-at-50-reach-historic-archaeological-status', + 'body' => array( + '//article/div[@class="body"] | //article[@class="layout-article"]/div[@class="elips"]', ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smallhousebliss.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smallhousebliss.com.php new file mode 100644 index 00000000..8c13c44c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smallhousebliss.com.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://smallhousebliss.com/2013/08/29/house-g-by-lode-architecture/', + 'body' => array( + '//div[@class="post-content"]', + ), + 'strip' => array( + '//*[contains(@class, "gallery")]', + '//*[contains(@class, "share")]', + '//*[contains(@class, "wpcnt")]', + '//*[contains(@class, "meta")]', + '//*[contains(@class, "postitle")]', + '//*[@id="nav-below"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smarthomewelt.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smarthomewelt.de.php new file mode 100644 index 00000000..7463abc7 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smarthomewelt.de.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://smarthomewelt.de/apple-tv-amazon-echo-smart-home/', + 'body' => array('//div[@class="entry-inner"]/p | //div[@class="entry-inner"]/div[contains(@class,"wp-caption")]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smashingmagazine.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smashingmagazine.com.php new file mode 100644 index 00000000..cbe10726 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smashingmagazine.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.smashingmagazine.com/2015/04/17/using-sketch-for-responsive-web-design-case-study/', + 'body' => array('//article[contains(@class,"post")]/p'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smbc-comics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smbc-comics.com.php new file mode 100644 index 00000000..42262dc9 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smbc-comics.com.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.smbc-comics.com/comic/the-troll-toll', + 'body' => array( + '//div[@id="cc-comicbody"]', + '//div[@id="aftercomic"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/snopes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/snopes.com.php new file mode 100644 index 00000000..b0fe6557 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/snopes.com.php @@ -0,0 +1,22 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.snopes.com/bacca-brides-on-tour/', + 'body' => array( + '//article', + ), + 'strip' => array( + '//span[@itemprop="author"]', + '//div[contains(@class,"author-")]', + '//h1', + '//style', + '//div[contains(@class,"socialShares")]', + '//div[contains(@class,"ad-unit")]', + '//aside', + '//div[contains(@class,"boomtrain")]', + '//footer' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/soundandvision.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/soundandvision.com.php new file mode 100644 index 00000000..6448bb05 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/soundandvision.com.php @@ -0,0 +1,21 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.soundandvision.com/content/james-guthrie-mixing-roger-waters-and-pink-floyd-51', + 'body' => array( + '//div[@id="left"]', + ), + 'strip' => array( + '//div[@class="meta"]', + '//div[@class="ratingsbox"]', + '//h1', + '//h2', + '//addthis', + '//comment-links', + '//div[@class="book-navigation"]', + '//div[@class="comment-links"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/spiegel.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/spiegel.de.php new file mode 100644 index 00000000..e2e3db00 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/spiegel.de.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.spiegel.de/politik/ausland/afrika-angola-geht-gegen-islam-vor-und-schliesst-moscheen-a-935788.html', + 'body' => array( + '//div[contains(@class, "article-section")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stereophile.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stereophile.com.php new file mode 100644 index 00000000..8e410be4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stereophile.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.stereophile.com/content/2015-rocky-mountain-audio-fest-starts-friday', + 'body' => array( + '//div[@class="content clear-block"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stupidfox.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stupidfox.net.php new file mode 100644 index 00000000..61182d72 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stupidfox.net.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://stupidfox.net/134-sleepy-time', + 'body' => array( + '//div[@class="comicmid"]/center/a/img', + '//div[@class="stand_high"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/subtraction.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/subtraction.com.php new file mode 100644 index 00000000..6d744277 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/subtraction.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.subtraction.com/2015/06/06/time-lapse-video-of-one-world-trade-center/', + 'body' => array('//article/div[@class="entry-content"]'), + 'strip' => array(), + ), + ), + 'filter' => array( + '%.*%' => array( + '%\+<h3.*/ul>%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sz.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sz.de.php new file mode 100644 index 00000000..90bde5a9 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sz.de.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://sz.de/1.2443161', + 'body' => array('//article[@id="sitecontent"]/section[@class="topenrichment"]//img | //article[@id="sitecontent"]/section[@class="body"]/section[@class="authors"]/preceding-sibling::*[not(contains(@class, "ad"))]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/takprosto.cc.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/takprosto.cc.php new file mode 100644 index 00000000..624ef907 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/takprosto.cc.php @@ -0,0 +1,21 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://takprosto.cc/kokteyl-dlya-pohudeniya-v-domashnih-usloviyah/', + 'body' => array( + '//div[contains(@class, "entry-contentt")]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//*[@class="views_post"]', + '//*[contains(@class, "mailchimp-box")]', + '//*[contains(@class, "essb_links")]', + '//*[contains(@rel, "nofollow")]', + '//*[contains(@class, "ads")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/techcrunch.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/techcrunch.com.php new file mode 100644 index 00000000..d6e620f6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/techcrunch.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://techcrunch.com/2013/08/31/indias-visa-maze/', + 'body' => array( + '//div[contains(@class, "media-container")]', + '//div[@class="body-copy"]', + ), + 'strip' => array( + '//*[contains(@class, "module-crunchbase")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/the-ebook-reader.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/the-ebook-reader.com.php new file mode 100644 index 00000000..3b01eb96 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/the-ebook-reader.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://blog.the-ebook-reader.com/2015/09/25/kobo-glo-hd-and-kobo-touch-2-0-covers-and-cases-roundup/', + 'body' => array( + '//div[@class="entry"]', + ), + 'strip' => array( + '//div[@id="share"]', + '//div[contains(@class,"ItemCenter")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theatlantic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theatlantic.com.php new file mode 100644 index 00000000..bfad4ab2 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theatlantic.com.php @@ -0,0 +1,23 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.theatlantic.com/politics/archive/2015/09/what-does-it-mean-to-lament-the-poor-inside-panem/407317/', + 'body' => array( + '//picture[@class="img"]', + '//figure/figcaption/span', + '//div/p[@itemprop="description"]', + '//div[@class="article-body"]', + '//ul[@class="photos"]', + ), + 'strip' => array( + '//aside[@class="callout"]', + '//span[@class="credit"]', + '//figcaption[@class="credit"]', + '//aside[contains(@class,"partner-box")]', + '//div[contains(@class,"ad")]', + '//a[contains(@class,"social-icon")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theawkwardyeti.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theawkwardyeti.com.php new file mode 100644 index 00000000..fd4f3d50 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theawkwardyeti.com.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%/comic/.*%' => array( + 'test_url' => 'http://theawkwardyeti.com/comic/things-to-do/', + 'body' => array( + '//div[@id="comic"]' + ), + 'strip' => array() + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thecodinglove.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thecodinglove.com.php new file mode 100644 index 00000000..c6ec5bf5 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thecodinglove.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://thecodinglove.com/post/116897934767', + 'body' => array('//div[@class="bodytype"]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thedoghousediaries.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thedoghousediaries.com.php new file mode 100644 index 00000000..d2f840d7 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thedoghousediaries.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array( + '//div[@class="comicpane"]/a/img', + '//div[@class="entry"]', + ), + 'strip' => array(), + 'test_url' => 'http://thedoghousediaries.com/6023', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thegamercat.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thegamercat.com.php new file mode 100644 index 00000000..f9b4637b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thegamercat.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.thegamercat.com/comic/just-no/', + 'body' => array('//div[@id="comic"] | //div[@class="post-content"]/div[@class="entry"]/p'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thehindu.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thehindu.com.php new file mode 100644 index 00000000..1e6735ba --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thehindu.com.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.thehindu.com/sci-tech/science/why-is-the-shape-of-cells-in-a-honeycomb-always-hexagonal/article7692306.ece?utm_source=RSS_Feed&utm_medium=RSS&utm_campaign=RSS_Syndication', + 'body' => array( + '//div/img[@class="main-image"]', + '//div[@class="photo-caption"]', + '//div[@class="articleLead"]', + '//p', + '//span[@class="upper"]', + ), + 'strip' => array( + '//div[@id="articleKeywords"]', + '//div[@class="photo-source"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thelocal.se.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thelocal.se.php new file mode 100644 index 00000000..c3ec250c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thelocal.se.php @@ -0,0 +1,17 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'www.thelocal.se/20161219/this-swede-can-memorize-hundreds-of-numbers-in-only-five-minutes', + 'body' => array( + '//div[@id="article-photo"]', + '//div[@id="article-description"]', + '//div[@id="article-body"]', + ), + 'strip' => array( + '//div[@id="article-info-middle"]', + ) + ) + ) +); + diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themerepublic.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themerepublic.net.php new file mode 100644 index 00000000..bc47b278 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themerepublic.net.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.themerepublic.net/2015/04/david-lopez-pitoko.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+blogspot%2FDngUJ+%28Theme+Republic%29&utm_content=FeedBurner', + 'body' => array('//*[@class="post-body"]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themoscowtimes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themoscowtimes.com.php new file mode 100644 index 00000000..0f5bf75e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themoscowtimes.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.themoscowtimes.com/business/article/535500.html', + 'body' => array( + '//div[@class="article_main_img"]', + '//div[@class="article_text"]', + ), + 'strip' => array( + '//div[@class="articlebottom"]', + '//p/b', + '//p/a[contains(@href, "/article.php?id=")]', + '//div[@class="disqus_wrap"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thenewslens.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thenewslens.com.php new file mode 100644 index 00000000..75381707 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thenewslens.com.php @@ -0,0 +1,21 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://international.thenewslens.com/post/255032/', + 'body' => array( + '//div[@class="article-section"]', + ), + 'strip' => array( + '//div[contains(@class,"ad-")]', + '//div[@class="article-title-box"]', + '//div[@class="function-box"]', + '//p/span', + '//aside', + '//footer', + '//div[@class="article-infoBot-box"]', + '//div[contains(@class,"standard-container")]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theodd1sout.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theodd1sout.com.php new file mode 100644 index 00000000..d06ed124 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theodd1sout.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%-150x150%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theonion.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theonion.com.php new file mode 100644 index 00000000..acbfd36b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theonion.com.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.theonion.com/article/wild-eyed-jim-harbaugh-informs-players-they-must-k-51397?utm_medium=RSS&utm_campaign=feeds', + 'body' => array( + '//div[@class="content-masthead"]/figure/div/noscript/img', + '//div[@class="content-text"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thestandard.com.hk.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thestandard.com.hk.php new file mode 100644 index 00000000..1163b345 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thestandard.com.hk.php @@ -0,0 +1,22 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.thestandard.com.hk/breaking_news_detail.asp?id=67156', + 'body' => array( + '//table/tr/td/span[@class="bodyCopy"]', + ), + 'strip' => array( + '//script', + '//br', + '//map[@name="gif_bar"]', + '//img[contains(@usemap,"gif_bar")]', + '//a', + '//span[@class="bodyHeadline"]', + '//i', + '//b', + '//table', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/threepanelsoul.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/threepanelsoul.com.php new file mode 100644 index 00000000..4af6196e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/threepanelsoul.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array( + '//img[@id="cc-comic"]', + ), + 'test_url' => 'http://www.threepanelsoul.com/comic/uncloaking', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/timesofindia.indiatimes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/timesofindia.indiatimes.com.php new file mode 100644 index 00000000..19246806 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/timesofindia.indiatimes.com.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://timesofindia.indiatimes.com/city/mangaluru/Adani-UPCL-to-release-CSR-grant-of-Rs-3-74-crore-to-YellurGram-Panchayat/articleshow/50512116.cms', + 'body' => array( + '//div[@class="article_content clearfix"]', + '//section[@class="highlight clearfix"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/travel-dealz.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/travel-dealz.de.php new file mode 100644 index 00000000..4ee4fcdc --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/travel-dealz.de.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%^/blog.*%' => array( + 'test_url' => 'http://travel-dealz.de/blog/venere-gutschein/', + 'body' => array('//div[@class="post-entry"]'), + 'strip' => array( + '//*[@id="jp-relatedposts"]', + '//*[@class="post-meta"]', + '//*[@class="post-data"]', + '//*[@id="author-meta"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treehugger.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treehugger.com.php new file mode 100644 index 00000000..55eb7e01 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treehugger.com.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.treehugger.com/uncategorized/top-ten-posts-week-bunnies-2.html', + 'body' => array( + '//div[contains(@class, "promo-image")]', + '//div[contains(@id, "entry-body")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treelobsters.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treelobsters.com.php new file mode 100644 index 00000000..3214c62a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treelobsters.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twogag.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twogag.com.php new file mode 100644 index 00000000..79f4f62e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twogag.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%http://www.twogag.com/comics-rss/([^.]+)\\.jpg%' => 'http://www.twogag.com/comics/$1.jpg', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twokinds.keenspot.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twokinds.keenspot.com.php new file mode 100644 index 00000000..3428fcb4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twokinds.keenspot.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://twokinds.keenspot.com/archive.php?p=0', + 'body' => array('//*[@class="comic"]/div/a/img | //*[@class="comic"]/div/img | //*[@id="cg_img"]/img | //*[@id="cg_img"]/a/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/undeadly.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/undeadly.org.php new file mode 100644 index 00000000..8b15900a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/undeadly.org.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://undeadly.org/cgi?action=article&sid=20141101181155', + 'body' => array( + '/html/body/table[3]/tbody/tr/td[1]/table[2]/tr/td[1]', + ), + 'strip' => array( + '//font', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/upi.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/upi.com.php new file mode 100644 index 00000000..ec8d1a1a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/upi.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.upi.com/Top_News/US/2015/09/26/Tech-giants-Hollywood-stars-among-guests-at-state-dinner-for-Chinas-Xi-Jinping/4541443281006/', + 'body' => array( + '//div[@class="img"]', + '//div/article[@itemprop="articleBody"]', + ), + 'strip' => array( + '//div[@align="center"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/usatoday.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/usatoday.com.php new file mode 100644 index 00000000..edd6aa44 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/usatoday.com.php @@ -0,0 +1,27 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.usatoday.com/story/life/music/2017/02/13/things-you-should-know-happened-grammy-awards-2017/97833734/', + 'body' => array( + '//div[@itemprop="articleBody"]', + ), + 'strip' => array( + '//script', + '//h1', + '//iframe', + '//span[@class="mycapture-small-btn mycapture-btn-with-text mycapture-expandable-photo-btn-small js-mycapture-btn-small"]', + '//div[@class="close-wrap"]', + '//div[contains(@class,"ui-video-wrapper")]', + '//div[contains(@class,"media-mob")]', + '//div[contains(@class,"left-mob")]', + '//div[contains(@class,"nerdbox")]', + '//div[contains(@class,"oembed-asset")]', + '//*[contains(@class,"share")]', + '//div[contains(@class,"gallery-asset")]', + '//div[contains(@class,"oembed-asset")]', + '//div[@class="article-print-url"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/version2.dk.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/version2.dk.php new file mode 100644 index 00000000..a6d49f2e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/version2.dk.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.version2.dk/artikel/surface-pro-2-fungerer-bedre-til-arbejde-end-fornoejelse-55195', + 'body' => array( + '//section[contains(@class, "teaser")]', + '//section[contains(@class, "body")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vgcats.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vgcats.com.php new file mode 100644 index 00000000..b2830a37 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vgcats.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%/comics.*%' => array( + 'test_url' => 'http://www.vgcats.com/comics/?strip_id=358', + 'body' => array('//*[@align="center"]/img'), + 'strip' => array(), + ), + '%/super.*%' => array( + 'test_url' => 'http://www.vgcats.com/super/?strip_id=84', + 'body' => array('//*[@align="center"]/p/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vuxml.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vuxml.org.php new file mode 100644 index 00000000..b9bef7a4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vuxml.org.php @@ -0,0 +1,17 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.vuxml.org/freebsd/a5f160fa-deee-11e4-99f8-080027ef73ec.html', + 'body' => array( + '//body', + ), + 'strip' => array( + '//h1', + '//div[@class="blurb"]', + '//hr', + '//p[@class="copyright"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/wausaudailyherald.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/wausaudailyherald.com.php new file mode 100644 index 00000000..58aceeaf --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/wausaudailyherald.com.php @@ -0,0 +1,27 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.wausaudailyherald.com/story/news/2017/04/01/hundreds-gather-remember-attorney-killed-shooting-spree/99826062/?from=global&sessionKey=&autologin=', + 'body' => array( + '//div[@itemprop="articleBody"]', + ), + 'strip' => array( + '//h1', + '//iframe', + '//span[@class="mycapture-small-btn mycapture-btn-with-text mycapture-expandable-photo-btn-small js-mycapture-btn-small"]', + '//div[@class="close-wrap"]', + '//div[contains(@class,"ui-video-wrapper")]', + '//div[contains(@class,"media-mob")]', + '//div[contains(@class,"left-mob")]', + '//div[contains(@class,"nerdbox")]', + '//p/span', + '//div[contains(@class,"oembed-asset")]', + '//*[contains(@class,"share")]', + '//div[contains(@class,"gallery-asset")]', + '//div[contains(@class,"oembed-asset")]', + '//div[@class="article-print-url"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bbc.co.uk.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bbc.co.uk.php new file mode 100644 index 00000000..98fc368a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bbc.co.uk.php @@ -0,0 +1,33 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.bbc.co.uk/news/world-middle-east-23911833', + 'body' => array( + '//div[@class="story-body__inner"] | //div[@class="article"]', + '//div[@class="indPost"]', + ), + 'strip' => array( + '//form', + '//div[@id="headline"]', + '//*[@class="warning"]', + '//span[@class="off-screen"]', + '//span[@class="story-image-copyright"]', + '//ul[@class="story-body__unordered-list"]', + '//div[@class="ad_wrapper"]', + '//div[@id="article-sidebar"]', + '//div[@class="data-table-outer"]', + '//*[@class="story-date"]', + '//*[@class="story-header"]', + '//figure[contains(@class,"has-caption")]', + '//*[@class="story-related"]', + '//*[contains(@class, "byline")]', + '//p[contains(@class, "media-message")]', + '//*[contains(@class, "story-feature")]', + '//*[@id="video-carousel-container"]', + '//*[@id="also-related-links"]', + '//*[contains(@class, "share") or contains(@class, "hidden") or contains(@class, "hyper")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bdgest.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bdgest.com.php new file mode 100644 index 00000000..41ef68d4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bdgest.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.bdgest.com/chronique-6027-BD-Adrastee-Tome-2.html', + 'body' => array( + '//*[contains(@class, "chronique")]', + ), + 'strip' => array( + '//*[contains(@class, "post-review")]', + '//*[contains(@class, "footer-review")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bgr.in.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bgr.in.php new file mode 100644 index 00000000..63ca069e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bgr.in.php @@ -0,0 +1,23 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.bgr.in/news/xiaomi-redmi-3-with-13-megapixel-camera-snapdragon-616-launched-price-specifications-and-features/', + 'body' => array( + '//div[@class="article-content"]', + ), + 'strip' => array( + '//*[@class="article-meta"]', + '//*[@class="contentAdsense300"]', + '//*[@class="iwpl-social-hide"]', + '//iframe[@class="iframeads"]', + '//*[@class="disqus_thread"]', + '//*[@class="outb-mobile OUTBRAIN"]', + '//*[@class="wdt_smart_alerts"]', + '//*[@class="footnote"]', + '//*[@id="gadget-widget"]', + '//header[@class="article-title entry-header"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.businessweek.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.businessweek.com.php new file mode 100644 index 00000000..0acc44ec --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.businessweek.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.businessweek.com/articles/2013-09-18/elon-musks-hyperloop-will-work-says-some-very-smart-software', + 'body' => array( + '//div[@id="lead_graphic"]', + '//div[@id="article_body"]', + ), + 'strip' => array( + '//*[contains(@class, "related_item")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.cnn.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.cnn.com.php new file mode 100644 index 00000000..31d03ed9 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.cnn.com.php @@ -0,0 +1,24 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.cnn.com/2013/08/31/world/meast/syria-civil-war/index.html?hpt=hp_t1', + 'body' => array( + '//div[@class="cnn_strycntntlft"]', + ), + 'strip' => array( + '//div[@class="cnn_stryshrwdgtbtm"]', + '//div[@class="cnn_strybtmcntnt"]', + '//div[@class="cnn_strylftcntnt"]', + '//div[contains(@class, "cnnGalleryContainer")]', + '//div[contains(@class, "cnn_strylftcexpbx")]', + '//div[contains(@class, "articleGalleryNavContainer")]', + '//div[contains(@class, "cnnArticleGalleryCaptionControl")]', + '//div[contains(@class, "cnnArticleGalleryNavPrevNextDisabled")]', + '//div[contains(@class, "cnnArticleGalleryNavPrevNext")]', + '//div[contains(@class, "cnn_html_media_title_new")]', + '//div[contains(@id, "disqus")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.developpez.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.developpez.com.php new file mode 100644 index 00000000..1535e437 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.developpez.com.php @@ -0,0 +1,21 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.developpez.com/actu/81757/Mozilla-annonce-la-disponibilite-de-Firefox-36-qui-passe-au-HTTP-2-et-permet-la-synchronisation-de-son-ecran-d-accueil/', + 'body' => array( + '//*[@itemprop="articleBody"]', + ), + 'strip' => array( + '//form', + '//div[@class="content"]/img', + '//a[last()]/following-sibling::*', + '//*[contains(@class,"actuTitle")]', + '//*[contains(@class,"date")]', + '//*[contains(@class,"inlineimg")]', + '//*[@id="signaler"]', + '//*[@id="signalerFrame"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.egscomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.egscomics.com.php new file mode 100644 index 00000000..263f0755 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.egscomics.com.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.egscomics.com/index.php?id=1690', + 'title' => '/html/head/title', + 'body' => array( + '//img[@id="comic"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.fakingnews.firstpost.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.fakingnews.firstpost.com.php new file mode 100644 index 00000000..c948c77b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.fakingnews.firstpost.com.php @@ -0,0 +1,17 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.fakingnews.firstpost.com/2016/01/engineering-student-creates-record-in-a-decade-becomes-the-first-to-completely-exhaust-ball-pen-refill/', + 'body' => array( + '//div[@class="entry"]', + ), + 'strip' => array( + '//*[@class="socialshare_bar"]', + '//*[@class="authorbox"]', + '//*[@class="cf5_rps"]', + '//*[@class="60563 fb-comments fb-social-plugin"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.forbes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.forbes.com.php new file mode 100644 index 00000000..fd16ed57 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.forbes.com.php @@ -0,0 +1,20 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.forbes.com/sites/andygreenberg/2013/09/05/follow-the-bitcoins-how-we-got-busted-buying-drugs-on-silk-roads-black-market/', + 'body' => array( + '//div[@id="leftRail"]/div[contains(@class, body)]', + ), + 'strip' => array( + '//aside', + '//div[contains(@class, "entity_block")]', + '//div[contains(@class, "vestpocket") and not contains(@class, "body")]', + '//div[contains(@style, "display")]', + '//div[contains(@id, "comment")]', + '//div[contains(@class, "widget")]', + '//div[contains(@class, "pagination")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.franceculture.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.franceculture.fr.php new file mode 100644 index 00000000..f7ec0d8d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.franceculture.fr.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.franceculture.fr/emission-culture-eco-la-finance-aime-toujours-la-france-2016-01-08', + 'body' => array( + '//div[@class="text-zone"]', + ), + 'strip' => array( + '//ul[@class="tags"]', + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.futura-sciences.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.futura-sciences.com.php new file mode 100644 index 00000000..ea94a0fb --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.futura-sciences.com.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.futura-sciences.com/magazines/espace/infos/actu/d/astronautique-curiosity-franchi-succes-dune-dingo-gap-52289/#xtor=RSS-8', + 'body' => array( + '//div[contains(@class, "content fiche-")]', + ), + 'strip' => array( + '//h1', + '//*[contains(@class, "content-date")]', + '//*[contains(@class, "diaporama")]', + '//*[contains(@class, "slider")]', + '//*[contains(@class, "cartouche")]', + '//*[contains(@class, "noprint")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.geekculture.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.geekculture.com.php new file mode 100644 index 00000000..3d0b6c75 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.geekculture.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.geekculture.com/joyoftech/joyarchives/2180.html', + 'body' => array( + '//p[contains(@class,"Maintext")][2]/a/img[contains(@src,"joyimages")]', + ), + 'strip' => array(), + ), + ), +); + diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.howtogeek.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.howtogeek.com.php new file mode 100644 index 00000000..6879e767 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.howtogeek.com.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.howtogeek.com/235283/what-is-a-wireless-hard-drive-and-should-i-get-one/', + 'body' => array( + '//div[@class="thecontent"]', + ), + 'strip' => array( + '//*[@class="relatedside"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lepoint.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lepoint.fr.php new file mode 100644 index 00000000..dcb7e484 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lepoint.fr.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.lepoint.fr/c-est-arrive-aujourd-hui/19-septembre-1783-pour-la-premiere-fois-un-mouton-un-canard-et-un-coq-s-envoient-en-l-air-devant-louis-xvi-18-09-2012-1507704_494.php', + 'body' => array( + '//article', + ), + 'strip' => array( + '//*[contains(@class, "info_article")]', + '//*[contains(@class, "fildariane_titre")]', + '//*[contains(@class, "entete2_article")]', + '//*[contains(@class, "signature_article")]', + '//*[contains(@id, "share")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lesnumeriques.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lesnumeriques.com.php new file mode 100644 index 00000000..0137e209 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lesnumeriques.com.php @@ -0,0 +1,25 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.lesnumeriques.com/blender/kitchenaid-diamond-5ksb1585-p27473/test.html', + 'body' => array( + '//*[@id="product-content"]', + '//*[@id="news-content"]', + '//*[@id="article-content"]', + ), + 'strip' => array( + '//form', + '//div[contains(@class, "price-v4"])', + '//div[contains(@class, "authors-and-date")]', + '//div[contains(@class, "mini-product")]', + '//div[@id="articles-related-authors"]', + '//div[@id="tags-socials"]', + '//div[@id="user-reviews"]', + '//div[@id="product-reviews"]', + '//div[@id="publication-breadcrumbs-and-date"]', + '//div[@id="publication-breadcrumbs-and-date"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.mac4ever.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.mac4ever.com.php new file mode 100644 index 00000000..60bc1bd4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.mac4ever.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.mac4ever.com/actu/87392_video-quand-steve-jobs-et-bill-gates-jouaient-au-bachelor-avec-le-mac', + 'body' => array( + '//div[contains(@class, "news-news-content")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.makeuseof.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.makeuseof.com.php new file mode 100644 index 00000000..a274564a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.makeuseof.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.makeuseof.com/tag/having-problems-with-audio-in-windows-10-heres-a-likely-fix/', + 'body' => array( + '//div[@class="entry"]', + ), + 'strip' => array( + '//*[@class="new_sharebar"]', + '//*[@class="author"]', + '//*[@class="wdt_grouvi"]', + '//*[@class="wdt_smart_alerts"]', + '//*[@class="modal fade grouvi"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.monsieur-le-chien.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.monsieur-le-chien.fr.php new file mode 100644 index 00000000..5f5e987b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.monsieur-le-chien.fr.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.monsieur-le-chien.fr/index.php?planche=672', + 'body' => array( + '//img[starts-with(@src, "i/planches/")]', + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.npr.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.npr.org.php new file mode 100644 index 00000000..ecc0213a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.npr.org.php @@ -0,0 +1,28 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.npr.org/blogs/thesalt/2013/09/17/223345977/auto-brewery-syndrome-apparently-you-can-make-beer-in-your-gut', + 'body' => array( + '//article[contains(@class,"story")]', + ), + 'strip' => array( + '//div[@class="story-tools"]', + '//h3[@class="slug"]', + '//div[@class="storytitle"]', + '//div[@id="story-meta"]', + '//a[@id="mainContent"]', + '//div[@class="credit-caption"]', + '//div[@class="enlarge_html"]', + '//button', + '//div[contains(@id,"pullquote")]', + '//div[contains(@class,"internallink")]', + '//div[contains(@class,"video")]', + '//div[@class="simplenodate"]', + '//div[contains(@class,"share-")]', + '//div[@class="tags"]', + '//aside' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.numerama.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.numerama.com.php new file mode 100644 index 00000000..fe4971c5 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.numerama.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.numerama.com/sciences/125959-recherches-ladn-recompensees-nobel-de-chimie.html', + 'body' => array( + '//article', + ), + 'strip' => array( + '//footer', + '//section[@class="related-article"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.oneindia.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.oneindia.com.php new file mode 100644 index 00000000..320c2147 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.oneindia.com.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.oneindia.com/india/b-luru-govt-likely-remove-word-eunuch-from-sec-36-a-karnataka-police-act-1981173.html', + 'body' => array( + '//div[@class="ecom-ad-content"]', + ), + 'strip' => array( + '//*[@id="view_cmtns"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.pseudo-sciences.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.pseudo-sciences.org.php new file mode 100644 index 00000000..9e467edf --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.pseudo-sciences.org.php @@ -0,0 +1,16 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.pseudo-sciences.org/spip.php?article2275', + 'body' => array( + '//div[@id="art_main"]', + ), + 'strip' => array( + '//div[@id="art_print"]', + '//div[@id="art_chapo"]', + '//img[@class="puce"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.sciencemag.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.sciencemag.org.php new file mode 100644 index 00000000..ae7a93ac --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.sciencemag.org.php @@ -0,0 +1,16 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.sciencemag.org/news/2016/01/could-bright-foamy-wak$', + 'body' => array( + '//div[@class="row--hero"]', + '//article[contains(@class,"primary")]', + ), + 'strip' => array( + '//header[@class="article__header"]', + '//footer[@class="article__foot"]', + ), + ), + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.slate.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.slate.fr.php new file mode 100644 index 00000000..8c8dc893 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.slate.fr.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.slate.fr/monde/77034/allemagne-2013-couacs-campagne', + 'body' => array( + '//div[@class="article_content"]', + ), + 'strip' => array( + '//*[@id="slate_associated_bn"]', + '//*[@id="ligatus-article"]', + '//*[@id="article_sidebar"]', + '//div[contains(@id, "reseaux")]', + '//*[contains(@class, "smart") or contains(@class, "article_tags") or contains(@class, "article_reactions")]', + '//*[contains(@class, "OUTBRAIN") or contains(@class, "related_item") or contains(@class, "share")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.universfreebox.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.universfreebox.com.php new file mode 100644 index 00000000..0747d0fb --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.universfreebox.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.universfreebox.com/article/24305/4G-Bouygues-Telecom-lance-une-vente-flash-sur-son-forfait-Sensation-3Go', + 'body' => array( + '//div[@id="corps_corps"]', + ), + 'strip' => array( + '//*[@id="formulaire"]', + '//*[@id="commentaire"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.zeit.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.zeit.de.php new file mode 100644 index 00000000..316c2656 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.zeit.de.php @@ -0,0 +1,41 @@ +<?php +return array( + 'grabber' => array( + '%^/zeit-magazin.*%' => array( + 'test_url' => 'http://www.zeit.de/zeit-magazin/2015/15/pegida-kathrin-oertel-lutz-bachmann', + 'body' => array( + '//article[@class="article"]', + ), + 'strip' => array( + '//header/div/h1', + '//header/div/div[@class="article__head__subtitle"]', + '//header/div/div[@class="article__column__author"]', + '//header/div/div[@class="article__column__author"]', + '//header/div/span[@class="article__head__meta-wrap"]', + '//form', + '//style', + '//div[contains(@class, "ad-tile")]', + '//div[@class="iqd-mobile-adplace"]', + '//div[@id="iq-artikelanker"]', + '//div[@id="js-social-services"]', + '//section[@id="js-comments"]', + '//aside', + ), + ), + '%.*%' => array( + 'test_url' => 'http://www.zeit.de/politik/ausland/2015-04/thessaloniki-krise-griechenland-yannis-boutaris/', + 'body' => array( + '//div[@class="article-body"]', + ), + 'strip' => array( + '//*[@class="articleheader"]', + '//*[@class="excerpt"]', + '//div[contains(@class, "ad")]', + '//div[@itemprop="video"]', + '//*[@class="articlemeta"]', + '//*[@class="articlemeta-clear"]', + '//*[@class="zol_inarticletools"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/xkcd.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/xkcd.com.php new file mode 100644 index 00000000..84957268 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/xkcd.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%alt="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ymatuhin.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ymatuhin.ru.php new file mode 100644 index 00000000..9fd83f18 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ymatuhin.ru.php @@ -0,0 +1,21 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://ymatuhin.ru/tools/git-default-editor/', + 'body' => array( + '//section', + ), + 'strip' => array( + "//script", + "//style", + "//h1", + "//time", + "//aside", + "/html/body/section/ul", + "//amp-iframe", + "/html/body/section/h4" + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/zdnet.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/zdnet.com.php new file mode 100644 index 00000000..79b35ddb --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/zdnet.com.php @@ -0,0 +1,23 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://zdnet.com.feedsportal.com/c/35462/f/675637/s/4a33c93e/sc/11/l/0L0Szdnet0N0Carticle0Cchina0Eus0Eagree0Eon0Ecybercrime0Ecooperation0Eamid0Econtinued0Etension0C0Tftag0FRSSbaffb68/story01.htm', + 'body' => array( + '//p[@class="summary"]', + '//div[contains(@class,"storyBody")]', + ), + 'strip' => array( + '//*[contains(@class,"ad-")]', + '//p/span', + '//script', + '//p[@class="summary"]', + '//div[contains(@class,"relatedContent")]', + '//div[contains(@class,"loader")]', + '//p[@class="photoDetails"]', + '//div[@class="thumbnailSlider"]', + '//div[@class="shortcodeGalleryWrapper"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/CandidateParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/CandidateParser.php new file mode 100644 index 00000000..6c53a289 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/CandidateParser.php @@ -0,0 +1,283 @@ +<?php + +namespace PicoFeed\Scraper; + +use DomDocument; +use DOMXPath; +use PicoFeed\Logging\Logger; +use PicoFeed\Parser\XmlParser; + +/** + * Candidate Parser. + * + * @author Frederic Guillot + */ +class CandidateParser implements ParserInterface +{ + private $dom; + private $xpath; + + /** + * List of attributes to try to get the content, order is important, generic terms at the end. + * + * @var array + */ + private $candidatesAttributes = array( + 'articleBody', + 'articlebody', + 'article-body', + 'articleContent', + 'articlecontent', + 'article-content', + 'articlePage', + 'post-content', + 'post_content', + 'entry-content', + 'entry-body', + 'main-content', + 'story_content', + 'storycontent', + 'entryBox', + 'entrytext', + 'comic', + 'post', + 'article', + 'content', + 'main', + ); + + /** + * List of attributes to strip. + * + * @var array + */ + private $stripAttributes = array( + 'comment', + 'share', + 'links', + 'toolbar', + 'fb', + 'footer', + 'credit', + 'bottom', + 'nav', + 'header', + 'social', + 'tag', + 'metadata', + 'entry-utility', + 'related-posts', + 'tweet', + 'categories', + 'post_title', + 'by_line', + 'byline', + 'sponsors', + ); + + /** + * Tags to remove. + * + * @var array + */ + private $stripTags = array( + 'nav', + 'header', + 'footer', + 'aside', + 'form', + ); + + /** + * Constructor. + * + * @param string $html + */ + public function __construct($html) + { + $this->dom = XmlParser::getHtmlDocument('<?xml version="1.0" encoding="UTF-8">'.$html); + $this->xpath = new DOMXPath($this->dom); + } + + /** + * Get the relevant content with the list of potential attributes. + * + * @return string + */ + public function execute() + { + $content = $this->findContentWithCandidates(); + + if (strlen($content) < 200) { + $content = $this->findContentWithArticle(); + } + + if (strlen($content) < 50) { + $content = $this->findContentWithBody(); + } + + return $this->stripGarbage($content); + } + + /** + * Find content based on the list of tag candidates. + * + * @return string + */ + public function findContentWithCandidates() + { + foreach ($this->candidatesAttributes as $candidate) { + Logger::setMessage(get_called_class().': Try this candidate: "'.$candidate.'"'); + + $nodes = $this->xpath->query('//*[(contains(@class, "'.$candidate.'") or @id="'.$candidate.'") and not (contains(@class, "nav") or contains(@class, "page"))]'); + + if ($nodes !== false && $nodes->length > 0) { + Logger::setMessage(get_called_class().': Find candidate "'.$candidate.'"'); + + return $this->dom->saveXML($nodes->item(0)); + } + } + + return ''; + } + + /** + * Find <article/> tag. + * + * @return string + */ + public function findContentWithArticle() + { + $nodes = $this->xpath->query('//article'); + + if ($nodes !== false && $nodes->length > 0) { + Logger::setMessage(get_called_class().': Find <article/> tag'); + + return $this->dom->saveXML($nodes->item(0)); + } + + return ''; + } + + /** + * Find <body/> tag. + * + * @return string + */ + public function findContentWithBody() + { + $nodes = $this->xpath->query('//body'); + + if ($nodes !== false && $nodes->length > 0) { + Logger::setMessage(get_called_class().' Find <body/>'); + + return $this->dom->saveXML($nodes->item(0)); + } + + return ''; + } + + /** + * Strip useless tags. + * + * @param string $content + * + * @return string + */ + public function stripGarbage($content) + { + $dom = XmlParser::getDomDocument($content); + + if ($dom !== false) { + $xpath = new DOMXPath($dom); + + $this->stripTags($xpath); + $this->stripAttributes($dom, $xpath); + + $content = $dom->saveXML($dom->documentElement); + } + + return $content; + } + + /** + * Remove blacklisted tags. + * + * @param DOMXPath $xpath + */ + public function stripTags(DOMXPath $xpath) + { + foreach ($this->stripTags as $tag) { + $nodes = $xpath->query('//'.$tag); + + if ($nodes !== false && $nodes->length > 0) { + Logger::setMessage(get_called_class().': Strip tag: "'.$tag.'"'); + + foreach ($nodes as $node) { + $node->parentNode->removeChild($node); + } + } + } + } + + /** + * Remove blacklisted attributes. + * + * @param DomDocument $dom + * @param DOMXPath $xpath + */ + public function stripAttributes(DomDocument $dom, DOMXPath $xpath) + { + foreach ($this->stripAttributes as $attribute) { + $nodes = $xpath->query('//*[contains(@class, "'.$attribute.'") or contains(@id, "'.$attribute.'")]'); + + if ($nodes !== false && $nodes->length > 0) { + Logger::setMessage(get_called_class().': Strip attribute: "'.$attribute.'"'); + + foreach ($nodes as $node) { + if ($this->shouldRemove($dom, $node)) { + $node->parentNode->removeChild($node); + } + } + } + } + } + + /** + * Find link for next page of the article. + * + * @return string + */ + public function findNextLink() + { + return null; + } + + /** + * Return false if the node should not be removed. + * + * @param DomDocument $dom + * @param DomNode $node + * + * @return bool + */ + public function shouldRemove(DomDocument $dom, $node) + { + $document_length = strlen($dom->textContent); + $node_length = strlen($node->textContent); + + if ($document_length === 0) { + return true; + } + + $ratio = $node_length * 100 / $document_length; + + if ($ratio >= 90) { + Logger::setMessage(get_called_class().': Should not remove this node ('.$node->nodeName.') ratio: '.$ratio.'%'); + + return false; + } + + return true; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/ParserInterface.php b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/ParserInterface.php new file mode 100644 index 00000000..3ded4b1c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/ParserInterface.php @@ -0,0 +1,20 @@ +<?php + +namespace PicoFeed\Scraper; + +interface ParserInterface +{ + /** + * Execute the parser and return the contents. + * + * @return string + */ + public function execute(); + + /** + * Find link for next page of the article. + * + * @return string + */ + public function findNextLink(); +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleLoader.php b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleLoader.php new file mode 100644 index 00000000..6650682d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleLoader.php @@ -0,0 +1,107 @@ +<?php + +namespace PicoFeed\Scraper; + +use PicoFeed\Base; +use PicoFeed\Logging\Logger; + +/** + * RuleLoader class. + * + * @author Frederic Guillot + * @author Bernhard Posselt + */ +class RuleLoader extends Base +{ + /** + * Get the rules for an URL. + * + * @param string $url the URL that should be looked up + * + * @return array the array containing the rules + */ + public function getRules($url) + { + $hostname = parse_url($url, PHP_URL_HOST); + + if ($hostname !== false) { + $files = $this->getRulesFileList($hostname); + + foreach ($this->getRulesFolders() as $folder) { + $rule = $this->loadRuleFile($folder, $files); + + if (!empty($rule)) { + return $rule; + } + } + } + + return array(); + } + + /** + * Get the list of possible rules file names for a given hostname. + * + * @param string $hostname Hostname + * + * @return array + */ + public function getRulesFileList($hostname) + { + $files = array($hostname); // subdomain.domain.tld + $parts = explode('.', $hostname); + $len = count($parts); + + if ($len > 2) { + $subdomain = array_shift($parts); + $files[] = implode('.', $parts); // domain.tld + $files[] = '.'.implode('.', $parts); // .domain.tld + $files[] = $subdomain; // subdomain + } elseif ($len === 2) { + $files[] = '.'.implode('.', $parts); // .domain.tld + $files[] = $parts[0]; // domain + } + + return $files; + } + + /** + * Load a rule file from the defined folder. + * + * @param string $folder Rule directory + * @param array $files List of possible file names + * + * @return array + */ + public function loadRuleFile($folder, array $files) + { + foreach ($files as $file) { + $filename = $folder.'/'.$file.'.php'; + if (file_exists($filename)) { + Logger::setMessage(get_called_class().' Load rule: '.$file); + + return include $filename; + } + } + + return array(); + } + + /** + * Get the list of folders that contains rules. + * + * @return array + */ + public function getRulesFolders() + { + $folders = array(); + + if ($this->config !== null && $this->config->getGrabberRulesFolder() !== null) { + $folders[] = $this->config->getGrabberRulesFolder(); + } + + $folders[] = __DIR__ . '/../Rules'; + + return $folders; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleParser.php new file mode 100644 index 00000000..9beb59c1 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleParser.php @@ -0,0 +1,102 @@ +<?php + +namespace PicoFeed\Scraper; + +use DOMXPath; +use PicoFeed\Parser\XmlParser; + +/** + * Rule Parser. + * + * @author Frederic Guillot + */ +class RuleParser implements ParserInterface +{ + private $dom; + private $xpath; + private $rules = array(); + + /** + * Constructor. + * + * @param string $html + * @param array $rules + */ + public function __construct($html, array $rules) + { + $this->rules = $rules; + $this->dom = XmlParser::getHtmlDocument('<?xml version="1.0" encoding="UTF-8">'.$html); + $this->xpath = new DOMXPath($this->dom); + } + + /** + * Get the relevant content with predefined rules. + * + * @return string + */ + public function execute() + { + $this->stripTags(); + + return $this->findContent(); + } + + /** + * Remove HTML tags. + */ + public function stripTags() + { + if (isset($this->rules['strip']) && is_array($this->rules['strip'])) { + foreach ($this->rules['strip'] as $pattern) { + $nodes = $this->xpath->query($pattern); + + if ($nodes !== false && $nodes->length > 0) { + foreach ($nodes as $node) { + $node->parentNode->removeChild($node); + } + } + } + } + } + + /** + * Fetch content based on Xpath rules. + */ + public function findContent() + { + $content = ''; + if (isset($this->rules['body']) && is_array($this->rules['body'])) { + foreach ($this->rules['body'] as $pattern) { + $nodes = $this->xpath->query($pattern); + + if ($nodes !== false && $nodes->length > 0) { + foreach ($nodes as $node) { + $content .= $this->dom->saveXML($node); + } + } + } + } + + return $content; + } + + /** + * Fetch next link based on Xpath rules. + * + * @return string + */ + public function findNextLink() + { + if (isset($this->rules['next_page']) && is_array($this->rules['next_page'])) { + foreach ($this->rules['next_page'] as $pattern) { + $nodes = $this->xpath->query($pattern); + if ($nodes !== false && $nodes->length > 0) { + foreach ($nodes as $node) { + return $node->getAttribute('href'); + } + } + } + } + return null; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/Scraper.php b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/Scraper.php new file mode 100644 index 00000000..e5b9817f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/Scraper.php @@ -0,0 +1,279 @@ +<?php + +namespace PicoFeed\Scraper; + +use PicoFeed\Base; +use PicoFeed\Client\Client; +use PicoFeed\Client\ClientException; +use PicoFeed\Client\Url; +use PicoFeed\Encoding\Encoding; +use PicoFeed\Filter\Filter; +use PicoFeed\Logging\Logger; +use PicoFeed\Parser\XmlParser; + +/** + * Scraper class. + * + * @author Frederic Guillot + */ +class Scraper extends Base +{ + /** + * URL. + * + * @var string + */ + private $url = ''; + + /** + * Relevant content. + * + * @var string + */ + private $content = ''; + + /** + * HTML content. + * + * @var string + */ + private $html = ''; + + /** + * HTML content encoding. + * + * @var string + */ + private $encoding = ''; + + /** + * Flag to enable candidates parsing. + * + * @var bool + */ + private $enableCandidateParser = true; + + /** + * Disable candidates parsing. + * + * @return Scraper + */ + public function disableCandidateParser() + { + $this->enableCandidateParser = false; + return $this; + } + + /** + * Get encoding. + * + * @return string + */ + public function getEncoding() + { + return $this->encoding; + } + + /** + * Set encoding. + * + * @param string $encoding + * + * @return Scraper + */ + public function setEncoding($encoding) + { + $this->encoding = $encoding; + + return $this; + } + + /** + * Get URL to download. + * + * @return string + */ + public function getUrl() + { + return $this->url; + } + + /** + * Set URL to download. + * + * @param string $url URL + * + * @return Scraper + */ + public function setUrl($url) + { + $this->url = $url; + + return $this; + } + + /** + * Return true if the scraper found relevant content. + * + * @return bool + */ + public function hasRelevantContent() + { + return !empty($this->content); + } + + /** + * Get relevant content. + * + * @return string + */ + public function getRelevantContent() + { + return $this->content; + } + + /** + * Get raw content (unfiltered). + * + * @return string + */ + public function getRawContent() + { + return $this->html; + } + + /** + * Set raw content (unfiltered). + * + * @param string $html + * + * @return Scraper + */ + public function setRawContent($html) + { + $this->html = $html; + + return $this; + } + + /** + * Get filtered relevant content. + * + * @return string + */ + public function getFilteredContent() + { + $filter = Filter::html($this->content, $this->url); + $filter->setConfig($this->config); + + return $filter->execute(); + } + + /** + * Download the HTML content. + * + * @return bool + */ + public function download() + { + if (!empty($this->url)) { + + // Clear everything + $this->html = ''; + $this->content = ''; + $this->encoding = ''; + + try { + $client = Client::getInstance(); + $client->setConfig($this->config); + $client->setTimeout($this->config->getGrabberTimeout()); + $client->setUserAgent($this->config->getGrabberUserAgent()); + $client->execute($this->url); + + $this->url = $client->getUrl(); + $this->html = $client->getContent(); + $this->encoding = $client->getEncoding(); + + return true; + } catch (ClientException $e) { + Logger::setMessage(get_called_class().': '.$e->getMessage()); + } + } + + return false; + } + + /** + * Execute the scraper. + */ + public function execute($pageContent = '', $recursionDepth = 0) + { + $this->html = ''; + $this->encoding = ''; + $this->content = ''; + $this->download(); + $this->prepareHtml(); + + $parser = $this->getParser(); + + if ($parser !== null) { + $maxRecursions = $this->config->getMaxRecursions(); + if(!isset($maxRecursions)){ + $maxRecursions = 25; + } + $pageContent .= $parser->execute(); + // check if there is a link to next page and recursively get content (max 25 pages) + if((($nextLink = $parser->findNextLink()) !== null) && $recursionDepth < $maxRecursions){ + $nextLink = Url::resolve($nextLink,$this->url); + $this->setUrl($nextLink); + $this->execute($pageContent,$recursionDepth+1); + } + else{ + $this->content = $pageContent; + } + Logger::setMessage(get_called_class().': Content length: '.strlen($this->content).' bytes'); + } + } + + /** + * Get the parser. + * + * @return ParserInterface + */ + public function getParser() + { + $ruleLoader = new RuleLoader($this->config); + $rules = $ruleLoader->getRules($this->url); + + if (!empty($rules['grabber'])) { + Logger::setMessage(get_called_class().': Parse content with rules'); + + foreach ($rules['grabber'] as $pattern => $rule) { + $url = new Url($this->url); + $sub_url = $url->getFullPath(); + + if (preg_match($pattern, $sub_url)) { + Logger::setMessage(get_called_class().': Matched url '.$sub_url); + return new RuleParser($this->html, $rule); + } + } + } elseif ($this->enableCandidateParser) { + Logger::setMessage(get_called_class().': Parse content with candidates'); + } + + return new CandidateParser($this->html); + } + + /** + * Normalize encoding and strip head tag. + */ + public function prepareHtml() + { + $html_encoding = XmlParser::getEncodingFromMetaTag($this->html); + + $this->html = Encoding::convert($this->html, $html_encoding ?: $this->encoding); + $this->html = Filter::stripHeadTags($this->html); + + Logger::setMessage(get_called_class().': HTTP Encoding "'.$this->encoding.'" ; HTML Encoding "'.$html_encoding.'"'); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/Subscription.php b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/Subscription.php new file mode 100644 index 00000000..12eccfd5 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/Subscription.php @@ -0,0 +1,175 @@ +<?php + +namespace PicoFeed\Serialization; + +/** + * Class Subscription + * + * @package PicoFeed\Serialization + * @author Frederic Guillot + */ +class Subscription +{ + protected $title = ''; + protected $feedUrl = ''; + protected $siteUrl = ''; + protected $category = ''; + protected $description = ''; + protected $type = ''; + + /** + * Create object instance + * + * @static + * @access public + * @return Subscription + */ + public static function create() + { + return new static(); + } + + /** + * Set title + * + * @access public + * @param string $title + * @return Subscription + */ + public function setTitle($title) + { + $this->title = $title; + return $this; + } + + /** + * Get title + * + * @access public + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * Set feed URL + * + * @access public + * @param string $feedUrl + * @return Subscription + */ + public function setFeedUrl($feedUrl) + { + $this->feedUrl = $feedUrl; + return $this; + } + + /** + * Get feed URL + * + * @access public + * @return string + */ + public function getFeedUrl() + { + return $this->feedUrl; + } + + /** + * Set site URL + * + * @access public + * @param string $siteUrl + * @return Subscription + */ + public function setSiteUrl($siteUrl) + { + $this->siteUrl = $siteUrl; + return $this; + } + + /** + * Get site URL + * + * @access public + * @return string + */ + public function getSiteUrl() + { + return $this->siteUrl; + } + + /** + * Set category + * + * @access public + * @param string $category + * @return Subscription + */ + public function setCategory($category) + { + $this->category = $category; + return $this; + } + + /** + * Get category + * + * @access public + * @return string + */ + public function getCategory() + { + return $this->category; + } + + /** + * Set description + * + * @access public + * @param string $description + * @return Subscription + */ + public function setDescription($description) + { + $this->description = $description; + return $this; + } + + /** + * Get description + * + * @access public + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * Set type + * + * @access public + * @param string $type + * @return Subscription + */ + public function setType($type) + { + $this->type = $type; + return $this; + } + + /** + * Get type + * + * @access public + * @return string + */ + public function getType() + { + return $this->type; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionList.php b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionList.php new file mode 100644 index 00000000..b173f89b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionList.php @@ -0,0 +1,75 @@ +<?php + +namespace PicoFeed\Serialization; + +/** + * Class SubscriptionList + * + * @package PicoFeed\Serialization + * @author Frederic Guillot + */ +class SubscriptionList +{ + /** + * OPML entries + * + * @var Subscription[] + */ + public $subscriptions = array(); + + /** + * Title + * + * @var string + */ + protected $title = ''; + + /** + * Create object instance + * + * @static + * @access public + * @return SubscriptionList + */ + public static function create() + { + return new static(); + } + + /** + * Set title + * + * @access public + * @param string $title + * @return SubscriptionList + */ + public function setTitle($title) + { + $this->title = $title; + return $this; + } + + /** + * Get title + * + * @access public + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * Add subscription + * + * @access public + * @param Subscription $subscription + * @return SubscriptionList + */ + public function addSubscription(Subscription $subscription) + { + $this->subscriptions[] = $subscription; + return $this; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListBuilder.php new file mode 100644 index 00000000..838e4cb5 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListBuilder.php @@ -0,0 +1,204 @@ +<?php + +namespace PicoFeed\Serialization; + +use DOMDocument; +use DOMElement; + +/** + * Class SubscriptionListBuilder + * + * @package PicoFeed\Serialization + * @author Frederic Guillot + */ +class SubscriptionListBuilder +{ + /** + * @var SubscriptionList + */ + protected $subscriptionList; + + /** + * @var DOMDocument + */ + protected $document; + + /** + * Constructor. + * + * @access public + * @param SubscriptionList $subscriptionList + */ + public function __construct(SubscriptionList $subscriptionList) + { + $this->subscriptionList = $subscriptionList; + } + + /** + * Get object instance + * + * @static + * @access public + * @param SubscriptionList $subscriptionList + * @return SubscriptionListBuilder + */ + public static function create(SubscriptionList $subscriptionList) + { + return new static($subscriptionList); + } + + /** + * Build OPML feed + * + * @access public + * @param string $filename + * @return string + */ + public function build($filename = '') + { + $this->document = new DomDocument('1.0', 'UTF-8'); + $this->document->formatOutput = true; + + $opmlElement = $this->document->createElement('opml'); + $opmlElement->setAttribute('version', '1.0'); + + $headElement = $this->document->createElement('head'); + + if ($this->subscriptionList->getTitle() !== '') { + $titleElement = $this->document->createElement('title'); + $titleElement->appendChild($this->document->createTextNode($this->subscriptionList->getTitle())); + $headElement->appendChild($titleElement); + } + + $opmlElement->appendChild($headElement); + $opmlElement->appendChild($this->buildBody()); + $this->document->appendChild($opmlElement); + + if ($filename !== '') { + $this->document->save($filename); + return ''; + } + + return $this->document->saveXML(); + } + + /** + * Return true if the list has categories + * + * @access public + * @return bool + */ + public function hasCategories() + { + foreach ($this->subscriptionList->subscriptions as $subscription) { + if ($subscription->getCategory() !== '') { + return true; + } + } + + return false; + } + + /** + * Build OPML body + * + * @access protected + * @return DOMElement + */ + protected function buildBody() + { + $bodyElement = $this->document->createElement('body'); + + if ($this->hasCategories()) { + $this->buildCategories($bodyElement); + return $bodyElement; + } + + foreach ($this->subscriptionList->subscriptions as $subscription) { + $bodyElement->appendChild($this->buildSubscription($subscription)); + } + + return $bodyElement; + } + + /** + * Build categories section + * + * @access protected + * @param DOMElement $bodyElement + */ + protected function buildCategories(DOMElement $bodyElement) + { + $categories = $this->groupByCategories(); + + foreach ($categories as $category => $subscriptions) { + $bodyElement->appendChild($this->buildCategory($category, $subscriptions)); + } + } + + /** + * Build category tag + * + * @access protected + * @param string $category + * @param array $subscriptions + * @return DOMElement + */ + protected function buildCategory($category, array $subscriptions) + { + $outlineElement = $this->document->createElement('outline'); + $outlineElement->setAttribute('text', $category); + + foreach ($subscriptions as $subscription) { + $outlineElement->appendChild($this->buildSubscription($subscription)); + } + + return $outlineElement; + } + + /** + * Build subscription entry + * + * @access public + * @param Subscription $subscription + * @return DOMElement + */ + protected function buildSubscription(Subscription $subscription) + { + $outlineElement = $this->document->createElement('outline'); + $outlineElement->setAttribute('type', $subscription->getType() ?: 'rss'); + $outlineElement->setAttribute('text', $subscription->getTitle() ?: $subscription->getFeedUrl()); + $outlineElement->setAttribute('xmlUrl', $subscription->getFeedUrl()); + + if ($subscription->getTitle() !== '') { + $outlineElement->setAttribute('title', $subscription->getTitle()); + } + + if ($subscription->getDescription() !== '') { + $outlineElement->setAttribute('description', $subscription->getDescription()); + } + + if ($subscription->getSiteUrl() !== '') { + $outlineElement->setAttribute('htmlUrl', $subscription->getSiteUrl()); + } + + return $outlineElement; + } + + /** + * Group subscriptions by category + * + * @access private + * @return array + */ + private function groupByCategories() + { + $categories = array(); + + foreach ($this->subscriptionList->subscriptions as $subscription) { + $categories[$subscription->getCategory()][] = $subscription; + } + + return $categories; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListParser.php new file mode 100644 index 00000000..9085588c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListParser.php @@ -0,0 +1,100 @@ +<?php + +namespace PicoFeed\Serialization; + +use PicoFeed\Parser\MalformedXmlException; +use PicoFeed\Parser\XmlParser; +use SimpleXMLElement; + +/** + * Class SubscriptionListParser + * + * @package PicoFeed\Serialization + * @author Frederic Guillot + */ +class SubscriptionListParser +{ + /** + * @var SubscriptionList + */ + protected $subscriptionList; + + /** + * @var string + */ + protected $data; + + /** + * Constructor + * + * @access public + * @param string $data + */ + public function __construct($data) + { + $this->subscriptionList = new SubscriptionList(); + $this->data = trim($data); + } + + /** + * Get object instance + * + * @static + * @access public + * @param string $data + * @return SubscriptionListParser + */ + public static function create($data) + { + return new static($data); + } + + /** + * Parse a subscription list entry + * + * @access public + * @throws MalformedXmlException + * @return SubscriptionList + */ + public function parse() + { + $xml = XmlParser::getSimpleXml($this->data); + + if (! $xml || !isset($xml->head) || !isset($xml->body)) { + throw new MalformedXmlException('Unable to parse OPML file: invalid XML'); + } + + $this->parseTitle($xml->head); + $this->parseEntries($xml->body); + + return $this->subscriptionList; + } + + /** + * Parse title + * + * @access protected + * @param SimpleXMLElement $xml + */ + protected function parseTitle(SimpleXMLElement $xml) + { + $this->subscriptionList->setTitle((string) $xml->title); + } + + /** + * Parse entries + * + * @access protected + * @param SimpleXMLElement $body + */ + private function parseEntries(SimpleXMLElement $body) + { + foreach ($body->outline as $outlineElement) { + if (isset($outlineElement->outline)) { + $this->parseEntries($outlineElement); + } else { + $this->subscriptionList->subscriptions[] = SubscriptionParser::create($body, $outlineElement)->parse(); + } + } + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionParser.php new file mode 100644 index 00000000..caff07c2 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionParser.php @@ -0,0 +1,142 @@ +<?php + +namespace PicoFeed\Serialization; + +use SimpleXMLElement; + +/** + * Class SubscriptionParser + * + * @package PicoFeed\Serialization + * @author Frederic Guillot + */ +class SubscriptionParser +{ + /** + * @var Subscription + */ + protected $subscription; + + /** + * @var SimpleXMLElement + */ + private $outlineElement; + + /** + * @var SimpleXMLElement + */ + private $parentElement; + + /** + * Constructor + * + * @access public + * @param SimpleXMLElement $parentElement + * @param SimpleXMLElement $outlineElement + */ + public function __construct(SimpleXMLElement $parentElement, SimpleXMLElement $outlineElement) + { + $this->parentElement = $parentElement; + $this->outlineElement = $outlineElement; + $this->subscription = new Subscription(); + } + + /** + * Get object instance + * + * @static + * @access public + * @param SimpleXMLElement $parentElement + * @param SimpleXMLElement $outlineElement + * @return SubscriptionParser + */ + public static function create(SimpleXMLElement $parentElement, SimpleXMLElement $outlineElement) + { + return new static($parentElement, $outlineElement); + } + + /** + * Parse subscription entry + * + * @access public + * @return Subscription + */ + public function parse() + { + $this->subscription->setCategory($this->findCategory()); + $this->subscription->setTitle($this->findTitle()); + $this->subscription->setFeedUrl($this->findFeedUrl()); + $this->subscription->setSiteUrl($this->findSiteUrl()); + $this->subscription->setType($this->findType()); + $this->subscription->setDescription($this->findDescription()); + + return $this->subscription; + } + + /** + * Find category. + * + * @access protected + * @return string + */ + protected function findCategory() + { + return isset($this->parentElement['text']) ? (string) $this->parentElement['text'] : ''; + } + + /** + * Find title. + * + * @access protected + * @return string + */ + protected function findTitle() + { + return isset($this->outlineElement['title']) ? (string) $this->outlineElement['title'] : (string) $this->outlineElement['text']; + } + + /** + * Find feed url. + * + * @access protected + * @return string + */ + protected function findFeedUrl() + { + return (string) $this->outlineElement['xmlUrl']; + } + + /** + * Find site url. + * + * @access protected + * @return string + */ + protected function findSiteUrl() + { + return isset($this->outlineElement['htmlUrl']) ? (string) $this->outlineElement['htmlUrl'] : $this->findFeedUrl(); + } + + /** + * Find type. + * + * @access protected + * @return string + */ + protected function findType() + { + return isset($this->outlineElement['version']) ? (string) $this->outlineElement['version'] : + isset($this->outlineElement['type']) ? (string) $this->outlineElement['type'] : 'rss'; + } + + /** + * Find description. + * + * @access protected + * @return string + */ + protected function findDescription() + { + return isset($this->outlineElement['description']) ? (string) $this->outlineElement['description'] : $this->findTitle(); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomFeedBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomFeedBuilder.php new file mode 100644 index 00000000..34f37800 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomFeedBuilder.php @@ -0,0 +1,65 @@ +<?php + +namespace PicoFeed\Syndication; + +use DOMAttr; +use DOMElement; + +/** + * Atom Feed Builder + * + * @package PicoFeed\Syndication + * @author Frederic Guillot + */ +class AtomFeedBuilder extends FeedBuilder +{ + /** + * @var DOMElement + */ + protected $feedElement; + + /** + * @var AtomHelper + */ + protected $helper; + + /** + * Build feed + * + * @access public + * @param string $filename + * @return string + */ + public function build($filename = '') + { + $this->helper = new AtomHelper($this->getDocument()); + + $this->feedElement = $this->getDocument()->createElement('feed'); + $this->feedElement->setAttributeNodeNS(new DomAttr('xmlns', 'http://www.w3.org/2005/Atom')); + + $generator = $this->getDocument()->createElement('generator', 'PicoFeed'); + $generator->setAttribute('uri', 'https://github.com/miniflux/picoFeed'); + $this->feedElement->appendChild($generator); + + $this->helper + ->buildTitle($this->feedElement, $this->feedTitle) + ->buildId($this->feedElement, $this->feedUrl) + ->buildDate($this->feedElement, $this->feedDate) + ->buildLink($this->feedElement, $this->siteUrl) + ->buildLink($this->feedElement, $this->feedUrl, 'self', 'application/atom+xml') + ->buildAuthor($this->feedElement, $this->authorName, $this->authorEmail, $this->authorUrl) + ; + + foreach ($this->items as $item) { + $this->feedElement->appendChild($item->build()); + } + + $this->getDocument()->appendChild($this->feedElement); + + if ($filename !== '') { + $this->getDocument()->save($filename); + } + + return $this->getDocument()->saveXML(); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomHelper.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomHelper.php new file mode 100644 index 00000000..def6b0b9 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomHelper.php @@ -0,0 +1,139 @@ +<?php + +namespace PicoFeed\Syndication; + +use DateTime; +use DOMDocument; +use DOMElement; + +/** + * Class AtomHelper + * + * @package PicoFeed\Syndication + * @author Frederic Guillot + */ +class AtomHelper +{ + /** + * @var DOMDocument + */ + protected $document; + + /** + * Constructor + * + * @param DOMDocument $document + */ + public function __construct(DOMDocument $document) + { + $this->document = $document; + } + + /** + * Build node + * + * @access public + * @param DOMElement $element + * @param string $tag + * @param string $value + * @return AtomHelper + */ + public function buildNode(DOMElement $element, $tag, $value) + { + $node = $this->document->createElement($tag); + $node->appendChild($this->document->createTextNode($value)); + $element->appendChild($node); + return $this; + } + + /** + * Build title + * + * @access public + * @param DOMElement $element + * @param string $title + * @return AtomHelper + */ + public function buildTitle(DOMElement $element, $title) + { + return $this->buildNode($element, 'title', $title); + } + + /** + * Build id + * + * @access public + * @param DOMElement $element + * @param string $id + * @return AtomHelper + */ + public function buildId(DOMElement $element, $id) + { + return $this->buildNode($element, 'id', $id); + } + + /** + * Build date element + * + * @access public + * @param DOMElement $element + * @param DateTime $date + * @param string $type + * @return AtomHelper + */ + public function buildDate(DOMElement $element, DateTime $date, $type = 'updated') + { + return $this->buildNode($element, $type, $date->format(DateTime::ATOM)); + } + + /** + * Build link element + * + * @access public + * @param DOMElement $element + * @param string $url + * @param string $rel + * @param string $type + * @return AtomHelper + */ + public function buildLink(DOMElement $element, $url, $rel = 'alternate', $type = 'text/html') + { + $node = $this->document->createElement('link'); + $node->setAttribute('rel', $rel); + $node->setAttribute('type', $type); + $node->setAttribute('href', $url); + $element->appendChild($node); + + return $this; + } + + /** + * Build author element + * + * @access public + * @param DOMElement $element + * @param string $authorName + * @param string $authorEmail + * @param string $authorUrl + * @return AtomHelper + */ + public function buildAuthor(DOMElement $element, $authorName, $authorEmail, $authorUrl) + { + if (!empty($authorName)) { + $author = $this->document->createElement('author'); + $this->buildNode($author, 'name', $authorName); + + if (!empty($authorEmail)) { + $this->buildNode($author, 'email', $authorEmail); + } + + if (!empty($authorUrl)) { + $this->buildNode($author, 'uri', $authorUrl); + } + + $element->appendChild($author); + } + + return $this; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomItemBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomItemBuilder.php new file mode 100644 index 00000000..dfdfe68d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomItemBuilder.php @@ -0,0 +1,63 @@ +<?php + +namespace PicoFeed\Syndication; + +use DOMElement; + +/** + * Atom Item Builder + * + * @package PicoFeed\Syndication + * @author Frederic Guillot + */ +class AtomItemBuilder extends ItemBuilder +{ + /** + * @var DOMElement + */ + protected $itemElement; + + /** + * @var AtomHelper + */ + protected $helper; + + /** + * Build item + * + * @access public + * @return DOMElement + */ + public function build() + { + $this->itemElement = $this->feedBuilder->getDocument()->createElement('entry'); + $this->helper = new AtomHelper($this->feedBuilder->getDocument()); + + if (!empty($this->itemId)) { + $this->helper->buildId($this->itemElement, $this->itemId); + } else { + $this->helper->buildId($this->itemElement, $this->itemUrl); + } + + $this->helper + ->buildTitle($this->itemElement, $this->itemTitle) + ->buildLink($this->itemElement, $this->itemUrl) + ->buildDate($this->itemElement, $this->itemUpdatedDate, 'updated') + ->buildDate($this->itemElement, $this->itemPublishedDate, 'published') + ->buildAuthor($this->itemElement, $this->authorName, $this->authorEmail, $this->authorUrl) + ; + + if (!empty($this->itemSummary)) { + $this->helper->buildNode($this->itemElement, 'summary', $this->itemSummary); + } + + if (!empty($this->itemContent)) { + $node = $this->feedBuilder->getDocument()->createElement('content'); + $node->setAttribute('type', 'html'); + $node->appendChild($this->feedBuilder->getDocument()->createCDATASection($this->itemContent)); + $this->itemElement->appendChild($node); + } + + return $this->itemElement; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/FeedBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/FeedBuilder.php new file mode 100644 index 00000000..cf9d024e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/FeedBuilder.php @@ -0,0 +1,185 @@ +<?php + +namespace PicoFeed\Syndication; + +use DateTime; +use DOMDocument; + +/** + * Class FeedBuilder + * + * @package PicoFeed\Syndication + * @author Frederic Guillot + */ +abstract class FeedBuilder +{ + /** + * @var DOMDocument + */ + protected $document; + + /** + * @var string + */ + protected $feedTitle; + + /** + * @var string + */ + protected $feedUrl; + + /** + * @var string + */ + protected $siteUrl; + + /** + * @var string + */ + protected $authorName; + + /** + * @var string + */ + protected $authorEmail; + + /** + * @var string + */ + protected $authorUrl; + + /** + * @var DateTime + */ + protected $feedDate; + + /** + * @var ItemBuilder[] + */ + protected $items = array(); + + /** + * Constructor + * + * @access public + */ + public function __construct() + { + $this->document = new DomDocument('1.0', 'UTF-8'); + $this->document->formatOutput = true; + } + + /** + * Get new object instance + * + * @access public + * @return static + */ + public static function create() + { + return new static(); + } + + /** + * Add feed title + * + * @access public + * @param string $title + * @return $this + */ + public function withTitle($title) + { + $this->feedTitle = $title; + return $this; + } + + /** + * Add feed url + * + * @access public + * @param string $url + * @return $this + */ + public function withFeedUrl($url) + { + $this->feedUrl = $url; + return $this; + } + + /** + * Add website url + * + * @access public + * @param string $url + * @return $this + */ + public function withSiteUrl($url) + { + $this->siteUrl = $url; + return $this; + } + + /** + * Add feed date + * + * @access public + * @param DateTime $date + * @return $this + */ + public function withDate(DateTime $date) + { + $this->feedDate = $date; + return $this; + } + + /** + * Add feed author + * + * @access public + * @param string $name + * @param string $email + * @param string $url + * @return $this + */ + public function withAuthor($name, $email = '', $url ='') + { + $this->authorName = $name; + $this->authorEmail = $email; + $this->authorUrl = $url; + return $this; + } + + /** + * Add feed item + * + * @access public + * @param ItemBuilder $item + * @return $this + */ + public function withItem(ItemBuilder $item) + { + $this->items[] = $item; + return $this; + } + + /** + * Get DOM document + * + * @access public + * @return DOMDocument + */ + public function getDocument() + { + return $this->document; + } + + /** + * Build feed + * + * @abstract + * @access public + * @param string $filename + * @return string + */ + abstract public function build($filename = ''); +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/ItemBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/ItemBuilder.php new file mode 100644 index 00000000..86985bc7 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/ItemBuilder.php @@ -0,0 +1,209 @@ +<?php + +namespace PicoFeed\Syndication; + +use DateTime; +use DOMElement; + +/** + * Class ItemBuilder + * + * @package PicoFeed\Syndication + * @author Frederic Guillot + */ +abstract class ItemBuilder +{ + /** + * @var string + */ + protected $itemTitle; + + /** + * @var string + */ + protected $itemId; + + /** + * @var string + */ + protected $itemSummary; + + /** + * @var string + */ + protected $authorName; + + /** + * @var string + */ + protected $authorEmail; + + /** + * @var string + */ + protected $authorUrl; + + /** + * @var DateTime + */ + protected $itemPublishedDate; + + /** + * @var DateTime + */ + protected $itemUpdatedDate; + + /** + * @var string + */ + protected $itemContent; + + /** + * @var string + */ + protected $itemUrl; + + /** + * @var FeedBuilder + */ + protected $feedBuilder; + + /** + * Constructor + * + * @param FeedBuilder $feedBuilder + */ + public function __construct(FeedBuilder $feedBuilder) + { + $this->feedBuilder = $feedBuilder; + } + + /** + * Get new object instance + * + * @access public + * @param FeedBuilder $feedBuilder + * @return static + */ + public static function create(FeedBuilder $feedBuilder) + { + return new static($feedBuilder); + } + + /** + * Add item title + * + * @access public + * @param string $title + * @return $this + */ + public function withTitle($title) + { + $this->itemTitle = $title; + return $this; + } + + /** + * Add item id + * + * @access public + * @param string $id + * @return $this + */ + public function withId($id) + { + $this->itemId = $id; + return $this; + } + + /** + * Add item url + * + * @access public + * @param string $url + * @return $this + */ + public function withUrl($url) + { + $this->itemUrl = $url; + return $this; + } + + /** + * Add item summary + * + * @access public + * @param string $summary + * @return $this + */ + public function withSummary($summary) + { + $this->itemSummary = $summary; + return $this; + } + + /** + * Add item content + * + * @access public + * @param string $content + * @return $this + */ + public function withContent($content) + { + $this->itemContent = $content; + return $this; + } + + /** + * Add item updated date + * + * @access public + * @param DateTime $date + * @return $this + */ + public function withUpdatedDate(DateTime $date) + { + $this->itemUpdatedDate = $date; + return $this; + } + + /** + * Add item published date + * + * @access public + * @param DateTime $date + * @return $this + */ + public function withPublishedDate(DateTime $date) + { + $this->itemPublishedDate = $date; + return $this; + } + + /** + * Add item author + * + * @access public + * @param string $name + * @param string $email + * @param string $url + * @return $this + */ + public function withAuthor($name, $email = '', $url ='') + { + $this->authorName = $name; + $this->authorEmail = $email; + $this->authorUrl = $url; + return $this; + } + + /** + * Build item + * + * @abstract + * @access public + * @return DOMElement + */ + abstract public function build(); +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20FeedBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20FeedBuilder.php new file mode 100644 index 00000000..bc3f5135 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20FeedBuilder.php @@ -0,0 +1,76 @@ +<?php + +namespace PicoFeed\Syndication; + +use DOMAttr; +use DOMElement; + +/** + * Rss20 Feed Builder + * + * @package PicoFeed\Syndication + * @author Frederic Guillot + */ +class Rss20FeedBuilder extends FeedBuilder +{ + /** + * @var DOMElement + */ + protected $rssElement; + + /** + * @var Rss20Helper + */ + protected $helper; + + /** + * @var DOMElement + */ + protected $channelElement; + + /** + * Build feed + * + * @access public + * @param string $filename + * @return string + */ + public function build($filename = '') + { + $this->helper = new Rss20Helper($this->getDocument()); + + $this->rssElement = $this->getDocument()->createElement('rss'); + $this->rssElement->setAttribute('version', '2.0'); + $this->rssElement->setAttributeNodeNS(new DomAttr('xmlns:content', 'http://purl.org/rss/1.0/modules/content/')); + $this->rssElement->setAttributeNodeNS(new DomAttr('xmlns:atom', 'http://www.w3.org/2005/Atom')); + + $this->channelElement = $this->getDocument()->createElement('channel'); + $this->helper + ->buildNode($this->channelElement, 'generator', 'PicoFeed (https://github.com/miniflux/picoFeed)') + ->buildTitle($this->channelElement, $this->feedTitle) + ->buildNode($this->channelElement, 'description', $this->feedTitle) + ->buildDate($this->channelElement, $this->feedDate) + ->buildAuthor($this->channelElement, 'webMaster', $this->authorName, $this->authorEmail) + ->buildLink($this->channelElement, $this->siteUrl) + ; + + $link = $this->getDocument()->createElement('atom:link'); + $link->setAttribute('href', $this->feedUrl); + $link->setAttribute('rel', 'self'); + $link->setAttribute('type', 'application/rss+xml'); + $this->channelElement->appendChild($link); + + foreach ($this->items as $item) { + $this->channelElement->appendChild($item->build()); + } + + $this->rssElement->appendChild($this->channelElement); + $this->getDocument()->appendChild($this->rssElement); + + if ($filename !== '') { + $this->getDocument()->save($filename); + } + + return $this->getDocument()->saveXML(); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20Helper.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20Helper.php new file mode 100644 index 00000000..72a19e56 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20Helper.php @@ -0,0 +1,115 @@ +<?php + +namespace PicoFeed\Syndication; + +use DateTime; +use DOMDocument; +use DOMElement; + +/** + * Class Rss20Helper + * + * @package PicoFeed\Syndication + * @author Frederic Guillot + */ +class Rss20Helper +{ + /** + * @var DOMDocument + */ + protected $document; + + /** + * Constructor + * + * @param DOMDocument $document + */ + public function __construct(DOMDocument $document) + { + $this->document = $document; + } + + /** + * Build node + * + * @access public + * @param DOMElement $element + * @param string $tag + * @param string $value + * @return $this + */ + public function buildNode(DOMElement $element, $tag, $value) + { + $node = $this->document->createElement($tag); + $node->appendChild($this->document->createTextNode($value)); + $element->appendChild($node); + return $this; + } + + /** + * Build title + * + * @access public + * @param DOMElement $element + * @param string $title + * @return $this + */ + public function buildTitle(DOMElement $element, $title) + { + return $this->buildNode($element, 'title', $title); + } + + /** + * Build date element + * + * @access public + * @param DOMElement $element + * @param DateTime $date + * @param string $type + * @return $this + */ + public function buildDate(DOMElement $element, DateTime $date, $type = 'pubDate') + { + return $this->buildNode($element, $type, $date->format(DateTime::RSS)); + } + + /** + * Build link element + * + * @access public + * @param DOMElement $element + * @param string $url + * @return $this + */ + public function buildLink(DOMElement $element, $url) + { + return $this->buildNode($element, 'link', $url); + } + + /** + * Build author element + * + * @access public + * @param DOMElement $element + * @param string $tag + * @param string $authorName + * @param string $authorEmail + * @return $this + */ + public function buildAuthor(DOMElement $element, $tag, $authorName, $authorEmail) + { + if (!empty($authorName)) { + $value = ''; + + if (!empty($authorEmail)) { + $value .= $authorEmail.' ('.$authorName.')'; + } else { + $value = $authorName; + } + + $this->buildNode($element, $tag, $value); + } + + return $this; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20ItemBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20ItemBuilder.php new file mode 100644 index 00000000..125dc6ac --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20ItemBuilder.php @@ -0,0 +1,67 @@ +<?php + +namespace PicoFeed\Syndication; + +use DOMElement; + +/** + * Rss20 Item Builder + * + * @package PicoFeed\Syndication + * @author Frederic Guillot + */ +class Rss20ItemBuilder extends ItemBuilder +{ + /** + * @var DOMElement + */ + protected $itemElement; + + /** + * @var Rss20Helper + */ + protected $helper; + + /** + * Build item + * + * @access public + * @return DOMElement + */ + public function build() + { + $this->itemElement = $this->feedBuilder->getDocument()->createElement('item'); + $this->helper = new Rss20Helper($this->feedBuilder->getDocument()); + + if (!empty($this->itemId)) { + $guid = $this->feedBuilder->getDocument()->createElement('guid'); + $guid->setAttribute('isPermaLink', 'false'); + $guid->appendChild($this->feedBuilder->getDocument()->createTextNode($this->itemId)); + $this->itemElement->appendChild($guid); + } else { + $guid = $this->feedBuilder->getDocument()->createElement('guid'); + $guid->setAttribute('isPermaLink', 'true'); + $guid->appendChild($this->feedBuilder->getDocument()->createTextNode($this->itemUrl)); + $this->itemElement->appendChild($guid); + } + + $this->helper + ->buildTitle($this->itemElement, $this->itemTitle) + ->buildLink($this->itemElement, $this->itemUrl) + ->buildDate($this->itemElement, $this->itemPublishedDate) + ->buildAuthor($this->itemElement, 'author', $this->authorName, $this->authorEmail) + ; + + if (!empty($this->itemSummary)) { + $this->helper->buildNode($this->itemElement, 'description', $this->itemSummary); + } + + if (!empty($this->itemContent)) { + $node = $this->feedBuilder->getDocument()->createElement('content:encoded'); + $node->appendChild($this->feedBuilder->getDocument()->createCDATASection($this->itemContent)); + $this->itemElement->appendChild($node); + } + + return $this->itemElement; + } +} diff --git a/vendor/miniflux/picofeed/picofeed b/vendor/miniflux/picofeed/picofeed new file mode 100755 index 00000000..8f35737a --- /dev/null +++ b/vendor/miniflux/picofeed/picofeed @@ -0,0 +1,135 @@ +#!/usr/bin/env php +<?php + +require_once 'vendor/autoload.php'; + +use PicoFeed\Config\Config; +use PicoFeed\Reader\Favicon; +use PicoFeed\Scraper\Scraper; +use PicoFeed\Reader\Reader; +use PicoFeed\Logging\Logger; +use PicoFeed\PicoFeedException; + +Logger::enable(); + +function get_feed($url, $disable_filtering = false) +{ + try { + + $reader = new Reader; + $resource = $reader->discover($url); + + $parser = $reader->getParser( + $resource->getUrl(), + $resource->getContent(), + $resource->getEncoding() + ); + + if ($disable_filtering) { + $parser->disableContentFiltering(); + } + + return $parser->execute(); + } + catch (PicoFeedException $e) { + echo 'Exception thrown ===> "'.$e->getMessage().'"'.PHP_EOL; + return false; + } +} + +function get_item($feed, $item_id) +{ + foreach ($feed->items as $item) { + if ($item->getId() === $item_id) { + echo $item; + echo "============= CONTENT ================\n"; + echo $item->getContent(); + echo "\n============= CONTENT ================\n"; + break; + } + } +} + +function dump_feed($url) +{ + $feed = get_feed($url); + echo $feed; +} + +function debug_feed($url) +{ + get_feed($url); + print_r(Logger::getMessages()); +} + +function dump_item($url, $item_id) +{ + $feed = get_feed($url); + + if ($feed !== false) { + get_item($feed, $item_id); + } +} + +function nofilter_item($url, $item_id) +{ + $feed = get_feed($url, true); + + if ($feed !== false) { + get_item($feed, $item_id); + } +} + +function grabber($url) +{ + $grabber = new Scraper(new Config); + $grabber->setUrl($url); + $grabber->execute(); + + print_r(Logger::getMessages()); + echo "============= CONTENT ================\n"; + echo $grabber->getRelevantContent().PHP_EOL; + echo "============= FILTERED ================\n"; + echo $grabber->getFilteredContent().PHP_EOL; +} + +function fetch_favicon($url) +{ + $favicon = new Favicon(); + echo $favicon->find($url) . PHP_EOL; +} + +// Parse command line arguments +if ($argc === 4) { + switch ($argv[1]) { + case 'item': + dump_item($argv[2], $argv[3]); + die; + case 'nofilter': + nofilter_item($argv[2], $argv[3]); + die; + } +} else if ($argc === 3) { + switch ($argv[1]) { + case 'feed': + dump_feed($argv[2]); + die; + case 'debug': + debug_feed($argv[2]); + die; + case 'grabber': + grabber($argv[2]); + die; + case 'favicon': + fetch_favicon($argv[2]); + die; + } +} + +printf("Usage:\n"); +printf("%s feed <feed-url>\n", $argv[0]); +printf("%s debug <feed-url>\n", $argv[0]); +printf("%s item <feed-url> <item-id>\n", $argv[0]); +printf("%s nofilter <feed-url> <item-id>\n", $argv[0]); +printf("%s grabber <url>\n", $argv[0]); +printf("%s favicon <url>\n", $argv[0]); diff --git a/vendor/paragonie/random_compat/CHANGELOG.md b/vendor/paragonie/random_compat/CHANGELOG.md new file mode 100644 index 00000000..247deace --- /dev/null +++ b/vendor/paragonie/random_compat/CHANGELOG.md @@ -0,0 +1,260 @@ +### Version 2.0.2 - 2016-04-03 + +Added a consistency check (discovered by Taylor Hornby in his +[PHP encryption library](https://github.com/defuse/php-encryption)). It +wasn't likely causing any trouble for us. + +### Version 2.0.1 - 2016-03-18 + +Update comment in random.php + +### Version 2.0.0 - 2016-03-18 + +Due to downstream errors, the OpenSSL removal now belongs in version +2.0.0. + +### Version 1.3.1 - 2016-03-18 + +* Add more possible values to `open_baseir` check. + +### Version 1.3.0 - 2016-03-17 + +* Removed `openssl_random_pseudo_bytes()` entirely. If you are using + random_compat in PHP on a Unix-like OS but cannot access + `/dev/urandom`, version 1.3+ will throw an `Exception`. If you want to + trust OpenSSL, feel free to write your own fallback code. e.g. + + ```php + try { + $bytes = random_bytes(32); + } catch (Exception $ex) { + $strong = false; + $bytes = openssl_random_pseudo_bytes(32, $strong); + if (!$strong) { + throw $ex; + } + } + ``` + +### Version 1.2.2 - 2016-03-11 + +* To prevent applications from hanging, if `/dev/urandom` is not + accessible to PHP, skip mcrypt (which just fails before giving OpenSSL + a chance and was morally equivalent to not offering OpenSSL at all). + +### Version 1.2.1 - 2016-02-29 + +* PHP 5.6.10 - 5.6.12 will hang when mcrypt is used on Unix-based operating + systems ([PHP bug 69833](https://bugs.php.net/bug.php?id=69833)). If you are + running one of these versions, please upgrade (or make sure `/dev/urandom` is + readable) otherwise you're relying on OpenSSL. + +### Version 1.2.0 - 2016-02-05 + +* Whitespace and other cosmetic changes +* Added a changelog. +* We now ship with a command line utility to build a PHP Archive from the + command line. + + Every time we publish a new release, we will also upload a .phar + to Github. Our public key is signed by our GPG key. + +### Version 1.1.6 - 2016-01-29 + +* Eliminate `open_basedir` warnings by detecting this configuration setting. + (Thanks [@oucil](https://github.com/oucil) for reporting this.) +* Added install instructions to the README. +* Documentation cleanup (there is, in fact, no `MCRYPT_CREATE_IV` constant, I + meant to write `MCRYPT_DEV_URANDOM`) + +### Version 1.1.5 - 2016-01-06 + +Prevent fatal errors on platforms with older versions of libsodium. + +### Version 1.1.4 - 2015-12-10 + +Thanks [@narfbg](https://github.com/narfbg) for [critiquing the previous patch](https://github.com/paragonie/random_compat/issues/79#issuecomment-163590589) +and suggesting a fix. + +### Version 1.1.3 - 2015-12-09 + +The test for COM in disabled_classes is now case-insensitive. + +### Version 1.1.2 - 2015-12-09 + +Don't instantiate COM if it's a disabled class. Removes the E_WARNING on Windows. + +### Version 1.1.1 - 2015-11-30 + +Fix a performance issue with `/dev/urandom` buffering. + +### Version 1.1.0 - 2015-11-09 + +Fix performance issues with ancient versions of PHP on Windows, but dropped +support for PHP < 5.4.1 without mcrypt on Windows 7+ in the process. Since this + is a BC break, semver dictates a minor version bump. + +### Version 1.0.10 - 2015-10-23 + +* Avoid a performance killer with OpenSSL on Windows PHP 5.3.0 - 5.3.3 that was + affecting [WordPress users](https://core.trac.wordpress.org/ticket/34409). +* Use `$var = null` instead of `unset($var)` to avoid triggering the garbage + collector and slowing things down. + +### Version 1.0.9 - 2015-10-20 + +There is an outstanding issue `mcrypt_create_iv()` and PHP 7's `random_bytes()` +on Windows reported by [@nicolas-grekas](https://github.com/nicolas-grekas) caused by `proc_open()` and environment +variable handling (discovered by Appveyor when developing Symfony). + +Since the break is consistent, it's not our responsibility to fix it, but we +should fail the same way PHP 7 will (i.e. throw an `Exception` rather than raise +an error and then throw an `Exception`). + +### Version 1.0.8 - 2015-10-18 + +* Fix usability issues with Windows (`new COM('CAPICOM.Utilities.1')` is not + always available). +* You can now test all the possible drivers by running `phpunit.sh each` in the + `tests` directory. + +### Version 1.0.7 - 2015-10-16 + +Several large integer handling bugfixes were contributed by [@oittaa](https://github.com/oittaa). + +### Version 1.0.6 - 2015-10-15 + +Don't let the version number fool you, this was a pretty significant change. + +1. Added support for ext-libsodium, if it exists on the system. This is morally + equivalent to adding `getrandom(2)` support without having to expose the + syscall interface in PHP-land. +2. Relaxed open_basedir restrictions. In previous versions, if open_basedir was + set, PHP wouldn't even try to read from `/dev/urandom`. Now it will still do + so if you can. +3. Fixed integer casting inconsistencies between random_compat and PHP 7. +4. Handle edge cases where an integer overflow turns one of the parameters into + a float. + +One change that we discussed was making `random_bytes()` and `random_int()` +strict typed; meaning you could *only* pass integers to either function. While +most veteran programmers are probably only doing this already (we strongly +encourage it), it wouldn't be consistent with how these functions behave in PHP +7. Please use these functions responsibly. + +We've had even more of the PHP community involved in this release; the +contributors list has been updated. If I forgot anybody, I promise you it's not +because your contributions (either code or ideas) aren't valued, it's because +I'm a bit overloaded with information at the moment. Please let me know +immediately and I will correct my oversight. + +Thanks everyone for helping make random_compat better. + +### Version 1.0.5 - 2015-10-08 + +Got rid of the methods in the `Throwable` interface, which was causing problems +on PHP 5.2. While we would normally not care about 5.2 (since [5.4 and earlier are EOL'd](https://secure.php.net/supported-versions.php)), +we do want to encourage widespread adoption (e.g. [Wordpress](https://core.trac.wordpress.org/ticket/28633)). + +### Version 1.0.4 - 2015-10-02 + +Removed redundant `if()` checks, since `lib/random.php` is the entrypoint people +should use. + +### Version 1.0.3 - 2015-10-02 + +This release contains bug fixes contributed by the community. + +* Avoid a PHP Notice when PHP is running without the mbstring extension +* Use a compatible version of PHPUnit for testing on older versions of PHP + +Although none of these bugs were outright security-affecting, updating ASAP is +still strongly encouraged. + +### Version 1.0.2 - 2015-09-23 + +Less strict input validation on `random_int()` parameters. PHP 7's `random_int()` +accepts strings and floats that look like numbers, so we should too. + +Thanks [@dd32](https://github.com/@dd32) for correcting this oversight. + +### Version 1.0.1 - 2015-09-10 + +Instead of throwing an Exception immediately on insecure platforms, only do so +when `random_bytes()` is invoked. + +### Version 1.0.0 - 2015-09-07 + +Our API is now stable and forward-compatible with the CSPRNG features in PHP 7 +(as of 7.0.0 RC3). + +A lot of great people have contributed their time and expertise to make this +compatibility library possible. That this library has reached a stable release +is more a reflection on the community than it is on PIE. + +We are confident that random_compat will serve as the simplest and most secure +CSPRNG interface available for PHP5 projects. + +### Version 0.9.7 (pre-release) - 2015-09-01 + +An attempt to achieve compatibility with Error/TypeError in the RFC. + +This should be identical to 1.0.0 sans any last-minute changes or performance enhancements. + +### Version 0.9.6 (pre-release) - 2015-08-06 + +* Split the implementations into their own file (for ease of auditing) +* Corrected the file type check after `/dev/urandom` has been opened (thanks + [@narfbg](https://github.com/narfbg) and [@jedisct1](https://github.com/jedisct1)) + +### Version 0.9.5 (pre-release) - 2015-07-31 + +* Validate that `/dev/urandom` is a character device + * Reported by [@lokdnet](https://twitter.com/lokdnet) + * Investigated by [@narfbg](https://github.com/narfbg) and [frymaster](http://stackoverflow.com/users/1226810/frymaster) on [StackOverflow](http://stackoverflow.com/q/31631066/2224584) +* Remove support for `/dev/arandom` which is an old OpenBSD feature, thanks [@jedisct1](https://github.com/jedisct1) +* Prevent race conditions on the `filetype()` check, thanks [@jedisct1](https://github.com/jedisct1) +* Buffer file reads to 8 bytes (performance optimization; PHP defaults to 8192 bytes) + +### Version 0.9.4 (pre-release) - 2015-07-27 + +* Add logic to verify that `/dev/arandom` and `/dev/urandom` are actually devices. +* Some clean-up in the comments + +### Version 0.9.3 (pre-release) - 2015-07-22 + +Unless the Exceptions change to PHP 7 fails, this should be the last pre-release +version. If need be, we'll make one more pre-release version with compatible +behavior. + +Changes since 0.9.2: + +* Prioritize `/dev/arandom` and `/dev/urandom` over mcrypt. +[@oittaa](https://github.com/oittaa) removed the -1 and +1 juggling on `$range` calculations for `random_int()` +* Whitespace and comment clean-up, plus better variable names +* Actually put a description in the composer.json file... + +### Version 0.9.2 (pre-release) - 2015-07-16 + +* Consolidated `$range > PHP_INT_MAX` logic with `$range <= PHP_INT_MAX` (thanks + [@oittaa](https://github.com/oittaa) and [@CodesInChaos](https://github.com/CodesInChaos)) +* `tests/phpunit.sh` now also runs the tests with `mbstring.func_overload` and + `open_basedir` +* Style consistency, whitespace cleanup, more meaningful variable names + +### Version 0.9.1 (pre-release) - 2015-07-09 + +* Return random values on integer ranges > `PHP_INT_MAX` (thanks [@CodesInChaos](https://github.com/CodesInChaos)) +* Determined CSPRNG preference: + 1. `mcrypt_create_iv()` with `MCRYPT_DEV_URANDOM` + 2. `/dev/arandom` + 3. `/dev/urandom` + 4. `openssl_random_pseudo_bytes()` +* Optimized backend selection (thanks [@lt](https://github.com/lt)) +* Fix #3 (thanks [@scottchiefbaker](https://github.com/scottchiefbaker)) + +### Version 0.9.0 (pre-release) - 2015-07-07 + +This should be a sane polyfill for PHP 7's `random_bytes()` and `random_int()`. +We hesitate to call it production ready until it has received sufficient third +party review.
\ No newline at end of file diff --git a/vendor/paragonie/random_compat/ERRATA.md b/vendor/paragonie/random_compat/ERRATA.md new file mode 100644 index 00000000..0561630d --- /dev/null +++ b/vendor/paragonie/random_compat/ERRATA.md @@ -0,0 +1,34 @@ +## Errata (Design Decisions) + +### Reasoning Behind the Order of Preferred Random Data Sources + +The order is: + + 1. `libsodium if available` + 2. `fread() /dev/urandom if available` + 3. `mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM)` + 4. `COM('CAPICOM.Utilities.1')->GetRandom()` + +If libsodium is available, we get random data from it. This is the preferred +method on all OSes, but libsodium is not very widely installed, so other +fallbacks are available. + +Next, we read `/dev/urandom` (if it exists). This is the preferred file to read +for random data for cryptographic purposes for BSD and Linux. + +Despite [strongly urging people not to use mcrypt in their projects](https://paragonie.com/blog/2015/05/if-you-re-typing-word-mcrypt-into-your-code-you-re-doing-it-wrong), +because libmcrypt is abandonware and the API puts too much responsibility on the +implementor, we prioritize `mcrypt_create_iv()` with `MCRYPT_DEV_URANDOM` above +the remaining implementations. + +The reason is simple: `mcrypt_create_iv()` is part of PHP's `ext/mcrypt` code, +and is not part `libmcrypt`. It actually does the right thing: + + * On Unix-based operating systems, it reads from `/dev/urandom`, which unlike `/dev/random` + is the sane and correct thing to do. + * On Windows, it reads from `CryptGenRandom`, which is an exclusively Windows + way to get random bytes. + +If we're on Windows and don't have access to `mcrypt`, we use `CAPICOM.Utilities.1`. + +As of random_compat 1.3, we no longer fall through to OpenSSL. diff --git a/vendor/paragonie/random_compat/LICENSE b/vendor/paragonie/random_compat/LICENSE new file mode 100644 index 00000000..45c7017d --- /dev/null +++ b/vendor/paragonie/random_compat/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Paragon Initiative Enterprises + +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. + diff --git a/vendor/paragonie/random_compat/README.md b/vendor/paragonie/random_compat/README.md new file mode 100644 index 00000000..80560862 --- /dev/null +++ b/vendor/paragonie/random_compat/README.md @@ -0,0 +1,176 @@ +# random_compat + +[](https://travis-ci.org/paragonie/random_compat) +[](https://scrutinizer-ci.com/g/paragonie/random_compat) + +PHP 5.x polyfill for `random_bytes()` and `random_int()` created and maintained +by [Paragon Initiative Enterprises](https://paragonie.com). + +Although this library *should* function in earlier versions of PHP, we will only +consider issues relevant to [supported PHP versions](https://secure.php.net/supported-versions.php). +**If you are using an unsupported version of PHP, please upgrade as soon as possible.** + +## Important + +Although this library has been examined by some security experts in the PHP +community, there will always be a chance that we overlooked something. Please +ask your favorite trusted hackers to hammer it for implementation errors and +bugs before even thinking about deploying it in production. + +**Do not use the master branch, use a [stable release](https://github.com/paragonie/random_compat/releases/latest).** + +For the background of this library, please refer to our blog post on +[Generating Random Integers and Strings in PHP](https://paragonie.com/blog/2015/07/how-safely-generate-random-strings-and-integers-in-php). + +### Usability Notice + +If PHP cannot safely generate random data, this library will throw an `Exception`. +It will never fall back to insecure random data. If this keeps happening, upgrade +to a newer version of PHP immediately. + +## Installing + +**With [Composer](https://getcomposer.org):** + + composer require paragonie/random_compat + +**Signed PHP Archive:** + +As of version 1.2.0, we also ship an ECDSA-signed PHP Archive with each stable +release on Github. + +1. Download [the `.phar`, `.phar.pubkey`, and `.phar.pubkey.asc`](https://github.com/paragonie/random_compat/releases/latest) files. +2. (**Recommended** but not required) Verify the PGP signature of `.phar.pubkey` + (contained within the `.asc` file) using the [PGP public key for Paragon Initiative Enterprises](https://paragonie.com/static/gpg-public-key.txt). +3. Extract both `.phar` and `.phar.pubkey` files to the same directory. +4. `require_once "/path/to/random_compat.phar";` +5. When a new version is released, you only need to replace the `.phar` file; + the `.pubkey` will not change (unless our signing key is ever compromised). + +**Manual Installation:** + +1. Download [a stable release](https://github.com/paragonie/random_compat/releases/latest). +2. Extract the files into your project. +3. `require_once "/path/to/random_compat/lib/random.php";` + +## Usage + +This library exposes the [CSPRNG functions added in PHP 7](https://secure.php.net/manual/en/ref.csprng.php) +for use in PHP 5 projects. Their behavior should be identical. + +### Generate a string of random bytes + +```php +try { + $string = random_bytes(32); +} catch (TypeError $e) { + // Well, it's an integer, so this IS unexpected. + die("An unexpected error has occurred"); +} catch (Error $e) { + // This is also unexpected because 32 is a reasonable integer. + die("An unexpected error has occurred"); +} catch (Exception $e) { + // If you get this message, the CSPRNG failed hard. + die("Could not generate a random string. Is our OS secure?"); +} + +var_dump(bin2hex($string)); +// string(64) "5787c41ae124b3b9363b7825104f8bc8cf27c4c3036573e5f0d4a91ad2eeac6f" +``` + +### Generate a random integer between two given integers (inclusive) + +```php +try { + $int = random_int(0,255); + +} catch (TypeError $e) { + // Well, it's an integer, so this IS unexpected. + die("An unexpected error has occurred"); +} catch (Error $e) { + // This is also unexpected because 0 and 255 are both reasonable integers. + die("An unexpected error has occurred"); +} catch (Exception $e) { + // If you get this message, the CSPRNG failed hard. + die("Could not generate a random string. Is our OS secure?"); +} + +var_dump($int); +// int(47) +``` + +### Exception handling + +When handling exceptions and errors you must account for differences between +PHP 5 and PHP7. + +The differences: + +* Catching `Error` works, so long as it is caught before `Exception`. +* Catching `Exception` has different behavior, without previously catching `Error`. +* There is *no* portable way to catch all errors/exceptions. + +#### Our recommendation + +**Always** catch `Error` before `Exception`. + +#### Example + +```php +try { + return random_int(1, $userInput); +} catch (TypeError $e) { + // This is okay, so long as `Error` is caught before `Exception`. + throw new Exception('Please enter a number!'); +} catch (Error $e) { + // This is required, if you do not need to do anything just rethrow. + throw $e; +} catch (Exception $e) { + // This is optional and maybe omitted if you do not want to handle errors + // during generation. + throw new InternalServerErrorException( + 'Oops, our server is bust and cannot generate any random data.', + 500, + $e + ); +} +``` + +## Contributors + +This project would not be anywhere near as excellent as it is today if it +weren't for the contributions of the following individuals: + +* [@AndrewCarterUK (Andrew Carter)](https://github.com/AndrewCarterUK) +* [@asgrim (James Titcumb)](https://github.com/asgrim) +* [@bcremer (Benjamin Cremer)](https://github.com/bcremer) +* [@CodesInChaos (Christian Winnerlein)](https://github.com/CodesInChaos) +* [@chriscct7 (Chris Christoff)](https://github.com/chriscct7) +* [@cs278 (Chris Smith)](https://github.com/cs278) +* [@cweagans (Cameron Eagans)](https://github.com/cweagans) +* [@dd32 (Dion Hulse)](https://github.com/dd32) +* [@geggleto (Glenn Eggleton)](https://github.com/geggleto) +* [@ircmaxell (Anthony Ferrara)](https://github.com/ircmaxell) +* [@jedisct1 (Frank Denis)](https://github.com/jedisct1) +* [@juliangut (Julián Gutiérrez)](https://github.com/juliangut) +* [@kelunik (Niklas Keller)](https://github.com/kelunik) +* [@lt (Leigh)](https://github.com/lt) +* [@MasonM (Mason Malone)](https://github.com/MasonM) +* [@mmeyer2k (Michael M)](https://github.com/mmeyer2k) +* [@narfbg (Andrey Andreev)](https://github.com/narfbg) +* [@nicolas-grekas (Nicolas Grekas)](https://github.com/nicolas-grekas) +* [@oittaa](https://github.com/oittaa) +* [@oucil (Kevin Farley)](https://github.com/oucil) +* [@redragonx (Stephen Chavez)](https://github.com/redragonx) +* [@rchouinard (Ryan Chouinard)](https://github.com/rchouinard) +* [@SammyK (Sammy Kaye Powers)](https://github.com/SammyK) +* [@scottchiefbaker (Scott Baker)](https://github.com/scottchiefbaker) +* [@skyosev (Stoyan Kyosev)](https://github.com/skyosev) +* [@stof (Christophe Coevoet)](https://github.com/stof) +* [@teohhanhui (Teoh Han Hui)](https://github.com/teohhanhui) +* [@tom-- (Tom Worster)](https://github.com/tom--) +* [@tsyr2ko](https://github.com/tsyr2ko) +* [@trowski (Aaron Piotrowski)](https://github.com/trowski) +* [@twistor (Chris Lepannen)](https://github.com/twistor) +* [@voku (Lars Moelleken)](https://github.com/voku) +* [@xabbuh (Christian Flothmann)](https://github.com/xabbuh) diff --git a/vendor/paragonie/random_compat/SECURITY.md b/vendor/paragonie/random_compat/SECURITY.md new file mode 100644 index 00000000..8f731b38 --- /dev/null +++ b/vendor/paragonie/random_compat/SECURITY.md @@ -0,0 +1,108 @@ +# An Invitation to Security Researchers + +Every company says they take security "very seriously." Rather than bore anyone +with banal boilerplate, here are some quick answers followed by detailed +elaboration. If you have any questions about our policies, please email them to +`scott@paragonie.com`. + +## Quick Answers + +* There is no compulsion to disclose vulnerabilities privately, but we + appreciate a head's up. +* `security@paragonie.com` will get your reports to the right person. Our GPG + fingerprint, should you decide to encrypt your report, is + `7F52 D5C6 1D12 55C7 3136 2E82 6B97 A1C2 8264 04DA`. + +* **YES**, we will reward security researchers who disclose vulnerabilities in + our software. +* In most cases, **No Proof-of-Concept Required.** + +## How to Report a Security Bug to Paragon Initiative Enterprises + +### There is no compulsion to disclose privately. + +We believe vulnerability disclosure style is a personal choice and enjoy working +with a diverse community. We understand and appreciate the importance of Full +Disclosure in the history and practice of security research. + +We would *like* to know about high-severity bugs before they become public +knowledge, so we can fix them in a timely manner, but **we do not believe in +threatening researchers or trying to enforce vulnerability embargoes**. + +Ultimately, if you discover a security-affecting vulnerability, what you do with +it is your choice. We would like to work with people, and to celebrate and +reward their skill, experience, and dedication. We appreciate being informed of +our mistakes so we can learn from them and build a better product. Our goal is +to empower the community. + +### Where to Send Security Vulnerabilities + +Our security email address is `security@paragonie.com`. Also feel free to open a +new issue on Github if you want to disclose publicly. + +``` +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG + +mQENBFUgwRUBCADcIpqNwyYc5UmY/tpx1sF/rQ3knR1YNXYZThzFV+Gmqhp1fDH5 +qBs9foh1xwI6O7knWmQngnf/nBumI3x6xj7PuOdEZUh2FwCG/VWnglW8rKmoHzHA +ivjiu9SLnPIPAgHSHeh2XD7q3Ndm3nenbjAiRFNl2iXcwA2cTQp9Mmfw9vVcw0G0 +z1o0G3s8cC8ZS6flFySIervvfSRWj7A1acI5eE3+AH/qXJRdEJ+9J8OB65p1JMfk +6+fWgOB1XZxMpz70S0rW6IX38WDSRhEK2fXyZJAJjyt+YGuzjZySNSoQR/V6vNYn +syrNPCJ2i5CgZQxAkyBBcr7koV9RIhPRzct/ABEBAAG0IVNlY3VyaXR5IDxzZWN1 +cml0eUBwYXJhZ29uaWUuY29tPokBOQQTAQIAIwUCVSDBFQIbAwcLCQgHAwIBBhUI +AgkKCwQWAgMBAh4BAheAAAoJEGuXocKCZATat2YIAIoejNFEQ2c1iaOEtSuB7Pn/ +WLbsDsHNLDKOV+UnfaCjv/vL7D+5NMChFCi2frde/NQb2TsjqmIH+V+XbnJtlrXD +Vj7yvMVal+Jqjwj7v4eOEWcKVcFZk+9cfUgh7t92T2BMX58RpgZF0IQZ6Z1R3FfC +9Ub4X6ykW+te1q0/4CoRycniwmlQi6iGSr99LQ5pfJq2Qlmz/luTZ0UX0h575T7d +cp2T1sX/zFRk/fHeANWSksipdDBjAXR7NMnYZgw2HghEdFk/xRDY7K1NRWNZBf05 +WrMHmh6AIVJiWZvI175URxEe268hh+wThBhXQHMhFNJM1qPIuzb4WogxM3UUD7m5 +AQ0EVSDBFQEIALNkpzSuJsHAHh79sc0AYWztdUe2MzyofQbbOnOCpWZebYsC3EXU +335fIg59k0m6f+O7GmEZzzIv5v0i99GS1R8CJm6FvhGqtH8ZqmOGbc71WdJSiNVE +0kpQoJlVzRbig6ZyyjzrggbM1eh5OXOk5pw4+23FFEdw7JWU0HJS2o71r1hwp05Z +vy21kcUEobz/WWQQyGS0Neo7PJn+9KS6wOxXul/UE0jct/5f7KLMdWMJ1VgniQmm +hjvkHLPSICteqCI04RfcmMseW9gueHQXeUu1SNIvsWa2MhxjeBej3pDnrZWszKwy +gF45GO9/v4tkIXNMy5J1AtOyRgQ3IUMqp8EAEQEAAYkBHwQYAQIACQUCVSDBFQIb +DAAKCRBrl6HCgmQE2jnIB/4/xFz8InpM7eybnBOAir3uGcYfs3DOmaKn7qWVtGzv +rKpQPYnVtlU2i6Z5UO4c4jDLT/8Xm1UDz3Lxvqt4xCaDwJvBZexU5BMK8l5DvOzH +6o6P2L1UDu6BvmPXpVZz7/qUhOnyf8VQg/dAtYF4/ax19giNUpI5j5o5mX5w80Rx +qSXV9NdSL4fdjeG1g/xXv2luhoV53T1bsycI3wjk/x5tV+M2KVhZBvvuOm/zhJje +oLWp0saaESkGXIXqurj6gZoujJvSvzl0n9F9VwqMEizDUfrXgtD1siQGhP0sVC6q +ha+F/SAEJ0jEquM4TfKWWU2S5V5vgPPpIQSYRnhQW4b1 +=xJPW +-----END PGP PUBLIC KEY BLOCK----- +``` + +### We Will Reward Security Researchers + +**This process has not been formalized; nor have dollar amounts been +discussed.** + +However, if you report a valid security-affecting bug, we will compensate you +for the time spent finding the vulnerability and reward you for being a good +neighbor. + +#### What does a "valid" bug mean? + +There are two sides to this: + +1. Some have spammed projects with invalid bug reports hoping to collect + bounties for pressing a button and running an automated analysis tool. This + is not cool. +2. There is a potential for the developers of a project to declare all security + bug reports as invalid to save money. + +Our team members have an established history of reporting vulnerabilities to +large open source projects. **We aren't in the business of ripping people off.** +When in doubt, our policy is to err on the side of generosity. + +### No Proof-of-Concept Required + +We might ask for one if we feel we do not understand some of the details +pertaining to a specific vulnerability. We certainly appreciate them if you +include them in your report, but we believe **the burden lies with the developer +to prove their software *is* secure** rather than with the researcher to prove +that it isn't. + +In our experience, most bugs are simpler to fix than they are to exploit. + diff --git a/vendor/paragonie/random_compat/build-phar.sh b/vendor/paragonie/random_compat/build-phar.sh new file mode 100755 index 00000000..b4a5ba31 --- /dev/null +++ b/vendor/paragonie/random_compat/build-phar.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +basedir=$( dirname $( readlink -f ${BASH_SOURCE[0]} ) ) + +php -dphar.readonly=0 "$basedir/other/build_phar.php" $*
\ No newline at end of file diff --git a/vendor/paragonie/random_compat/composer.json b/vendor/paragonie/random_compat/composer.json new file mode 100644 index 00000000..d363f4c8 --- /dev/null +++ b/vendor/paragonie/random_compat/composer.json @@ -0,0 +1,35 @@ +{ + "name": "paragonie/random_compat", + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "random", + "pseudorandom" + ], + "license": "MIT", + "type": "library", + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "support": { + "issues": "https://github.com/paragonie/random_compat/issues", + "email": "info@paragonie.com", + "source": "https://github.com/paragonie/random_compat" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "autoload": { + "files": ["lib/random.php"] + } +} diff --git a/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey b/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey new file mode 100644 index 00000000..eb50ebfc --- /dev/null +++ b/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey @@ -0,0 +1,5 @@ +-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEEd+wCqJDrx5B4OldM0dQE0ZMX+lx1ZWm +pui0SUqD4G29L3NGsz9UhJ/0HjBdbnkhIK5xviT0X5vtjacF6ajgcCArbTB+ds+p ++h7Q084NuSuIpNb6YPfoUFgC/CL9kAoc +-----END PUBLIC KEY----- diff --git a/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc b/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc new file mode 100644 index 00000000..6a1d7f30 --- /dev/null +++ b/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc @@ -0,0 +1,11 @@ +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v2.0.22 (MingW32) + +iQEcBAABAgAGBQJWtW1hAAoJEGuXocKCZATaJf0H+wbZGgskK1dcRTsuVJl9IWip +QwGw/qIKI280SD6/ckoUMxKDCJiFuPR14zmqnS36k7N5UNPnpdTJTS8T11jttSpg +1LCmgpbEIpgaTah+cELDqFCav99fS+bEiAL5lWDAHBTE/XPjGVCqeehyPYref4IW +NDBIEsvnHPHPLsn6X5jq4+Yj5oUixgxaMPiR+bcO4Sh+RzOVB6i2D0upWfRXBFXA +NNnsg9/zjvoC7ZW73y9uSH+dPJTt/Vgfeiv52/v41XliyzbUyLalf02GNPY+9goV +JHG1ulEEBJOCiUD9cE1PUIJwHA/HqyhHIvV350YoEFiHl8iSwm7SiZu5kPjaq74= +=B6+8 +-----END PGP SIGNATURE----- diff --git a/vendor/paragonie/random_compat/lib/byte_safe_strings.php b/vendor/paragonie/random_compat/lib/byte_safe_strings.php new file mode 100644 index 00000000..dec5d306 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/byte_safe_strings.php @@ -0,0 +1,181 @@ +<?php +/** + * Random_* Compatibility Library + * for using the new PHP 7 random_* API in PHP 5 projects + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Paragon Initiative Enterprises + * + * 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. + */ + +if (!function_exists('RandomCompat_strlen')) { + if ( + defined('MB_OVERLOAD_STRING') && + ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING + ) { + /** + * strlen() implementation that isn't brittle to mbstring.func_overload + * + * This version uses mb_strlen() in '8bit' mode to treat strings as raw + * binary rather than UTF-8, ISO-8859-1, etc + * + * @param string $binary_string + * + * @throws TypeError + * + * @return int + */ + function RandomCompat_strlen($binary_string) + { + if (!is_string($binary_string)) { + throw new TypeError( + 'RandomCompat_strlen() expects a string' + ); + } + + return mb_strlen($binary_string, '8bit'); + } + + } else { + /** + * strlen() implementation that isn't brittle to mbstring.func_overload + * + * This version just used the default strlen() + * + * @param string $binary_string + * + * @throws TypeError + * + * @return int + */ + function RandomCompat_strlen($binary_string) + { + if (!is_string($binary_string)) { + throw new TypeError( + 'RandomCompat_strlen() expects a string' + ); + } + return strlen($binary_string); + } + } +} + +if (!function_exists('RandomCompat_substr')) { + + if ( + defined('MB_OVERLOAD_STRING') + && + ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING + ) { + /** + * substr() implementation that isn't brittle to mbstring.func_overload + * + * This version uses mb_substr() in '8bit' mode to treat strings as raw + * binary rather than UTF-8, ISO-8859-1, etc + * + * @param string $binary_string + * @param int $start + * @param int $length (optional) + * + * @throws TypeError + * + * @return string + */ + function RandomCompat_substr($binary_string, $start, $length = null) + { + if (!is_string($binary_string)) { + throw new TypeError( + 'RandomCompat_substr(): First argument should be a string' + ); + } + + if (!is_int($start)) { + throw new TypeError( + 'RandomCompat_substr(): Second argument should be an integer' + ); + } + + if ($length === null) { + /** + * mb_substr($str, 0, NULL, '8bit') returns an empty string on + * PHP 5.3, so we have to find the length ourselves. + */ + $length = RandomCompat_strlen($length) - $start; + } elseif (!is_int($length)) { + throw new TypeError( + 'RandomCompat_substr(): Third argument should be an integer, or omitted' + ); + } + + // Consistency with PHP's behavior + if ($start === RandomCompat_strlen($binary_string) && $length === 0) { + return ''; + } + if ($start > RandomCompat_strlen($binary_string)) { + return false; + } + + return mb_substr($binary_string, $start, $length, '8bit'); + } + + } else { + + /** + * substr() implementation that isn't brittle to mbstring.func_overload + * + * This version just uses the default substr() + * + * @param string $binary_string + * @param int $start + * @param int $length (optional) + * + * @throws TypeError + * + * @return string + */ + function RandomCompat_substr($binary_string, $start, $length = null) + { + if (!is_string($binary_string)) { + throw new TypeError( + 'RandomCompat_substr(): First argument should be a string' + ); + } + + if (!is_int($start)) { + throw new TypeError( + 'RandomCompat_substr(): Second argument should be an integer' + ); + } + + if ($length !== null) { + if (!is_int($length)) { + throw new TypeError( + 'RandomCompat_substr(): Third argument should be an integer, or omitted' + ); + } + + return substr($binary_string, $start, $length); + } + + return substr($binary_string, $start); + } + } +} diff --git a/vendor/paragonie/random_compat/lib/cast_to_int.php b/vendor/paragonie/random_compat/lib/cast_to_int.php new file mode 100644 index 00000000..f441c5d9 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/cast_to_int.php @@ -0,0 +1,71 @@ +<?php +/** + * Random_* Compatibility Library + * for using the new PHP 7 random_* API in PHP 5 projects + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Paragon Initiative Enterprises + * + * 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. + */ + +if (!function_exists('RandomCompat_intval')) { + + /** + * Cast to an integer if we can, safely. + * + * If you pass it a float in the range (~PHP_INT_MAX, PHP_INT_MAX) + * (non-inclusive), it will sanely cast it to an int. If you it's equal to + * ~PHP_INT_MAX or PHP_INT_MAX, we let it fail as not an integer. Floats + * lose precision, so the <= and => operators might accidentally let a float + * through. + * + * @param int|float $number The number we want to convert to an int + * @param boolean $fail_open Set to true to not throw an exception + * + * @return int (or float if $fail_open) + * + * @throws TypeError + */ + function RandomCompat_intval($number, $fail_open = false) + { + if (is_numeric($number)) { + $number += 0; + } + + if ( + is_float($number) + && + $number > ~PHP_INT_MAX + && + $number < PHP_INT_MAX + ) { + $number = (int) $number; + } + + if (is_int($number) || $fail_open) { + return $number; + } + + throw new TypeError( + 'Expected an integer.' + ); + } +} diff --git a/vendor/paragonie/random_compat/lib/error_polyfill.php b/vendor/paragonie/random_compat/lib/error_polyfill.php new file mode 100644 index 00000000..57cfefdc --- /dev/null +++ b/vendor/paragonie/random_compat/lib/error_polyfill.php @@ -0,0 +1,42 @@ +<?php +/** + * Random_* Compatibility Library + * for using the new PHP 7 random_* API in PHP 5 projects + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Paragon Initiative Enterprises + * + * 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. + */ + +if (!class_exists('Error', false)) { + // We can't really avoid making this extend Exception in PHP 5. + class Error extends Exception + { + + } +} + +if (!class_exists('TypeError', false)) { + class TypeError extends Error + { + + } +} diff --git a/vendor/paragonie/random_compat/lib/random.php b/vendor/paragonie/random_compat/lib/random.php new file mode 100644 index 00000000..a8802597 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random.php @@ -0,0 +1,197 @@ +<?php +/** + * Random_* Compatibility Library + * for using the new PHP 7 random_* API in PHP 5 projects + * + * @version 2.0.2 + * @released 2016-04-03 + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Paragon Initiative Enterprises + * + * 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. + */ + +if (!defined('PHP_VERSION_ID')) { + // This constant was introduced in PHP 5.2.7 + $RandomCompatversion = explode('.', PHP_VERSION); + define( + 'PHP_VERSION_ID', + $RandomCompatversion[0] * 10000 + + $RandomCompatversion[1] * 100 + + $RandomCompatversion[2] + ); + $RandomCompatversion = null; +} + +if (PHP_VERSION_ID < 70000) { + + if (!defined('RANDOM_COMPAT_READ_BUFFER')) { + define('RANDOM_COMPAT_READ_BUFFER', 8); + } + + $RandomCompatDIR = dirname(__FILE__); + + require_once $RandomCompatDIR.'/byte_safe_strings.php'; + require_once $RandomCompatDIR.'/cast_to_int.php'; + require_once $RandomCompatDIR.'/error_polyfill.php'; + + if (!function_exists('random_bytes')) { + /** + * PHP 5.2.0 - 5.6.x way to implement random_bytes() + * + * We use conditional statements here to define the function in accordance + * to the operating environment. It's a micro-optimization. + * + * In order of preference: + * 1. Use libsodium if available. + * 2. fread() /dev/urandom if available (never on Windows) + * 3. mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM) + * 4. COM('CAPICOM.Utilities.1')->GetRandom() + * 5. openssl_random_pseudo_bytes() (absolute last resort) + * + * See ERRATA.md for our reasoning behind this particular order + */ + if (extension_loaded('libsodium')) { + // See random_bytes_libsodium.php + if (PHP_VERSION_ID >= 50300 && function_exists('\\Sodium\\randombytes_buf')) { + require_once $RandomCompatDIR.'/random_bytes_libsodium.php'; + } elseif (method_exists('Sodium', 'randombytes_buf')) { + require_once $RandomCompatDIR.'/random_bytes_libsodium_legacy.php'; + } + } + + /** + * Reading directly from /dev/urandom: + */ + if (DIRECTORY_SEPARATOR === '/') { + // DIRECTORY_SEPARATOR === '/' on Unix-like OSes -- this is a fast + // way to exclude Windows. + $RandomCompatUrandom = true; + $RandomCompat_basedir = ini_get('open_basedir'); + + if (!empty($RandomCompat_basedir)) { + $RandomCompat_open_basedir = explode( + PATH_SEPARATOR, + strtolower($RandomCompat_basedir) + ); + $RandomCompatUrandom = (array() !== array_intersect( + array('/dev', '/dev/', '/dev/urandom'), + $RandomCompat_open_basedir + )); + $RandomCompat_open_basedir = null; + } + + if ( + !function_exists('random_bytes') + && + $RandomCompatUrandom + && + @is_readable('/dev/urandom') + ) { + // Error suppression on is_readable() in case of an open_basedir + // or safe_mode failure. All we care about is whether or not we + // can read it at this point. If the PHP environment is going to + // panic over trying to see if the file can be read in the first + // place, that is not helpful to us here. + + // See random_bytes_dev_urandom.php + require_once $RandomCompatDIR.'/random_bytes_dev_urandom.php'; + } + // Unset variables after use + $RandomCompat_basedir = null; + } else { + $RandomCompatUrandom = false; + } + + /** + * mcrypt_create_iv() + */ + if ( + !function_exists('random_bytes') + && + PHP_VERSION_ID >= 50307 + && + extension_loaded('mcrypt') + && + (DIRECTORY_SEPARATOR !== '/' || $RandomCompatUrandom) + ) { + // Prevent this code from hanging indefinitely on non-Windows; + // see https://bugs.php.net/bug.php?id=69833 + if ( + DIRECTORY_SEPARATOR !== '/' || + (PHP_VERSION_ID <= 50609 || PHP_VERSION_ID >= 50613) + ) { + // See random_bytes_mcrypt.php + require_once $RandomCompatDIR.'/random_bytes_mcrypt.php'; + } + } + $RandomCompatUrandom = null; + + if ( + !function_exists('random_bytes') + && + extension_loaded('com_dotnet') + && + class_exists('COM') + ) { + $RandomCompat_disabled_classes = preg_split( + '#\s*,\s*#', + strtolower(ini_get('disable_classes')) + ); + + if (!in_array('com', $RandomCompat_disabled_classes)) { + try { + $RandomCompatCOMtest = new COM('CAPICOM.Utilities.1'); + if (method_exists($RandomCompatCOMtest, 'GetRandom')) { + // See random_bytes_com_dotnet.php + require_once $RandomCompatDIR.'/random_bytes_com_dotnet.php'; + } + } catch (com_exception $e) { + // Don't try to use it. + } + } + $RandomCompat_disabled_classes = null; + $RandomCompatCOMtest = null; + } + + /** + * throw new Exception + */ + if (!function_exists('random_bytes')) { + /** + * We don't have any more options, so let's throw an exception right now + * and hope the developer won't let it fail silently. + */ + function random_bytes($length) + { + throw new Exception( + 'There is no suitable CSPRNG installed on your system' + ); + } + } + } + + if (!function_exists('random_int')) { + require_once $RandomCompatDIR.'/random_int.php'; + } + + $RandomCompatDIR = null; +} diff --git a/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php b/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php new file mode 100644 index 00000000..34228254 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php @@ -0,0 +1,81 @@ +<?php +/** + * Random_* Compatibility Library + * for using the new PHP 7 random_* API in PHP 5 projects + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Paragon Initiative Enterprises + * + * 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. + */ + +/** + * Windows with PHP < 5.3.0 will not have the function + * openssl_random_pseudo_bytes() available, so let's use + * CAPICOM to work around this deficiency. + * + * @param int $bytes + * + * @throws Exception + * + * @return string + */ +function random_bytes($bytes) +{ + try { + $bytes = RandomCompat_intval($bytes); + } catch (TypeError $ex) { + throw new TypeError( + 'random_bytes(): $bytes must be an integer' + ); + } + + if ($bytes < 1) { + throw new Error( + 'Length must be greater than 0' + ); + } + + $buf = ''; + $util = new COM('CAPICOM.Utilities.1'); + $execCount = 0; + + /** + * Let's not let it loop forever. If we run N times and fail to + * get N bytes of random data, then CAPICOM has failed us. + */ + do { + $buf .= base64_decode($util->GetRandom($bytes, 0)); + if (RandomCompat_strlen($buf) >= $bytes) { + /** + * Return our random entropy buffer here: + */ + return RandomCompat_substr($buf, 0, $bytes); + } + ++$execCount; + } while ($execCount < $bytes); + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); +} diff --git a/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php b/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php new file mode 100644 index 00000000..db93b075 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php @@ -0,0 +1,148 @@ +<?php +/** + * Random_* Compatibility Library + * for using the new PHP 7 random_* API in PHP 5 projects + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Paragon Initiative Enterprises + * + * 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. + */ + +if (!defined('RANDOM_COMPAT_READ_BUFFER')) { + define('RANDOM_COMPAT_READ_BUFFER', 8); +} + +/** + * Unless open_basedir is enabled, use /dev/urandom for + * random numbers in accordance with best practices + * + * Why we use /dev/urandom and not /dev/random + * @ref http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers + * + * @param int $bytes + * + * @throws Exception + * + * @return string + */ +function random_bytes($bytes) +{ + static $fp = null; + /** + * This block should only be run once + */ + if (empty($fp)) { + /** + * We use /dev/urandom if it is a char device. + * We never fall back to /dev/random + */ + $fp = fopen('/dev/urandom', 'rb'); + if (!empty($fp)) { + $st = fstat($fp); + if (($st['mode'] & 0170000) !== 020000) { + fclose($fp); + $fp = false; + } + } + + if (!empty($fp)) { + /** + * stream_set_read_buffer() does not exist in HHVM + * + * If we don't set the stream's read buffer to 0, PHP will + * internally buffer 8192 bytes, which can waste entropy + * + * stream_set_read_buffer returns 0 on success + */ + if (function_exists('stream_set_read_buffer')) { + stream_set_read_buffer($fp, RANDOM_COMPAT_READ_BUFFER); + } + if (function_exists('stream_set_chunk_size')) { + stream_set_chunk_size($fp, RANDOM_COMPAT_READ_BUFFER); + } + } + } + + try { + $bytes = RandomCompat_intval($bytes); + } catch (TypeError $ex) { + throw new TypeError( + 'random_bytes(): $bytes must be an integer' + ); + } + + if ($bytes < 1) { + throw new Error( + 'Length must be greater than 0' + ); + } + + /** + * This if() block only runs if we managed to open a file handle + * + * It does not belong in an else {} block, because the above + * if (empty($fp)) line is logic that should only be run once per + * page load. + */ + if (!empty($fp)) { + $remaining = $bytes; + $buf = ''; + + /** + * We use fread() in a loop to protect against partial reads + */ + do { + $read = fread($fp, $remaining); + if ($read === false) { + /** + * We cannot safely read from the file. Exit the + * do-while loop and trigger the exception condition + */ + $buf = false; + break; + } + /** + * Decrease the number of bytes returned from remaining + */ + $remaining -= RandomCompat_strlen($read); + $buf .= $read; + } while ($remaining > 0); + + /** + * Is our result valid? + */ + if ($buf !== false) { + if (RandomCompat_strlen($buf) === $bytes) { + /** + * Return our random entropy buffer here: + */ + return $buf; + } + } + } + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Error reading from source device' + ); +} diff --git a/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php b/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php new file mode 100644 index 00000000..f802d4e1 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php @@ -0,0 +1,86 @@ +<?php +/** + * Random_* Compatibility Library + * for using the new PHP 7 random_* API in PHP 5 projects + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Paragon Initiative Enterprises + * + * 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. + */ + +/** + * If the libsodium PHP extension is loaded, we'll use it above any other + * solution. + * + * libsodium-php project: + * @ref https://github.com/jedisct1/libsodium-php + * + * @param int $bytes + * + * @throws Exception + * + * @return string + */ +function random_bytes($bytes) +{ + try { + $bytes = RandomCompat_intval($bytes); + } catch (TypeError $ex) { + throw new TypeError( + 'random_bytes(): $bytes must be an integer' + ); + } + + if ($bytes < 1) { + throw new Error( + 'Length must be greater than 0' + ); + } + + /** + * \Sodium\randombytes_buf() doesn't allow more than 2147483647 bytes to be + * generated in one invocation. + */ + if ($bytes > 2147483647) { + $buf = ''; + for ($i = 0; $i < $bytes; $i += 1073741824) { + $n = ($bytes - $i) > 1073741824 + ? 1073741824 + : $bytes - $i; + $buf .= \Sodium\randombytes_buf($n); + } + } else { + $buf = \Sodium\randombytes_buf($bytes); + } + + if ($buf !== false) { + if (RandomCompat_strlen($buf) === $bytes) { + return $buf; + } + } + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); +} diff --git a/vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php b/vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php new file mode 100644 index 00000000..44fddbf6 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php @@ -0,0 +1,86 @@ +<?php +/** + * Random_* Compatibility Library + * for using the new PHP 7 random_* API in PHP 5 projects + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Paragon Initiative Enterprises + * + * 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. + */ + +/** + * If the libsodium PHP extension is loaded, we'll use it above any other + * solution. + * + * libsodium-php project: + * @ref https://github.com/jedisct1/libsodium-php + * + * @param int $bytes + * + * @throws Exception + * + * @return string + */ +function random_bytes($bytes) +{ + try { + $bytes = RandomCompat_intval($bytes); + } catch (TypeError $ex) { + throw new TypeError( + 'random_bytes(): $bytes must be an integer' + ); + } + + if ($bytes < 1) { + throw new Error( + 'Length must be greater than 0' + ); + } + + /** + * \Sodium\randombytes_buf() doesn't allow more than 2147483647 bytes to be + * generated in one invocation. + */ + if ($bytes > 2147483647) { + $buf = ''; + for ($i = 0; $i < $bytes; $i += 1073741824) { + $n = ($bytes - $i) > 1073741824 + ? 1073741824 + : $bytes - $i; + $buf .= Sodium::randombytes_buf($n); + } + } else { + $buf = Sodium::randombytes_buf($bytes); + } + + if ($buf !== false) { + if (RandomCompat_strlen($buf) === $bytes) { + return $buf; + } + } + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); +} diff --git a/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php b/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php new file mode 100644 index 00000000..7ac9d910 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php @@ -0,0 +1,76 @@ +<?php +/** + * Random_* Compatibility Library + * for using the new PHP 7 random_* API in PHP 5 projects + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Paragon Initiative Enterprises + * + * 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. + */ + + +/** + * Powered by ext/mcrypt (and thankfully NOT libmcrypt) + * + * @ref https://bugs.php.net/bug.php?id=55169 + * @ref https://github.com/php/php-src/blob/c568ffe5171d942161fc8dda066bce844bdef676/ext/mcrypt/mcrypt.c#L1321-L1386 + * + * @param int $bytes + * + * @throws Exception + * + * @return string + */ +function random_bytes($bytes) +{ + try { + $bytes = RandomCompat_intval($bytes); + } catch (TypeError $ex) { + throw new TypeError( + 'random_bytes(): $bytes must be an integer' + ); + } + + if ($bytes < 1) { + throw new Error( + 'Length must be greater than 0' + ); + } + + $buf = @mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM); + if ( + $buf !== false + && + RandomCompat_strlen($buf) === $bytes + ) { + /** + * Return our random entropy buffer here: + */ + return $buf; + } + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); +} diff --git a/vendor/paragonie/random_compat/lib/random_int.php b/vendor/paragonie/random_compat/lib/random_int.php new file mode 100644 index 00000000..fd3ef87a --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random_int.php @@ -0,0 +1,191 @@ +<?php +/** + * Random_* Compatibility Library + * for using the new PHP 7 random_* API in PHP 5 projects + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Paragon Initiative Enterprises + * + * 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. + */ + +/** + * Fetch a random integer between $min and $max inclusive + * + * @param int $min + * @param int $max + * + * @throws Exception + * + * @return int + */ +function random_int($min, $max) +{ + /** + * Type and input logic checks + * + * If you pass it a float in the range (~PHP_INT_MAX, PHP_INT_MAX) + * (non-inclusive), it will sanely cast it to an int. If you it's equal to + * ~PHP_INT_MAX or PHP_INT_MAX, we let it fail as not an integer. Floats + * lose precision, so the <= and => operators might accidentally let a float + * through. + */ + + try { + $min = RandomCompat_intval($min); + } catch (TypeError $ex) { + throw new TypeError( + 'random_int(): $min must be an integer' + ); + } + + try { + $max = RandomCompat_intval($max); + } catch (TypeError $ex) { + throw new TypeError( + 'random_int(): $max must be an integer' + ); + } + + /** + * Now that we've verified our weak typing system has given us an integer, + * let's validate the logic then we can move forward with generating random + * integers along a given range. + */ + if ($min > $max) { + throw new Error( + 'Minimum value must be less than or equal to the maximum value' + ); + } + + if ($max === $min) { + return $min; + } + + /** + * Initialize variables to 0 + * + * We want to store: + * $bytes => the number of random bytes we need + * $mask => an integer bitmask (for use with the &) operator + * so we can minimize the number of discards + */ + $attempts = $bits = $bytes = $mask = $valueShift = 0; + + /** + * At this point, $range is a positive number greater than 0. It might + * overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to + * a float and we will lose some precision. + */ + $range = $max - $min; + + /** + * Test for integer overflow: + */ + if (!is_int($range)) { + + /** + * Still safely calculate wider ranges. + * Provided by @CodesInChaos, @oittaa + * + * @ref https://gist.github.com/CodesInChaos/03f9ea0b58e8b2b8d435 + * + * We use ~0 as a mask in this case because it generates all 1s + * + * @ref https://eval.in/400356 (32-bit) + * @ref http://3v4l.org/XX9r5 (64-bit) + */ + $bytes = PHP_INT_SIZE; + $mask = ~0; + + } else { + + /** + * $bits is effectively ceil(log($range, 2)) without dealing with + * type juggling + */ + while ($range > 0) { + if ($bits % 8 === 0) { + ++$bytes; + } + ++$bits; + $range >>= 1; + $mask = $mask << 1 | 1; + } + $valueShift = $min; + } + + /** + * Now that we have our parameters set up, let's begin generating + * random integers until one falls between $min and $max + */ + do { + /** + * The rejection probability is at most 0.5, so this corresponds + * to a failure probability of 2^-128 for a working RNG + */ + if ($attempts > 128) { + throw new Exception( + 'random_int: RNG is broken - too many rejections' + ); + } + + /** + * Let's grab the necessary number of random bytes + */ + $randomByteString = random_bytes($bytes); + if ($randomByteString === false) { + throw new Exception( + 'Random number generator failure' + ); + } + + /** + * Let's turn $randomByteString into an integer + * + * This uses bitwise operators (<< and |) to build an integer + * out of the values extracted from ord() + * + * Example: [9F] | [6D] | [32] | [0C] => + * 159 + 27904 + 3276800 + 201326592 => + * 204631455 + */ + $val = 0; + for ($i = 0; $i < $bytes; ++$i) { + $val |= ord($randomByteString[$i]) << ($i * 8); + } + + /** + * Apply mask + */ + $val &= $mask; + $val += $valueShift; + + ++$attempts; + /** + * If $val overflows to a floating point number, + * ... or is larger than $max, + * ... or smaller than $min, + * then try again. + */ + } while (!is_int($val) || $val > $max || $val < $min); + + return (int) $val; +} diff --git a/vendor/paragonie/random_compat/other/build_phar.php b/vendor/paragonie/random_compat/other/build_phar.php new file mode 100644 index 00000000..70ef4b2e --- /dev/null +++ b/vendor/paragonie/random_compat/other/build_phar.php @@ -0,0 +1,57 @@ +<?php +$dist = dirname(__DIR__).'/dist'; +if (!is_dir($dist)) { + mkdir($dist, 0755); +} +if (file_exists($dist.'/random_compat.phar')) { + unlink($dist.'/random_compat.phar'); +} +$phar = new Phar( + $dist.'/random_compat.phar', + FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::KEY_AS_FILENAME, + 'random_compat.phar' +); +rename( + dirname(__DIR__).'/lib/random.php', + dirname(__DIR__).'/lib/index.php' +); +$phar->buildFromDirectory(dirname(__DIR__).'/lib'); +rename( + dirname(__DIR__).'/lib/index.php', + dirname(__DIR__).'/lib/random.php' +); + +/** + * If we pass an (optional) path to a private key as a second argument, we will + * sign the Phar with OpenSSL. + * + * If you leave this out, it will produce an unsigned .phar! + */ +if ($argc > 1) { + if (!@is_readable($argv[1])) { + echo 'Could not read the private key file:', $argv[1], "\n"; + exit(255); + } + $pkeyFile = file_get_contents($argv[1]); + + $private = openssl_get_privatekey($pkeyFile); + if ($private !== false) { + $pkey = ''; + openssl_pkey_export($private, $pkey); + $phar->setSignatureAlgorithm(Phar::OPENSSL, $pkey); + + /** + * Save the corresponding public key to the file + */ + if (!@is_readable($dist.'/random_compat.phar.pubkey')) { + $details = openssl_pkey_get_details($private); + file_put_contents( + $dist.'/random_compat.phar.pubkey', + $details['key'] + ); + } + } else { + echo 'An error occurred reading the private key from OpenSSL.', "\n"; + exit(255); + } +} diff --git a/vendor/pimple/pimple/.gitignore b/vendor/pimple/pimple/.gitignore new file mode 100644 index 00000000..c089b095 --- /dev/null +++ b/vendor/pimple/pimple/.gitignore @@ -0,0 +1,3 @@ +phpunit.xml +composer.lock +/vendor/ diff --git a/vendor/pimple/pimple/.travis.yml b/vendor/pimple/pimple/.travis.yml new file mode 100644 index 00000000..5f8bb7c9 --- /dev/null +++ b/vendor/pimple/pimple/.travis.yml @@ -0,0 +1,32 @@ +language: php + +env: + matrix: + - PIMPLE_EXT=no + - PIMPLE_EXT=yes + global: + - REPORT_EXIT_STATUS=1 + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +before_script: + - composer self-update + - COMPOSER_ROOT_VERSION=dev-master composer dump-autoload + - if [ "$PIMPLE_EXT" == "yes" ]; then sh -c "cd ext/pimple && phpize && ./configure && make && sudo make install"; fi + - if [ "$PIMPLE_EXT" == "yes" ]; then echo "extension=pimple.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`; fi + +script: + - cd ext/pimple + - if [ "$PIMPLE_EXT" == "yes" ]; then yes n | make test | tee output ; grep -E 'Tests failed +. +0' output; fi + - cd ../.. + - phpunit + +matrix: + exclude: + - php: hhvm + env: PIMPLE_EXT=yes diff --git a/vendor/pimple/pimple/CHANGELOG b/vendor/pimple/pimple/CHANGELOG new file mode 100644 index 00000000..cc679972 --- /dev/null +++ b/vendor/pimple/pimple/CHANGELOG @@ -0,0 +1,35 @@ +* 3.0.2 (2015-09-11) + + * refactored the C extension + * minor non-significant changes + +* 3.0.1 (2015-07-30) + + * simplified some code + * fixed a segfault in the C extension + +* 3.0.0 (2014-07-24) + + * removed the Pimple class alias (use Pimple\Container instead) + +* 2.1.1 (2014-07-24) + + * fixed compiler warnings for the C extension + * fixed code when dealing with circular references + +* 2.1.0 (2014-06-24) + + * moved the Pimple to Pimple\Container (with a BC layer -- Pimple is now a + deprecated alias which will be removed in Pimple 3.0) + * added Pimple\ServiceProviderInterface (and Pimple::register()) + +* 2.0.0 (2014-02-10) + + * changed extend to automatically re-assign the extended service and keep it as shared or factory + (to keep BC, extend still returns the extended service) + * changed services to be shared by default (use factory() for factory + services) + +* 1.0.0 + + * initial version diff --git a/vendor/pimple/pimple/LICENSE b/vendor/pimple/pimple/LICENSE new file mode 100644 index 00000000..d7949e2f --- /dev/null +++ b/vendor/pimple/pimple/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2009-2015 Fabien Potencier + +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. diff --git a/vendor/pimple/pimple/README.rst b/vendor/pimple/pimple/README.rst new file mode 100644 index 00000000..93fb35a8 --- /dev/null +++ b/vendor/pimple/pimple/README.rst @@ -0,0 +1,201 @@ +Pimple +====== + +.. caution:: + + This is the documentation for Pimple 3.x. If you are using Pimple 1.x, read + the `Pimple 1.x documentation`_. Reading the Pimple 1.x code is also a good + way to learn more about how to create a simple Dependency Injection + Container (recent versions of Pimple are more focused on performance). + +Pimple is a small Dependency Injection Container for PHP. + +Installation +------------ + +Before using Pimple in your project, add it to your ``composer.json`` file: + +.. code-block:: bash + + $ ./composer.phar require pimple/pimple ~3.0 + +Alternatively, Pimple is also available as a PHP C extension: + +.. code-block:: bash + + $ git clone https://github.com/silexphp/Pimple + $ cd Pimple/ext/pimple + $ phpize + $ ./configure + $ make + $ make install + +Usage +----- + +Creating a container is a matter of creating a ``Container`` instance: + +.. code-block:: php + + use Pimple\Container; + + $container = new Container(); + +As many other dependency injection containers, Pimple manages two different +kind of data: **services** and **parameters**. + +Defining Services +~~~~~~~~~~~~~~~~~ + +A service is an object that does something as part of a larger system. Examples +of services: a database connection, a templating engine, or a mailer. Almost +any **global** object can be a service. + +Services are defined by **anonymous functions** that return an instance of an +object: + +.. code-block:: php + + // define some services + $container['session_storage'] = function ($c) { + return new SessionStorage('SESSION_ID'); + }; + + $container['session'] = function ($c) { + return new Session($c['session_storage']); + }; + +Notice that the anonymous function has access to the current container +instance, allowing references to other services or parameters. + +As objects are only created when you get them, the order of the definitions +does not matter. + +Using the defined services is also very easy: + +.. code-block:: php + + // get the session object + $session = $container['session']; + + // the above call is roughly equivalent to the following code: + // $storage = new SessionStorage('SESSION_ID'); + // $session = new Session($storage); + +Defining Factory Services +~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, each time you get a service, Pimple returns the **same instance** +of it. If you want a different instance to be returned for all calls, wrap your +anonymous function with the ``factory()`` method + +.. code-block:: php + + $container['session'] = $container->factory(function ($c) { + return new Session($c['session_storage']); + }); + +Now, each call to ``$container['session']`` returns a new instance of the +session. + +Defining Parameters +~~~~~~~~~~~~~~~~~~~ + +Defining a parameter allows to ease the configuration of your container from +the outside and to store global values: + +.. code-block:: php + + // define some parameters + $container['cookie_name'] = 'SESSION_ID'; + $container['session_storage_class'] = 'SessionStorage'; + +If you change the ``session_storage`` service definition like below: + +.. code-block:: php + + $container['session_storage'] = function ($c) { + return new $c['session_storage_class']($c['cookie_name']); + }; + +You can now easily change the cookie name by overriding the +``session_storage_class`` parameter instead of redefining the service +definition. + +Protecting Parameters +~~~~~~~~~~~~~~~~~~~~~ + +Because Pimple sees anonymous functions as service definitions, you need to +wrap anonymous functions with the ``protect()`` method to store them as +parameters: + +.. code-block:: php + + $container['random_func'] = $container->protect(function () { + return rand(); + }); + +Modifying Services after Definition +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In some cases you may want to modify a service definition after it has been +defined. You can use the ``extend()`` method to define additional code to be +run on your service just after it is created: + +.. code-block:: php + + $container['session_storage'] = function ($c) { + return new $c['session_storage_class']($c['cookie_name']); + }; + + $container->extend('session_storage', function ($storage, $c) { + $storage->...(); + + return $storage; + }); + +The first argument is the name of the service to extend, the second a function +that gets access to the object instance and the container. + +Extending a Container +~~~~~~~~~~~~~~~~~~~~~ + +If you use the same libraries over and over, you might want to reuse some +services from one project to the next one; package your services into a +**provider** by implementing ``Pimple\ServiceProviderInterface``: + +.. code-block:: php + + use Pimple\Container; + + class FooProvider implements Pimple\ServiceProviderInterface + { + public function register(Container $pimple) + { + // register some services and parameters + // on $pimple + } + } + +Then, register the provider on a Container: + +.. code-block:: php + + $pimple->register(new FooProvider()); + +Fetching the Service Creation Function +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When you access an object, Pimple automatically calls the anonymous function +that you defined, which creates the service object for you. If you want to get +raw access to this function, you can use the ``raw()`` method: + +.. code-block:: php + + $container['session'] = function ($c) { + return new Session($c['session_storage']); + }; + + $sessionFunction = $container->raw('session'); + +.. _Pimple 1.x documentation: https://github.com/silexphp/Pimple/tree/1.1 diff --git a/vendor/pimple/pimple/composer.json b/vendor/pimple/pimple/composer.json new file mode 100644 index 00000000..a5268f16 --- /dev/null +++ b/vendor/pimple/pimple/composer.json @@ -0,0 +1,25 @@ +{ + "name": "pimple/pimple", + "type": "library", + "description": "Pimple, a simple Dependency Injection Container", + "keywords": ["dependency injection", "container"], + "homepage": "http://pimple.sensiolabs.org", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-0": { "Pimple": "src/" } + }, + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + } +} diff --git a/vendor/pimple/pimple/ext/pimple/.gitignore b/vendor/pimple/pimple/ext/pimple/.gitignore new file mode 100644 index 00000000..1861088a --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/.gitignore @@ -0,0 +1,30 @@ +*.sw* +.deps +Makefile +Makefile.fragments +Makefile.global +Makefile.objects +acinclude.m4 +aclocal.m4 +build/ +config.cache +config.guess +config.h +config.h.in +config.log +config.nice +config.status +config.sub +configure +configure.in +install-sh +libtool +ltmain.sh +missing +mkinstalldirs +run-tests.php +*.loT +.libs/ +modules/ +*.la +*.lo diff --git a/vendor/pimple/pimple/ext/pimple/README.md b/vendor/pimple/pimple/ext/pimple/README.md new file mode 100644 index 00000000..7b39eb29 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/README.md @@ -0,0 +1,12 @@ +This is Pimple 2 implemented in C + +* PHP >= 5.3 +* Not tested under Windows, might work + +Install +======= + + > phpize + > ./configure + > make + > make install diff --git a/vendor/pimple/pimple/ext/pimple/config.m4 b/vendor/pimple/pimple/ext/pimple/config.m4 new file mode 100644 index 00000000..c9ba17dd --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/config.m4 @@ -0,0 +1,63 @@ +dnl $Id$ +dnl config.m4 for extension pimple + +dnl Comments in this file start with the string 'dnl'. +dnl Remove where necessary. This file will not work +dnl without editing. + +dnl If your extension references something external, use with: + +dnl PHP_ARG_WITH(pimple, for pimple support, +dnl Make sure that the comment is aligned: +dnl [ --with-pimple Include pimple support]) + +dnl Otherwise use enable: + +PHP_ARG_ENABLE(pimple, whether to enable pimple support, +dnl Make sure that the comment is aligned: +[ --enable-pimple Enable pimple support]) + +if test "$PHP_PIMPLE" != "no"; then + dnl Write more examples of tests here... + + dnl # --with-pimple -> check with-path + dnl SEARCH_PATH="/usr/local /usr" # you might want to change this + dnl SEARCH_FOR="/include/pimple.h" # you most likely want to change this + dnl if test -r $PHP_PIMPLE/$SEARCH_FOR; then # path given as parameter + dnl PIMPLE_DIR=$PHP_PIMPLE + dnl else # search default path list + dnl AC_MSG_CHECKING([for pimple files in default path]) + dnl for i in $SEARCH_PATH ; do + dnl if test -r $i/$SEARCH_FOR; then + dnl PIMPLE_DIR=$i + dnl AC_MSG_RESULT(found in $i) + dnl fi + dnl done + dnl fi + dnl + dnl if test -z "$PIMPLE_DIR"; then + dnl AC_MSG_RESULT([not found]) + dnl AC_MSG_ERROR([Please reinstall the pimple distribution]) + dnl fi + + dnl # --with-pimple -> add include path + dnl PHP_ADD_INCLUDE($PIMPLE_DIR/include) + + dnl # --with-pimple -> check for lib and symbol presence + dnl LIBNAME=pimple # you may want to change this + dnl LIBSYMBOL=pimple # you most likely want to change this + + dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, + dnl [ + dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $PIMPLE_DIR/lib, PIMPLE_SHARED_LIBADD) + dnl AC_DEFINE(HAVE_PIMPLELIB,1,[ ]) + dnl ],[ + dnl AC_MSG_ERROR([wrong pimple lib version or lib not found]) + dnl ],[ + dnl -L$PIMPLE_DIR/lib -lm + dnl ]) + dnl + dnl PHP_SUBST(PIMPLE_SHARED_LIBADD) + + PHP_NEW_EXTENSION(pimple, pimple.c, $ext_shared) +fi diff --git a/vendor/pimple/pimple/ext/pimple/config.w32 b/vendor/pimple/pimple/ext/pimple/config.w32 new file mode 100644 index 00000000..39857b32 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/config.w32 @@ -0,0 +1,13 @@ +// $Id$ +// vim:ft=javascript + +// If your extension references something external, use ARG_WITH +// ARG_WITH("pimple", "for pimple support", "no"); + +// Otherwise, use ARG_ENABLE +// ARG_ENABLE("pimple", "enable pimple support", "no"); + +if (PHP_PIMPLE != "no") { + EXTENSION("pimple", "pimple.c"); +} + diff --git a/vendor/pimple/pimple/ext/pimple/php_pimple.h b/vendor/pimple/pimple/ext/pimple/php_pimple.h new file mode 100644 index 00000000..49431f08 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/php_pimple.h @@ -0,0 +1,121 @@ + +/* + * This file is part of Pimple. + * + * Copyright (c) 2014 Fabien Potencier + * + * 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. + */ + +#ifndef PHP_PIMPLE_H +#define PHP_PIMPLE_H + +extern zend_module_entry pimple_module_entry; +#define phpext_pimple_ptr &pimple_module_entry + +#ifdef PHP_WIN32 +# define PHP_PIMPLE_API __declspec(dllexport) +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define PHP_PIMPLE_API __attribute__ ((visibility("default"))) +#else +# define PHP_PIMPLE_API +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +#define PIMPLE_VERSION "3.0.2" +#define PIMPLE_NS "Pimple" + +#define PIMPLE_DEFAULT_ZVAL_CACHE_NUM 5 +#define PIMPLE_DEFAULT_ZVAL_VALUES_NUM 10 + +zend_module_entry *get_module(void); + +PHP_MINIT_FUNCTION(pimple); +PHP_MINFO_FUNCTION(pimple); + +PHP_METHOD(Pimple, __construct); +PHP_METHOD(Pimple, factory); +PHP_METHOD(Pimple, protect); +PHP_METHOD(Pimple, raw); +PHP_METHOD(Pimple, extend); +PHP_METHOD(Pimple, keys); +PHP_METHOD(Pimple, register); +PHP_METHOD(Pimple, offsetSet); +PHP_METHOD(Pimple, offsetUnset); +PHP_METHOD(Pimple, offsetGet); +PHP_METHOD(Pimple, offsetExists); + +PHP_METHOD(PimpleClosure, invoker); + +typedef struct _pimple_bucket_value { + zval *value; /* Must be the first element */ + zval *raw; + zend_object_handle handle_num; + enum { + PIMPLE_IS_PARAM = 0, + PIMPLE_IS_SERVICE = 2 + } type; + zend_bool initialized; + zend_fcall_info_cache fcc; +} pimple_bucket_value; + +typedef struct _pimple_object { + zend_object zobj; + HashTable values; + HashTable factories; + HashTable protected; +} pimple_object; + +typedef struct _pimple_closure_object { + zend_object zobj; + zval *callable; + zval *factory; +} pimple_closure_object; + +static const char sensiolabs_logo[] = "<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHYAAAAUCAMAAABvRTlyAAAAz1BMVEUAAAAAAAAAAAAsThWB5j4AAACD6T8AAACC6D+C6D6C6D+C6D4AAAAAAACC6D4AAAAAAACC6D8AAAAAAAAAAAAAAAAAAAAAAACC6D4AAAAAAAAAAACC6D4AAAAAAAAAAAAAAAAAAAAAAACC6D8AAACC6D4AAAAAAAAAAAAAAAAAAACC6D8AAACC6D6C6D+B6D+C6D+C6D+C6D8AAACC6D6C6D4AAACC6D/K/2KC6D+B6D6C6D6C6D+C6D8sTxUyWRhEeiEAAACC6D+C5z6B6D7drnEVAAAAQXRSTlMAE3oCNSUuDHFHzxaF9UFsu+irX+zlKzYimaJXktyOSFD6BolxqT7QGMMdarMIpuO28r9EolXKgR16OphfXYd4V14GtB4AAAMpSURBVEjHvVSJctowEF1jjME2RziMwUCoMfd9heZqG4n//6buLpJjkmYm03byZmxJa2nf6u2uQcG2bfhqRN4LoTKBzyGDm68M7mAwcOEdjo4zhA/Rf9Go/CVtTgiRhXfIC3EDH8F/eUX1/9KexRo+QgOdtHDsEe/sM7QT32/+K61Z1LFXcXJxN4pTbu1aTQUzuy2PIA0rDo0/0Aa5XFaJvKaVTrubywXvaa1Wq4Vu/Snr3Y7Aojh4VccwykW2N2oQ8wmjyut6+Q1t5ywIG5Npj1sh5E0B7YOzFDjfuRfaOh3O+MbbVNfTWS9COZk3Obd2su5d0a6IU9KLREbw8gEehWSr1r2sPWciXLG38r5NdW0xu9eioU87omjC9yNaMi5GNf6WppVSOqXCFkmCvMB3p9SROLoYQn5pDgQOujA1xjYvqH+plUdkwnmII8VxR/PKYkrfLLomhVlE3b/LhNbNr7hp0H2JaOc4v8dFB58HSsFTSafaqtY1sT3GO8wsy5rhokYPlRJdjPMajyYqTt1EHF/2uqSWQWmAjCUSmQ1MS3g8Btf1XOsy7YIC0CB1b5Xw1Vhba0zbxiCAQLH9TNPmHJXQUtJAN0KcDsoqLxsNvJrJExa7mKIdp2lRE2WexiS4pqWk/0jROlw6K6bV9YOBDGAuqMJ0bnuUKGB0L27bxgRhGEbzihbhxxXaQC88Vkwq8ldCi86RApWUb0Q+4VDosBCc+1s81lUdnBavH4Zp2mm3O44USwOfvSo9oBiwpFg71lMS1VKJLKljS3j9p+fOTvXXlsSNuEv6YPaZda9uRope0VJfKdo7fPiYfSmvFjXQbkhY0d9hCbBWIktRgEDieDhf1N3wbbkmNNgRy8hyl620yGQat/grV3HMpc2HDKTVmOPFz6ylPCKt/nXcAyV260jaAowwIW0YuBzrOgb/KrddZS9OmJaLgpWK4JX2DDuklcLZSDGcn8Vmx9YDNvT6UsjyBApRyFQVX7Vxm9TGxE16nmfRd8/zQoDmggQOTRh5Hv8pMt9Q/L2JmSwkMCE7dA4BuDjHJwfu0Om4QAhOjrN5XkIatglfiN/bUPdCQFjTYgAAAABJRU5ErkJggg==\">"; + +static int pimple_zval_to_pimpleval(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC); +static int pimple_zval_is_valid_callback(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC); + +static void pimple_bucket_dtor(pimple_bucket_value *bucket); +static void pimple_free_bucket(pimple_bucket_value *bucket); + +static zval *pimple_object_read_dimension(zval *object, zval *offset, int type TSRMLS_DC); +static void pimple_object_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC); +static int pimple_object_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC); +static void pimple_object_unset_dimension(zval *object, zval *offset TSRMLS_DC); +static zend_object_value pimple_object_create(zend_class_entry *ce TSRMLS_DC); +static void pimple_free_object_storage(pimple_object *obj TSRMLS_DC); + +static void pimple_closure_free_object_storage(pimple_closure_object *obj TSRMLS_DC); +static zend_object_value pimple_closure_object_create(zend_class_entry *ce TSRMLS_DC); +static zend_function *pimple_closure_get_constructor(zval * TSRMLS_DC); +static int pimple_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, union _zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC); + +#ifdef ZTS +#define PIMPLE_G(v) TSRMG(pimple_globals_id, zend_pimple_globals *, v) +#else +#define PIMPLE_G(v) (pimple_globals.v) +#endif + +#endif /* PHP_PIMPLE_H */ + diff --git a/vendor/pimple/pimple/ext/pimple/pimple.c b/vendor/pimple/pimple/ext/pimple/pimple.c new file mode 100644 index 00000000..239c01d6 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/pimple.c @@ -0,0 +1,922 @@ + +/* + * This file is part of Pimple. + * + * Copyright (c) 2014 Fabien Potencier + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_pimple.h" +#include "pimple_compat.h" +#include "zend_interfaces.h" +#include "zend.h" +#include "Zend/zend_closures.h" +#include "ext/spl/spl_exceptions.h" +#include "Zend/zend_exceptions.h" +#include "main/php_output.h" +#include "SAPI.h" + +static zend_class_entry *pimple_ce; +static zend_object_handlers pimple_object_handlers; +static zend_class_entry *pimple_closure_ce; +static zend_class_entry *pimple_serviceprovider_ce; +static zend_object_handlers pimple_closure_object_handlers; +static zend_internal_function pimple_closure_invoker_function; + +#define FETCH_DIM_HANDLERS_VARS pimple_object *pimple_obj = NULL; \ + ulong index; \ + pimple_obj = (pimple_object *)zend_object_store_get_object(object TSRMLS_CC); \ + +#define PIMPLE_OBJECT_HANDLE_INHERITANCE_OBJECT_HANDLERS do { \ + if (ce != pimple_ce) { \ + zend_hash_find(&ce->function_table, ZEND_STRS("offsetget"), (void **)&function); \ + if (function->common.scope != ce) { /* if the function is not defined in this actual class */ \ + pimple_object_handlers.read_dimension = pimple_object_read_dimension; /* then overwrite the handler to use custom one */ \ + } \ + zend_hash_find(&ce->function_table, ZEND_STRS("offsetset"), (void **)&function); \ + if (function->common.scope != ce) { \ + pimple_object_handlers.write_dimension = pimple_object_write_dimension; \ + } \ + zend_hash_find(&ce->function_table, ZEND_STRS("offsetexists"), (void **)&function); \ + if (function->common.scope != ce) { \ + pimple_object_handlers.has_dimension = pimple_object_has_dimension; \ + } \ + zend_hash_find(&ce->function_table, ZEND_STRS("offsetunset"), (void **)&function); \ + if (function->common.scope != ce) { \ + pimple_object_handlers.unset_dimension = pimple_object_unset_dimension; \ + } \ + } else { \ + pimple_object_handlers.read_dimension = pimple_object_read_dimension; \ + pimple_object_handlers.write_dimension = pimple_object_write_dimension; \ + pimple_object_handlers.has_dimension = pimple_object_has_dimension; \ + pimple_object_handlers.unset_dimension = pimple_object_unset_dimension; \ + }\ + } while(0); + +#define PIMPLE_CALL_CB do { \ + zend_fcall_info_argn(&fci TSRMLS_CC, 1, &object); \ + fci.size = sizeof(fci); \ + fci.object_ptr = retval->fcc.object_ptr; \ + fci.function_name = retval->value; \ + fci.no_separation = 1; \ + fci.retval_ptr_ptr = &retval_ptr_ptr; \ +\ + zend_call_function(&fci, &retval->fcc TSRMLS_CC); \ + efree(fci.params); \ + if (EG(exception)) { \ + return EG(uninitialized_zval_ptr); \ + } \ + } while(0); + +ZEND_BEGIN_ARG_INFO_EX(arginfo___construct, 0, 0, 0) +ZEND_ARG_ARRAY_INFO(0, value, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetset, 0, 0, 2) +ZEND_ARG_INFO(0, offset) +ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetget, 0, 0, 1) +ZEND_ARG_INFO(0, offset) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetexists, 0, 0, 1) +ZEND_ARG_INFO(0, offset) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetunset, 0, 0, 1) +ZEND_ARG_INFO(0, offset) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_factory, 0, 0, 1) +ZEND_ARG_INFO(0, callable) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_protect, 0, 0, 1) +ZEND_ARG_INFO(0, callable) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_raw, 0, 0, 1) +ZEND_ARG_INFO(0, id) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_extend, 0, 0, 2) +ZEND_ARG_INFO(0, id) +ZEND_ARG_INFO(0, callable) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_register, 0, 0, 1) +ZEND_ARG_OBJ_INFO(0, provider, Pimple\\ServiceProviderInterface, 0) +ZEND_ARG_ARRAY_INFO(0, values, 1) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_serviceprovider_register, 0, 0, 1) +ZEND_ARG_OBJ_INFO(0, pimple, Pimple\\Container, 0) +ZEND_END_ARG_INFO() + +static const zend_function_entry pimple_ce_functions[] = { + PHP_ME(Pimple, __construct, arginfo___construct, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, factory, arginfo_factory, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, protect, arginfo_protect, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, raw, arginfo_raw, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, extend, arginfo_extend, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, keys, arginfo_keys, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, register, arginfo_register, ZEND_ACC_PUBLIC) + + PHP_ME(Pimple, offsetSet, arginfo_offsetset, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, offsetGet, arginfo_offsetget, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, offsetExists, arginfo_offsetexists, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, offsetUnset, arginfo_offsetunset, ZEND_ACC_PUBLIC) + PHP_FE_END +}; + +static const zend_function_entry pimple_serviceprovider_iface_ce_functions[] = { + PHP_ABSTRACT_ME(ServiceProviderInterface, register, arginfo_serviceprovider_register) + PHP_FE_END +}; + +static void pimple_closure_free_object_storage(pimple_closure_object *obj TSRMLS_DC) +{ + zend_object_std_dtor(&obj->zobj TSRMLS_CC); + if (obj->factory) { + zval_ptr_dtor(&obj->factory); + } + if (obj->callable) { + zval_ptr_dtor(&obj->callable); + } + efree(obj); +} + +static void pimple_free_object_storage(pimple_object *obj TSRMLS_DC) +{ + zend_hash_destroy(&obj->factories); + zend_hash_destroy(&obj->protected); + zend_hash_destroy(&obj->values); + zend_object_std_dtor(&obj->zobj TSRMLS_CC); + efree(obj); +} + +static void pimple_free_bucket(pimple_bucket_value *bucket) +{ + if (bucket->raw) { + zval_ptr_dtor(&bucket->raw); + } +} + +static zend_object_value pimple_closure_object_create(zend_class_entry *ce TSRMLS_DC) +{ + zend_object_value retval; + pimple_closure_object *pimple_closure_obj = NULL; + + pimple_closure_obj = ecalloc(1, sizeof(pimple_closure_object)); + ZEND_OBJ_INIT(&pimple_closure_obj->zobj, ce); + + pimple_closure_object_handlers.get_constructor = pimple_closure_get_constructor; + retval.handlers = &pimple_closure_object_handlers; + retval.handle = zend_objects_store_put(pimple_closure_obj, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) pimple_closure_free_object_storage, NULL TSRMLS_CC); + + return retval; +} + +static zend_function *pimple_closure_get_constructor(zval *obj TSRMLS_DC) +{ + zend_error(E_ERROR, "Pimple\\ContainerClosure is an internal class and cannot be instantiated"); + + return NULL; +} + +static int pimple_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, union _zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC) +{ + *zobj_ptr = obj; + *ce_ptr = Z_OBJCE_P(obj); + *fptr_ptr = (zend_function *)&pimple_closure_invoker_function; + + return SUCCESS; +} + +static zend_object_value pimple_object_create(zend_class_entry *ce TSRMLS_DC) +{ + zend_object_value retval; + pimple_object *pimple_obj = NULL; + zend_function *function = NULL; + + pimple_obj = emalloc(sizeof(pimple_object)); + ZEND_OBJ_INIT(&pimple_obj->zobj, ce); + + PIMPLE_OBJECT_HANDLE_INHERITANCE_OBJECT_HANDLERS + + retval.handlers = &pimple_object_handlers; + retval.handle = zend_objects_store_put(pimple_obj, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) pimple_free_object_storage, NULL TSRMLS_CC); + + zend_hash_init(&pimple_obj->factories, PIMPLE_DEFAULT_ZVAL_CACHE_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0); + zend_hash_init(&pimple_obj->protected, PIMPLE_DEFAULT_ZVAL_CACHE_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0); + zend_hash_init(&pimple_obj->values, PIMPLE_DEFAULT_ZVAL_VALUES_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0); + + return retval; +} + +static void pimple_object_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC) +{ + FETCH_DIM_HANDLERS_VARS + + pimple_bucket_value pimple_value = {0}, *found_value = NULL; + ulong hash; + + pimple_zval_to_pimpleval(value, &pimple_value TSRMLS_CC); + + if (!offset) {/* $p[] = 'foo' when not overloaded */ + zend_hash_next_index_insert(&pimple_obj->values, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL); + Z_ADDREF_P(value); + return; + } + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + hash = zend_hash_func(Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1); + zend_hash_quick_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, hash, (void **)&found_value); + if (found_value && found_value->type == PIMPLE_IS_SERVICE && found_value->initialized == 1) { + pimple_free_bucket(&pimple_value); + zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "Cannot override frozen service \"%s\".", Z_STRVAL_P(offset)); + return; + } + if (zend_hash_quick_update(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, hash, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL) == FAILURE) { + pimple_free_bucket(&pimple_value); + return; + } + Z_ADDREF_P(value); + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + zend_hash_index_find(&pimple_obj->values, index, (void **)&found_value); + if (found_value && found_value->type == PIMPLE_IS_SERVICE && found_value->initialized == 1) { + pimple_free_bucket(&pimple_value); + zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "Cannot override frozen service \"%ld\".", index); + return; + } + if (zend_hash_index_update(&pimple_obj->values, index, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL) == FAILURE) { + pimple_free_bucket(&pimple_value); + return; + } + Z_ADDREF_P(value); + break; + case IS_NULL: /* $p[] = 'foo' when overloaded */ + zend_hash_next_index_insert(&pimple_obj->values, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL); + Z_ADDREF_P(value); + break; + default: + pimple_free_bucket(&pimple_value); + zend_error(E_WARNING, "Unsupported offset type"); + } +} + +static void pimple_object_unset_dimension(zval *object, zval *offset TSRMLS_DC) +{ + FETCH_DIM_HANDLERS_VARS + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + zend_symtable_del(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1); + zend_symtable_del(&pimple_obj->factories, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1); + zend_symtable_del(&pimple_obj->protected, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1); + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + zend_hash_index_del(&pimple_obj->values, index); + zend_hash_index_del(&pimple_obj->factories, index); + zend_hash_index_del(&pimple_obj->protected, index); + break; + default: + zend_error(E_WARNING, "Unsupported offset type"); + } +} + +static int pimple_object_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC) +{ + FETCH_DIM_HANDLERS_VARS + + pimple_bucket_value *retval = NULL; + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + if (zend_symtable_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void **)&retval) == SUCCESS) { + switch (check_empty) { + case 0: /* isset */ + return 1; /* Differs from PHP behavior (Z_TYPE_P(retval->value) != IS_NULL;) */ + case 1: /* empty */ + default: + return zend_is_true(retval->value); + } + } + return 0; + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + if (zend_hash_index_find(&pimple_obj->values, index, (void **)&retval) == SUCCESS) { + switch (check_empty) { + case 0: /* isset */ + return 1; /* Differs from PHP behavior (Z_TYPE_P(retval->value) != IS_NULL;)*/ + case 1: /* empty */ + default: + return zend_is_true(retval->value); + } + } + return 0; + break; + default: + zend_error(E_WARNING, "Unsupported offset type"); + return 0; + } +} + +static zval *pimple_object_read_dimension(zval *object, zval *offset, int type TSRMLS_DC) +{ + FETCH_DIM_HANDLERS_VARS + + pimple_bucket_value *retval = NULL; + zend_fcall_info fci = {0}; + zval *retval_ptr_ptr = NULL; + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + if (zend_symtable_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void **)&retval) == FAILURE) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%s\" is not defined.", Z_STRVAL_P(offset)); + return EG(uninitialized_zval_ptr); + } + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + if (zend_hash_index_find(&pimple_obj->values, index, (void **)&retval) == FAILURE) { + return EG(uninitialized_zval_ptr); + } + break; + case IS_NULL: /* $p[][3] = 'foo' first dim access */ + return EG(uninitialized_zval_ptr); + break; + default: + zend_error(E_WARNING, "Unsupported offset type"); + return EG(uninitialized_zval_ptr); + } + + if(retval->type == PIMPLE_IS_PARAM) { + return retval->value; + } + + if (zend_hash_index_exists(&pimple_obj->protected, retval->handle_num)) { + /* Service is protected, return the value every time */ + return retval->value; + } + + if (zend_hash_index_exists(&pimple_obj->factories, retval->handle_num)) { + /* Service is a factory, call it everytime and never cache its result */ + PIMPLE_CALL_CB + Z_DELREF_P(retval_ptr_ptr); /* fetch dim addr will increment refcount */ + return retval_ptr_ptr; + } + + if (retval->initialized == 1) { + /* Service has already been called, return its cached value */ + return retval->value; + } + + ALLOC_INIT_ZVAL(retval->raw); + MAKE_COPY_ZVAL(&retval->value, retval->raw); + + PIMPLE_CALL_CB + + retval->initialized = 1; + zval_ptr_dtor(&retval->value); + retval->value = retval_ptr_ptr; + + return retval->value; +} + +static int pimple_zval_is_valid_callback(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC) +{ + if (Z_TYPE_P(_zval) != IS_OBJECT) { + return FAILURE; + } + + if (_pimple_bucket_value->fcc.called_scope) { + return SUCCESS; + } + + if (Z_OBJ_HANDLER_P(_zval, get_closure) && Z_OBJ_HANDLER_P(_zval, get_closure)(_zval, &_pimple_bucket_value->fcc.calling_scope, &_pimple_bucket_value->fcc.function_handler, &_pimple_bucket_value->fcc.object_ptr TSRMLS_CC) == SUCCESS) { + _pimple_bucket_value->fcc.called_scope = _pimple_bucket_value->fcc.calling_scope; + return SUCCESS; + } else { + return FAILURE; + } +} + +static int pimple_zval_to_pimpleval(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC) +{ + _pimple_bucket_value->value = _zval; + + if (Z_TYPE_P(_zval) != IS_OBJECT) { + return PIMPLE_IS_PARAM; + } + + if (pimple_zval_is_valid_callback(_zval, _pimple_bucket_value TSRMLS_CC) == SUCCESS) { + _pimple_bucket_value->type = PIMPLE_IS_SERVICE; + _pimple_bucket_value->handle_num = Z_OBJ_HANDLE_P(_zval); + } + + return PIMPLE_IS_SERVICE; +} + +static void pimple_bucket_dtor(pimple_bucket_value *bucket) +{ + zval_ptr_dtor(&bucket->value); + pimple_free_bucket(bucket); +} + +PHP_METHOD(Pimple, protect) +{ + zval *protected = NULL; + pimple_object *pobj = NULL; + pimple_bucket_value bucket = {0}; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &protected) == FAILURE) { + return; + } + + if (pimple_zval_is_valid_callback(protected, &bucket TSRMLS_CC) == FAILURE) { + pimple_free_bucket(&bucket); + zend_throw_exception(spl_ce_InvalidArgumentException, "Callable is not a Closure or invokable object.", 0 TSRMLS_CC); + return; + } + + pimple_zval_to_pimpleval(protected, &bucket TSRMLS_CC); + pobj = (pimple_object *)zend_object_store_get_object(getThis() TSRMLS_CC); + + if (zend_hash_index_update(&pobj->protected, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL) == SUCCESS) { + Z_ADDREF_P(protected); + RETURN_ZVAL(protected, 1 , 0); + } else { + pimple_free_bucket(&bucket); + } + RETURN_FALSE; +} + +PHP_METHOD(Pimple, raw) +{ + zval *offset = NULL; + pimple_object *pobj = NULL; + pimple_bucket_value *value = NULL; + ulong index; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) { + return; + } + + pobj = zend_object_store_get_object(getThis() TSRMLS_CC); + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + if (zend_symtable_find(&pobj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void *)&value) == FAILURE) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%s\" is not defined.", Z_STRVAL_P(offset)); + RETURN_NULL(); + } + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + if (zend_hash_index_find(&pobj->values, index, (void *)&value) == FAILURE) { + RETURN_NULL(); + } + break; + case IS_NULL: + default: + zend_error(E_WARNING, "Unsupported offset type"); + } + + if (value->raw) { + RETVAL_ZVAL(value->raw, 1, 0); + } else { + RETVAL_ZVAL(value->value, 1, 0); + } +} + +PHP_METHOD(Pimple, extend) +{ + zval *offset = NULL, *callable = NULL, *pimple_closure_obj = NULL; + pimple_bucket_value bucket = {0}, *value = NULL; + pimple_object *pobj = NULL; + pimple_closure_object *pcobj = NULL; + ulong index; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &offset, &callable) == FAILURE) { + return; + } + + pobj = zend_object_store_get_object(getThis() TSRMLS_CC); + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + if (zend_symtable_find(&pobj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void *)&value) == FAILURE) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%s\" is not defined.", Z_STRVAL_P(offset)); + RETURN_NULL(); + } + if (value->type != PIMPLE_IS_SERVICE) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%s\" does not contain an object definition.", Z_STRVAL_P(offset)); + RETURN_NULL(); + } + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + if (zend_hash_index_find(&pobj->values, index, (void *)&value) == FAILURE) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%ld\" is not defined.", index); + RETURN_NULL(); + } + if (value->type != PIMPLE_IS_SERVICE) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%ld\" does not contain an object definition.", index); + RETURN_NULL(); + } + break; + case IS_NULL: + default: + zend_error(E_WARNING, "Unsupported offset type"); + } + + if (pimple_zval_is_valid_callback(callable, &bucket TSRMLS_CC) == FAILURE) { + pimple_free_bucket(&bucket); + zend_throw_exception(spl_ce_InvalidArgumentException, "Extension service definition is not a Closure or invokable object.", 0 TSRMLS_CC); + RETURN_NULL(); + } + pimple_free_bucket(&bucket); + + ALLOC_INIT_ZVAL(pimple_closure_obj); + object_init_ex(pimple_closure_obj, pimple_closure_ce); + + pcobj = zend_object_store_get_object(pimple_closure_obj TSRMLS_CC); + pcobj->callable = callable; + pcobj->factory = value->value; + Z_ADDREF_P(callable); + Z_ADDREF_P(value->value); + + if (zend_hash_index_exists(&pobj->factories, value->handle_num)) { + pimple_zval_to_pimpleval(pimple_closure_obj, &bucket TSRMLS_CC); + zend_hash_index_del(&pobj->factories, value->handle_num); + zend_hash_index_update(&pobj->factories, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL); + Z_ADDREF_P(pimple_closure_obj); + } + + pimple_object_write_dimension(getThis(), offset, pimple_closure_obj TSRMLS_CC); + + RETVAL_ZVAL(pimple_closure_obj, 1, 1); +} + +PHP_METHOD(Pimple, keys) +{ + HashPosition pos; + pimple_object *pobj = NULL; + zval **value = NULL; + zval *endval = NULL; + char *str_index = NULL; + int str_len; + ulong num_index; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + pobj = zend_object_store_get_object(getThis() TSRMLS_CC); + array_init_size(return_value, zend_hash_num_elements(&pobj->values)); + + zend_hash_internal_pointer_reset_ex(&pobj->values, &pos); + + while(zend_hash_get_current_data_ex(&pobj->values, (void **)&value, &pos) == SUCCESS) { + MAKE_STD_ZVAL(endval); + switch (zend_hash_get_current_key_ex(&pobj->values, &str_index, (uint *)&str_len, &num_index, 0, &pos)) { + case HASH_KEY_IS_STRING: + ZVAL_STRINGL(endval, str_index, str_len - 1, 1); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &endval, sizeof(zval *), NULL); + break; + case HASH_KEY_IS_LONG: + ZVAL_LONG(endval, num_index); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &endval, sizeof(zval *), NULL); + break; + } + zend_hash_move_forward_ex(&pobj->values, &pos); + } +} + +PHP_METHOD(Pimple, factory) +{ + zval *factory = NULL; + pimple_object *pobj = NULL; + pimple_bucket_value bucket = {0}; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &factory) == FAILURE) { + return; + } + + if (pimple_zval_is_valid_callback(factory, &bucket TSRMLS_CC) == FAILURE) { + pimple_free_bucket(&bucket); + zend_throw_exception(spl_ce_InvalidArgumentException, "Service definition is not a Closure or invokable object.", 0 TSRMLS_CC); + return; + } + + pimple_zval_to_pimpleval(factory, &bucket TSRMLS_CC); + pobj = (pimple_object *)zend_object_store_get_object(getThis() TSRMLS_CC); + + if (zend_hash_index_update(&pobj->factories, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL) == SUCCESS) { + Z_ADDREF_P(factory); + RETURN_ZVAL(factory, 1 , 0); + } else { + pimple_free_bucket(&bucket); + } + + RETURN_FALSE; +} + +PHP_METHOD(Pimple, offsetSet) +{ + zval *offset = NULL, *value = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &offset, &value) == FAILURE) { + return; + } + + pimple_object_write_dimension(getThis(), offset, value TSRMLS_CC); +} + +PHP_METHOD(Pimple, offsetGet) +{ + zval *offset = NULL, *retval = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) { + return; + } + + retval = pimple_object_read_dimension(getThis(), offset, 0 TSRMLS_CC); + + RETVAL_ZVAL(retval, 1, 0); +} + +PHP_METHOD(Pimple, offsetUnset) +{ + zval *offset = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) { + return; + } + + pimple_object_unset_dimension(getThis(), offset TSRMLS_CC); +} + +PHP_METHOD(Pimple, offsetExists) +{ + zval *offset = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) { + return; + } + + RETVAL_BOOL(pimple_object_has_dimension(getThis(), offset, 1 TSRMLS_CC)); +} + +PHP_METHOD(Pimple, register) +{ + zval *provider; + zval **data; + zval *retval = NULL; + zval key; + + HashTable *array = NULL; + HashPosition pos; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|h", &provider, pimple_serviceprovider_ce, &array) == FAILURE) { + return; + } + + RETVAL_ZVAL(getThis(), 1, 0); + + zend_call_method_with_1_params(&provider, Z_OBJCE_P(provider), NULL, "register", &retval, getThis()); + + if (retval) { + zval_ptr_dtor(&retval); + } + + if (!array) { + return; + } + + zend_hash_internal_pointer_reset_ex(array, &pos); + + while(zend_hash_get_current_data_ex(array, (void **)&data, &pos) == SUCCESS) { + zend_hash_get_current_key_zval_ex(array, &key, &pos); + pimple_object_write_dimension(getThis(), &key, *data TSRMLS_CC); + zend_hash_move_forward_ex(array, &pos); + } +} + +PHP_METHOD(Pimple, __construct) +{ + zval *values = NULL, **pData = NULL, offset; + HashPosition pos; + char *str_index = NULL; + zend_uint str_length; + ulong num_index; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!", &values) == FAILURE || !values) { + return; + } + + zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(values), &pos); + while (zend_hash_has_more_elements_ex(Z_ARRVAL_P(values), &pos) == SUCCESS) { + zend_hash_get_current_data_ex(Z_ARRVAL_P(values), (void **)&pData, &pos); + zend_hash_get_current_key_ex(Z_ARRVAL_P(values), &str_index, &str_length, &num_index, 0, &pos); + INIT_ZVAL(offset); + if (zend_hash_get_current_key_type_ex(Z_ARRVAL_P(values), &pos) == HASH_KEY_IS_LONG) { + ZVAL_LONG(&offset, num_index); + } else { + ZVAL_STRINGL(&offset, str_index, (str_length - 1), 0); + } + pimple_object_write_dimension(getThis(), &offset, *pData TSRMLS_CC); + zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos); + } +} + +/* + * This is PHP code snippet handling extend()s calls : + + $extended = function ($c) use ($callable, $factory) { + return $callable($factory($c), $c); + }; + + */ +PHP_METHOD(PimpleClosure, invoker) +{ + pimple_closure_object *pcobj = NULL; + zval *arg = NULL, *retval = NULL, *newretval = NULL; + zend_fcall_info fci = {0}; + zval **args[2]; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) { + return; + } + + pcobj = zend_object_store_get_object(getThis() TSRMLS_CC); + + fci.function_name = pcobj->factory; + args[0] = &arg; + zend_fcall_info_argp(&fci TSRMLS_CC, 1, args); + fci.retval_ptr_ptr = &retval; + fci.size = sizeof(fci); + + if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE || EG(exception)) { + efree(fci.params); + return; /* Should here return default zval */ + } + + efree(fci.params); + memset(&fci, 0, sizeof(fci)); + fci.size = sizeof(fci); + + fci.function_name = pcobj->callable; + args[0] = &retval; + args[1] = &arg; + zend_fcall_info_argp(&fci TSRMLS_CC, 2, args); + fci.retval_ptr_ptr = &newretval; + + if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE || EG(exception)) { + efree(fci.params); + zval_ptr_dtor(&retval); + return; + } + + efree(fci.params); + zval_ptr_dtor(&retval); + + RETVAL_ZVAL(newretval, 1 ,1); +} + +PHP_MINIT_FUNCTION(pimple) +{ + zend_class_entry tmp_pimple_ce, tmp_pimple_closure_ce, tmp_pimple_serviceprovider_iface_ce; + INIT_NS_CLASS_ENTRY(tmp_pimple_ce, PIMPLE_NS, "Container", pimple_ce_functions); + INIT_NS_CLASS_ENTRY(tmp_pimple_closure_ce, PIMPLE_NS, "ContainerClosure", NULL); + INIT_NS_CLASS_ENTRY(tmp_pimple_serviceprovider_iface_ce, PIMPLE_NS, "ServiceProviderInterface", pimple_serviceprovider_iface_ce_functions); + + tmp_pimple_ce.create_object = pimple_object_create; + tmp_pimple_closure_ce.create_object = pimple_closure_object_create; + + pimple_ce = zend_register_internal_class(&tmp_pimple_ce TSRMLS_CC); + zend_class_implements(pimple_ce TSRMLS_CC, 1, zend_ce_arrayaccess); + + pimple_closure_ce = zend_register_internal_class(&tmp_pimple_closure_ce TSRMLS_CC); + pimple_closure_ce->ce_flags |= ZEND_ACC_FINAL_CLASS; + + pimple_serviceprovider_ce = zend_register_internal_interface(&tmp_pimple_serviceprovider_iface_ce TSRMLS_CC); + + memcpy(&pimple_closure_object_handlers, zend_get_std_object_handlers(), sizeof(*zend_get_std_object_handlers())); + pimple_object_handlers = std_object_handlers; + pimple_closure_object_handlers.get_closure = pimple_closure_get_closure; + + pimple_closure_invoker_function.function_name = "Pimple closure internal invoker"; + pimple_closure_invoker_function.fn_flags |= ZEND_ACC_CLOSURE; + pimple_closure_invoker_function.handler = ZEND_MN(PimpleClosure_invoker); + pimple_closure_invoker_function.num_args = 1; + pimple_closure_invoker_function.required_num_args = 1; + pimple_closure_invoker_function.scope = pimple_closure_ce; + pimple_closure_invoker_function.type = ZEND_INTERNAL_FUNCTION; + pimple_closure_invoker_function.module = &pimple_module_entry; + + return SUCCESS; +} + +PHP_MINFO_FUNCTION(pimple) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "SensioLabs Pimple C support", "enabled"); + php_info_print_table_row(2, "Pimple supported version", PIMPLE_VERSION); + php_info_print_table_end(); + + php_info_print_box_start(0); + php_write((void *)ZEND_STRL("SensioLabs Pimple C support developed by Julien Pauli") TSRMLS_CC); + if (!sapi_module.phpinfo_as_text) { + php_write((void *)ZEND_STRL(sensiolabs_logo) TSRMLS_CC); + } + php_info_print_box_end(); +} + +zend_module_entry pimple_module_entry = { + STANDARD_MODULE_HEADER, + "pimple", + NULL, + PHP_MINIT(pimple), + NULL, + NULL, + NULL, + PHP_MINFO(pimple), + PIMPLE_VERSION, + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_PIMPLE +ZEND_GET_MODULE(pimple) +#endif diff --git a/vendor/pimple/pimple/ext/pimple/pimple_compat.h b/vendor/pimple/pimple/ext/pimple/pimple_compat.h new file mode 100644 index 00000000..d234e174 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/pimple_compat.h @@ -0,0 +1,81 @@ + +/* + * This file is part of Pimple. + * + * Copyright (c) 2014 Fabien Potencier + * + * 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. + */ + +#ifndef PIMPLE_COMPAT_H_ +#define PIMPLE_COMPAT_H_ + +#include "Zend/zend_extensions.h" /* for ZEND_EXTENSION_API_NO */ + +#define PHP_5_0_X_API_NO 220040412 +#define PHP_5_1_X_API_NO 220051025 +#define PHP_5_2_X_API_NO 220060519 +#define PHP_5_3_X_API_NO 220090626 +#define PHP_5_4_X_API_NO 220100525 +#define PHP_5_5_X_API_NO 220121212 +#define PHP_5_6_X_API_NO 220131226 + +#define IS_PHP_56 ZEND_EXTENSION_API_NO == PHP_5_6_X_API_NO +#define IS_AT_LEAST_PHP_56 ZEND_EXTENSION_API_NO >= PHP_5_6_X_API_NO + +#define IS_PHP_55 ZEND_EXTENSION_API_NO == PHP_5_5_X_API_NO +#define IS_AT_LEAST_PHP_55 ZEND_EXTENSION_API_NO >= PHP_5_5_X_API_NO + +#define IS_PHP_54 ZEND_EXTENSION_API_NO == PHP_5_4_X_API_NO +#define IS_AT_LEAST_PHP_54 ZEND_EXTENSION_API_NO >= PHP_5_4_X_API_NO + +#define IS_PHP_53 ZEND_EXTENSION_API_NO == PHP_5_3_X_API_NO +#define IS_AT_LEAST_PHP_53 ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + +#if IS_PHP_53 +#define object_properties_init(obj, ce) do { \ + zend_hash_copy(obj->properties, &ce->default_properties, zval_copy_property_ctor(ce), NULL, sizeof(zval *)); \ + } while (0); +#endif + +#define ZEND_OBJ_INIT(obj, ce) do { \ + zend_object_std_init(obj, ce TSRMLS_CC); \ + object_properties_init((obj), (ce)); \ + } while(0); + +#if IS_PHP_53 || IS_PHP_54 +static void zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, HashPosition *pos) { + Bucket *p; + + p = pos ? (*pos) : ht->pInternalPointer; + + if (!p) { + Z_TYPE_P(key) = IS_NULL; + } else if (p->nKeyLength) { + Z_TYPE_P(key) = IS_STRING; + Z_STRVAL_P(key) = estrndup(p->arKey, p->nKeyLength - 1); + Z_STRLEN_P(key) = p->nKeyLength - 1; + } else { + Z_TYPE_P(key) = IS_LONG; + Z_LVAL_P(key) = p->h; + } +} +#endif + +#endif /* PIMPLE_COMPAT_H_ */ diff --git a/vendor/pimple/pimple/ext/pimple/tests/001.phpt b/vendor/pimple/pimple/ext/pimple/tests/001.phpt new file mode 100644 index 00000000..0809ea23 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/001.phpt @@ -0,0 +1,45 @@ +--TEST-- +Test for read_dim/write_dim handlers +--SKIPIF-- +<?php if (!extension_loaded("pimple")) print "skip"; ?> +--FILE-- +<?php +$p = new Pimple\Container(); +$p[42] = 'foo'; +$p['foo'] = 42; + +echo $p[42]; +echo "\n"; +echo $p['foo']; +echo "\n"; +try { + var_dump($p['nonexistant']); + echo "Exception excpected"; +} catch (InvalidArgumentException $e) { } + +$p[54.2] = 'foo2'; +echo $p[54]; +echo "\n"; +$p[242.99] = 'foo99'; +echo $p[242]; + +echo "\n"; + +$p[5] = 'bar'; +$p[5] = 'baz'; +echo $p[5]; + +echo "\n"; + +$p['str'] = 'str'; +$p['str'] = 'strstr'; +echo $p['str']; +?> + +--EXPECTF-- +foo +42 +foo2 +foo99 +baz +strstr
\ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/002.phpt b/vendor/pimple/pimple/ext/pimple/tests/002.phpt new file mode 100644 index 00000000..7b56d2c1 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/002.phpt @@ -0,0 +1,15 @@ +--TEST-- +Test for constructor +--SKIPIF-- +<?php if (!extension_loaded("pimple")) print "skip"; ?> +--FILE-- +<?php +$p = new Pimple\Container(); +var_dump($p[42]); + +$p = new Pimple\Container(array(42=>'foo')); +var_dump($p[42]); +?> +--EXPECT-- +NULL +string(3) "foo" diff --git a/vendor/pimple/pimple/ext/pimple/tests/003.phpt b/vendor/pimple/pimple/ext/pimple/tests/003.phpt new file mode 100644 index 00000000..a22cfa35 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/003.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test empty dimensions +--SKIPIF-- +<?php if (!extension_loaded("pimple")) print "skip"; ?> +--FILE-- +<?php +$p = new Pimple\Container(); +$p[] = 42; +var_dump($p[0]); +$p[41] = 'foo'; +$p[] = 'bar'; +var_dump($p[42]); +?> +--EXPECT-- +int(42) +string(3) "bar"
\ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/004.phpt b/vendor/pimple/pimple/ext/pimple/tests/004.phpt new file mode 100644 index 00000000..1e1d2513 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/004.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test has/unset dim handlers +--SKIPIF-- +<?php if (!extension_loaded("pimple")) print "skip"; ?> +--FILE-- +<?php +$p = new Pimple\Container(); +$p[] = 42; +var_dump($p[0]); +unset($p[0]); +var_dump($p[0]); +$p['foo'] = 'bar'; +var_dump(isset($p['foo'])); +unset($p['foo']); +try { + var_dump($p['foo']); + echo "Excpected exception"; +} catch (InvalidArgumentException $e) { } +var_dump(isset($p['bar'])); +$p['bar'] = NULL; +var_dump(isset($p['bar'])); +var_dump(empty($p['bar'])); +?> +--EXPECT-- +int(42) +NULL +bool(true) +bool(false) +bool(true) +bool(true)
\ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/005.phpt b/vendor/pimple/pimple/ext/pimple/tests/005.phpt new file mode 100644 index 00000000..0479ee05 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/005.phpt @@ -0,0 +1,27 @@ +--TEST-- +Test simple class inheritance +--SKIPIF-- +<?php if (!extension_loaded("pimple")) print "skip"; ?> +--FILE-- +<?php +class MyPimple extends Pimple\Container +{ + public $someAttr = 'fooAttr'; + + public function offsetget($o) + { + var_dump("hit"); + return parent::offsetget($o); + } +} + +$p = new MyPimple; +$p[42] = 'foo'; +echo $p[42]; +echo "\n"; +echo $p->someAttr; +?> +--EXPECT-- +string(3) "hit" +foo +fooAttr
\ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/006.phpt b/vendor/pimple/pimple/ext/pimple/tests/006.phpt new file mode 100644 index 00000000..cfe8a119 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/006.phpt @@ -0,0 +1,51 @@ +--TEST-- +Test complex class inheritance +--SKIPIF-- +<?php if (!extension_loaded("pimple")) print "skip"; ?> +--FILE-- +<?php +class MyPimple extends Pimple\Container +{ + public function offsetget($o) + { + var_dump("hit offsetget in " . __CLASS__); + return parent::offsetget($o); + } +} + +class TestPimple extends MyPimple +{ + public function __construct($values) + { + array_shift($values); + parent::__construct($values); + } + + public function offsetget($o) + { + var_dump('hit offsetget in ' . __CLASS__); + return parent::offsetget($o); + } + + public function offsetset($o, $v) + { + var_dump('hit offsetset'); + return parent::offsetset($o, $v); + } +} + +$defaultValues = array('foo' => 'bar', 88 => 'baz'); + +$p = new TestPimple($defaultValues); +$p[42] = 'foo'; +var_dump($p[42]); +var_dump($p[0]); +?> +--EXPECT-- +string(13) "hit offsetset" +string(27) "hit offsetget in TestPimple" +string(25) "hit offsetget in MyPimple" +string(3) "foo" +string(27) "hit offsetget in TestPimple" +string(25) "hit offsetget in MyPimple" +string(3) "baz"
\ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/007.phpt b/vendor/pimple/pimple/ext/pimple/tests/007.phpt new file mode 100644 index 00000000..5aac6838 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/007.phpt @@ -0,0 +1,22 @@ +--TEST-- +Test for read_dim/write_dim handlers +--SKIPIF-- +<?php if (!extension_loaded("pimple")) print "skip"; ?> +--FILE-- +<?php +$p = new Pimple\Container(); +$p[42] = 'foo'; +$p['foo'] = 42; + +echo $p[42]; +echo "\n"; +echo $p['foo']; +echo "\n"; +try { + var_dump($p['nonexistant']); + echo "Exception excpected"; +} catch (InvalidArgumentException $e) { } +?> +--EXPECTF-- +foo +42
\ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/008.phpt b/vendor/pimple/pimple/ext/pimple/tests/008.phpt new file mode 100644 index 00000000..db7eeec4 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/008.phpt @@ -0,0 +1,29 @@ +--TEST-- +Test frozen services +--SKIPIF-- +<?php if (!extension_loaded("pimple")) print "skip"; ?> +--FILE-- +<?php +$p = new Pimple\Container(); +$p[42] = 'foo'; +$p[42] = 'bar'; + +$p['foo'] = function () { }; +$p['foo'] = function () { }; + +$a = $p['foo']; + +try { + $p['foo'] = function () { }; + echo "Exception excpected"; +} catch (RuntimeException $e) { } + +$p[42] = function() { }; +$a = $p[42]; + +try { + $p[42] = function () { }; + echo "Exception excpected"; +} catch (RuntimeException $e) { } +?> +--EXPECTF-- diff --git a/vendor/pimple/pimple/ext/pimple/tests/009.phpt b/vendor/pimple/pimple/ext/pimple/tests/009.phpt new file mode 100644 index 00000000..bb05ea29 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/009.phpt @@ -0,0 +1,13 @@ +--TEST-- +Test service is called as callback, and only once +--SKIPIF-- +<?php if (!extension_loaded("pimple")) print "skip"; ?> +--FILE-- +<?php +$p = new Pimple\Container(); +$p['foo'] = function($arg) use ($p) { var_dump($p === $arg); }; +$a = $p['foo']; +$b = $p['foo']; /* should return not calling the callback */ +?> +--EXPECTF-- +bool(true)
\ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/010.phpt b/vendor/pimple/pimple/ext/pimple/tests/010.phpt new file mode 100644 index 00000000..badce014 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/010.phpt @@ -0,0 +1,45 @@ +--TEST-- +Test service is called as callback for every callback type +--SKIPIF-- +<?php if (!extension_loaded("pimple")) print "skip"; ?> +--FILE-- +<?php +function callme() +{ + return 'called'; +} + +$a = function() { return 'called'; }; + +class Foo +{ + public static function bar() + { + return 'called'; + } +} + +$p = new Pimple\Container(); +$p['foo'] = 'callme'; +echo $p['foo'] . "\n"; + +$p['bar'] = $a; +echo $p['bar'] . "\n"; + +$p['baz'] = "Foo::bar"; +echo $p['baz'] . "\n"; + +$p['foobar'] = array('Foo', 'bar'); +var_dump($p['foobar']); + +?> +--EXPECTF-- +callme +called +Foo::bar +array(2) { + [0]=> + string(3) "Foo" + [1]=> + string(3) "bar" +}
\ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/011.phpt b/vendor/pimple/pimple/ext/pimple/tests/011.phpt new file mode 100644 index 00000000..6682ab8e --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/011.phpt @@ -0,0 +1,19 @@ +--TEST-- +Test service callback throwing an exception +--SKIPIF-- +<?php if (!extension_loaded("pimple")) print "skip"; ?> +--FILE-- +<?php +class CallBackException extends RuntimeException { } + +$p = new Pimple\Container(); +$p['foo'] = function () { throw new CallBackException; }; +try { + echo $p['foo'] . "\n"; + echo "should not come here"; +} catch (CallBackException $e) { + echo "all right!"; +} +?> +--EXPECTF-- +all right!
\ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/012.phpt b/vendor/pimple/pimple/ext/pimple/tests/012.phpt new file mode 100644 index 00000000..4c6ac486 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/012.phpt @@ -0,0 +1,28 @@ +--TEST-- +Test service factory +--SKIPIF-- +<?php if (!extension_loaded("pimple")) print "skip"; ?> +--FILE-- +<?php + +$p = new Pimple\Container(); + +$p->factory($f = function() { var_dump('called-1'); return 'ret-1';}); + +$p[] = $f; + +$p[] = function () { var_dump('called-2'); return 'ret-2'; }; + +var_dump($p[0]); +var_dump($p[0]); +var_dump($p[1]); +var_dump($p[1]); +?> +--EXPECTF-- +string(8) "called-1" +string(5) "ret-1" +string(8) "called-1" +string(5) "ret-1" +string(8) "called-2" +string(5) "ret-2" +string(5) "ret-2"
\ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/013.phpt b/vendor/pimple/pimple/ext/pimple/tests/013.phpt new file mode 100644 index 00000000..f419958c --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/013.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test keys() +--SKIPIF-- +<?php if (!extension_loaded("pimple")) print "skip"; ?> +--FILE-- +<?php + +$p = new Pimple\Container(); + +var_dump($p->keys()); + +$p['foo'] = 'bar'; +$p[] = 'foo'; + +var_dump($p->keys()); + +unset($p['foo']); + +var_dump($p->keys()); +?> +--EXPECTF-- +array(0) { +} +array(2) { + [0]=> + string(3) "foo" + [1]=> + int(0) +} +array(1) { + [0]=> + int(0) +}
\ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/014.phpt b/vendor/pimple/pimple/ext/pimple/tests/014.phpt new file mode 100644 index 00000000..ac937213 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/014.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test raw() +--SKIPIF-- +<?php if (!extension_loaded("pimple")) print "skip"; ?> +--FILE-- +<?php + +$p = new Pimple\Container(); +$f = function () { var_dump('called-2'); return 'ret-2'; }; + +$p['foo'] = $f; +$p[42] = $f; + +var_dump($p['foo']); +var_dump($p->raw('foo')); +var_dump($p[42]); + +unset($p['foo']); + +try { + $p->raw('foo'); + echo "expected exception"; +} catch (InvalidArgumentException $e) { } +--EXPECTF-- +string(8) "called-2" +string(5) "ret-2" +object(Closure)#%i (0) { +} +string(8) "called-2" +string(5) "ret-2"
\ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/015.phpt b/vendor/pimple/pimple/ext/pimple/tests/015.phpt new file mode 100644 index 00000000..314f008a --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/015.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test protect() +--SKIPIF-- +<?php if (!extension_loaded("pimple")) print "skip"; ?> +--FILE-- +<?php + +$p = new Pimple\Container(); +$f = function () { return 'foo'; }; +$p['foo'] = $f; + +$p->protect($f); + +var_dump($p['foo']); +--EXPECTF-- +object(Closure)#%i (0) { +}
\ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/016.phpt b/vendor/pimple/pimple/ext/pimple/tests/016.phpt new file mode 100644 index 00000000..e55edb0a --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/016.phpt @@ -0,0 +1,24 @@ +--TEST-- +Test extend() +--SKIPIF-- +<?php if (!extension_loaded("pimple")) print "skip"; ?> +--FILE-- +<?php +/* + This is part of Pimple::extend() code : + + $extended = function ($c) use ($callable, $factory) { + return $callable($factory($c), $c); + }; +*/ + +$p = new Pimple\Container(); +$p[12] = function ($v) { var_dump($v); return 'foo';}; /* $factory in code above */ + +$c = $p->extend(12, function ($w) { var_dump($w); return 'bar'; }); /* $callable in code above */ + +var_dump($c('param')); +--EXPECTF-- +string(5) "param" +string(3) "foo" +string(3) "bar"
\ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/017.phpt b/vendor/pimple/pimple/ext/pimple/tests/017.phpt new file mode 100644 index 00000000..bac23ce0 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/017.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test extend() with exception in service extension +--SKIPIF-- +<?php if (!extension_loaded("pimple")) print "skip"; ?> +--FILE-- +<?php + +$p = new Pimple\Container(); +$p[12] = function ($v) { return 'foo';}; + +$c = $p->extend(12, function ($w) { throw new BadMethodCallException; }); + +try { + $p[12]; + echo "Exception expected"; +} catch (BadMethodCallException $e) { } +--EXPECTF-- diff --git a/vendor/pimple/pimple/ext/pimple/tests/017_1.phpt b/vendor/pimple/pimple/ext/pimple/tests/017_1.phpt new file mode 100644 index 00000000..8f881d6e --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/017_1.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test extend() with exception in service factory +--SKIPIF-- +<?php if (!extension_loaded("pimple")) print "skip"; ?> +--FILE-- +<?php + +$p = new Pimple\Container(); +$p[12] = function ($v) { throw new BadMethodCallException; }; + +$c = $p->extend(12, function ($w) { return 'foobar'; }); + +try { + $p[12]; + echo "Exception expected"; +} catch (BadMethodCallException $e) { } +--EXPECTF-- diff --git a/vendor/pimple/pimple/ext/pimple/tests/018.phpt b/vendor/pimple/pimple/ext/pimple/tests/018.phpt new file mode 100644 index 00000000..27c12a14 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/018.phpt @@ -0,0 +1,23 @@ +--TEST-- +Test register() +--SKIPIF-- +<?php if (!extension_loaded("pimple")) print "skip"; ?> +--FILE-- +<?php + +class Foo implements Pimple\ServiceProviderInterface +{ + public function register(Pimple\Container $p) + { + var_dump($p); + } +} + +$p = new Pimple\Container(); +$p->register(new Foo, array(42 => 'bar')); + +var_dump($p[42]); +--EXPECTF-- +object(Pimple\Container)#1 (0) { +} +string(3) "bar"
\ No newline at end of file diff --git a/vendor/pimple/pimple/ext/pimple/tests/019.phpt b/vendor/pimple/pimple/ext/pimple/tests/019.phpt new file mode 100644 index 00000000..28a9aeca --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/019.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test register() returns static and is a fluent interface +--SKIPIF-- +<?php if (!extension_loaded("pimple")) print "skip"; ?> +--FILE-- +<?php + +class Foo implements Pimple\ServiceProviderInterface +{ + public function register(Pimple\Container $p) + { + } +} + +$p = new Pimple\Container(); +var_dump($p === $p->register(new Foo)); +--EXPECTF-- +bool(true) diff --git a/vendor/pimple/pimple/ext/pimple/tests/bench.phpb b/vendor/pimple/pimple/ext/pimple/tests/bench.phpb new file mode 100644 index 00000000..8f983e65 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/bench.phpb @@ -0,0 +1,51 @@ +<?php + +if (!class_exists('Pimple\Container')) { + require_once __DIR__ . '/../../../src/Pimple/Container.php'; +} else { + echo "pimple-c extension detected, using...\n\n"; +} + +$time = microtime(true); + +function foo() { } +$factory = function () { }; + +for ($i=0; $i<10000; $i++) { + +$p = new Pimple\Container; + +$p['foo'] = 'bar'; + +if (!isset($p[3])) { + $p[3] = $p['foo']; + $p[] = 'bar'; +} + +$p[2] = 42; + +if (isset($p[2])) { + unset($p[2]); +} + +$p[42] = $p['foo']; + +$p['cb'] = function($arg) { }; + +$p[] = $p['cb']; + +echo $p['cb']; +echo $p['cb']; +echo $p['cb']; + +//$p->factory($factory); + +$p['factory'] = $factory; + +echo $p['factory']; +echo $p['factory']; +echo $p['factory']; + +} + +echo microtime(true) - $time; diff --git a/vendor/pimple/pimple/ext/pimple/tests/bench_shared.phpb b/vendor/pimple/pimple/ext/pimple/tests/bench_shared.phpb new file mode 100644 index 00000000..aec541f0 --- /dev/null +++ b/vendor/pimple/pimple/ext/pimple/tests/bench_shared.phpb @@ -0,0 +1,25 @@ +<?php + +if (!class_exists('Pimple\Container')) { + require_once __DIR__ . '/../../../src/Pimple/Container.php'; +} else { + echo "pimple-c extension detected, using...\n\n"; +} + +$time = microtime(true); + + +$service = function ($arg) { return "I'm a service"; }; + +for ($i=0; $i<10000; $i++) { + +$p = new Pimple\Container; +$p['my_service'] = $service; + +$a = $p['my_service']; +$b = $p['my_service']; + +} + +echo microtime(true) - $time; +?> diff --git a/vendor/pimple/pimple/phpunit.xml.dist b/vendor/pimple/pimple/phpunit.xml.dist new file mode 100644 index 00000000..5c8d487f --- /dev/null +++ b/vendor/pimple/pimple/phpunit.xml.dist @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd" + backupGlobals="false" + colors="true" + bootstrap="vendor/autoload.php" +> + <testsuites> + <testsuite name="Pimple Test Suite"> + <directory>./src/Pimple/Tests</directory> + </testsuite> + </testsuites> +</phpunit> diff --git a/vendor/pimple/pimple/src/Pimple/Container.php b/vendor/pimple/pimple/src/Pimple/Container.php new file mode 100644 index 00000000..c976431e --- /dev/null +++ b/vendor/pimple/pimple/src/Pimple/Container.php @@ -0,0 +1,282 @@ +<?php + +/* + * This file is part of Pimple. + * + * Copyright (c) 2009 Fabien Potencier + * + * 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. + */ + +namespace Pimple; + +/** + * Container main class. + * + * @author Fabien Potencier + */ +class Container implements \ArrayAccess +{ + private $values = array(); + private $factories; + private $protected; + private $frozen = array(); + private $raw = array(); + private $keys = array(); + + /** + * Instantiate the container. + * + * Objects and parameters can be passed as argument to the constructor. + * + * @param array $values The parameters or objects. + */ + public function __construct(array $values = array()) + { + $this->factories = new \SplObjectStorage(); + $this->protected = new \SplObjectStorage(); + + foreach ($values as $key => $value) { + $this->offsetSet($key, $value); + } + } + + /** + * Sets a parameter or an object. + * + * Objects must be defined as Closures. + * + * Allowing any PHP callable leads to difficult to debug problems + * as function names (strings) are callable (creating a function with + * the same name as an existing parameter would break your container). + * + * @param string $id The unique identifier for the parameter or object + * @param mixed $value The value of the parameter or a closure to define an object + * + * @throws \RuntimeException Prevent override of a frozen service + */ + public function offsetSet($id, $value) + { + if (isset($this->frozen[$id])) { + throw new \RuntimeException(sprintf('Cannot override frozen service "%s".', $id)); + } + + $this->values[$id] = $value; + $this->keys[$id] = true; + } + + /** + * Gets a parameter or an object. + * + * @param string $id The unique identifier for the parameter or object + * + * @return mixed The value of the parameter or an object + * + * @throws \InvalidArgumentException if the identifier is not defined + */ + public function offsetGet($id) + { + if (!isset($this->keys[$id])) { + throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); + } + + if ( + isset($this->raw[$id]) + || !is_object($this->values[$id]) + || isset($this->protected[$this->values[$id]]) + || !method_exists($this->values[$id], '__invoke') + ) { + return $this->values[$id]; + } + + if (isset($this->factories[$this->values[$id]])) { + return $this->values[$id]($this); + } + + $raw = $this->values[$id]; + $val = $this->values[$id] = $raw($this); + $this->raw[$id] = $raw; + + $this->frozen[$id] = true; + + return $val; + } + + /** + * Checks if a parameter or an object is set. + * + * @param string $id The unique identifier for the parameter or object + * + * @return bool + */ + public function offsetExists($id) + { + return isset($this->keys[$id]); + } + + /** + * Unsets a parameter or an object. + * + * @param string $id The unique identifier for the parameter or object + */ + public function offsetUnset($id) + { + if (isset($this->keys[$id])) { + if (is_object($this->values[$id])) { + unset($this->factories[$this->values[$id]], $this->protected[$this->values[$id]]); + } + + unset($this->values[$id], $this->frozen[$id], $this->raw[$id], $this->keys[$id]); + } + } + + /** + * Marks a callable as being a factory service. + * + * @param callable $callable A service definition to be used as a factory + * + * @return callable The passed callable + * + * @throws \InvalidArgumentException Service definition has to be a closure of an invokable object + */ + public function factory($callable) + { + if (!method_exists($callable, '__invoke')) { + throw new \InvalidArgumentException('Service definition is not a Closure or invokable object.'); + } + + $this->factories->attach($callable); + + return $callable; + } + + /** + * Protects a callable from being interpreted as a service. + * + * This is useful when you want to store a callable as a parameter. + * + * @param callable $callable A callable to protect from being evaluated + * + * @return callable The passed callable + * + * @throws \InvalidArgumentException Service definition has to be a closure of an invokable object + */ + public function protect($callable) + { + if (!method_exists($callable, '__invoke')) { + throw new \InvalidArgumentException('Callable is not a Closure or invokable object.'); + } + + $this->protected->attach($callable); + + return $callable; + } + + /** + * Gets a parameter or the closure defining an object. + * + * @param string $id The unique identifier for the parameter or object + * + * @return mixed The value of the parameter or the closure defining an object + * + * @throws \InvalidArgumentException if the identifier is not defined + */ + public function raw($id) + { + if (!isset($this->keys[$id])) { + throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); + } + + if (isset($this->raw[$id])) { + return $this->raw[$id]; + } + + return $this->values[$id]; + } + + /** + * Extends an object definition. + * + * Useful when you want to extend an existing object definition, + * without necessarily loading that object. + * + * @param string $id The unique identifier for the object + * @param callable $callable A service definition to extend the original + * + * @return callable The wrapped callable + * + * @throws \InvalidArgumentException if the identifier is not defined or not a service definition + */ + public function extend($id, $callable) + { + if (!isset($this->keys[$id])) { + throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); + } + + if (!is_object($this->values[$id]) || !method_exists($this->values[$id], '__invoke')) { + throw new \InvalidArgumentException(sprintf('Identifier "%s" does not contain an object definition.', $id)); + } + + if (!is_object($callable) || !method_exists($callable, '__invoke')) { + throw new \InvalidArgumentException('Extension service definition is not a Closure or invokable object.'); + } + + $factory = $this->values[$id]; + + $extended = function ($c) use ($callable, $factory) { + return $callable($factory($c), $c); + }; + + if (isset($this->factories[$factory])) { + $this->factories->detach($factory); + $this->factories->attach($extended); + } + + return $this[$id] = $extended; + } + + /** + * Returns all defined value names. + * + * @return array An array of value names + */ + public function keys() + { + return array_keys($this->values); + } + + /** + * Registers a service provider. + * + * @param ServiceProviderInterface $provider A ServiceProviderInterface instance + * @param array $values An array of values that customizes the provider + * + * @return static + */ + public function register(ServiceProviderInterface $provider, array $values = array()) + { + $provider->register($this); + + foreach ($values as $key => $value) { + $this[$key] = $value; + } + + return $this; + } +} diff --git a/vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php b/vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php new file mode 100644 index 00000000..c004594b --- /dev/null +++ b/vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php @@ -0,0 +1,46 @@ +<?php + +/* + * This file is part of Pimple. + * + * Copyright (c) 2009 Fabien Potencier + * + * 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. + */ + +namespace Pimple; + +/** + * Pimple service provider interface. + * + * @author Fabien Potencier + * @author Dominik Zogg + */ +interface ServiceProviderInterface +{ + /** + * Registers services on the given container. + * + * This method should only be used to configure services and parameters. + * It should not get services. + * + * @param Container $pimple A container instance + */ + public function register(Container $pimple); +} diff --git a/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Invokable.php b/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Invokable.php new file mode 100644 index 00000000..aba453bf --- /dev/null +++ b/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Invokable.php @@ -0,0 +1,38 @@ +<?php + +/* + * This file is part of Pimple. + * + * Copyright (c) 2009 Fabien Potencier + * + * 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. + */ + +namespace Pimple\Tests\Fixtures; + +class Invokable +{ + public function __invoke($value = null) + { + $service = new Service(); + $service->value = $value; + + return $service; + } +} diff --git a/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php b/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php new file mode 100644 index 00000000..33cd4e54 --- /dev/null +++ b/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php @@ -0,0 +1,34 @@ +<?php + +/* + * This file is part of Pimple. + * + * Copyright (c) 2009 Fabien Potencier + * + * 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. + */ + +namespace Pimple\Tests\Fixtures; + +class NonInvokable +{ + public function __call($a, $b) + { + } +} diff --git a/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/PimpleServiceProvider.php b/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/PimpleServiceProvider.php new file mode 100644 index 00000000..0c910af7 --- /dev/null +++ b/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/PimpleServiceProvider.php @@ -0,0 +1,54 @@ +<?php + +/* + * This file is part of Pimple. + * + * Copyright (c) 2009 Fabien Potencier + * + * 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. + */ + +namespace Pimple\Tests\Fixtures; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; + +class PimpleServiceProvider implements ServiceProviderInterface +{ + /** + * Registers services on the given container. + * + * This method should only be used to configure services and parameters. + * It should not get services. + * + * @param Container $pimple An Container instance + */ + public function register(Container $pimple) + { + $pimple['param'] = 'value'; + + $pimple['service'] = function () { + return new Service(); + }; + + $pimple['factory'] = $pimple->factory(function () { + return new Service(); + }); + } +} diff --git a/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php b/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php new file mode 100644 index 00000000..d71b184d --- /dev/null +++ b/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php @@ -0,0 +1,35 @@ +<?php + +/* + * This file is part of Pimple. + * + * Copyright (c) 2009 Fabien Potencier + * + * 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. + */ + +namespace Pimple\Tests\Fixtures; + +/** + * @author Igor Wiedler <igor@wiedler.ch> + */ +class Service +{ + public $value; +} diff --git a/vendor/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php b/vendor/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php new file mode 100644 index 00000000..8e5c4c73 --- /dev/null +++ b/vendor/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php @@ -0,0 +1,76 @@ +<?php + +/* + * This file is part of Pimple. + * + * Copyright (c) 2009 Fabien Potencier + * + * 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. + */ + +namespace Pimple\Tests; + +use Pimple\Container; + +/** + * @author Dominik Zogg <dominik.zogg@gmail.com> + */ +class PimpleServiceProviderInterfaceTest extends \PHPUnit_Framework_TestCase +{ + public function testProvider() + { + $pimple = new Container(); + + $pimpleServiceProvider = new Fixtures\PimpleServiceProvider(); + $pimpleServiceProvider->register($pimple); + + $this->assertEquals('value', $pimple['param']); + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['service']); + + $serviceOne = $pimple['factory']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + + $serviceTwo = $pimple['factory']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + + $this->assertNotSame($serviceOne, $serviceTwo); + } + + public function testProviderWithRegisterMethod() + { + $pimple = new Container(); + + $pimple->register(new Fixtures\PimpleServiceProvider(), array( + 'anotherParameter' => 'anotherValue', + )); + + $this->assertEquals('value', $pimple['param']); + $this->assertEquals('anotherValue', $pimple['anotherParameter']); + + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['service']); + + $serviceOne = $pimple['factory']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + + $serviceTwo = $pimple['factory']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + + $this->assertNotSame($serviceOne, $serviceTwo); + } +} diff --git a/vendor/pimple/pimple/src/Pimple/Tests/PimpleTest.php b/vendor/pimple/pimple/src/Pimple/Tests/PimpleTest.php new file mode 100644 index 00000000..918f620d --- /dev/null +++ b/vendor/pimple/pimple/src/Pimple/Tests/PimpleTest.php @@ -0,0 +1,440 @@ +<?php + +/* + * This file is part of Pimple. + * + * Copyright (c) 2009 Fabien Potencier + * + * 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. + */ + +namespace Pimple\Tests; + +use Pimple\Container; + +/** + * @author Igor Wiedler <igor@wiedler.ch> + */ +class PimpleTest extends \PHPUnit_Framework_TestCase +{ + public function testWithString() + { + $pimple = new Container(); + $pimple['param'] = 'value'; + + $this->assertEquals('value', $pimple['param']); + } + + public function testWithClosure() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['service']); + } + + public function testServicesShouldBeDifferent() + { + $pimple = new Container(); + $pimple['service'] = $pimple->factory(function () { + return new Fixtures\Service(); + }); + + $serviceOne = $pimple['service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + + $serviceTwo = $pimple['service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + + $this->assertNotSame($serviceOne, $serviceTwo); + } + + public function testShouldPassContainerAsParameter() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + $pimple['container'] = function ($container) { + return $container; + }; + + $this->assertNotSame($pimple, $pimple['service']); + $this->assertSame($pimple, $pimple['container']); + } + + public function testIsset() + { + $pimple = new Container(); + $pimple['param'] = 'value'; + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + + $pimple['null'] = null; + + $this->assertTrue(isset($pimple['param'])); + $this->assertTrue(isset($pimple['service'])); + $this->assertTrue(isset($pimple['null'])); + $this->assertFalse(isset($pimple['non_existent'])); + } + + public function testConstructorInjection() + { + $params = array('param' => 'value'); + $pimple = new Container($params); + + $this->assertSame($params['param'], $pimple['param']); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Identifier "foo" is not defined. + */ + public function testOffsetGetValidatesKeyIsPresent() + { + $pimple = new Container(); + echo $pimple['foo']; + } + + public function testOffsetGetHonorsNullValues() + { + $pimple = new Container(); + $pimple['foo'] = null; + $this->assertNull($pimple['foo']); + } + + public function testUnset() + { + $pimple = new Container(); + $pimple['param'] = 'value'; + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + + unset($pimple['param'], $pimple['service']); + $this->assertFalse(isset($pimple['param'])); + $this->assertFalse(isset($pimple['service'])); + } + + /** + * @dataProvider serviceDefinitionProvider + */ + public function testShare($service) + { + $pimple = new Container(); + $pimple['shared_service'] = $service; + + $serviceOne = $pimple['shared_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + + $serviceTwo = $pimple['shared_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + + $this->assertSame($serviceOne, $serviceTwo); + } + + /** + * @dataProvider serviceDefinitionProvider + */ + public function testProtect($service) + { + $pimple = new Container(); + $pimple['protected'] = $pimple->protect($service); + + $this->assertSame($service, $pimple['protected']); + } + + public function testGlobalFunctionNameAsParameterValue() + { + $pimple = new Container(); + $pimple['global_function'] = 'strlen'; + $this->assertSame('strlen', $pimple['global_function']); + } + + public function testRaw() + { + $pimple = new Container(); + $pimple['service'] = $definition = $pimple->factory(function () { return 'foo'; }); + $this->assertSame($definition, $pimple->raw('service')); + } + + public function testRawHonorsNullValues() + { + $pimple = new Container(); + $pimple['foo'] = null; + $this->assertNull($pimple->raw('foo')); + } + + public function testFluentRegister() + { + $pimple = new Container(); + $this->assertSame($pimple, $pimple->register($this->getMock('Pimple\ServiceProviderInterface'))); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Identifier "foo" is not defined. + */ + public function testRawValidatesKeyIsPresent() + { + $pimple = new Container(); + $pimple->raw('foo'); + } + + /** + * @dataProvider serviceDefinitionProvider + */ + public function testExtend($service) + { + $pimple = new Container(); + $pimple['shared_service'] = function () { + return new Fixtures\Service(); + }; + $pimple['factory_service'] = $pimple->factory(function () { + return new Fixtures\Service(); + }); + + $pimple->extend('shared_service', $service); + $serviceOne = $pimple['shared_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + $serviceTwo = $pimple['shared_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + $this->assertSame($serviceOne, $serviceTwo); + $this->assertSame($serviceOne->value, $serviceTwo->value); + + $pimple->extend('factory_service', $service); + $serviceOne = $pimple['factory_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + $serviceTwo = $pimple['factory_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + $this->assertNotSame($serviceOne, $serviceTwo); + $this->assertNotSame($serviceOne->value, $serviceTwo->value); + } + + public function testExtendDoesNotLeakWithFactories() + { + if (extension_loaded('pimple')) { + $this->markTestSkipped('Pimple extension does not support this test'); + } + $pimple = new Container(); + + $pimple['foo'] = $pimple->factory(function () { return; }); + $pimple['foo'] = $pimple->extend('foo', function ($foo, $pimple) { return; }); + unset($pimple['foo']); + + $p = new \ReflectionProperty($pimple, 'values'); + $p->setAccessible(true); + $this->assertEmpty($p->getValue($pimple)); + + $p = new \ReflectionProperty($pimple, 'factories'); + $p->setAccessible(true); + $this->assertCount(0, $p->getValue($pimple)); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Identifier "foo" is not defined. + */ + public function testExtendValidatesKeyIsPresent() + { + $pimple = new Container(); + $pimple->extend('foo', function () {}); + } + + public function testKeys() + { + $pimple = new Container(); + $pimple['foo'] = 123; + $pimple['bar'] = 123; + + $this->assertEquals(array('foo', 'bar'), $pimple->keys()); + } + + /** @test */ + public function settingAnInvokableObjectShouldTreatItAsFactory() + { + $pimple = new Container(); + $pimple['invokable'] = new Fixtures\Invokable(); + + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['invokable']); + } + + /** @test */ + public function settingNonInvokableObjectShouldTreatItAsParameter() + { + $pimple = new Container(); + $pimple['non_invokable'] = new Fixtures\NonInvokable(); + + $this->assertInstanceOf('Pimple\Tests\Fixtures\NonInvokable', $pimple['non_invokable']); + } + + /** + * @dataProvider badServiceDefinitionProvider + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Service definition is not a Closure or invokable object. + */ + public function testFactoryFailsForInvalidServiceDefinitions($service) + { + $pimple = new Container(); + $pimple->factory($service); + } + + /** + * @dataProvider badServiceDefinitionProvider + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Callable is not a Closure or invokable object. + */ + public function testProtectFailsForInvalidServiceDefinitions($service) + { + $pimple = new Container(); + $pimple->protect($service); + } + + /** + * @dataProvider badServiceDefinitionProvider + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Identifier "foo" does not contain an object definition. + */ + public function testExtendFailsForKeysNotContainingServiceDefinitions($service) + { + $pimple = new Container(); + $pimple['foo'] = $service; + $pimple->extend('foo', function () {}); + } + + /** + * @dataProvider badServiceDefinitionProvider + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Extension service definition is not a Closure or invokable object. + */ + public function testExtendFailsForInvalidServiceDefinitions($service) + { + $pimple = new Container(); + $pimple['foo'] = function () {}; + $pimple->extend('foo', $service); + } + + /** + * Provider for invalid service definitions. + */ + public function badServiceDefinitionProvider() + { + return array( + array(123), + array(new Fixtures\NonInvokable()), + ); + } + + /** + * Provider for service definitions. + */ + public function serviceDefinitionProvider() + { + return array( + array(function ($value) { + $service = new Fixtures\Service(); + $service->value = $value; + + return $service; + }), + array(new Fixtures\Invokable()), + ); + } + + public function testDefiningNewServiceAfterFreeze() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $foo = $pimple['foo']; + + $pimple['bar'] = function () { + return 'bar'; + }; + $this->assertSame('bar', $pimple['bar']); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Cannot override frozen service "foo". + */ + public function testOverridingServiceAfterFreeze() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $foo = $pimple['foo']; + + $pimple['foo'] = function () { + return 'bar'; + }; + } + + public function testRemovingServiceAfterFreeze() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $foo = $pimple['foo']; + + unset($pimple['foo']); + $pimple['foo'] = function () { + return 'bar'; + }; + $this->assertSame('bar', $pimple['foo']); + } + + public function testExtendingService() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $pimple['foo'] = $pimple->extend('foo', function ($foo, $app) { + return "$foo.bar"; + }); + $pimple['foo'] = $pimple->extend('foo', function ($foo, $app) { + return "$foo.baz"; + }); + $this->assertSame('foo.bar.baz', $pimple['foo']); + } + + public function testExtendingServiceAfterOtherServiceFreeze() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $pimple['bar'] = function () { + return 'bar'; + }; + $foo = $pimple['foo']; + + $pimple['bar'] = $pimple->extend('bar', function ($bar, $app) { + return "$bar.baz"; + }); + $this->assertSame('bar.baz', $pimple['bar']); + } +} diff --git a/vendor/psr/log/.gitignore b/vendor/psr/log/.gitignore new file mode 100644 index 00000000..22d0d82f --- /dev/null +++ b/vendor/psr/log/.gitignore @@ -0,0 +1 @@ +vendor diff --git a/vendor/psr/log/LICENSE b/vendor/psr/log/LICENSE new file mode 100644 index 00000000..474c952b --- /dev/null +++ b/vendor/psr/log/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012 PHP Framework Interoperability Group + +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. diff --git a/vendor/psr/log/Psr/Log/AbstractLogger.php b/vendor/psr/log/Psr/Log/AbstractLogger.php new file mode 100644 index 00000000..90e721af --- /dev/null +++ b/vendor/psr/log/Psr/Log/AbstractLogger.php @@ -0,0 +1,128 @@ +<?php + +namespace Psr\Log; + +/** + * This is a simple Logger implementation that other Loggers can inherit from. + * + * It simply delegates all log-level-specific methods to the `log` method to + * reduce boilerplate code that a simple Logger that does the same thing with + * messages regardless of the error level has to implement. + */ +abstract class AbstractLogger implements LoggerInterface +{ + /** + * System is unusable. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function emergency($message, array $context = array()) + { + $this->log(LogLevel::EMERGENCY, $message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function alert($message, array $context = array()) + { + $this->log(LogLevel::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function critical($message, array $context = array()) + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function error($message, array $context = array()) + { + $this->log(LogLevel::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function warning($message, array $context = array()) + { + $this->log(LogLevel::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function notice($message, array $context = array()) + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function info($message, array $context = array()) + { + $this->log(LogLevel::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function debug($message, array $context = array()) + { + $this->log(LogLevel::DEBUG, $message, $context); + } +} diff --git a/vendor/psr/log/Psr/Log/InvalidArgumentException.php b/vendor/psr/log/Psr/Log/InvalidArgumentException.php new file mode 100644 index 00000000..67f852d1 --- /dev/null +++ b/vendor/psr/log/Psr/Log/InvalidArgumentException.php @@ -0,0 +1,7 @@ +<?php + +namespace Psr\Log; + +class InvalidArgumentException extends \InvalidArgumentException +{ +} diff --git a/vendor/psr/log/Psr/Log/LogLevel.php b/vendor/psr/log/Psr/Log/LogLevel.php new file mode 100644 index 00000000..9cebcace --- /dev/null +++ b/vendor/psr/log/Psr/Log/LogLevel.php @@ -0,0 +1,18 @@ +<?php + +namespace Psr\Log; + +/** + * Describes log levels. + */ +class LogLevel +{ + const EMERGENCY = 'emergency'; + const ALERT = 'alert'; + const CRITICAL = 'critical'; + const ERROR = 'error'; + const WARNING = 'warning'; + const NOTICE = 'notice'; + const INFO = 'info'; + const DEBUG = 'debug'; +} diff --git a/vendor/psr/log/Psr/Log/LoggerAwareInterface.php b/vendor/psr/log/Psr/Log/LoggerAwareInterface.php new file mode 100644 index 00000000..4d64f478 --- /dev/null +++ b/vendor/psr/log/Psr/Log/LoggerAwareInterface.php @@ -0,0 +1,18 @@ +<?php + +namespace Psr\Log; + +/** + * Describes a logger-aware instance. + */ +interface LoggerAwareInterface +{ + /** + * Sets a logger instance on the object. + * + * @param LoggerInterface $logger + * + * @return void + */ + public function setLogger(LoggerInterface $logger); +} diff --git a/vendor/psr/log/Psr/Log/LoggerAwareTrait.php b/vendor/psr/log/Psr/Log/LoggerAwareTrait.php new file mode 100644 index 00000000..639f79bd --- /dev/null +++ b/vendor/psr/log/Psr/Log/LoggerAwareTrait.php @@ -0,0 +1,26 @@ +<?php + +namespace Psr\Log; + +/** + * Basic Implementation of LoggerAwareInterface. + */ +trait LoggerAwareTrait +{ + /** + * The logger instance. + * + * @var LoggerInterface + */ + protected $logger; + + /** + * Sets a logger. + * + * @param LoggerInterface $logger + */ + public function setLogger(LoggerInterface $logger) + { + $this->logger = $logger; + } +} diff --git a/vendor/psr/log/Psr/Log/LoggerInterface.php b/vendor/psr/log/Psr/Log/LoggerInterface.php new file mode 100644 index 00000000..5ea72438 --- /dev/null +++ b/vendor/psr/log/Psr/Log/LoggerInterface.php @@ -0,0 +1,123 @@ +<?php + +namespace Psr\Log; + +/** + * Describes a logger instance. + * + * The message MUST be a string or object implementing __toString(). + * + * The message MAY contain placeholders in the form: {foo} where foo + * will be replaced by the context data in key "foo". + * + * The context array can contain arbitrary data. The only assumption that + * can be made by implementors is that if an Exception instance is given + * to produce a stack trace, it MUST be in a key named "exception". + * + * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md + * for the full interface specification. + */ +interface LoggerInterface +{ + /** + * System is unusable. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function emergency($message, array $context = array()); + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function alert($message, array $context = array()); + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function critical($message, array $context = array()); + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function error($message, array $context = array()); + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function warning($message, array $context = array()); + + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function notice($message, array $context = array()); + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function info($message, array $context = array()); + + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function debug($message, array $context = array()); + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * + * @return void + */ + public function log($level, $message, array $context = array()); +} diff --git a/vendor/psr/log/Psr/Log/LoggerTrait.php b/vendor/psr/log/Psr/Log/LoggerTrait.php new file mode 100644 index 00000000..867225df --- /dev/null +++ b/vendor/psr/log/Psr/Log/LoggerTrait.php @@ -0,0 +1,140 @@ +<?php + +namespace Psr\Log; + +/** + * This is a simple Logger trait that classes unable to extend AbstractLogger + * (because they extend another class, etc) can include. + * + * It simply delegates all log-level-specific methods to the `log` method to + * reduce boilerplate code that a simple Logger that does the same thing with + * messages regardless of the error level has to implement. + */ +trait LoggerTrait +{ + /** + * System is unusable. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function emergency($message, array $context = array()) + { + $this->log(LogLevel::EMERGENCY, $message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function alert($message, array $context = array()) + { + $this->log(LogLevel::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function critical($message, array $context = array()) + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function error($message, array $context = array()) + { + $this->log(LogLevel::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function warning($message, array $context = array()) + { + $this->log(LogLevel::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function notice($message, array $context = array()) + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function info($message, array $context = array()) + { + $this->log(LogLevel::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function debug($message, array $context = array()) + { + $this->log(LogLevel::DEBUG, $message, $context); + } + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * + * @return void + */ + abstract public function log($level, $message, array $context = array()); +} diff --git a/vendor/psr/log/Psr/Log/NullLogger.php b/vendor/psr/log/Psr/Log/NullLogger.php new file mode 100644 index 00000000..d8cd682c --- /dev/null +++ b/vendor/psr/log/Psr/Log/NullLogger.php @@ -0,0 +1,28 @@ +<?php + +namespace Psr\Log; + +/** + * This Logger can be used to avoid conditional log calls. + * + * Logging should always be optional, and if no logger is provided to your + * library creating a NullLogger instance to have something to throw logs at + * is a good way to avoid littering your code with `if ($this->logger) { }` + * blocks. + */ +class NullLogger extends AbstractLogger +{ + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * + * @return void + */ + public function log($level, $message, array $context = array()) + { + // noop + } +} diff --git a/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php b/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php new file mode 100644 index 00000000..a0391a52 --- /dev/null +++ b/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php @@ -0,0 +1,140 @@ +<?php + +namespace Psr\Log\Test; + +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; + +/** + * Provides a base test class for ensuring compliance with the LoggerInterface. + * + * Implementors can extend the class and implement abstract methods to run this + * as part of their test suite. + */ +abstract class LoggerInterfaceTest extends \PHPUnit_Framework_TestCase +{ + /** + * @return LoggerInterface + */ + abstract public function getLogger(); + + /** + * This must return the log messages in order. + * + * The simple formatting of the messages is: "<LOG LEVEL> <MESSAGE>". + * + * Example ->error('Foo') would yield "error Foo". + * + * @return string[] + */ + abstract public function getLogs(); + + public function testImplements() + { + $this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger()); + } + + /** + * @dataProvider provideLevelsAndMessages + */ + public function testLogsAtAllLevels($level, $message) + { + $logger = $this->getLogger(); + $logger->{$level}($message, array('user' => 'Bob')); + $logger->log($level, $message, array('user' => 'Bob')); + + $expected = array( + $level.' message of level '.$level.' with context: Bob', + $level.' message of level '.$level.' with context: Bob', + ); + $this->assertEquals($expected, $this->getLogs()); + } + + public function provideLevelsAndMessages() + { + return array( + LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'), + LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'), + LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'), + LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'), + LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'), + LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'), + LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'), + LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'), + ); + } + + /** + * @expectedException \Psr\Log\InvalidArgumentException + */ + public function testThrowsOnInvalidLevel() + { + $logger = $this->getLogger(); + $logger->log('invalid level', 'Foo'); + } + + public function testContextReplacement() + { + $logger = $this->getLogger(); + $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar')); + + $expected = array('info {Message {nothing} Bob Bar a}'); + $this->assertEquals($expected, $this->getLogs()); + } + + public function testObjectCastToString() + { + if (method_exists($this, 'createPartialMock')) { + $dummy = $this->createPartialMock('Psr\Log\Test\DummyTest', array('__toString')); + } else { + $dummy = $this->getMock('Psr\Log\Test\DummyTest', array('__toString')); + } + $dummy->expects($this->once()) + ->method('__toString') + ->will($this->returnValue('DUMMY')); + + $this->getLogger()->warning($dummy); + + $expected = array('warning DUMMY'); + $this->assertEquals($expected, $this->getLogs()); + } + + public function testContextCanContainAnything() + { + $context = array( + 'bool' => true, + 'null' => null, + 'string' => 'Foo', + 'int' => 0, + 'float' => 0.5, + 'nested' => array('with object' => new DummyTest), + 'object' => new \DateTime, + 'resource' => fopen('php://memory', 'r'), + ); + + $this->getLogger()->warning('Crazy context data', $context); + + $expected = array('warning Crazy context data'); + $this->assertEquals($expected, $this->getLogs()); + } + + public function testContextExceptionKeyCanBeExceptionOrOtherValues() + { + $logger = $this->getLogger(); + $logger->warning('Random message', array('exception' => 'oops')); + $logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail'))); + + $expected = array( + 'warning Random message', + 'critical Uncaught Exception!' + ); + $this->assertEquals($expected, $this->getLogs()); + } +} + +class DummyTest +{ + public function __toString() + { + } +} diff --git a/vendor/psr/log/README.md b/vendor/psr/log/README.md new file mode 100644 index 00000000..574bc1cb --- /dev/null +++ b/vendor/psr/log/README.md @@ -0,0 +1,45 @@ +PSR Log +======= + +This repository holds all interfaces/classes/traits related to +[PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). + +Note that this is not a logger of its own. It is merely an interface that +describes a logger. See the specification for more details. + +Usage +----- + +If you need a logger, you can use the interface like this: + +```php +<?php + +use Psr\Log\LoggerInterface; + +class Foo +{ + private $logger; + + public function __construct(LoggerInterface $logger = null) + { + $this->logger = $logger; + } + + public function doSomething() + { + if ($this->logger) { + $this->logger->info('Doing work'); + } + + // do something useful + } +} +``` + +You can then pick one of the implementations of the interface to get a logger. + +If you want to implement the interface, you can require this package and +implement `Psr\Log\LoggerInterface` in your code. Please read the +[specification text](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) +for details. diff --git a/vendor/psr/log/composer.json b/vendor/psr/log/composer.json new file mode 100644 index 00000000..87934d70 --- /dev/null +++ b/vendor/psr/log/composer.json @@ -0,0 +1,26 @@ +{ + "name": "psr/log", + "description": "Common interface for logging libraries", + "keywords": ["psr", "psr-3", "log"], + "homepage": "https://github.com/php-fig/log", + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/vendor/ramsey/array_column/.gitignore b/vendor/ramsey/array_column/.gitignore new file mode 100644 index 00000000..7e8f330b --- /dev/null +++ b/vendor/ramsey/array_column/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +phpunit.xml +composer.lock +build +vendor +*.phar diff --git a/vendor/ramsey/array_column/.travis.yml b/vendor/ramsey/array_column/.travis.yml new file mode 100644 index 00000000..ffc234e7 --- /dev/null +++ b/vendor/ramsey/array_column/.travis.yml @@ -0,0 +1,26 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + +before_script: + - travis_retry composer self-update + - travis_retry composer install --no-interaction --prefer-source + +script: + - mkdir -p build/logs + - ./vendor/bin/parallel-lint src tests + - ./vendor/bin/phpunit --coverage-text + - ./vendor/bin/phpcs src --standard=psr2 -sp + +after_script: + - php vendor/bin/coveralls + +matrix: + allow_failures: + - php: hhvm diff --git a/vendor/ramsey/array_column/CHANGELOG.md b/vendor/ramsey/array_column/CHANGELOG.md new file mode 100644 index 00000000..1dd01361 --- /dev/null +++ b/vendor/ramsey/array_column/CHANGELOG.md @@ -0,0 +1,37 @@ +# array_column() Changelog + +## 1.1.3 + +_Released 2015-03-20_ + +* Changing package name from "rhumsaa/array_column" to "ramsey/array_column" +* Updated Travis CI build config to test more versions of PHP +* Updated Travis CI config to lint and check PSR-2 coding standards +* Added project badges +* Added CHANGELOG + +## 1.1.2 + +_Released 2013-07-22_ + +* Remove unnecessary conditionals +* Whitespace changes + +## 1.1.1 + +_Released 2013-07-06_ + +* Documentation updates + +## 1.1.0 + +_Released 2013-07-06_ + +* Catching up array_column() library functionality and tests with PHP 5.5.0 +* Set Travis CI to run tests on PHP 5.5 + +## 1.0.0 + +_Released 2013-03-22_ + +* Initial release of userland library diff --git a/vendor/ramsey/array_column/LICENSE b/vendor/ramsey/array_column/LICENSE new file mode 100644 index 00000000..d099612d --- /dev/null +++ b/vendor/ramsey/array_column/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2013-2015 Ben Ramsey (http://benramsey.com) + +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. diff --git a/vendor/ramsey/array_column/README.md b/vendor/ramsey/array_column/README.md new file mode 100644 index 00000000..24b5b73f --- /dev/null +++ b/vendor/ramsey/array_column/README.md @@ -0,0 +1,91 @@ +# array_column() for PHP + +[](https://travis-ci.org/ramsey/array_column) +[](https://coveralls.io/r/ramsey/array_column) +[](https://packagist.org/packages/ramsey/array_column) +[](https://packagist.org/packages/ramsey/array_column) +[](https://packagist.org/packages/ramsey/array_column) +[](https://packagist.org/packages/ramsey/array_column) + +This simple library provides functionality for [`array_column()`](http://php.net/array_column) +to versions of PHP earlier than 5.5. It mimics the functionality of the built-in +function in every way. + + +## Usage + +``` +array array_column(array $input, mixed $columnKey[, mixed $indexKey]) +``` + +Given a multi-dimensional array of data, `array_column()` returns the values +from a single column of the input array, identified by the `$columnKey`. +Optionally, you may provide an `$indexKey` to index the values in the returned +array by the values from the `$indexKey` column in the input array. + +For example, using the following array of data, we tell `array_column()` to +return an array of just the last names, indexed by their record IDs. + +``` php +<?php +$records = array( + array( + 'id' => 2135, + 'first_name' => 'John', + 'last_name' => 'Doe' + ), + array( + 'id' => 3245, + 'first_name' => 'Sally', + 'last_name' => 'Smith' + ), + array( + 'id' => 5342, + 'first_name' => 'Jane', + 'last_name' => 'Jones' + ), + array( + 'id' => 5623, + 'first_name' => 'Peter', + 'last_name' => 'Doe' + ) +); + +$lastNames = array_column($records, 'last_name', 'id'); +``` + +If we call `print_r()` on `$lastNames`, you'll see a resulting array that looks +a bit like this: + +``` text +Array +( + [2135] => Doe + [3245] => Smith + [5342] => Jones + [5623] => Doe +) +``` + + +## Installation + +The easiest way to install this library is to use [Composer](https://getcomposer.org/): + +``` +php composer.phar require ramsey/array_column +``` + +Then, when you run `composer install`, everything will fall magically into place, +and the `array_column()` function will be available to your project, as long as +you are including Composer's autoloader. + +_However, you do not need Composer to use this library._ + +This library has no dependencies and should work on older versions of PHP. +Download the code and include `src/array_column.php` in your project, and all +should work fine. + +When you are ready to run your project on PHP 5.5, everything should +continue to run well without conflicts, even if this library remains included +in your project. diff --git a/vendor/ramsey/array_column/composer.json b/vendor/ramsey/array_column/composer.json new file mode 100644 index 00000000..2316b225 --- /dev/null +++ b/vendor/ramsey/array_column/composer.json @@ -0,0 +1,27 @@ +{ + "name": "ramsey/array_column", + "description": "Provides functionality for array_column() to projects using PHP earlier than version 5.5.", + "type": "library", + "keywords": ["array", "column", "array_column"], + "homepage": "https://github.com/ramsey/array_column", + "license": "MIT", + "authors": [ + { + "name": "Ben Ramsey", + "homepage": "http://benramsey.com" + } + ], + "support": { + "issues": "https://github.com/ramsey/array_column/issues", + "source": "https://github.com/ramsey/array_column" + }, + "require-dev": { + "phpunit/phpunit": "~4.5", + "squizlabs/php_codesniffer": "~2.2", + "jakub-onderka/php-parallel-lint": "0.8.*", + "satooshi/php-coveralls": "0.6.*" + }, + "autoload": { + "files": ["src/array_column.php"] + } +} diff --git a/vendor/ramsey/array_column/phpunit.xml.dist b/vendor/ramsey/array_column/phpunit.xml.dist new file mode 100644 index 00000000..61da3c17 --- /dev/null +++ b/vendor/ramsey/array_column/phpunit.xml.dist @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<phpunit bootstrap="./tests/bootstrap.php" colors="true"> + <testsuites> + <testsuite> + <directory>./tests</directory> + </testsuite> + </testsuites> + <filter> + <whitelist> + <directory suffix=".php">./src</directory> + </whitelist> + </filter> + <logging> + <log type="coverage-html" target="build/coverage" title="array_column()" + charset="UTF-8" yui="true" highlight="true" + lowUpperBound="35" highLowerBound="70"/> + <log type="coverage-clover" target="build/logs/clover.xml"/> + <log type="junit" target="build/logs/junit.xml" + logIncompleteSkipped="false"/> + </logging> +</phpunit> diff --git a/vendor/ramsey/array_column/src/array_column.php b/vendor/ramsey/array_column/src/array_column.php new file mode 100644 index 00000000..1e7fa99f --- /dev/null +++ b/vendor/ramsey/array_column/src/array_column.php @@ -0,0 +1,115 @@ +<?php +/** + * This file is part of the array_column library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey (http://benramsey.com) + * @license http://opensource.org/licenses/MIT MIT + */ + +if (!function_exists('array_column')) { + /** + * Returns the values from a single column of the input array, identified by + * the $columnKey. + * + * Optionally, you may provide an $indexKey to index the values in the returned + * array by the values from the $indexKey column in the input array. + * + * @param array $input A multi-dimensional array (record set) from which to pull + * a column of values. + * @param mixed $columnKey The column of values to return. This value may be the + * integer key of the column you wish to retrieve, or it + * may be the string key name for an associative array. + * @param mixed $indexKey (Optional.) The column to use as the index/keys for + * the returned array. This value may be the integer key + * of the column, or it may be the string key name. + * @return array + */ + function array_column($input = null, $columnKey = null, $indexKey = null) + { + // Using func_get_args() in order to check for proper number of + // parameters and trigger errors exactly as the built-in array_column() + // does in PHP 5.5. + $argc = func_num_args(); + $params = func_get_args(); + + if ($argc < 2) { + trigger_error("array_column() expects at least 2 parameters, {$argc} given", E_USER_WARNING); + return null; + } + + if (!is_array($params[0])) { + trigger_error( + 'array_column() expects parameter 1 to be array, ' . gettype($params[0]) . ' given', + E_USER_WARNING + ); + return null; + } + + if (!is_int($params[1]) + && !is_float($params[1]) + && !is_string($params[1]) + && $params[1] !== null + && !(is_object($params[1]) && method_exists($params[1], '__toString')) + ) { + trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING); + return false; + } + + if (isset($params[2]) + && !is_int($params[2]) + && !is_float($params[2]) + && !is_string($params[2]) + && !(is_object($params[2]) && method_exists($params[2], '__toString')) + ) { + trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING); + return false; + } + + $paramsInput = $params[0]; + $paramsColumnKey = ($params[1] !== null) ? (string) $params[1] : null; + + $paramsIndexKey = null; + if (isset($params[2])) { + if (is_float($params[2]) || is_int($params[2])) { + $paramsIndexKey = (int) $params[2]; + } else { + $paramsIndexKey = (string) $params[2]; + } + } + + $resultArray = array(); + + foreach ($paramsInput as $row) { + $key = $value = null; + $keySet = $valueSet = false; + + if ($paramsIndexKey !== null && array_key_exists($paramsIndexKey, $row)) { + $keySet = true; + $key = (string) $row[$paramsIndexKey]; + } + + if ($paramsColumnKey === null) { + $valueSet = true; + $value = $row; + } elseif (is_array($row) && array_key_exists($paramsColumnKey, $row)) { + $valueSet = true; + $value = $row[$paramsColumnKey]; + } + + if ($valueSet) { + if ($keySet) { + $resultArray[$key] = $value; + } else { + $resultArray[] = $value; + } + } + + } + + return $resultArray; + } + +} diff --git a/vendor/ramsey/array_column/tests/ArrayColumnTest.php b/vendor/ramsey/array_column/tests/ArrayColumnTest.php new file mode 100644 index 00000000..597b1dea --- /dev/null +++ b/vendor/ramsey/array_column/tests/ArrayColumnTest.php @@ -0,0 +1,422 @@ +<?php +class ArrayColumnTest extends \PHPUnit_Framework_TestCase +{ + protected $recordSet; + protected $multiDataTypes; + protected $numericColumns; + protected $mismatchedColumns; + + protected function setUp() + { + $this->recordSet = array( + array( + 'id' => 1, + 'first_name' => 'John', + 'last_name' => 'Doe' + ), + array( + 'id' => 2, + 'first_name' => 'Sally', + 'last_name' => 'Smith' + ), + array( + 'id' => 3, + 'first_name' => 'Jane', + 'last_name' => 'Jones' + ), + ); + + $fh = fopen(__FILE__, 'r', true); + + $this->multiDataTypes = array( + array( + 'id' => 1, + 'value' => new stdClass + ), + array( + 'id' => 2, + 'value' => 34.2345 + ), + array( + 'id' => 3, + 'value' => true + ), + array( + 'id' => 4, + 'value' => false + ), + array( + 'id' => 5, + 'value' => null + ), + array( + 'id' => 6, + 'value' => 1234 + ), + array( + 'id' => 7, + 'value' => 'Foo' + ), + array( + 'id' => 8, + 'value' => $fh + ), + ); + + $this->numericColumns = array( + array('aaa', '111'), + array('bbb', '222'), + array('ccc', '333', -1 => 'ddd'), + ); + + $this->mismatchedColumns = array( + array('a' => 'foo', 'b' => 'bar', 'e' => 'bbb'), + array('a' => 'baz', 'c' => 'qux', 'd' => 'aaa'), + array('a' => 'eee', 'b' => 'fff', 'e' => 'ggg'), + ); + } + + public function testFirstNameColumnFromRecordset() + { + $expected = array('John', 'Sally', 'Jane'); + $this->assertEquals($expected, array_column($this->recordSet, 'first_name')); + } + + public function testIdColumnFromRecordset() + { + $expected = array(1, 2, 3); + $this->assertEquals($expected, array_column($this->recordSet, 'id')); + } + + public function testLastNameColumnKeyedByIdColumnFromRecordset() + { + $expected = array(1 => 'Doe', 2 => 'Smith', 3 => 'Jones'); + $this->assertEquals($expected, array_column($this->recordSet, 'last_name', 'id')); + } + + public function testLastNameColumnKeyedByFirstNameColumnFromRecordset() + { + $expected = array('John' => 'Doe', 'Sally' => 'Smith', 'Jane' => 'Jones'); + $this->assertEquals($expected, array_column($this->recordSet, 'last_name', 'first_name')); + } + + public function testValueColumnWithMultipleDataTypes() + { + $expected = array(); + foreach ($this->multiDataTypes as $row) { + $expected[] = $row['value']; + } + $this->assertEquals($expected, array_column($this->multiDataTypes, 'value')); + } + + public function testValueColumnKeyedByIdWithMultipleDataTypes() + { + $expected = array(); + foreach ($this->multiDataTypes as $row) { + $expected[$row['id']] = $row['value']; + } + $this->assertEquals($expected, array_column($this->multiDataTypes, 'value', 'id')); + } + + public function testNumericColumnKeys1() + { + $expected = array('111', '222', '333'); + $this->assertEquals($expected, array_column($this->numericColumns, 1)); + } + + public function testNumericColumnKeys2() + { + $expected = array('aaa' => '111', 'bbb' => '222', 'ccc' => '333'); + $this->assertEquals($expected, array_column($this->numericColumns, 1, 0)); + } + + public function testNumericColumnKeys3() + { + $expected = array('aaa' => '111', 'bbb' => '222', 'ccc' => '333'); + $this->assertEquals($expected, array_column($this->numericColumns, 1, 0.123)); + } + + public function testNumericColumnKeys4() + { + $expected = array(0 => '111', 1 => '222', 'ddd' => '333'); + $this->assertEquals($expected, array_column($this->numericColumns, 1, -1)); + } + + public function testFailureToFindColumn1() + { + $this->assertEquals(array(), array_column($this->numericColumns, 2)); + } + + public function testFailureToFindColumn2() + { + $this->assertEquals(array(), array_column($this->numericColumns, 'foo')); + } + + public function testFailureToFindColumn3() + { + $expected = array('aaa', 'bbb', 'ccc'); + $this->assertEquals($expected, array_column($this->numericColumns, 0, 'foo')); + } + + public function testFailureToFindColumn4() + { + $this->assertEquals(array(), array_column($this->numericColumns, 3.14)); + } + + public function testSingleDimensionalArray() + { + $singleDimension = array('foo', 'bar', 'baz'); + $this->assertEquals(array(), array_column($singleDimension, 1)); + } + + public function testMismatchedColumns1() + { + $expected = array('qux'); + $this->assertEquals($expected, array_column($this->mismatchedColumns, 'c')); + } + + public function testMismatchedColumns2() + { + $expected = array('baz' => 'qux'); + $this->assertEquals($expected, array_column($this->mismatchedColumns, 'c', 'a')); + } + + public function testMismatchedColumns3() + { + $expected = array('foo', 'aaa' => 'baz', 'eee'); + $this->assertEquals($expected, array_column($this->mismatchedColumns, 'a', 'd')); + } + + public function testMismatchedColumns4() + { + $expected = array('bbb' => 'foo', 'baz', 'ggg' => 'eee'); + $this->assertEquals($expected, array_column($this->mismatchedColumns, 'a', 'e')); + } + + public function testMismatchedColumns5() + { + $expected = array('bar', 'fff'); + $this->assertEquals($expected, array_column($this->mismatchedColumns, 'b')); + } + + public function testMismatchedColumns6() + { + $expected = array('foo' => 'bar', 'eee' => 'fff'); + $this->assertEquals($expected, array_column($this->mismatchedColumns, 'b', 'a')); + } + + public function testObjectConvertedToString() + { + $f = new Foo(); + $b = new Bar(); + $this->assertEquals(array('Doe', 'Smith', 'Jones'), array_column($this->recordSet, $f)); + $this->assertEquals(array('John' => 'Doe', 'Sally' => 'Smith', 'Jane' => 'Jones'), array_column($this->recordSet, $f, $b)); + } + + /** + * @expectedException PHPUnit_Framework_Error_Warning + * @expectedExceptionMessage array_column() expects at least 2 parameters, 0 given + */ + public function testFunctionWithZeroArgs() + { + $foo = array_column(); + } + + public function testFunctionWithZeroArgsReturnValue() + { + $foo = @array_column(); + $this->assertNull($foo); + } + + /** + * @expectedException PHPUnit_Framework_Error_Warning + * @expectedExceptionMessage array_column() expects at least 2 parameters, 1 given + */ + public function testFunctionWithOneArg() + { + $foo = array_column(array()); + } + + public function testFunctionWithOneArgReturnValue() + { + $foo = @array_column(array()); + $this->assertNull($foo); + } + + /** + * @expectedException PHPUnit_Framework_Error_Warning + * @expectedExceptionMessage array_column() expects parameter 1 to be array, string given + */ + public function testFunctionWithStringAsFirstArg() + { + $foo = array_column('foo', 0); + } + + public function testFunctionWithStringAsFirstArgReturnValue() + { + $foo = @array_column('foo', 0); + $this->assertNull($foo); + } + + /** + * @expectedException PHPUnit_Framework_Error_Warning + * @expectedExceptionMessage array_column() expects parameter 1 to be array, integer given + */ + public function testFunctionWithIntAsFirstArg() + { + $foo = array_column(1, 'foo'); + } + + public function testFunctionWithIntAsFirstArgReturnValue() + { + $foo = @array_column(1, 'foo'); + $this->assertNull($foo); + } + + /** + * @expectedException PHPUnit_Framework_Error_Warning + * @expectedExceptionMessage array_column(): The column key should be either a string or an integer + */ + public function testFunctionWithColumnKeyAsBool() + { + $foo = array_column(array(), true); + } + + public function testFunctionWithColumnKeyAsBoolReturnValue() + { + $foo = @array_column(array(), true); + $this->assertFalse($foo); + } + + /** + * @expectedException PHPUnit_Framework_Error_Warning + * @expectedExceptionMessage array_column(): The column key should be either a string or an integer + */ + public function testFunctionWithColumnKeyAsArray() + { + $foo = array_column(array(), array()); + } + + public function testFunctionWithColumnKeyAsArrayReturnValue() + { + $foo = @array_column(array(), array()); + $this->assertFalse($foo); + } + + /** + * @expectedException PHPUnit_Framework_Error_Warning + * @expectedExceptionMessage array_column(): The index key should be either a string or an integer + */ + public function testFunctionWithIndexKeyAsBool() + { + $foo = array_column(array(), 'foo', true); + } + + public function testFunctionWithIndexKeyAsBoolReturnValue() + { + $foo = @array_column(array(), 'foo', true); + $this->assertFalse($foo); + } + + /** + * @expectedException PHPUnit_Framework_Error_Warning + * @expectedExceptionMessage array_column(): The index key should be either a string or an integer + */ + public function testFunctionWithIndexKeyAsArray() + { + $foo = array_column(array(), 'foo', array()); + } + + public function testFunctionWithIndexKeyAsArrayReturnValue() + { + $foo = @array_column(array(), 'foo', array()); + $this->assertFalse($foo); + } + + /** + * @link https://bugs.php.net/bug.php?id=64493 + */ + public function testBugRequest64493() + { + // Array from Bug Request #64493 test script + $rows = array( + 456 => array('id' => '3', 'title' => 'Foo', 'date' => '2013-03-25'), + 457 => array('id' => '5', 'title' => 'Bar', 'date' => '2012-05-20'), + ); + + // pass null as second parameter to get back all columns indexed by third parameter + $expected1 = array( + 3 => array('id' => '3', 'title' => 'Foo', 'date' => '2013-03-25'), + 5 => array('id' => '5', 'title' => 'Bar', 'date' => '2012-05-20'), + ); + $this->assertEquals($expected1, array_column($rows, null, 'id')); + + // pass null as second parameter and bogus third param to get back zero-indexed array of all columns + $expected2 = array( + array('id' => '3', 'title' => 'Foo', 'date' => '2013-03-25'), + array('id' => '5', 'title' => 'Bar', 'date' => '2012-05-20'), + ); + $this->assertEquals($expected2, array_column($rows, null, 'foo')); + + // pass null as second parameter and no third param to get back array_values(input) (same as $expected2) + $this->assertEquals($expected2, array_column($rows, null)); + } + + public function testObjectCast() + { + $columnKey = new ColumnKeyClass(); + $indexKey = new IndexKeyClass(); + $value = new ValueClass(); + + $records = array( + array( + 'id' => $value, + 'first_name' => 'John', + 'last_name' => 'XXX' + ), + array( + 'id' => 3245, + 'first_name' => 'Sally', + 'last_name' => 'Smith' + ), + ); + + $expected = array( + 2135 => 'John', + 3245 => 'Sally', + ); + + $this->assertEquals($expected, array_column($records, $columnKey, $indexKey)); + } +} + +class Foo +{ + public function __toString() + { + return 'last_name'; + } +} + +class Bar +{ + public function __toString() + { + return 'first_name'; + } +} + +class ColumnKeyClass +{ + function __toString() { return 'first_name'; } +} + +class IndexKeyClass +{ + function __toString() { return 'id'; } +} + +class ValueClass +{ + function __toString() { return '2135'; } +} diff --git a/vendor/ramsey/array_column/tests/bootstrap.php b/vendor/ramsey/array_column/tests/bootstrap.php new file mode 100644 index 00000000..97eabbac --- /dev/null +++ b/vendor/ramsey/array_column/tests/bootstrap.php @@ -0,0 +1,5 @@ +<?php + +error_reporting(E_ALL | E_STRICT); + +require_once 'src/array_column.php'; diff --git a/vendor/swiftmailer/swiftmailer/.gitattributes b/vendor/swiftmailer/swiftmailer/.gitattributes new file mode 100644 index 00000000..33b8efdb --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/.gitattributes @@ -0,0 +1,9 @@ +*.crt -crlf +*.key -crlf +*.srl -crlf +*.pub -crlf +*.priv -crlf +*.txt -crlf + +# ignore /notes in the git-generated distributed .zip archive +/notes export-ignore diff --git a/vendor/swiftmailer/swiftmailer/.gitignore b/vendor/swiftmailer/swiftmailer/.gitignore new file mode 100644 index 00000000..9a23ffe6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/.gitignore @@ -0,0 +1,4 @@ +/tests/acceptance.conf.php +/tests/smoke.conf.php +/build/* +/vendor/ diff --git a/vendor/swiftmailer/swiftmailer/.travis.yml b/vendor/swiftmailer/swiftmailer/.travis.yml new file mode 100644 index 00000000..0adf8d54 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/.travis.yml @@ -0,0 +1,27 @@ +language: php + +sudo: false + +before_script: + - cp tests/acceptance.conf.php.default tests/acceptance.conf.php + - cp tests/smoke.conf.php.default tests/smoke.conf.php + - composer self-update + - composer update --no-interaction --prefer-source + - gem install mime-types -v 2.99.1 + - gem install mailcatcher + - mailcatcher --smtp-port 4456 + +script: + - phpunit --verbose + +matrix: + include: + - php: 5.3 + - php: 5.4 + - php: 5.5 + - php: 5.6 + - php: 7.0 + - php: hhvm + allow_failures: + - php: hhvm + fast_finish: true diff --git a/vendor/swiftmailer/swiftmailer/CHANGES b/vendor/swiftmailer/swiftmailer/CHANGES new file mode 100644 index 00000000..282e89e1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/CHANGES @@ -0,0 +1,241 @@ +Changelog +========= + +5.4.5 (2016-XX-XX) +------------------ + + * fixed CVE-2016-10033 and CVE-2016-10045 + * deprecated the mail transport + +5.4.4 (2016-11-23) +------------------ + + * reverted escaping command-line args to mail (PHP mail() function already does it) + +5.4.3 (2016-07-08) +------------------ + + * fixed SimpleHeaderSet::has()/get() when the 0 index is removed + * removed the need to have mcrypt installed + * fixed broken MIME header encoding with quotes/colons and non-ascii chars + * allowed mail transport send for messages without To header + * fixed PHP 7 support + +5.4.2 (2016-05-01) +------------------ + + * fixed support for IPv6 sockets + * added auto-retry when sending messages from the memory spool + * fixed consecutive read calls in Swift_ByteStream_FileByteStream + * added support for iso-8859-15 encoding + * fixed PHP mail extra params on missing reversePath + * added methods to set custom stream context options + * fixed charset changes in QpContentEncoderProxy + * added return-path header to the ignoredHeaders list of DKIMSigner + * fixed crlf for subject using mail + * fixed add soft line break only when necessary + * fixed escaping command-line args to mail + +5.4.1 (2015-06-06) +------------------ + + * made Swiftmailer exceptions confirm to PHP base exception constructor signature + * fixed MAIL FROM & RCPT TO headers to be RFC compliant + +5.4.0 (2015-03-14) +------------------ + + * added the possibility to add extra certs to PKCS#7 signature + * fix base64 encoding with streams + * added a new RESULT_SPOOLED status for SpoolTransport + * fixed getBody() on attachments when called more than once + * removed dots from generated filenames in filespool + +5.3.1 (2014-12-05) +------------------ + + * fixed cloning of messages with attachments + +5.3.0 (2014-10-04) +------------------ + + * fixed cloning when using signers + * reverted removal of Swift_Encoding + * drop support for PHP 5.2.x + +5.2.2 (2014-09-20) +------------------ + + * fixed Japanese support + * fixed the memory spool when the message changes when in the pool + * added support for cloning messages + * fixed PHP warning in the redirect plugin + * changed the way to and cc-ed email are sent to only use one transaction + +5.2.1 (2014-06-13) +------------------ + + * SECURITY FIX: fixed CLI escaping when using sendmail as a transport + + Prior to 5.2.1, the sendmail transport (Swift_Transport_SendmailTransport) + was vulnerable to an arbitrary shell execution if the "From" header came + from a non-trusted source and no "Return-Path" is configured. + + * fixed parameter in DKIMSigner + * fixed compatibility with PHP < 5.4 + +5.2.0 (2014-05-08) +------------------ + + * fixed Swift_ByteStream_FileByteStream::read() to match to the specification + * fixed from-charset and to-charset arguments in mbstring_convert_encoding() usages + * fixed infinite loop in StreamBuffer + * fixed NullTransport to return the number of ignored emails instead of 0 + * Use phpunit and mockery for unit testing (realityking) + +5.1.0 (2014-03-18) +------------------ + + * fixed data writing to stream when sending large messages + * added support for libopendkim (https://github.com/xdecock/php-opendkim) + * merged SignedMessage and Message + * added Gmail XOAuth2 authentication + * updated the list of known mime types + * added NTLM authentication + +5.0.3 (2013-12-03) +------------------ + + * fixed double-dot bug + * fixed DKIM signer + +5.0.2 (2013-08-30) +------------------ + + * handled correct exception type while reading IoBuffer output + +5.0.1 (2013-06-17) +------------------ + + * changed the spool to only start the transport when a mail has to be sent + * fixed compatibility with PHP 5.2 + * fixed LICENSE file + +5.0.0 (2013-04-30) +------------------ + + * changed the license from LGPL to MIT + +4.3.1 (2013-04-11) +------------------ + + * removed usage of the native QP encoder when the charset is not UTF-8 + * fixed usage of uniqid to avoid collisions + * made a performance improvement when tokenizing large headers + * fixed usage of the PHP native QP encoder on PHP 5.4.7+ + +4.3.0 (2013-01-08) +------------------ + + * made the temporary directory configurable via the TMPDIR env variable + * added S/MIME signer and encryption support + +4.2.2 (2012-10-25) +------------------ + + * added the possibility to throttle messages per second in ThrottlerPlugin (mostly for Amazon SES) + * switched mime.qpcontentencoder to automatically use the PHP native encoder on PHP 5.4.7+ + * allowed specifying a whitelist with regular expressions in RedirectingPlugin + +4.2.1 (2012-07-13) +------------------ + + * changed the coding standards to PSR-1/2 + * fixed issue with autoloading + * added NativeQpContentEncoder to enhance performance (for PHP 5.3+) + +4.2.0 (2012-06-29) +------------------ + + * added documentation about how to use the Japanese support introduced in 4.1.8 + * added a way to override the default configuration in a lazy way + * changed the PEAR init script to lazy-load the initialization + * fixed a bug when calling Swift_Preferences before anything else (regression introduced in 4.1.8) + +4.1.8 (2012-06-17) +------------------ + + * added Japanese iso-2022-jp support + * changed the init script to lazy-load the initialization + * fixed docblocks (@id) which caused some problems with libraries parsing the dobclocks + * fixed Swift_Mime_Headers_IdentificationHeader::setId() when passed an array of ids + * fixed encoding of email addresses in headers + * added replacements setter to the Decorator plugin + +4.1.7 (2012-04-26) +------------------ + + * fixed QpEncoder safeMapShareId property + +4.1.6 (2012-03-23) +------------------ + + * reduced the size of serialized Messages + +4.1.5 (2012-01-04) +------------------ + + * enforced Swift_Spool::queueMessage() to return a Boolean + * made an optimization to the memory spool: start the transport only when required + * prevented stream_socket_client() from generating an error and throw a Swift_TransportException instead + * fixed a PHP warning when calling to mail() when safe_mode is off + * many doc tweaks + +4.1.4 (2011-12-16) +------------------ + + * added a memory spool (Swift_MemorySpool) + * fixed too many opened files when sending emails with attachments + +4.1.3 (2011-10-27) +------------------ + + * added STARTTLS support + * added missing @return tags on fluent methods + * added a MessageLogger plugin that logs all sent messages + * added composer.json + +4.1.2 (2011-09-13) +------------------ + + * fixed wrong detection of magic_quotes_runtime + * fixed fatal errors when no To or Subject header has been set + * fixed charset on parameter header continuations + * added documentation about how to install Swiftmailer from the PEAR channel + * fixed various typos and markup problem in the documentation + * fixed warning when cache directory does not exist + * fixed "slashes are escaped" bug + * changed require_once() to require() in autoload + +4.1.1 (2011-07-04) +------------------ + + * added missing file in PEAR package + +4.1.0 (2011-06-30) +------------------ + + * documentation has been converted to ReST + +4.1.0 RC1 (2011-06-17) +---------------------- + +New features: + + * changed the Decorator Plugin to allow replacements in all headers + * added Swift_Mime_Grammar and Swift_Validate to validate an email address + * modified the autoloader to lazy-initialize Swiftmailer + * removed Swift_Mailer::batchSend() + * added NullTransport + * added new plugins: RedirectingPlugin and ImpersonatePlugin + * added a way to send messages asynchronously (Spool) diff --git a/vendor/swiftmailer/swiftmailer/LICENSE b/vendor/swiftmailer/swiftmailer/LICENSE new file mode 100644 index 00000000..485f1d6d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2013-2016 Fabien Potencier + +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. diff --git a/vendor/swiftmailer/swiftmailer/README b/vendor/swiftmailer/swiftmailer/README new file mode 100644 index 00000000..c3bf0318 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/README @@ -0,0 +1,15 @@ +Swift Mailer +------------ + +Swift Mailer is a component based mailing solution for PHP 5. +It is released under the MIT license. + +Homepage: http://swiftmailer.org +Documentation: http://swiftmailer.org/docs +Bugs: https://github.com/swiftmailer/swiftmailer/issues +Repository: https://github.com/swiftmailer/swiftmailer + +Swift Mailer is highly object-oriented by design and lends itself +to use in complex web application with a great deal of flexibility. + +For full details on usage, see the documentation. diff --git a/vendor/swiftmailer/swiftmailer/VERSION b/vendor/swiftmailer/swiftmailer/VERSION new file mode 100644 index 00000000..e130e22a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/VERSION @@ -0,0 +1 @@ +Swift-5.4.5-DEV diff --git a/vendor/swiftmailer/swiftmailer/composer.json b/vendor/swiftmailer/swiftmailer/composer.json new file mode 100644 index 00000000..8335f58a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/composer.json @@ -0,0 +1,37 @@ +{ + "name": "swiftmailer/swiftmailer", + "type": "library", + "description": "Swiftmailer, free feature-rich PHP mailer", + "keywords": ["mail","mailer","email"], + "homepage": "http://swiftmailer.org", + "license": "MIT", + "authors": [ + { + "name": "Chris Corbyn" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "mockery/mockery": "~0.9.1", + "symfony/phpunit-bridge": "~3.2" + }, + "autoload": { + "files": ["lib/swift_required.php"] + }, + "autoload-dev": { + "psr-0": { + "Swift_": "tests/unit" + } + }, + "extra": { + "branch-alias": { + "dev-master": "5.4-dev" + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/doc/headers.rst b/vendor/swiftmailer/swiftmailer/doc/headers.rst new file mode 100644 index 00000000..6aec23ff --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/headers.rst @@ -0,0 +1,742 @@ +Message Headers +=============== + +Sometimes you'll want to add your own headers to a message or modify/remove +headers that are already present. You work with the message's HeaderSet to do +this. + +Header Basics +------------- + +All MIME entities in Swift Mailer -- including the message itself -- +store their headers in a single object called a HeaderSet. This HeaderSet is +retrieved with the ``getHeaders()`` method. + +As mentioned in the previous chapter, everything that forms a part of a message +in Swift Mailer is a MIME entity that is represented by an instance of +``Swift_Mime_MimeEntity``. This includes -- most notably -- the message object +itself, attachments, MIME parts and embedded images. Each of these MIME entities +consists of a body and a set of headers that describe the body. + +For all of the "standard" headers in these MIME entities, such as the +``Content-Type``, there are named methods for working with them, such as +``setContentType()`` and ``getContentType()``. This is because headers are a +moderately complex area of the library. Each header has a slightly different +required structure that it must meet in order to comply with the standards that +govern email (and that are checked by spam blockers etc). + +You fetch the HeaderSet from a MIME entity like so: + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + // Fetch the HeaderSet from a Message object + $headers = $message->getHeaders(); + + $attachment = Swift_Attachment::fromPath('document.pdf'); + + // Fetch the HeaderSet from an attachment object + $headers = $attachment->getHeaders(); + +The job of the HeaderSet is to contain and manage instances of Header objects. +Depending upon the MIME entity the HeaderSet came from, the contents of the +HeaderSet will be different, since an attachment for example has a different +set of headers to those in a message. + +You can find out what the HeaderSet contains with a quick loop, dumping out +the names of the headers: + +.. code-block:: php + + foreach ($headers->getAll() as $header) { + printf("%s<br />\n", $header->getFieldName()); + } + + /* + Content-Transfer-Encoding + Content-Type + MIME-Version + Date + Message-ID + From + Subject + To + */ + +You can also dump out the rendered HeaderSet by calling its ``toString()`` +method: + +.. code-block:: php + + echo $headers->toString(); + + /* + Message-ID: <1234869991.499a9ee7f1d5e@swift.generated> + Date: Tue, 17 Feb 2009 22:26:31 +1100 + Subject: Awesome subject! + From: sender@example.org + To: recipient@example.org + MIME-Version: 1.0 + Content-Type: text/plain; charset=utf-8 + Content-Transfer-Encoding: quoted-printable + */ + +Where the complexity comes in is when you want to modify an existing header. +This complexity comes from the fact that each header can be of a slightly +different type (such as a Date header, or a header that contains email +addresses, or a header that has key-value parameters on it!). Each header in the +HeaderSet is an instance of ``Swift_Mime_Header``. They all have common +functionality, but knowing exactly what type of header you're working with will +allow you a little more control. + +You can determine the type of header by comparing the return value of its +``getFieldType()`` method with the constants ``TYPE_TEXT``, +``TYPE_PARAMETERIZED``, ``TYPE_DATE``, ``TYPE_MAILBOX``, ``TYPE_ID`` and +``TYPE_PATH`` which are defined in ``Swift_Mime_Header``. + + +.. code-block:: php + + foreach ($headers->getAll() as $header) { + switch ($header->getFieldType()) { + case Swift_Mime_Header::TYPE_TEXT: $type = 'text'; + break; + case Swift_Mime_Header::TYPE_PARAMETERIZED: $type = 'parameterized'; + break; + case Swift_Mime_Header::TYPE_MAILBOX: $type = 'mailbox'; + break; + case Swift_Mime_Header::TYPE_DATE: $type = 'date'; + break; + case Swift_Mime_Header::TYPE_ID: $type = 'ID'; + break; + case Swift_Mime_Header::TYPE_PATH: $type = 'path'; + break; + } + printf("%s: is a %s header<br />\n", $header->getFieldName(), $type); + } + + /* + Content-Transfer-Encoding: is a text header + Content-Type: is a parameterized header + MIME-Version: is a text header + Date: is a date header + Message-ID: is a ID header + From: is a mailbox header + Subject: is a text header + To: is a mailbox header + */ + +Headers can be removed from the set, modified within the set, or added to the +set. + +The following sections show you how to work with the HeaderSet and explain the +details of each implementation of ``Swift_Mime_Header`` that may +exist within the HeaderSet. + +Header Types +------------ + +Because all headers are modeled on different data (dates, addresses, text!) +there are different types of Header in Swift Mailer. Swift Mailer attempts to +categorize all possible MIME headers into more general groups, defined by a +small number of classes. + +Text Headers +~~~~~~~~~~~~ + +Text headers are the simplest type of Header. They contain textual information +with no special information included within it -- for example the Subject +header in a message. + +There's nothing particularly interesting about a text header, though it is +probably the one you'd opt to use if you need to add a custom header to a +message. It represents text just like you'd think it does. If the text +contains characters that are not permitted in a message header (such as new +lines, or non-ascii characters) then the header takes care of encoding the +text so that it can be used. + +No header -- including text headers -- in Swift Mailer is vulnerable to +header-injection attacks. Swift Mailer breaks any attempt at header injection by +encoding the dangerous data into a non-dangerous form. + +It's easy to add a new text header to a HeaderSet. You do this by calling the +HeaderSet's ``addTextHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addTextHeader('Your-Header-Name', 'the header value'); + +Changing the value of an existing text header is done by calling it's +``setValue()`` method. + +.. code-block:: php + + $subject = $message->getHeaders()->get('Subject'); + + $subject->setValue('new subject'); + +When output via ``toString()``, a text header produces something like the +following: + +.. code-block:: php + + $subject = $message->getHeaders()->get('Subject'); + + $subject->setValue('amazing subject line'); + + echo $subject->toString(); + + /* + + Subject: amazing subject line + + */ + +If the header contains any characters that are outside of the US-ASCII range +however, they will be encoded. This is nothing to be concerned about since +mail clients will decode them back. + +.. code-block:: php + + $subject = $message->getHeaders()->get('Subject'); + + $subject->setValue('contains – dash'); + + echo $subject->toString(); + + /* + + Subject: contains =?utf-8?Q?=E2=80=93?= dash + + */ + +Parameterized Headers +~~~~~~~~~~~~~~~~~~~~~ + +Parameterized headers are text headers that contain key-value parameters +following the textual content. The Content-Type header of a message is a +parameterized header since it contains charset information after the content +type. + +The parameterized header type is a special type of text header. It extends the +text header by allowing additional information to follow it. All of the methods +from text headers are available in addition to the methods described here. + +Adding a parameterized header to a HeaderSet is done by using the +``addParameterizedHeader()`` method which takes a text value like +``addTextHeader()`` but it also accepts an associative array of +key-value parameters. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addParameterizedHeader( + 'Header-Name', 'header value', + array('foo' => 'bar') + ); + +To change the text value of the header, call it's ``setValue()`` method just as +you do with text headers. + +To change the parameters in the header, call the header's ``setParameters()`` +method or the ``setParameter()`` method (note the pluralization). + +.. code-block:: php + + $type = $message->getHeaders()->get('Content-Type'); + + // setParameters() takes an associative array + $type->setParameters(array( + 'name' => 'file.txt', + 'charset' => 'iso-8859-1' + )); + + // setParameter() takes two args for $key and $value + $type->setParameter('charset', 'iso-8859-1'); + +When output via ``toString()``, a parameterized header produces something like +the following: + +.. code-block:: php + + $type = $message->getHeaders()->get('Content-Type'); + + $type->setValue('text/html'); + $type->setParameter('charset', 'utf-8'); + + echo $type->toString(); + + /* + + Content-Type: text/html; charset=utf-8 + + */ + +If the header contains any characters that are outside of the US-ASCII range +however, they will be encoded, just like they are for text headers. This is +nothing to be concerned about since mail clients will decode them back. +Likewise, if the parameters contain any non-ascii characters they will be +encoded so that they can be transmitted safely. + +.. code-block:: php + + $attachment = Swift_Attachment::newInstance(); + + $disp = $attachment->getHeaders()->get('Content-Disposition'); + + $disp->setValue('attachment'); + $disp->setParameter('filename', 'report–may.pdf'); + + echo $disp->toString(); + + /* + + Content-Disposition: attachment; filename*=utf-8''report%E2%80%93may.pdf + + */ + +Date Headers +~~~~~~~~~~~~ + +Date headers contains an RFC 2822 formatted date (i.e. what PHP's ``date('r')`` +returns). They are used anywhere a date or time is needed to be presented as a +message header. + +The data on which a date header is modeled is simply a UNIX timestamp such as +that returned by ``time()`` or ``strtotime()``. The timestamp is used to create +a correctly structured RFC 2822 formatted date such as +``Tue, 17 Feb 2009 22:26:31 +1100``. + +The obvious place this header type is used is in the ``Date:`` header of the +message itself. + +It's easy to add a new date header to a HeaderSet. You do this by calling +the HeaderSet's ``addDateHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addDateHeader('Your-Header-Name', strtotime('3 days ago')); + +Changing the value of an existing date header is done by calling it's +``setTimestamp()`` method. + +.. code-block:: php + + $date = $message->getHeaders()->get('Date'); + + $date->setTimestamp(time()); + +When output via ``toString()``, a date header produces something like the +following: + +.. code-block:: php + + $date = $message->getHeaders()->get('Date'); + + echo $date->toString(); + + /* + + Date: Wed, 18 Feb 2009 13:35:02 +1100 + + */ + +Mailbox (e-mail address) Headers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Mailbox headers contain one or more email addresses, possibly with +personalized names attached to them. The data on which they are modeled is +represented by an associative array of email addresses and names. + +Mailbox headers are probably the most complex header type to understand in +Swift Mailer because they accept their input as an array which can take various +forms, as described in the previous chapter. + +All of the headers that contain e-mail addresses in a message -- with the +exception of ``Return-Path:`` which has a stricter syntax -- use this header +type. That is, ``To:``, ``From:`` etc. + +You add a new mailbox header to a HeaderSet by calling the HeaderSet's +``addMailboxHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addMailboxHeader('Your-Header-Name', array( + 'person1@example.org' => 'Person Name One', + 'person2@example.org', + 'person3@example.org', + 'person4@example.org' => 'Another named person' + )); + +Changing the value of an existing mailbox header is done by calling it's +``setNameAddresses()`` method. + +.. code-block:: php + + $to = $message->getHeaders()->get('To'); + + $to->setNameAddresses(array( + 'joe@example.org' => 'Joe Bloggs', + 'john@example.org' => 'John Doe', + 'no-name@example.org' + )); + +If you don't wish to concern yourself with the complicated accepted input +formats accepted by ``setNameAddresses()`` as described in the previous chapter +and you only want to set one or more addresses (not names) then you can just +use the ``setAddresses()`` method instead. + +.. code-block:: php + + $to = $message->getHeaders()->get('To'); + + $to->setAddresses(array( + 'joe@example.org', + 'john@example.org', + 'no-name@example.org' + )); + +.. note:: + + Both methods will accept the above input format in practice. + +If all you want to do is set a single address in the header, you can use a +string as the input parameter to ``setAddresses()`` and/or +``setNameAddresses()``. + +.. code-block:: php + + $to = $message->getHeaders()->get('To'); + + $to->setAddresses('joe-bloggs@example.org'); + +When output via ``toString()``, a mailbox header produces something like the +following: + +.. code-block:: php + + $to = $message->getHeaders()->get('To'); + + $to->setNameAddresses(array( + 'person1@example.org' => 'Name of Person', + 'person2@example.org', + 'person3@example.org' => 'Another Person' + )); + + echo $to->toString(); + + /* + + To: Name of Person <person1@example.org>, person2@example.org, Another Person + <person3@example.org> + + */ + +ID Headers +~~~~~~~~~~ + +ID headers contain identifiers for the entity (or the message). The most +notable ID header is the Message-ID header on the message itself. + +An ID that exists inside an ID header looks more-or-less less like an email +address. For example, ``<1234955437.499becad62ec2@example.org>``. +The part to the left of the @ sign is usually unique, based on the current time +and some random factor. The part on the right is usually a domain name. + +Any ID passed to the header's ``setId()`` method absolutely MUST conform to +this structure, otherwise you'll get an Exception thrown at you by Swift Mailer +(a ``Swift_RfcComplianceException``). This is to ensure that the generated +email complies with relevant RFC documents and therefore is less likely to be +blocked as spam. + +It's easy to add a new ID header to a HeaderSet. You do this by calling +the HeaderSet's ``addIdHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addIdHeader('Your-Header-Name', '123456.unqiue@example.org'); + +Changing the value of an existing date header is done by calling its +``setId()`` method. + +.. code-block:: php + + $msgId = $message->getHeaders()->get('Message-ID'); + + $msgId->setId(time() . '.' . uniqid('thing') . '@example.org'); + +When output via ``toString()``, an ID header produces something like the +following: + +.. code-block:: php + + $msgId = $message->getHeaders()->get('Message-ID'); + + echo $msgId->toString(); + + /* + + Message-ID: <1234955437.499becad62ec2@example.org> + + */ + +Path Headers +~~~~~~~~~~~~ + +Path headers are like very-restricted mailbox headers. They contain a single +email address with no associated name. The Return-Path header of a message is +a path header. + +You add a new path header to a HeaderSet by calling the HeaderSet's +``addPathHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addPathHeader('Your-Header-Name', 'person@example.org'); + + +Changing the value of an existing path header is done by calling its +``setAddress()`` method. + +.. code-block:: php + + $return = $message->getHeaders()->get('Return-Path'); + + $return->setAddress('my-address@example.org'); + +When output via ``toString()``, a path header produces something like the +following: + +.. code-block:: php + + $return = $message->getHeaders()->get('Return-Path'); + + $return->setAddress('person@example.org'); + + echo $return->toString(); + + /* + + Return-Path: <person@example.org> + + */ + +Header Operations +----------------- + +Working with the headers in a message involves knowing how to use the methods +on the HeaderSet and on the individual Headers within the HeaderSet. + +Adding new Headers +~~~~~~~~~~~~~~~~~~ + +New headers can be added to the HeaderSet by using one of the provided +``add..Header()`` methods. + +To add a header to a MIME entity (such as the message): + +Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Add the header to the HeaderSet by calling one of the ``add..Header()`` + methods. + +The added header will appear in the message when it is sent. + +.. code-block:: php + + // Adding a custom header to a message + $message = Swift_Message::newInstance(); + $headers = $message->getHeaders(); + $headers->addTextHeader('X-Mine', 'something here'); + + // Adding a custom header to an attachment + $attachment = Swift_Attachment::fromPath('/path/to/doc.pdf'); + $attachment->getHeaders()->addDateHeader('X-Created-Time', time()); + +Retrieving Headers +~~~~~~~~~~~~~~~~~~ + +Headers are retrieved through the HeaderSet's ``get()`` and ``getAll()`` +methods. + +To get a header, or several headers from a MIME entity: + +* Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Get the header(s) from the HeaderSet by calling either ``get()`` or + ``getAll()``. + +When using ``get()`` a single header is returned that matches the name (case +insensitive) that is passed to it. When using ``getAll()`` with a header name, +an array of headers with that name are returned. Calling ``getAll()`` with no +arguments returns an array of all headers present in the entity. + +.. note:: + + It's valid for some headers to appear more than once in a message (e.g. + the Received header). For this reason ``getAll()`` exists to fetch all + headers with a specified name. In addition, ``get()`` accepts an optional + numerical index, starting from zero to specify which header you want more + specifically. + +.. note:: + + If you want to modify the contents of the header and you don't know for + sure what type of header it is then you may need to check the type by + calling its ``getFieldType()`` method. + + .. code-block:: php + + $headers = $message->getHeaders(); + + // Get the To: header + $toHeader = $headers->get('To'); + + // Get all headers named "X-Foo" + $fooHeaders = $headers->getAll('X-Foo'); + + // Get the second header named "X-Foo" + $foo = $headers->get('X-Foo', 1); + + // Get all headers that are present + $all = $headers->getAll(); + +Check if a Header Exists +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can check if a named header is present in a HeaderSet by calling its +``has()`` method. + +To check if a header exists: + +* Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Call the HeaderSet's ``has()`` method specifying the header you're looking + for. + +If the header exists, ``true`` will be returned or ``false`` if not. + +.. note:: + + It's valid for some headers to appear more than once in a message (e.g. + the Received header). For this reason ``has()`` accepts an optional + numerical index, starting from zero to specify which header you want to + check more specifically. + + .. code-block:: php + + $headers = $message->getHeaders(); + + // Check if the To: header exists + if ($headers->has('To')) { + echo 'To: exists'; + } + + // Check if an X-Foo header exists twice (i.e. check for the 2nd one) + if ($headers->has('X-Foo', 1)) { + echo 'Second X-Foo header exists'; + } + +Removing Headers +~~~~~~~~~~~~~~~~ + +Removing a Header from the HeaderSet is done by calling the HeaderSet's +``remove()`` or ``removeAll()`` methods. + +To remove an existing header: + +* Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Call the HeaderSet's ``remove()`` or ``removeAll()`` methods specifying the + header you want to remove. + +When calling ``remove()`` a single header will be removed. When calling +``removeAll()`` all headers with the given name will be removed. If no headers +exist with the given name, no errors will occur. + +.. note:: + + It's valid for some headers to appear more than once in a message (e.g. + the Received header). For this reason ``remove()`` accepts an optional + numerical index, starting from zero to specify which header you want to + check more specifically. For the same reason, ``removeAll()`` exists to + remove all headers that have the given name. + + .. code-block:: php + + $headers = $message->getHeaders(); + + // Remove the Subject: header + $headers->remove('Subject'); + + // Remove all X-Foo headers + $headers->removeAll('X-Foo'); + + // Remove only the second X-Foo header + $headers->remove('X-Foo', 1); + +Modifying a Header's Content +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To change a Header's content you should know what type of header it is and then +call it's appropriate setter method. All headers also have a +``setFieldBodyModel()`` method that accepts a mixed parameter and delegates to +the correct setter. + +To modify an existing header: + +* Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Get the Header by using the HeaderSet's ``get()``. + +* Call the Header's appropriate setter method or call the header's + ``setFieldBodyModel()`` method. + +The header will be updated inside the HeaderSet and the changes will be seen +when the message is sent. + +.. code-block:: php + + $headers = $message->getHeaders(); + + // Change the Subject: header + $subj = $headers->get('Subject'); + $subj->setValue('new subject here'); + + // Change the To: header + $to = $headers->get('To'); + $to->setNameAddresses(array( + 'person@example.org' => 'Person', + 'thing@example.org' + )); + + // Using the setFieldBodyModel() just delegates to the correct method + // So here to calls setNameAddresses() + $to->setFieldBodyModel(array( + 'person@example.org' => 'Person', + 'thing@example.org' + )); diff --git a/vendor/swiftmailer/swiftmailer/doc/help-resources.rst b/vendor/swiftmailer/swiftmailer/doc/help-resources.rst new file mode 100644 index 00000000..42089359 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/help-resources.rst @@ -0,0 +1,44 @@ +Getting Help +============ + +There are a number of ways you can get help when using Swift Mailer, depending +upon the nature of your problem. For bug reports and feature requests create a +new ticket in GitHub. For general advice ask on the Google Group +(swiftmailer). + +Submitting Bugs & Feature Requests +---------------------------------- + +Bugs and feature requests should be posted on GitHub. + +If you post a bug or request a feature in the forum, or on the Google Group +you will most likely be asked to create a ticket in `GitHub`_ since it is +simply not feasible to manage such requests from a number of a different +sources. + +When you go to GitHub you will be asked to create a username and password +before you can create a ticket. This is free and takes very little time. + +When you create your ticket, do not assign it to any milestones. A developer +will assess your ticket and re-assign it as needed. + +If your ticket is reporting a bug present in the current version, which was +not present in the previous version please include the tag "regression" in +your ticket. + +GitHub will update you when work is performed on your ticket. + +Ask on the Google Group +----------------------- + +You can seek advice at Google Groups, within the "swiftmailer" `group`_. + +You can post messages to this group if you want help, or there's something you +wish to discuss with the developers and with other users. + +This is probably the fastest way to get help since it is primarily email-based +for most users, though bug reports should not be posted here since they may +not be resolved. + +.. _`GitHub`: https://github.com/swiftmailer/swiftmailer/issues +.. _`group`: http://groups.google.com/group/swiftmailer diff --git a/vendor/swiftmailer/swiftmailer/doc/including-the-files.rst b/vendor/swiftmailer/swiftmailer/doc/including-the-files.rst new file mode 100644 index 00000000..978dca20 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/including-the-files.rst @@ -0,0 +1,46 @@ +Including Swift Mailer (Autoloading) +==================================== + +If you are using Composer, Swift Mailer will be automatically autoloaded. + +If not, you can use the built-in autoloader by requiring the +``swift_required.php`` file:: + + require_once '/path/to/swift-mailer/lib/swift_required.php'; + + /* rest of code goes here */ + +If you want to override the default Swift Mailer configuration, call the +``init()`` method on the ``Swift`` class and pass it a valid PHP callable (a +PHP function name, a PHP 5.3 anonymous function, ...):: + + require_once '/path/to/swift-mailer/lib/swift_required.php'; + + function swiftmailer_configurator() { + // configure Swift Mailer + + Swift_DependencyContainer::getInstance()->... + Swift_Preferences::getInstance()->... + } + + Swift::init('swiftmailer_configurator'); + + /* rest of code goes here */ + +The advantage of using the ``init()`` method is that your code will be +executed only if you use Swift Mailer in your script. + +.. note:: + + While Swift Mailer's autoloader is designed to play nicely with other + autoloaders, sometimes you may have a need to avoid using Swift Mailer's + autoloader and use your own instead. Include the ``swift_init.php`` + instead of the ``swift_required.php`` if you need to do this. The very + minimum include is the ``swift_init.php`` file since Swift Mailer will not + work without the dependency injection this file sets up: + + .. code-block:: php + + require_once '/path/to/swift-mailer/lib/swift_init.php'; + + /* rest of code goes here */ diff --git a/vendor/swiftmailer/swiftmailer/doc/index.rst b/vendor/swiftmailer/swiftmailer/doc/index.rst new file mode 100644 index 00000000..a1a0a924 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/index.rst @@ -0,0 +1,16 @@ +Swiftmailer +=========== + +.. toctree:: + :maxdepth: 2 + + introduction + overview + installing + help-resources + including-the-files + messages + headers + sending + plugins + japanese diff --git a/vendor/swiftmailer/swiftmailer/doc/installing.rst b/vendor/swiftmailer/swiftmailer/doc/installing.rst new file mode 100644 index 00000000..557211d4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/installing.rst @@ -0,0 +1,89 @@ +Installing the Library +====================== + +Installing with Composer +------------------------ + +The recommended way to install Swiftmailer is via Composer: + +.. code-block:: bash + + $ php composer.phar require swiftmailer/swiftmailer @stable + +Installing from Git +------------------- + +It's possible to download and install Swift Mailer directly from github.com if +you want to keep up-to-date with ease. + +Swift Mailer's source code is kept in a git repository at github.com so you +can get the source directly from the repository. + +.. note:: + + You do not need to have git installed to use Swift Mailer from GitHub. If + you don't have git installed, go to `GitHub`_ and click the "Download" + button. + +Cloning the Repository +~~~~~~~~~~~~~~~~~~~~~~ + +The repository can be cloned from git://github.com/swiftmailer/swiftmailer.git +using the ``git clone`` command. + +You will need to have ``git`` installed before you can use the +``git clone`` command. + +To clone the repository: + +* Open your favorite terminal environment (command line). + +* Move to the directory you want to clone to. + +* Run the command ``git clone git://github.com/swiftmailer/swiftmailer.git + swiftmailer``. + +The source code will be downloaded into a directory called "swiftmailer". + +The example shows the process on a UNIX-like system such as Linux, BSD or Mac +OS X. + +.. code-block:: bash + + $ cd source_code/ + $ git clone git://github.com/swiftmailer/swiftmailer.git swiftmailer + Initialized empty Git repository in /Users/chris/source_code/swiftmailer/.git/ + remote: Counting objects: 6815, done. + remote: Compressing objects: 100% (2761/2761), done. + remote: Total 6815 (delta 3641), reused 6326 (delta 3286) + Receiving objects: 100% (6815/6815), 4.35 MiB | 162 KiB/s, done. + Resolving deltas: 100% (3641/3641), done. + Checking out files: 100% (1847/1847), done. + $ cd swiftmailer/ + $ ls + CHANGES LICENSE ... + $ + +Troubleshooting +--------------- + +Swift Mailer does not work when used with function overloading as implemented +by ``mbstring`` (``mbstring.func_overload`` set to ``2``). A workaround is to +temporarily change the internal encoding to ``ASCII`` when sending an email: + +.. code-block:: php + + if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) + { + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('ASCII'); + } + + // Create your message and send it with Swift Mailer + + if (isset($mbEncoding)) + { + mb_internal_encoding($mbEncoding); + } + +.. _`GitHub`: http://github.com/swiftmailer/swiftmailer diff --git a/vendor/swiftmailer/swiftmailer/doc/introduction.rst b/vendor/swiftmailer/swiftmailer/doc/introduction.rst new file mode 100644 index 00000000..a85336b7 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/introduction.rst @@ -0,0 +1,135 @@ +Introduction +============ + +Swift Mailer is a component-based library for sending e-mails from PHP +applications. + +Organization of this Book +------------------------- + +This book has been written so that those who need information quickly are able +to find what they need, and those who wish to learn more advanced topics can +read deeper into each chapter. + +The book begins with an overview of Swift Mailer, discussing what's included +in the package and preparing you for the remainder of the book. + +It is possible to read this user guide just like any other book (from +beginning to end). Each chapter begins with a discussion of the contents it +contains, followed by a short code sample designed to give you a head start. +As you get further into a chapter you will learn more about Swift Mailer's +capabilities, but often you will be able to head directly to the topic you +wish to learn about. + +Throughout this book you will be presented with code samples, which most +people should find ample to implement Swift Mailer appropriately in their own +projects. We will also use diagrams where appropriate, and where we believe +readers may find it helpful we will discuss some related theory, including +reference to certain documents you are able to find online. + +Code Samples +------------ + +Code samples presented in this book will be displayed on a different colored +background in a monospaced font. Samples are not to be taken as copy & paste +code snippets. + +Code examples are used through the book to clarify what is written in text. +They will sometimes be usable as-is, but they should always be taken as +outline/pseudo code only. + +A code sample will look like this:: + + class AClass + { + ... + } + + // A Comment + $obj = new AClass($arg1, $arg2, ... ); + + /* A note about another way of doing something + $obj = AClass::newInstance($arg1, $arg2, ... ); + + */ + +The presence of 3 dots ``...`` in a code sample indicates that we have left +out a chunk of the code for brevity, they are not actually part of the code. + +We will often place multi-line comments ``/* ... */`` in the code so that we +can show alternative ways of achieving the same result. + +You should read the code examples given and try to understand them. They are +kept concise so that you are not overwhelmed with information. + +History of Swift Mailer +----------------------- + +Swift Mailer began back in 2005 as a one-class project for sending mail over +SMTP. It has since grown into the flexible component-based library that is in +development today. + +Chris Corbyn first posted Swift Mailer on a web forum asking for comments from +other developers. It was never intended as a fully supported open source +project, but members of the forum began to adopt it and make use of it. + +Very quickly feature requests were coming for the ability to add attachments +and use SMTP authentication, along with a number of other "obvious" missing +features. Considering the only alternative was PHPMailer it seemed like a good +time to bring some fresh tools to the table. Chris began working towards a +more component based, PHP5-like approach unlike the existing single-class, +legacy PHP4 approach taken by PHPMailer. + +Members of the forum offered a lot of advice and critique on the code as he +worked through this project and released versions 2 and 3 of the library in +2005 and 2006, which by then had been broken down into smaller classes +offering more flexibility and supporting plugins. To this day the Swift Mailer +team still receive a lot of feature requests from users both on the forum and +in by email. + +Until 2008 Chris was the sole developer of Swift Mailer, but entering 2009 he +gained the support of two experienced developers well-known to him: Paul +Annesley and Christopher Thompson. This has been an extremely welcome change. + +As of September 2009, Chris handed over the maintenance of Swift Mailer to +Fabien Potencier. + +Now 2009 and in its fourth major version Swift Mailer is more object-oriented +and flexible than ever, both from a usability standpoint and from a +development standpoint. + +By no means is Swift Mailer ready to call "finished". There are still many +features that can be added to the library along with the constant refactoring +that happens behind the scenes. + +It's a Library! +--------------- + +Swift Mailer is not an application - it's a library. + +To most experienced developers this is probably an obvious point to make, but +it's certainly worth mentioning. Many people often contact us having gotten +the completely wrong end of the stick in terms of what Swift Mailer is +actually for. + +It's not an application. It does not have a graphical user interface. It +cannot be opened in your web browser directly. + +It's a library (or a framework if you like). It provides a whole lot of +classes that do some very complicated things, so that you don't have to. You +"use" Swift Mailer within an application so that your application can have the +ability to send emails. + +The component-based structure of the library means that you are free to +implement it in a number of different ways and that you can pick and choose +what you want to use. + +An application on the other hand (such as a blog or a forum) is already "put +together" in a particular way, (usually) provides a graphical user interface +and most likely doesn't offer a great deal of integration with your own +application. + +Embrace the structure of the library and use the components it offers to your +advantage. Learning what the components do, rather than blindly copying and +pasting existing code will put you in a great position to build a powerful +application! diff --git a/vendor/swiftmailer/swiftmailer/doc/japanese.rst b/vendor/swiftmailer/swiftmailer/doc/japanese.rst new file mode 100644 index 00000000..34afa7b8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/japanese.rst @@ -0,0 +1,22 @@ +Using Swift Mailer for Japanese Emails +====================================== + +To send emails in Japanese, you need to tweak the default configuration. + +After requiring the Swift Mailer autoloader (by including the +``swift_required.php`` file), call the ``Swift::init()`` method with the +following code:: + + require_once '/path/to/swift-mailer/lib/swift_required.php'; + + Swift::init(function () { + Swift_DependencyContainer::getInstance() + ->register('mime.qpheaderencoder') + ->asAliasOf('mime.base64headerencoder'); + + Swift_Preferences::getInstance()->setCharset('iso-2022-jp'); + }); + + /* rest of code goes here */ + +That's all! diff --git a/vendor/swiftmailer/swiftmailer/doc/messages.rst b/vendor/swiftmailer/swiftmailer/doc/messages.rst new file mode 100644 index 00000000..fb5e7fc3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/messages.rst @@ -0,0 +1,1061 @@ +Creating Messages +================= + +Creating messages in Swift Mailer is done by making use of the various MIME +entities provided with the library. Complex messages can be quickly created +with very little effort. + +Quick Reference for Creating a Message +--------------------------------------- + +You can think of creating a Message as being similar to the steps you perform +when you click the Compose button in your mail client. You give it a subject, +specify some recipients, add any attachments and write your message. + +To create a Message: + +* Call the ``newInstance()`` method of ``Swift_Message``. + +* Set your sender address (``From:``) with ``setFrom()`` or ``setSender()``. + +* Set a subject line with ``setSubject()``. + +* Set recipients with ``setTo()``, ``setCc()`` and/or ``setBcc()``. + +* Set a body with ``setBody()``. + +* Add attachments with ``attach()``. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the message + $message = Swift_Message::newInstance() + + // Give the message a subject + ->setSubject('Your subject') + + // Set the From address with an associative array + ->setFrom(array('john@doe.com' => 'John Doe')) + + // Set the To addresses with an associative array + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + + // Give it a body + ->setBody('Here is the message itself') + + // And optionally an alternative body + ->addPart('<q>Here is the message itself</q>', 'text/html') + + // Optionally add any attachments + ->attach(Swift_Attachment::fromPath('my-document.pdf')) + ; + +Message Basics +-------------- + +A message is a container for anything you want to send to somebody else. There +are several basic aspects of a message that you should know. + +An e-mail message is made up of several relatively simple entities that are +combined in different ways to achieve different results. All of these entities +have the same fundamental outline but serve a different purpose. The Message +itself can be defined as a MIME entity, an Attachment is a MIME entity, all +MIME parts are MIME entities -- and so on! + +The basic units of each MIME entity -- be it the Message itself, or an +Attachment -- are its Headers and its body: + +.. code-block:: text + + Header-Name: A header value + Other-Header: Another value + + The body content itself + +The Headers of a MIME entity, and its body must conform to some strict +standards defined by various RFC documents. Swift Mailer ensures that these +specifications are followed by using various types of object, including +Encoders and different Header types to generate the entity. + +The Structure of a Message +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Of all of the MIME entities, a message -- ``Swift_Message`` +is the largest and most complex. It has many properties that can be updated +and it can contain other MIME entities -- attachments for example -- +nested inside it. + +A Message has a lot of different Headers which are there to present +information about the message to the recipients' mail client. Most of these +headers will be familiar to the majority of users, but we'll list the basic +ones. Although it's possible to work directly with the Headers of a Message +(or other MIME entity), the standard Headers have accessor methods provided to +abstract away the complex details for you. For example, although the Date on a +message is written with a strict format, you only need to pass a UNIX +timestamp to ``setDate()``. + ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| Header | Description | Accessors | ++===============================+====================================================================================================================================+=============================================+ +| ``Message-ID`` | Identifies this message with a unique ID, usually containing the domain name and time generated | ``getId()`` / ``setId()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Return-Path`` | Specifies where bounces should go (Swift Mailer reads this for other uses) | ``getReturnPath()`` / ``setReturnPath()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``From`` | Specifies the address of the person who the message is from. This can be multiple addresses if multiple people wrote the message. | ``getFrom()`` / ``setFrom()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Sender`` | Specifies the address of the person who physically sent the message (higher precedence than ``From:``) | ``getSender()`` / ``setSender()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``To`` | Specifies the addresses of the intended recipients | ``getTo()`` / ``setTo()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Cc`` | Specifies the addresses of recipients who will be copied in on the message | ``getCc()`` / ``setCc()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Bcc`` | Specifies the addresses of recipients who the message will be blind-copied to. Other recipients will not be aware of these copies. | ``getBcc()`` / ``setBcc()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Reply-To`` | Specifies the address where replies are sent to | ``getReplyTo()`` / ``setReplyTo()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Subject`` | Specifies the subject line that is displayed in the recipients' mail client | ``getSubject()`` / ``setSubject()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Date`` | Specifies the date at which the message was sent | ``getDate()`` / ``setDate()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Content-Type`` | Specifies the format of the message (usually text/plain or text/html) | ``getContentType()`` / ``setContentType()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Content-Transfer-Encoding`` | Specifies the encoding scheme in the message | ``getEncoder()`` / ``setEncoder()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ + +Working with a Message Object +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Although there are a lot of available methods on a message object, you only +need to make use of a small subset of them. Usually you'll use +``setSubject()``, ``setTo()`` and +``setFrom()`` before setting the body of your message with +``setBody()``. + +Calling methods is simple. You just call them like functions, but using the +object operator "``->``" to do so. If you've created +a message object and called it ``$message`` then you'd set a +subject on it like so: + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + $message = Swift_Message::newInstance(); + $message->setSubject('My subject'); + +All MIME entities (including a message) have a ``toString()`` +method that you can call if you want to take a look at what is going to be +sent. For example, if you ``echo +$message->toString();`` you would see something like this: + +.. code-block:: bash + + Message-ID: <1230173678.4952f5eeb1432@swift.generated> + Date: Thu, 25 Dec 2008 13:54:38 +1100 + Subject: Example subject + From: Chris Corbyn <chris@w3style.co.uk> + To: Receiver Name <recipient@example.org> + MIME-Version: 1.0 + Content-Type: text/plain; charset=utf-8 + Content-Transfer-Encoding: quoted-printable + + Here is the message + +We'll take a closer look at the methods you use to create your message in the +following sections. + +Adding Content to Your Message +------------------------------ + +Rich content can be added to messages in Swift Mailer with relative ease by +calling methods such as ``setSubject()``, ``setBody()``, ``addPart()`` and +``attach()``. + +Setting the Subject Line +~~~~~~~~~~~~~~~~~~~~~~~~ + +The subject line, displayed in the recipients' mail client can be set with the +``setSubject()`` method, or as a parameter to ``Swift_Message::newInstance()``. + +To set the subject of your Message: + +* Call the ``setSubject()`` method of the Message, or specify it at the time + you create the message. + + .. code-block:: php + + // Pass it as a parameter when you create the message + $message = Swift_Message::newInstance('My amazing subject'); + + // Or set it after like this + $message->setSubject('My amazing subject'); + +Setting the Body Content +~~~~~~~~~~~~~~~~~~~~~~~~ + +The body of the message -- seen when the user opens the message -- +is specified by calling the ``setBody()`` method. If an alternative body is to +be included ``addPart()`` can be used. + +The body of a message is the main part that is read by the user. Often people +want to send a message in HTML format (``text/html``), other +times people want to send in plain text (``text/plain``), or +sometimes people want to send both versions and allow the recipient to choose +how they view the message. + +As a rule of thumb, if you're going to send a HTML email, always include a +plain-text equivalent of the same content so that users who prefer to read +plain text can do so. + +To set the body of your Message: + +* Call the ``setBody()`` method of the Message, or specify it at the time you + create the message. + +* Add any alternative bodies with ``addPart()``. + +If the recipient's mail client offers preferences for displaying text vs. HTML +then the mail client will present that part to the user where available. In +other cases the mail client will display the "best" part it can - usually HTML +if you've included HTML. + +.. code-block:: php + + // Pass it as a parameter when you create the message + $message = Swift_Message::newInstance('Subject here', 'My amazing body'); + + // Or set it after like this + $message->setBody('My <em>amazing</em> body', 'text/html'); + + // Add alternative parts with addPart() + $message->addPart('My amazing body in plain text', 'text/plain'); + +Attaching Files +--------------- + +Attachments are downloadable parts of a message and can be added by calling +the ``attach()`` method on the message. You can add attachments that exist on +disk, or you can create attachments on-the-fly. + +Attachments are actually an interesting area of Swift Mailer and something +that could put a lot of power at your fingertips if you grasp the concept +behind the way a message is held together. + +Although we refer to files sent over e-mails as "attachments" -- because +they're attached to the message -- lots of other parts of the message are +actually "attached" even if we don't refer to these parts as attachments. + +File attachments are created by the ``Swift_Attachment`` class +and then attached to the message via the ``attach()`` method on +it. For all of the "every day" MIME types such as all image formats, word +documents, PDFs and spreadsheets you don't need to explicitly set the +content-type of the attachment, though it would do no harm to do so. For less +common formats you should set the content-type -- which we'll cover in a +moment. + +Attaching Existing Files +~~~~~~~~~~~~~~~~~~~~~~~~ + +Files that already exist, either on disk or at a URL can be attached to a +message with just one line of code, using ``Swift_Attachment::fromPath()``. + +You can attach files that exist locally, or if your PHP installation has +``allow_url_fopen`` turned on you can attach files from other +websites. + +To attach an existing file: + +* Create an attachment with ``Swift_Attachment::fromPath()``. + +* Add the attachment to the message with ``attach()``. + +The attachment will be presented to the recipient as a downloadable file with +the same filename as the one you attached. + +.. code-block:: php + + // Create the attachment + // * Note that you can technically leave the content-type parameter out + $attachment = Swift_Attachment::fromPath('/path/to/image.jpg', 'image/jpeg'); + + // Attach it to the message + $message->attach($attachment); + + + // The two statements above could be written in one line instead + $message->attach(Swift_Attachment::fromPath('/path/to/image.jpg')); + + + // You can attach files from a URL if allow_url_fopen is on in php.ini + $message->attach(Swift_Attachment::fromPath('http://site.tld/logo.png')); + +Setting the Filename +~~~~~~~~~~~~~~~~~~~~ + +Usually you don't need to explicitly set the filename of an attachment because +the name of the attached file will be used by default, but if you want to set +the filename you use the ``setFilename()`` method of the Attachment. + +To change the filename of an attachment: + +* Call its ``setFilename()`` method. + +The attachment will be attached in the normal way, but meta-data sent inside +the email will rename the file to something else. + +.. code-block:: php + + // Create the attachment and call its setFilename() method + $attachment = Swift_Attachment::fromPath('/path/to/image.jpg') + ->setFilename('cool.jpg'); + + + // Because there's a fluid interface, you can do this in one statement + $message->attach( + Swift_Attachment::fromPath('/path/to/image.jpg')->setFilename('cool.jpg') + ); + +Attaching Dynamic Content +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Files that are generated at runtime, such as PDF documents or images created +via GD can be attached directly to a message without writing them out to disk. +Use the standard ``Swift_Attachment::newInstance()`` method. + +To attach dynamically created content: + +* Create your content as you normally would. + +* Create an attachment with ``Swift_Attachment::newInstance()``, specifying + the source data of your content along with a name and the content-type. + +* Add the attachment to the message with ``attach()``. + +The attachment will be presented to the recipient as a downloadable file +with the filename and content-type you specify. + +.. note:: + + If you would usually write the file to disk anyway you should just attach + it with ``Swift_Attachment::fromPath()`` since this will use less memory: + + .. code-block:: php + + // Create your file contents in the normal way, but don't write them to disk + $data = create_my_pdf_data(); + + // Create the attachment with your data + $attachment = Swift_Attachment::newInstance($data, 'my-file.pdf', 'application/pdf'); + + // Attach it to the message + $message->attach($attachment); + + + // You can alternatively use method chaining to build the attachment + $attachment = Swift_Attachment::newInstance() + ->setFilename('my-file.pdf') + ->setContentType('application/pdf') + ->setBody($data) + ; + +Changing the Disposition +~~~~~~~~~~~~~~~~~~~~~~~~ + +Attachments just appear as files that can be saved to the Desktop if desired. +You can make attachment appear inline where possible by using the +``setDisposition()`` method of an attachment. + +To make an attachment appear inline: + +* Call its ``setDisposition()`` method. + +The attachment will be displayed within the email viewing window if the mail +client knows how to display it. + +.. note:: + + If you try to create an inline attachment for a non-displayable file type + such as a ZIP file, the mail client should just present the attachment as + normal: + + .. code-block:: php + + // Create the attachment and call its setDisposition() method + $attachment = Swift_Attachment::fromPath('/path/to/image.jpg') + ->setDisposition('inline'); + + + // Because there's a fluid interface, you can do this in one statement + $message->attach( + Swift_Attachment::fromPath('/path/to/image.jpg')->setDisposition('inline') + ); + +Embedding Inline Media Files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Often people want to include an image or other content inline with a HTML +message. It's easy to do this with HTML linking to remote resources, but this +approach is usually blocked by mail clients. Swift Mailer allows you to embed +your media directly into the message. + +Mail clients usually block downloads from remote resources because this +technique was often abused as a mean of tracking who opened an email. If +you're sending a HTML email and you want to include an image in the message +another approach you can take is to embed the image directly. + +Swift Mailer makes embedding files into messages extremely streamlined. You +embed a file by calling the ``embed()`` method of the message, +which returns a value you can use in a ``src`` or +``href`` attribute in your HTML. + +Just like with attachments, it's possible to embed dynamically generated +content without having an existing file available. + +The embedded files are sent in the email as a special type of attachment that +has a unique ID used to reference them within your HTML attributes. On mail +clients that do not support embedded files they may appear as attachments. + +Although this is commonly done for images, in theory it will work for any +displayable (or playable) media type. Support for other media types (such as +video) is dependent on the mail client however. + +Embedding Existing Files +........................ + +Files that already exist, either on disk or at a URL can be embedded in a +message with just one line of code, using ``Swift_EmbeddedFile::fromPath()``. + +You can embed files that exist locally, or if your PHP installation has +``allow_url_fopen`` turned on you can embed files from other websites. + +To embed an existing file: + +* Create a message object with ``Swift_Message::newInstance()``. + +* Set the body as HTML, and embed a file at the correct point in the message with ``embed()``. + +The file will be displayed with the message inline with the HTML wherever its ID +is used as a ``src`` attribute. + +.. note:: + + ``Swift_Image`` and ``Swift_EmbeddedFile`` are just aliases of one + another. ``Swift_Image`` exists for semantic purposes. + +.. note:: + + You can embed files in two stages if you prefer. Just capture the return + value of ``embed()`` in a variable and use that as the ``src`` attribute. + + .. code-block:: php + + // Create the message + $message = Swift_Message::newInstance('My subject'); + + // Set the body + $message->setBody( + '<html>' . + ' <head></head>' . + ' <body>' . + ' Here is an image <img src="' . // Embed the file + $message->embed(Swift_Image::fromPath('image.png')) . + '" alt="Image" />' . + ' Rest of message' . + ' </body>' . + '</html>', + 'text/html' // Mark the content-type as HTML + ); + + // You can embed files from a URL if allow_url_fopen is on in php.ini + $message->setBody( + '<html>' . + ' <head></head>' . + ' <body>' . + ' Here is an image <img src="' . + $message->embed(Swift_Image::fromPath('http://site.tld/logo.png')) . + '" alt="Image" />' . + ' Rest of message' . + ' </body>' . + '</html>', + 'text/html' + ); + + + // If placing the embed() code inline becomes cumbersome + // it's easy to do this in two steps + $cid = $message->embed(Swift_Image::fromPath('image.png')); + + $message->setBody( + '<html>' . + ' <head></head>' . + ' <body>' . + ' Here is an image <img src="' . $cid . '" alt="Image" />' . + ' Rest of message' . + ' </body>' . + '</html>', + 'text/html' // Mark the content-type as HTML + ); + +Embedding Dynamic Content +......................... + +Images that are generated at runtime, such as images created via GD can be +embedded directly to a message without writing them out to disk. Use the +standard ``Swift_Image::newInstance()`` method. + +To embed dynamically created content: + +* Create a message object with ``Swift_Message::newInstance()``. + +* Set the body as HTML, and embed a file at the correct point in the message + with ``embed()``. You will need to specify a filename and a content-type. + +The file will be displayed with the message inline with the HTML wherever its ID +is used as a ``src`` attribute. + +.. note:: + + ``Swift_Image`` and ``Swift_EmbeddedFile`` are just aliases of one + another. ``Swift_Image`` exists for semantic purposes. + +.. note:: + + You can embed files in two stages if you prefer. Just capture the return + value of ``embed()`` in a variable and use that as the ``src`` attribute. + + .. code-block:: php + + // Create your file contents in the normal way, but don't write them to disk + $img_data = create_my_image_data(); + + // Create the message + $message = Swift_Message::newInstance('My subject'); + + // Set the body + $message->setBody( + '<html>' . + ' <head></head>' . + ' <body>' . + ' Here is an image <img src="' . // Embed the file + $message->embed(Swift_Image::newInstance($img_data, 'image.jpg', 'image/jpeg')) . + '" alt="Image" />' . + ' Rest of message' . + ' </body>' . + '</html>', + 'text/html' // Mark the content-type as HTML + ); + + + // If placing the embed() code inline becomes cumbersome + // it's easy to do this in two steps + $cid = $message->embed(Swift_Image::newInstance($img_data, 'image.jpg', 'image/jpeg')); + + $message->setBody( + '<html>' . + ' <head></head>' . + ' <body>' . + ' Here is an image <img src="' . $cid . '" alt="Image" />' . + ' Rest of message' . + ' </body>' . + '</html>', + 'text/html' // Mark the content-type as HTML + ); + +Adding Recipients to Your Message +--------------------------------- + +Recipients are specified within the message itself via ``setTo()``, ``setCc()`` +and ``setBcc()``. Swift Mailer reads these recipients from the message when it +gets sent so that it knows where to send the message to. + +Message recipients are one of three types: + +* ``To:`` recipients -- the primary recipients (required) + +* ``Cc:`` recipients -- receive a copy of the message (optional) + +* ``Bcc:`` recipients -- hidden from other recipients (optional) + +Each type can contain one, or several addresses. It's possible to list only +the addresses of the recipients, or you can personalize the address by +providing the real name of the recipient. + +Make sure to add only valid email addresses as recipients. If you try to add an +invalid email address with ``setTo()``, ``setCc()`` or ``setBcc()``, Swift +Mailer will throw a ``Swift_RfcComplianceException``. + +If you add recipients automatically based on a data source that may contain +invalid email addresses, you can prevent possible exceptions by validating the +addresses using ``Swift_Validate::email($email)`` and only adding addresses +that validate. Another way would be to wrap your ``setTo()``, ``setCc()`` and +``setBcc()`` calls in a try-catch block and handle the +``Swift_RfcComplianceException`` in the catch block. + +.. sidebar:: Syntax for Addresses + + If you only wish to refer to a single email address (for example your + ``From:`` address) then you can just use a string. + + .. code-block:: php + + $message->setFrom('some@address.tld'); + + If you want to include a name then you must use an associative array. + + .. code-block:: php + + $message->setFrom(array('some@address.tld' => 'The Name')); + + If you want to include multiple addresses then you must use an array. + + .. code-block:: php + + $message->setTo(array('some@address.tld', 'other@address.tld')); + + You can mix personalized (addresses with a name) and non-personalized + addresses in the same list by mixing the use of associative and + non-associative array syntax. + + .. code-block:: php + + $message->setTo(array( + 'recipient-with-name@example.org' => 'Recipient Name One', + 'no-name@example.org', // Note that this is not a key-value pair + 'named-recipient@example.org' => 'Recipient Name Two' + )); + +Setting ``To:`` Recipients +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``To:`` recipients are required in a message and are set with the +``setTo()`` or ``addTo()`` methods of the message. + +To set ``To:`` recipients, create the message object using either +``new Swift_Message( ... )`` or ``Swift_Message::newInstance( ... )``, +then call the ``setTo()`` method with a complete array of addresses, or use the +``addTo()`` method to iteratively add recipients. + +The ``setTo()`` method accepts input in various formats as described earlier in +this chapter. The ``addTo()`` method takes either one or two parameters. The +first being the email address and the second optional parameter being the name +of the recipient. + +``To:`` recipients are visible in the message headers and will be +seen by the other recipients. + +.. note:: + + Multiple calls to ``setTo()`` will not add new recipients -- each + call overrides the previous calls. If you want to iteratively add + recipients, use the ``addTo()`` method. + + .. code-block:: php + + // Using setTo() to set all recipients in one go + $message->setTo(array( + 'person1@example.org', + 'person2@otherdomain.org' => 'Person 2 Name', + 'person3@example.org', + 'person4@example.org', + 'person5@example.org' => 'Person 5 Name' + )); + + // Using addTo() to add recipients iteratively + $message->addTo('person1@example.org'); + $message->addTo('person2@example.org', 'Person 2 Name'); + +Setting ``Cc:`` Recipients +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Cc:`` recipients are set with the ``setCc()`` or ``addCc()`` methods of the +message. + +To set ``Cc:`` recipients, create the message object using either +``new Swift_Message( ... )`` or ``Swift_Message::newInstance( ... )``, then call +the ``setCc()`` method with a complete array of addresses, or use the +``addCc()`` method to iteratively add recipients. + +The ``setCc()`` method accepts input in various formats as described earlier in +this chapter. The ``addCc()`` method takes either one or two parameters. The +first being the email address and the second optional parameter being the name +of the recipient. + +``Cc:`` recipients are visible in the message headers and will be +seen by the other recipients. + +.. note:: + + Multiple calls to ``setCc()`` will not add new recipients -- each + call overrides the previous calls. If you want to iteratively add Cc: + recipients, use the ``addCc()`` method. + + .. code-block:: php + + // Using setCc() to set all recipients in one go + $message->setCc(array( + 'person1@example.org', + 'person2@otherdomain.org' => 'Person 2 Name', + 'person3@example.org', + 'person4@example.org', + 'person5@example.org' => 'Person 5 Name' + )); + + // Using addCc() to add recipients iteratively + $message->addCc('person1@example.org'); + $message->addCc('person2@example.org', 'Person 2 Name'); + +Setting ``Bcc:`` Recipients +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Bcc:`` recipients receive a copy of the message without anybody else knowing +it, and are set with the ``setBcc()`` or ``addBcc()`` methods of the message. + +To set ``Bcc:`` recipients, create the message object using either ``new +Swift_Message( ... )`` or ``Swift_Message::newInstance( ... )``, then call the +``setBcc()`` method with a complete array of addresses, or use +the ``addBcc()`` method to iteratively add recipients. + +The ``setBcc()`` method accepts input in various formats as described earlier in +this chapter. The ``addBcc()`` method takes either one or two parameters. The +first being the email address and the second optional parameter being the name +of the recipient. + +Only the individual ``Bcc:`` recipient will see their address in the message +headers. Other recipients (including other ``Bcc:`` recipients) will not see the +address. + +.. note:: + + Multiple calls to ``setBcc()`` will not add new recipients -- each + call overrides the previous calls. If you want to iteratively add Bcc: + recipients, use the ``addBcc()`` method. + + .. code-block:: php + + // Using setBcc() to set all recipients in one go + $message->setBcc(array( + 'person1@example.org', + 'person2@otherdomain.org' => 'Person 2 Name', + 'person3@example.org', + 'person4@example.org', + 'person5@example.org' => 'Person 5 Name' + )); + + // Using addBcc() to add recipients iteratively + $message->addBcc('person1@example.org'); + $message->addBcc('person2@example.org', 'Person 2 Name'); + +Specifying Sender Details +------------------------- + +An email must include information about who sent it. Usually this is managed +by the ``From:`` address, however there are other options. + +The sender information is contained in three possible places: + +* ``From:`` -- the address(es) of who wrote the message (required) + +* ``Sender:`` -- the address of the single person who sent the message + (optional) + +* ``Return-Path:`` -- the address where bounces should go to (optional) + +You must always include a ``From:`` address by using ``setFrom()`` on the +message. Swift Mailer will use this as the default ``Return-Path:`` unless +otherwise specified. + +The ``Sender:`` address exists because the person who actually sent the email +may not be the person who wrote the email. It has a higher precedence than the +``From:`` address and will be used as the ``Return-Path:`` unless otherwise +specified. + +Setting the ``From:`` Address +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A ``From:`` address is required and is set with the ``setFrom()`` method of the +message. ``From:`` addresses specify who actually wrote the email, and usually who sent it. + +What most people probably don't realise is that you can have more than one +``From:`` address if more than one person wrote the email -- for example if an +email was put together by a committee. + +To set the ``From:`` address(es): + +* Call the ``setFrom()`` method on the Message. + +The ``From:`` address(es) are visible in the message headers and +will be seen by the recipients. + +.. note:: + + If you set multiple ``From:`` addresses then you absolutely must set a + ``Sender:`` address to indicate who physically sent the message. + + .. code-block:: php + + // Set a single From: address + $message->setFrom('your@address.tld'); + + // Set a From: address including a name + $message->setFrom(array('your@address.tld' => 'Your Name')); + + // Set multiple From: addresses if multiple people wrote the email + $message->setFrom(array( + 'person1@example.org' => 'Sender One', + 'person2@example.org' => 'Sender Two' + )); + +Setting the ``Sender:`` Address +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A ``Sender:`` address specifies who sent the message and is set with the +``setSender()`` method of the message. + +To set the ``Sender:`` address: + +* Call the ``setSender()`` method on the Message. + +The ``Sender:`` address is visible in the message headers and will be seen by +the recipients. + +This address will be used as the ``Return-Path:`` unless otherwise specified. + +.. note:: + + If you set multiple ``From:`` addresses then you absolutely must set a + ``Sender:`` address to indicate who physically sent the message. + +You must not set more than one sender address on a message because it's not +possible for more than one person to send a single message. + +.. code-block:: php + + $message->setSender('your@address.tld'); + +Setting the ``Return-Path:`` (Bounce) Address +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``Return-Path:`` address specifies where bounce notifications should +be sent and is set with the ``setReturnPath()`` method of the message. + +You can only have one ``Return-Path:`` and it must not include +a personal name. + +To set the ``Return-Path:`` address: + +* Call the ``setReturnPath()`` method on the Message. + +Bounce notifications will be sent to this address. + +.. code-block:: php + + $message->setReturnPath('bounces@address.tld'); + + +Signed/Encrypted Message +------------------------ + +To increase the integrity/security of a message it is possible to sign and/or +encrypt an message using one or multiple signers. + +S/MIME +~~~~~~ + +S/MIME can sign and/or encrypt a message using the OpenSSL extension. + +When signing a message, the signer creates a signature of the entire content of the message (including attachments). + +The certificate and private key must be PEM encoded, and can be either created using for example OpenSSL or +obtained at an official Certificate Authority (CA). + +**The recipient must have the CA certificate in the list of trusted issuers in order to verify the signature.** + +**Make sure the certificate supports emailProtection.** + +When using OpenSSL this can done by the including the *-addtrust emailProtection* parameter when creating the certificate. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $smimeSigner = Swift_Signers_SMimeSigner::newInstance(); + $smimeSigner->setSignCertificate('/path/to/certificate.pem', '/path/to/private-key.pem'); + $message->attachSigner($smimeSigner); + +When the private key is secured using a passphrase use the following instead. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $smimeSigner = Swift_Signers_SMimeSigner::newInstance(); + $smimeSigner->setSignCertificate('/path/to/certificate.pem', array('/path/to/private-key.pem', 'passphrase')); + $message->attachSigner($smimeSigner); + +By default the signature is added as attachment, +making the message still readable for mailing agents not supporting signed messages. + +Storing the message as binary is also possible but not recommended. + +.. code-block:: php + + $smimeSigner->setSignCertificate('/path/to/certificate.pem', '/path/to/private-key.pem', PKCS7_BINARY); + +When encrypting the message (also known as enveloping), the entire message (including attachments) +is encrypted using a certificate, and the recipient can then decrypt the message using corresponding private key. + +Encrypting ensures nobody can read the contents of the message without the private key. + +Normally the recipient provides a certificate for encrypting and keeping the decryption key private. + +Using both signing and encrypting is also possible. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $smimeSigner = Swift_Signers_SMimeSigner::newInstance(); + $smimeSigner->setSignCertificate('/path/to/sign-certificate.pem', '/path/to/private-key.pem'); + $smimeSigner->setEncryptCertificate('/path/to/encrypt-certificate.pem'); + $message->attachSigner($smimeSigner); + +The used encryption cipher can be set as the second parameter of setEncryptCertificate() + +See http://php.net/manual/openssl.ciphers for a list of supported ciphers. + +By default the message is first signed and then encrypted, this can be changed by adding. + +.. code-block:: php + + $smimeSigner->setSignThenEncrypt(false); + +**Changing this is not recommended as most mail agents don't support this none-standard way.** + +Only when having trouble with sign then encrypt method, this should be changed. + +Requesting a Read Receipt +------------------------- + +It is possible to request a read-receipt to be sent to an address when the +email is opened. To request a read receipt set the address with +``setReadReceiptTo()``. + +To request a read receipt: + +* Set the address you want the receipt to be sent to with the + ``setReadReceiptTo()`` method on the Message. + +When the email is opened, if the mail client supports it a notification will be sent to this address. + +.. note:: + + Read receipts won't work for the majority of recipients since many mail + clients auto-disable them. Those clients that will send a read receipt + will make the user aware that one has been requested. + + .. code-block:: php + + $message->setReadReceiptTo('your@address.tld'); + +Setting the Character Set +------------------------- + +The character set of the message (and it's MIME parts) is set with the +``setCharset()`` method. You can also change the global default of UTF-8 by +working with the ``Swift_Preferences`` class. + +Swift Mailer will default to the UTF-8 character set unless otherwise +overridden. UTF-8 will work in most instances since it includes all of the +standard US keyboard characters in addition to most international characters. + +It is absolutely vital however that you know what character set your message +(or it's MIME parts) are written in otherwise your message may be received +completely garbled. + +There are two places in Swift Mailer where you can change the character set: + +* In the ``Swift_Preferences`` class + +* On each individual message and/or MIME part + +To set the character set of your Message: + +* Change the global UTF-8 setting by calling + ``Swift_Preferences::setCharset()``; or + +* Call the ``setCharset()`` method on the message or the MIME part. + + .. code-block:: php + + // Approach 1: Change the global setting (suggested) + Swift_Preferences::getInstance()->setCharset('iso-8859-2'); + + // Approach 2: Call the setCharset() method of the message + $message = Swift_Message::newInstance() + ->setCharset('iso-8859-2'); + + // Approach 3: Specify the charset when setting the body + $message->setBody('My body', 'text/html', 'iso-8859-2'); + + // Approach 4: Specify the charset for each part added + $message->addPart('My part', 'text/plain', 'iso-8859-2'); + +Setting the Line Length +----------------------- + +The length of lines in a message can be changed by using the ``setMaxLineLength()`` method on the message. It should be kept to less than +1000 characters. + +Swift Mailer defaults to using 78 characters per line in a message. This is +done for historical reasons and so that the message can be easily viewed in +plain-text terminals. + +To change the maximum length of lines in your Message: + +* Call the ``setMaxLineLength()`` method on the Message. + +Lines that are longer than the line length specified will be wrapped between +words. + +.. note:: + + You should never set a maximum length longer than 1000 characters + according to RFC 2822. Doing so could have unspecified side-effects such + as truncating parts of your message when it is transported between SMTP + servers. + + .. code-block:: php + + $message->setMaxLineLength(1000); + +Setting the Message Priority +---------------------------- + +You can change the priority of the message with ``setPriority()``. Setting the +priority will not change the way your email is sent -- it is purely an +indicative setting for the recipient. + +The priority of a message is an indication to the recipient what significance +it has. Swift Mailer allows you to set the priority by calling the +``setPriority`` method. This method takes an integer value between 1 and 5: + +* `Swift_Mime_SimpleMessage::PRIORITY_HIGHEST`: 1 +* `Swift_Mime_SimpleMessage::PRIORITY_HIGH`: 2 +* `Swift_Mime_SimpleMessage::PRIORITY_NORMAL`: 3 +* `Swift_Mime_SimpleMessage::PRIORITY_LOW`: 4 +* `Swift_Mime_SimpleMessage::PRIORITY_LOWEST`: 5 + +To set the message priority: + +* Set the priority as an integer between 1 and 5 with the ``setPriority()`` + method on the Message. + +.. code-block:: php + + // Indicate "High" priority + $message->setPriority(2); + + // Or use the constant to be more explicit + $message->setPriority(Swift_Mime_SimpleMessage::PRIORITY_HIGH); diff --git a/vendor/swiftmailer/swiftmailer/doc/overview.rst b/vendor/swiftmailer/swiftmailer/doc/overview.rst new file mode 100644 index 00000000..ebfe0083 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/overview.rst @@ -0,0 +1,159 @@ +Library Overview +================ + +Most features (and more) of your every day mail client software are provided +by Swift Mailer, using object-oriented PHP code as the interface. + +In this chapter we will take a short tour of the various components, which put +together form the Swift Mailer library as a whole. You will learn key +terminology used throughout the rest of this book and you will gain a little +understanding of the classes you will work with as you integrate Swift Mailer +into your application. + +This chapter is intended to prepare you for the information contained in the +subsequent chapters of this book. You may choose to skip this chapter if you +are fairly technically minded, though it is likely to save you some time in +the long run if you at least read between the lines here. + +System Requirements +------------------- + +The basic requirements to operate Swift Mailer are extremely minimal and +easily achieved. Historically, Swift Mailer has supported both PHP 4 and PHP 5 +by following a parallel development workflow. Now in it's fourth major +version, and Swift Mailer operates on servers running PHP 5.3.3 or higher. + +The library aims to work with as many PHP 5 projects as possible: + +* PHP 5.3.3 or higher, with the SPL extension (standard) + +* Limited network access to connect to remote SMTP servers + +* 8 MB or more memory limit (Swift Mailer uses around 2 MB) + +Component Breakdown +------------------- + +Swift Mailer is made up of many classes. Each of these classes can be grouped +into a general "component" group which describes the task it is designed to +perform. + +We'll take a brief look at the components which form Swift Mailer in this +section of the book. + +The Mailer +~~~~~~~~~~ + +The mailer class, ``Swift_Mailer`` is the central class in the library where +all of the other components meet one another. ``Swift_Mailer`` acts as a sort +of message dispatcher, communicating with the underlying Transport to deliver +your Message to all intended recipients. + +If you were to dig around in the source code for Swift Mailer you'd notice +that ``Swift_Mailer`` itself is pretty bare. It delegates to other objects for +most tasks and in theory, if you knew the internals of Swift Mailer well you +could by-pass this class entirely. We wouldn't advise doing such a thing +however -- there are reasons this class exists: + +* for consistency, regardless of the Transport used + +* to provide abstraction from the internals in the event internal API changes + are made + +* to provide convenience wrappers around aspects of the internal API + +An instance of ``Swift_Mailer`` is created by the developer before sending any +Messages. + +Transports +~~~~~~~~~~ + +Transports are the classes in Swift Mailer that are responsible for +communicating with a service in order to deliver a Message. There are several +types of Transport in Swift Mailer, all of which implement the Swift_Transport +interface and offer underlying start(), stop() and send() methods. + +Typically you will not need to know how a Transport works under-the-surface, +you will only need to know how to create an instance of one, and which one to +use for your environment. + ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| Class | Features | Pros/cons | ++=================================+=============================================================================================+===============================================================================================================================================+ +| ``Swift_SmtpTransport`` | Sends messages over SMTP; Supports Authentication; Supports Encryption | Very portable; Pleasingly predictable results; Provides good feedback | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| ``Swift_SendmailTransport`` | Communicates with a locally installed ``sendmail`` executable (Linux/UNIX) | Quick time-to-run; Provides less-accurate feedback than SMTP; Requires ``sendmail`` installation | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| ``Swift_LoadBalancedTransport`` | Cycles through a collection of the other Transports to manage load-reduction | Provides graceful fallback if one Transport fails (e.g. an SMTP server is down); Keeps the load on remote services down by spreading the work | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| ``Swift_FailoverTransport`` | Works in conjunction with a collection of the other Transports to provide high-availability | Provides graceful fallback if one Transport fails (e.g. an SMTP server is down) | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ + +MIME Entities +~~~~~~~~~~~~~ + +Everything that forms part of a Message is called a MIME Entity. All MIME +entities in Swift Mailer share a common set of features. There are various +types of MIME entity that serve different purposes such as Attachments and +MIME parts. + +An e-mail message is made up of several relatively simple entities that are +combined in different ways to achieve different results. All of these entities +have the same fundamental outline but serve a different purpose. The Message +itself can be defined as a MIME entity, an Attachment is a MIME entity, all +MIME parts are MIME entities -- and so on! + +The basic units of each MIME entity -- be it the Message itself, or an +Attachment -- are its Headers and its body: + +.. code-block:: text + + Other-Header: Another value + + The body content itself + +The Headers of a MIME entity, and its body must conform to some strict +standards defined by various RFC documents. Swift Mailer ensures that these +specifications are followed by using various types of object, including +Encoders and different Header types to generate the entity. + +Each MIME component implements the base ``Swift_Mime_MimeEntity`` interface, +which offers methods for retrieving Headers, adding new Headers, changing the +Encoder, updating the body and so on! + +All MIME entities have one Header in common -- the Content-Type Header, +updated with the entity's ``setContentType()`` method. + +Encoders +~~~~~~~~ + +Encoders are used to transform the content of Messages generated in Swift +Mailer into a format that is safe to send across the internet and that +conforms to RFC specifications. + +Generally speaking you will not need to interact with the Encoders in Swift +Mailer -- the correct settings will be handled by the library itself. +However they are probably worth a brief mention in the event that you do want +to play with them. + +Both the Headers and the body of all MIME entities (including the Message +itself) use Encoders to ensure the data they contain can be sent over the +internet without becoming corrupted or misinterpreted. + +There are two types of Encoder: Base64 and Quoted-Printable. + +Plugins +~~~~~~~ + +Plugins exist to extend, or modify the behaviour of Swift Mailer. They respond +to Events that are fired within the Transports during sending. + +There are a number of Plugins provided as part of the base Swift Mailer +package and they all follow a common interface to respond to Events fired +within the library. Interfaces are provided to "listen" to each type of Event +fired and to act as desired when a listened-to Event occurs. + +Although several plugins are provided with Swift Mailer out-of-the-box, the +Events system has been specifically designed to make it easy for experienced +object-oriented developers to write their own plugins in order to achieve +goals that may not be possible with the base library. diff --git a/vendor/swiftmailer/swiftmailer/doc/plugins.rst b/vendor/swiftmailer/swiftmailer/doc/plugins.rst new file mode 100644 index 00000000..16ae3356 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/plugins.rst @@ -0,0 +1,385 @@ +Plugins +======= + +Plugins are provided with Swift Mailer and can be used to extend the behavior +of the library in situations where using simple class inheritance would be more complex. + +AntiFlood Plugin +---------------- + +Many SMTP servers have limits on the number of messages that may be sent +during any single SMTP connection. The AntiFlood plugin provides a way to stay +within this limit while still managing a large number of emails. + +A typical limit for a single connection is 100 emails. If the server you +connect to imposes such a limit, it expects you to disconnect after that +number of emails has been sent. You could manage this manually within a loop, +but the AntiFlood plugin provides the necessary wrapper code so that you don't +need to worry about this logic. + +Regardless of limits imposed by the server, it's usually a good idea to be +conservative with the resources of the SMTP server. Sending will become +sluggish if the server is being over-used so using the AntiFlood plugin will +not be a bad idea even if no limits exist. + +The AntiFlood plugin's logic is basically to disconnect and the immediately +re-connect with the SMTP server every X number of emails sent, where X is a +number you specify to the plugin. + +You can also specify a time period in seconds that Swift Mailer should pause +for between the disconnect/re-connect process. It's a good idea to pause for a +short time (say 30 seconds every 100 emails) simply to give the SMTP server a +chance to process its queue and recover some resources. + +Using the AntiFlood Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The AntiFlood Plugin -- like all plugins -- is added with the Mailer class's +``registerPlugin()`` method. It takes two constructor parameters: the number of +emails to pause after, and optionally the number of seconds to pause for. + +To use the AntiFlood plugin: + +* Create an instance of the Mailer using any Transport you choose. + +* Create an instance of the ``Swift_Plugins_AntiFloodPlugin`` class, passing + in one or two constructor parameters. + +* Register the plugin using the Mailer's ``registerPlugin()`` method. + +* Continue using Swift Mailer to send messages as normal. + +When Swift Mailer sends messages it will count the number of messages that +have been sent since the last re-connect. Once the number hits your specified +threshold it will disconnect and re-connect, optionally pausing for a +specified amount of time. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Mailer using any Transport + $mailer = Swift_Mailer::newInstance( + Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ); + + // Use AntiFlood to re-connect after 100 emails + $mailer->registerPlugin(new Swift_Plugins_AntiFloodPlugin(100)); + + // And specify a time in seconds to pause for (30 secs) + $mailer->registerPlugin(new Swift_Plugins_AntiFloodPlugin(100, 30)); + + // Continue sending as normal + for ($lotsOfRecipients as $recipient) { + ... + + $mailer->send( ... ); + } + +Throttler Plugin +---------------- + +If your SMTP server has restrictions in place to limit the rate at which you +send emails, then your code will need to be aware of this rate-limiting. The +Throttler plugin makes Swift Mailer run at a rate-limited speed. + +Many shared hosts don't open their SMTP servers as a free-for-all. Usually +they have policies in place (probably to discourage spammers) that only allow +you to send a fixed number of emails per-hour/day. + +The Throttler plugin supports two modes of rate-limiting and with each, you +will need to do that math to figure out the values you want. The plugin can +limit based on the number of emails per minute, or the number of +bytes-transferred per-minute. + +Using the Throttler Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Throttler Plugin -- like all plugins -- is added with the Mailer class' +``registerPlugin()`` method. It has two required constructor parameters that +tell it how to do its rate-limiting. + +To use the Throttler plugin: + +* Create an instance of the Mailer using any Transport you choose. + +* Create an instance of the ``Swift_Plugins_ThrottlerPlugin`` class, passing + the number of emails, or bytes you wish to limit by, along with the mode + you're using. + +* Register the plugin using the Mailer's ``registerPlugin()`` method. + +* Continue using Swift Mailer to send messages as normal. + +When Swift Mailer sends messages it will keep track of the rate at which sending +messages is occurring. If it realises that sending is happening too fast, it +will cause your program to ``sleep()`` for enough time to average out the rate. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Mailer using any Transport + $mailer = Swift_Mailer::newInstance( + Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ); + + // Rate limit to 100 emails per-minute + $mailer->registerPlugin(new Swift_Plugins_ThrottlerPlugin( + 100, Swift_Plugins_ThrottlerPlugin::MESSAGES_PER_MINUTE + )); + + // Rate limit to 10MB per-minute + $mailer->registerPlugin(new Swift_Plugins_ThrottlerPlugin( + 1024 * 1024 * 10, Swift_Plugins_ThrottlerPlugin::BYTES_PER_MINUTE + )); + + // Continue sending as normal + for ($lotsOfRecipients as $recipient) { + ... + + $mailer->send( ... ); + } + +Logger Plugin +------------- + +The Logger plugins helps with debugging during the process of sending. It can +help to identify why an SMTP server is rejecting addresses, or any other +hard-to-find problems that may arise. + +The Logger plugin comes in two parts. There's the plugin itself, along with +one of a number of possible Loggers that you may choose to use. For example, +the logger may output messages directly in realtime, or it may capture +messages in an array. + +One other notable feature is the way in which the Logger plugin changes +Exception messages. If Exceptions are being thrown but the error message does +not provide conclusive information as to the source of the problem (such as an +ambiguous SMTP error) the Logger plugin includes the entire SMTP transcript in +the error message so that debugging becomes a simpler task. + +There are a few available Loggers included with Swift Mailer, but writing your +own implementation is incredibly simple and is achieved by creating a short +class that implements the ``Swift_Plugins_Logger`` interface. + +* ``Swift_Plugins_Loggers_ArrayLogger``: Keeps a collection of log messages + inside an array. The array content can be cleared or dumped out to the + screen. + +* ``Swift_Plugins_Loggers_EchoLogger``: Prints output to the screen in + realtime. Handy for very rudimentary debug output. + +Using the Logger Plugin +~~~~~~~~~~~~~~~~~~~~~~~ + +The Logger Plugin -- like all plugins -- is added with the Mailer class' +``registerPlugin()`` method. It accepts an instance of ``Swift_Plugins_Logger`` +in its constructor. + +To use the Logger plugin: + +* Create an instance of the Mailer using any Transport you choose. + +* Create an instance of the a Logger implementation of + ``Swift_Plugins_Logger``. + +* Create an instance of the ``Swift_Plugins_LoggerPlugin`` class, passing the + created Logger instance to its constructor. + +* Register the plugin using the Mailer's ``registerPlugin()`` method. + +* Continue using Swift Mailer to send messages as normal. + +* Dump the contents of the log with the logger's ``dump()`` method. + +When Swift Mailer sends messages it will keep a log of all the interactions +with the underlying Transport being used. Depending upon the Logger that has +been used the behaviour will differ, but all implementations offer a way to +get the contents of the log. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Mailer using any Transport + $mailer = Swift_Mailer::newInstance( + Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ); + + // To use the ArrayLogger + $logger = new Swift_Plugins_Loggers_ArrayLogger(); + $mailer->registerPlugin(new Swift_Plugins_LoggerPlugin($logger)); + + // Or to use the Echo Logger + $logger = new Swift_Plugins_Loggers_EchoLogger(); + $mailer->registerPlugin(new Swift_Plugins_LoggerPlugin($logger)); + + // Continue sending as normal + for ($lotsOfRecipients as $recipient) { + ... + + $mailer->send( ... ); + } + + // Dump the log contents + // NOTE: The EchoLogger dumps in realtime so dump() does nothing for it + echo $logger->dump(); + +Decorator Plugin +---------------- + +Often there's a need to send the same message to multiple recipients, but with +tiny variations such as the recipient's name being used inside the message +body. The Decorator plugin aims to provide a solution for allowing these small +differences. + +The decorator plugin works by intercepting the sending process of Swift +Mailer, reading the email address in the To: field and then looking up a set +of replacements for a template. + +While the use of this plugin is simple, it is probably the most commonly +misunderstood plugin due to the way in which it works. The typical mistake +users make is to try registering the plugin multiple times (once for each +recipient) -- inside a loop for example. This is incorrect. + +The Decorator plugin should be registered just once, but containing the list +of all recipients prior to sending. It will use this list of recipients to +find the required replacements during sending. + +Using the Decorator Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To use the Decorator plugin, simply create an associative array of replacements +based on email addresses and then use the mailer's ``registerPlugin()`` method +to add the plugin. + +First create an associative array of replacements based on the email addresses +you'll be sending the message to. + +.. note:: + + The replacements array becomes a 2-dimensional array whose keys are the + email addresses and whose values are an associative array of replacements + for that email address. The curly braces used in this example can be any + type of syntax you choose, provided they match the placeholders in your + email template. + + .. code-block:: php + + $replacements = array(); + foreach ($users as $user) { + $replacements[$user['email']] = array( + '{username}'=>$user['username'], + '{password}'=>$user['password'] + ); + } + +Now create an instance of the Decorator plugin using this array of replacements +and then register it with the Mailer. Do this only once! + +.. code-block:: php + + $decorator = new Swift_Plugins_DecoratorPlugin($replacements); + + $mailer->registerPlugin($decorator); + +When you create your message, replace elements in the body (and/or the subject +line) with your placeholders. + +.. code-block:: php + + $message = Swift_Message::newInstance() + ->setSubject('Important notice for {username}') + ->setBody( + "Hello {username}, we have reset your password to {password}\n" . + "Please log in and change it at your earliest convenience." + ) + ; + + foreach ($users as $user) { + $message->addTo($user['email']); + } + +When you send this message to each of your recipients listed in your +``$replacements`` array they will receive a message customized for just +themselves. For example, the message used above when received may appear like +this to one user: + +.. code-block:: text + + Subject: Important notice for smilingsunshine2009 + + Hello smilingsunshine2009, we have reset your password to rainyDays + Please log in and change it at your earliest convenience. + +While another use may receive the message as: + +.. code-block:: text + + Subject: Important notice for billy-bo-bob + + Hello billy-bo-bob, we have reset your password to dancingOctopus + Please log in and change it at your earliest convenience. + +While the decorator plugin provides a means to solve this problem, there are +various ways you could tackle this problem without the need for a plugin. +We're trying to come up with a better way ourselves and while we have several +(obvious) ideas we don't quite have the perfect solution to go ahead and +implement it. Watch this space. + +Providing Your Own Replacements Lookup for the Decorator +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Filling an array with replacements may not be the best solution for providing +replacement information to the decorator. If you have a more elegant algorithm +that performs replacement lookups on-the-fly you may provide your own +implementation. + +Providing your own replacements lookup implementation for the Decorator is +simply a matter of passing an instance of ``Swift_Plugins_Decorator_Replacements`` to the decorator plugin's constructor, +rather than passing in an array. + +The Replacements interface is very simple to implement since it has just one +method: ``getReplacementsFor($address)``. + +Imagine you want to look up replacements from a database on-the-fly, you might +provide an implementation that does this. You need to create a small class. + +.. code-block:: php + + class DbReplacements implements Swift_Plugins_Decorator_Replacements { + public function getReplacementsFor($address) { + $sql = sprintf( + "SELECT * FROM user WHERE email = '%s'", + mysql_real_escape_string($address) + ); + + $result = mysql_query($sql); + + if ($row = mysql_fetch_assoc($result)) { + return array( + '{username}'=>$row['username'], + '{password}'=>$row['password'] + ); + } + } + } + +Now all you need to do is pass an instance of your class into the Decorator +plugin's constructor instead of passing an array. + +.. code-block:: php + + $decorator = new Swift_Plugins_DecoratorPlugin(new DbReplacements()); + + $mailer->registerPlugin($decorator); + +For each message sent, the plugin will call your class' ``getReplacementsFor()`` +method to find the array of replacements it needs. + +.. note:: + + If your lookup algorithm is case sensitive, you should transform the + ``$address`` argument as appropriate -- for example by passing it + through ``strtolower()``. diff --git a/vendor/swiftmailer/swiftmailer/doc/sending.rst b/vendor/swiftmailer/swiftmailer/doc/sending.rst new file mode 100644 index 00000000..f340404c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/sending.rst @@ -0,0 +1,571 @@ +Sending Messages +================ + +Quick Reference for Sending a Message +------------------------------------- + +Sending a message is very straightforward. You create a Transport, use it to +create the Mailer, then you use the Mailer to send the message. + +To send a Message: + +* Create a Transport from one of the provided Transports -- + ``Swift_SmtpTransport``, ``Swift_SendmailTransport`` + or one of the aggregate Transports. + +* Create an instance of the ``Swift_Mailer`` class, using the Transport as + it's constructor parameter. + +* Create a Message. + +* Send the message via the ``send()`` method on the Mailer object. + +.. caution:: + + The ``Swift_SmtpTransport`` and ``Swift_SendmailTransport`` transports use + ``proc_*`` PHP functions, which might not be available on your PHP + installation. You can easily check if that's the case by running the + following PHP script: ``<?php echo function_exists('proc_open') ? "Yep, + that will work" : "Sorry, that won't work";`` + +When using ``send()`` the message will be sent just like it would be sent if you +used your mail client. An integer is returned which includes the number of +successful recipients. If none of the recipients could be sent to then zero will +be returned, which equates to a boolean ``false``. If you set two ``To:`` +recipients and three ``Bcc:`` recipients in the message and all of the +recipients are delivered to successfully then the value 5 will be returned. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ->setUsername('your username') + ->setPassword('your password') + ; + + /* + You could alternatively use a different transport such as Sendmail: + + // Sendmail + $transport = Swift_SendmailTransport::newInstance('/usr/sbin/sendmail -bs'); + */ + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + // Create a message + $message = Swift_Message::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself') + ; + + // Send the message + $result = $mailer->send($message); + +Transport Types +~~~~~~~~~~~~~~~ + +A Transport is the component which actually does the sending. You need to +provide a Transport object to the Mailer class and there are several possible +options. + +Typically you will not need to know how a Transport works under-the-surface, +you will only need to know how to create an instance of one, and which one to +use for your environment. + +The SMTP Transport +.................. + +The SMTP Transport sends messages over the (standardized) Simple Message +Transfer Protocol. It can deal with encryption and authentication. + +The SMTP Transport, ``Swift_SmtpTransport`` is without doubt the most commonly +used Transport because it will work on 99% of web servers (I just made that +number up, but you get the idea). All the server needs is the ability to +connect to a remote (or even local) SMTP server on the correct port number +(usually 25). + +SMTP servers often require users to authenticate with a username and password +before any mail can be sent to other domains. This is easily achieved using +Swift Mailer with the SMTP Transport. + +SMTP is a protocol -- in other words it's a "way" of communicating a job +to be done (i.e. sending a message). The SMTP protocol is the fundamental +basis on which messages are delivered all over the internet 7 days a week, 365 +days a year. For this reason it's the most "direct" method of sending messages +you can use and it's the one that will give you the most power and feedback +(such as delivery failures) when using Swift Mailer. + +Because SMTP is generally run as a remote service (i.e. you connect to it over +the network/internet) it's extremely portable from server-to-server. You can +easily store the SMTP server address and port number in a configuration file +within your application and adjust the settings accordingly if the code is +moved or if the SMTP server is changed. + +Some SMTP servers -- Google for example -- use encryption for security reasons. +Swift Mailer supports using both SSL and TLS encryption settings. + +Using the SMTP Transport +^^^^^^^^^^^^^^^^^^^^^^^^ + +The SMTP Transport is easy to use. Most configuration options can be set with +the constructor. + +To use the SMTP Transport you need to know which SMTP server your code needs +to connect to. Ask your web host if you're not sure. Lots of people ask me who +to connect to -- I really can't answer that since it's a setting that's +extremely specific to your hosting environment. + +To use the SMTP Transport: + +* Call ``Swift_SmtpTransport::newInstance()`` with the SMTP server name and + optionally with a port number (defaults to 25). + +* Use the returned object to create the Mailer. + +A connection to the SMTP server will be established upon the first call to +``send()``. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('smtp.example.org', 25); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + /* + It's also possible to use multiple method calls + + $transport = Swift_SmtpTransport::newInstance() + ->setHost('smtp.example.org') + ->setPort(25) + ; + */ + +Encrypted SMTP +^^^^^^^^^^^^^^ + +You can use SSL or TLS encryption with the SMTP Transport by specifying it as +a parameter or with a method call. + +To use encryption with the SMTP Transport: + +* Pass the encryption setting as a third parameter to + ``Swift_SmtpTransport::newInstance()``; or + +* Call the ``setEncryption()`` method on the Transport. + +A connection to the SMTP server will be established upon the first call to +``send()``. The connection will be initiated with the correct encryption +settings. + +.. note:: + + For SSL or TLS encryption to work your PHP installation must have + appropriate OpenSSL transports wrappers. You can check if "tls" and/or + "ssl" are present in your PHP installation by using the PHP function + ``stream_get_transports()`` + + .. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('smtp.example.org', 587, 'ssl'); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + /* + It's also possible to use multiple method calls + + $transport = Swift_SmtpTransport::newInstance() + ->setHost('smtp.example.org') + ->setPort(587) + ->setEncryption('ssl') + ; + */ + +SMTP with a Username and Password +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Some servers require authentication. You can provide a username and password +with ``setUsername()`` and ``setPassword()`` methods. + +To use a username and password with the SMTP Transport: + +* Create the Transport with ``Swift_SmtpTransport::newInstance()``. + +* Call the ``setUsername()`` and ``setPassword()`` methods on the Transport. + +Your username and password will be used to authenticate upon first connect +when ``send()`` are first used on the Mailer. + +If authentication fails, an Exception of type ``Swift_TransportException`` will +be thrown. + +.. note:: + + If you need to know early whether or not authentication has failed and an + Exception is going to be thrown, call the ``start()`` method on the + created Transport. + + .. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport the call setUsername() and setPassword() + $transport = Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ->setUsername('username') + ->setPassword('password') + ; + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + +The Sendmail Transport +...................... + +The Sendmail Transport sends messages by communicating with a locally +installed MTA -- such as ``sendmail``. + +The Sendmail Transport, ``Swift_SendmailTransport`` does not directly connect to +any remote services. It is designed for Linux servers that have ``sendmail`` +installed. The Transport starts a local ``sendmail`` process and sends messages +to it. Usually the ``sendmail`` process will respond quickly as it spools your +messages to disk before sending them. + +The Transport is named the Sendmail Transport for historical reasons +(``sendmail`` was the "standard" UNIX tool for sending e-mail for years). It +will send messages using other transfer agents such as Exim or Postfix despite +its name, provided they have the relevant sendmail wrappers so that they can be +started with the correct command-line flags. + +It's a common misconception that because the Sendmail Transport returns a +result very quickly it must therefore deliver messages to recipients quickly +-- this is not true. It's not slow by any means, but it's certainly not +faster than SMTP when it comes to getting messages to the intended recipients. +This is because sendmail itself sends the messages over SMTP once they have +been quickly spooled to disk. + +The Sendmail Transport has the potential to be just as smart of the SMTP +Transport when it comes to notifying Swift Mailer about which recipients were +rejected, but in reality the majority of locally installed ``sendmail`` +instances are not configured well enough to provide any useful feedback. As such +Swift Mailer may report successful deliveries where they did in fact fail before +they even left your server. + +You can run the Sendmail Transport in two different modes specified by command +line flags: + +* "``-bs``" runs in SMTP mode so theoretically it will act like the SMTP + Transport + +* "``-t``" runs in piped mode with no feedback, but theoretically faster, + though not advised + +You can think of the Sendmail Transport as a sort of asynchronous SMTP Transport +-- though if you have problems with delivery failures you should try using the +SMTP Transport instead. Swift Mailer isn't doing the work here, it's simply +passing the work to somebody else (i.e. ``sendmail``). + +Using the Sendmail Transport +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To use the Sendmail Transport you simply need to call +``Swift_SendmailTransport::newInstance()`` with the command as a parameter. + +To use the Sendmail Transport you need to know where ``sendmail`` or another MTA +exists on the server. Swift Mailer uses a default value of +``/usr/sbin/sendmail``, which should work on most systems. + +You specify the entire command as a parameter (i.e. including the command line +flags). Swift Mailer supports operational modes of "``-bs``" (default) and +"``-t``". + +.. note:: + + If you run sendmail in "``-t``" mode you will get no feedback as to whether + or not sending has succeeded. Use "``-bs``" unless you have a reason not to. + +To use the Sendmail Transport: + +* Call ``Swift_SendmailTransport::newInstance()`` with the command, including + the correct command line flags. The default is to use ``/usr/sbin/sendmail + -bs`` if this is not specified. + +* Use the returned object to create the Mailer. + +A sendmail process will be started upon the first call to ``send()``. If the +process cannot be started successfully an Exception of type +``Swift_TransportException`` will be thrown. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SendmailTransport::newInstance('/usr/sbin/exim -bs'); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + +The Mail Transport +.................. + +The Mail Transport sends messages by delegating to PHP's internal +``mail()`` function. + +In my experience -- and others' -- the ``mail()`` function is not particularly +predictable, or helpful. + +Quite notably, the ``mail()`` function behaves entirely differently between +Linux and Windows servers. On linux it uses ``sendmail``, but on Windows it uses +SMTP. + +In order for the ``mail()`` function to even work at all ``php.ini`` needs to be +configured correctly, specifying the location of sendmail or of an SMTP server. + +The problem with ``mail()`` is that it "tries" to simplify things to the point +that it actually makes things more complex due to poor interface design. The +developers of Swift Mailer have gone to a lot of effort to make the Mail +Transport work with a reasonable degree of consistency. + +Serious drawbacks when using this Transport are: + +* Unpredictable message headers + +* Lack of feedback regarding delivery failures + +* Lack of support for several plugins that require real-time delivery feedback + +It's a last resort, and we say that with a passion! + +Available Methods for Sending Messages +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Mailer class offers two methods for sending Messages -- ``send()``. +Each behaves in a slightly different way. + +When a message is sent in Swift Mailer, the Mailer class communicates with +whichever Transport class you have chosen to use. + +Each recipient in the message should either be accepted or rejected by the +Transport. For example, if the domain name on the email address is not +reachable the SMTP Transport may reject the address because it cannot process +it. Whichever method you use -- ``send()`` -- Swift Mailer will return +an integer indicating the number of accepted recipients. + +.. note:: + + It's possible to find out which recipients were rejected -- we'll cover that + later in this chapter. + +Using the ``send()`` Method +........................... + +The ``send()`` method of the ``Swift_Mailer`` class sends a message using +exactly the same logic as your Desktop mail client would use. Just pass it a +Message and get a result. + +To send a Message with ``send()``: + +* Create a Transport from one of the provided Transports -- + ``Swift_SmtpTransport``, ``Swift_SendmailTransport``, + or one of the aggregate Transports. + +* Create an instance of the ``Swift_Mailer`` class, using the Transport as + it's constructor parameter. + +* Create a Message. + +* Send the message via the ``send()`` method on the Mailer object. + +The message will be sent just like it would be sent if you used your mail +client. An integer is returned which includes the number of successful +recipients. If none of the recipients could be sent to then zero will be +returned, which equates to a boolean ``false``. If you set two +``To:`` recipients and three ``Bcc:`` recipients in the message and all of the +recipients are delivered to successfully then the value 5 will be returned. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('localhost', 25); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + // Create a message + $message = Swift_Message::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself') + ; + + // Send the message + $numSent = $mailer->send($message); + + printf("Sent %d messages\n", $numSent); + + /* Note that often that only the boolean equivalent of the + return value is of concern (zero indicates FALSE) + + if ($mailer->send($message)) + { + echo "Sent\n"; + } + else + { + echo "Failed\n"; + } + + */ + +Sending Emails in Batch +....................... + +If you want to send a separate message to each recipient so that only their +own address shows up in the ``To:`` field, follow the following recipe: + +* Create a Transport from one of the provided Transports -- + ``Swift_SmtpTransport``, ``Swift_SendmailTransport``, + or one of the aggregate Transports. + +* Create an instance of the ``Swift_Mailer`` class, using the Transport as + it's constructor parameter. + +* Create a Message. + +* Iterate over the recipients and send message via the ``send()`` method on + the Mailer object. + +Each recipient of the messages receives a different copy with only their own +email address on the ``To:`` field. + +Make sure to add only valid email addresses as recipients. If you try to add an +invalid email address with ``setTo()``, ``setCc()`` or ``setBcc()``, Swift +Mailer will throw a ``Swift_RfcComplianceException``. + +If you add recipients automatically based on a data source that may contain +invalid email addresses, you can prevent possible exceptions by validating the +addresses using ``Swift_Validate::email($email)`` and only adding addresses +that validate. Another way would be to wrap your ``setTo()``, ``setCc()`` and +``setBcc()`` calls in a try-catch block and handle the +``Swift_RfcComplianceException`` in the catch block. + +Handling invalid addresses properly is especially important when sending emails +in large batches since a single invalid address might cause an unhandled +exception and stop the execution or your script early. + +.. note:: + + In the following example, two emails are sent. One to each of + ``receiver@domain.org`` and ``other@domain.org``. These recipients will + not be aware of each other. + + .. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('localhost', 25); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + // Create a message + $message = Swift_Message::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setBody('Here is the message itself') + ; + + // Send the message + $failedRecipients = array(); + $numSent = 0; + $to = array('receiver@domain.org', 'other@domain.org' => 'A name'); + + foreach ($to as $address => $name) + { + if (is_int($address)) { + $message->setTo($name); + } else { + $message->setTo(array($address => $name)); + } + + $numSent += $mailer->send($message, $failedRecipients); + } + + printf("Sent %d messages\n", $numSent); + +Finding out Rejected Addresses +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It's possible to get a list of addresses that were rejected by the Transport +by using a by-reference parameter to ``send()``. + +As Swift Mailer attempts to send the message to each address given to it, if a +recipient is rejected it will be added to the array. You can pass an existing +array, otherwise one will be created by-reference. + +Collecting the list of recipients that were rejected can be useful in +circumstances where you need to "prune" a mailing list for example when some +addresses cannot be delivered to. + +Getting Failures By-reference +............................. + +Collecting delivery failures by-reference with the ``send()`` method is as +simple as passing a variable name to the method call. + +To get failed recipients by-reference: + +* Pass a by-reference variable name to the ``send()`` method of the Mailer + class. + +If the Transport rejects any of the recipients, the culprit addresses will be +added to the array provided by-reference. + +.. note:: + + If the variable name does not yet exist, it will be initialized as an + empty array and then failures will be added to that array. If the variable + already exists it will be type-cast to an array and failures will be added + to it. + + .. code-block:: php + + $mailer = Swift_Mailer::newInstance( ... ); + + $message = Swift_Message::newInstance( ... ) + ->setFrom( ... ) + ->setTo(array( + 'receiver@bad-domain.org' => 'Receiver Name', + 'other@domain.org' => 'A name', + 'other-receiver@bad-domain.org' => 'Other Name' + )) + ->setBody( ... ) + ; + + // Pass a variable name to the send() method + if (!$mailer->send($message, $failures)) + { + echo "Failures:"; + print_r($failures); + } + + /* + Failures: + Array ( + 0 => receiver@bad-domain.org, + 1 => other-receiver@bad-domain.org + ) + */ diff --git a/vendor/swiftmailer/swiftmailer/doc/uml/Encoders.graffle b/vendor/swiftmailer/swiftmailer/doc/uml/Encoders.graffle Binary files differnew file mode 100644 index 00000000..f895752b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/uml/Encoders.graffle diff --git a/vendor/swiftmailer/swiftmailer/doc/uml/Mime.graffle b/vendor/swiftmailer/swiftmailer/doc/uml/Mime.graffle Binary files differnew file mode 100644 index 00000000..e1e33cbf --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/uml/Mime.graffle diff --git a/vendor/swiftmailer/swiftmailer/doc/uml/Transports.graffle b/vendor/swiftmailer/swiftmailer/doc/uml/Transports.graffle Binary files differnew file mode 100644 index 00000000..5670e2b6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/uml/Transports.graffle diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift.php new file mode 100644 index 00000000..82c381b7 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift.php @@ -0,0 +1,80 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * General utility class in Swift Mailer, not to be instantiated. + * + * + * @author Chris Corbyn + */ +abstract class Swift +{ + /** Swift Mailer Version number generated during dist release process */ + const VERSION = '@SWIFT_VERSION_NUMBER@'; + + public static $initialized = false; + public static $inits = array(); + + /** + * Registers an initializer callable that will be called the first time + * a SwiftMailer class is autoloaded. + * + * This enables you to tweak the default configuration in a lazy way. + * + * @param mixed $callable A valid PHP callable that will be called when autoloading the first Swift class + */ + public static function init($callable) + { + self::$inits[] = $callable; + } + + /** + * Internal autoloader for spl_autoload_register(). + * + * @param string $class + */ + public static function autoload($class) + { + // Don't interfere with other autoloaders + if (0 !== strpos($class, 'Swift_')) { + return; + } + + $path = __DIR__.'/'.str_replace('_', '/', $class).'.php'; + + if (!file_exists($path)) { + return; + } + + require $path; + + if (self::$inits && !self::$initialized) { + self::$initialized = true; + foreach (self::$inits as $init) { + call_user_func($init); + } + } + } + + /** + * Configure autoloading using Swift Mailer. + * + * This is designed to play nicely with other autoloaders. + * + * @param mixed $callable A valid PHP callable that will be called when autoloading the first Swift class + */ + public static function registerAutoload($callable = null) + { + if (null !== $callable) { + self::$inits[] = $callable; + } + spl_autoload_register(array('Swift', 'autoload')); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Attachment.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Attachment.php new file mode 100644 index 00000000..a95bccfd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Attachment.php @@ -0,0 +1,71 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Attachment class for attaching files to a {@link Swift_Mime_Message}. + * + * @author Chris Corbyn + */ +class Swift_Attachment extends Swift_Mime_Attachment +{ + /** + * Create a new Attachment. + * + * Details may be optionally provided to the constructor. + * + * @param string|Swift_OutputByteStream $data + * @param string $filename + * @param string $contentType + */ + public function __construct($data = null, $filename = null, $contentType = null) + { + call_user_func_array( + array($this, 'Swift_Mime_Attachment::__construct'), + Swift_DependencyContainer::getInstance() + ->createDependenciesFor('mime.attachment') + ); + + $this->setBody($data); + $this->setFilename($filename); + if ($contentType) { + $this->setContentType($contentType); + } + } + + /** + * Create a new Attachment. + * + * @param string|Swift_OutputByteStream $data + * @param string $filename + * @param string $contentType + * + * @return Swift_Mime_Attachment + */ + public static function newInstance($data = null, $filename = null, $contentType = null) + { + return new self($data, $filename, $contentType); + } + + /** + * Create a new Attachment from a filesystem path. + * + * @param string $path + * @param string $contentType optional + * + * @return Swift_Mime_Attachment + */ + public static function fromPath($path, $contentType = null) + { + return self::newInstance()->setFile( + new Swift_ByteStream_FileByteStream($path), + $contentType + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php new file mode 100644 index 00000000..a7b0e3a6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php @@ -0,0 +1,181 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides the base functionality for an InputStream supporting filters. + * + * @author Chris Corbyn + */ +abstract class Swift_ByteStream_AbstractFilterableInputStream implements Swift_InputByteStream, Swift_Filterable +{ + /** + * Write sequence. + */ + protected $_sequence = 0; + + /** + * StreamFilters. + * + * @var Swift_StreamFilter[] + */ + private $_filters = array(); + + /** + * A buffer for writing. + */ + private $_writeBuffer = ''; + + /** + * Bound streams. + * + * @var Swift_InputByteStream[] + */ + private $_mirrors = array(); + + /** + * Commit the given bytes to the storage medium immediately. + * + * @param string $bytes + */ + abstract protected function _commit($bytes); + + /** + * Flush any buffers/content with immediate effect. + */ + abstract protected function _flush(); + + /** + * Add a StreamFilter to this InputByteStream. + * + * @param Swift_StreamFilter $filter + * @param string $key + */ + public function addFilter(Swift_StreamFilter $filter, $key) + { + $this->_filters[$key] = $filter; + } + + /** + * Remove an already present StreamFilter based on its $key. + * + * @param string $key + */ + public function removeFilter($key) + { + unset($this->_filters[$key]); + } + + /** + * Writes $bytes to the end of the stream. + * + * @param string $bytes + * + * @throws Swift_IoException + * + * @return int + */ + public function write($bytes) + { + $this->_writeBuffer .= $bytes; + foreach ($this->_filters as $filter) { + if ($filter->shouldBuffer($this->_writeBuffer)) { + return; + } + } + $this->_doWrite($this->_writeBuffer); + + return ++$this->_sequence; + } + + /** + * For any bytes that are currently buffered inside the stream, force them + * off the buffer. + * + * @throws Swift_IoException + */ + public function commit() + { + $this->_doWrite($this->_writeBuffer); + } + + /** + * Attach $is to this stream. + * + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + */ + public function bind(Swift_InputByteStream $is) + { + $this->_mirrors[] = $is; + } + + /** + * Remove an already bound stream. + * + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + */ + public function unbind(Swift_InputByteStream $is) + { + foreach ($this->_mirrors as $k => $stream) { + if ($is === $stream) { + if ($this->_writeBuffer !== '') { + $stream->write($this->_writeBuffer); + } + unset($this->_mirrors[$k]); + } + } + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + * + * @throws Swift_IoException + */ + public function flushBuffers() + { + if ($this->_writeBuffer !== '') { + $this->_doWrite($this->_writeBuffer); + } + $this->_flush(); + + foreach ($this->_mirrors as $stream) { + $stream->flushBuffers(); + } + } + + /** Run $bytes through all filters */ + private function _filter($bytes) + { + foreach ($this->_filters as $filter) { + $bytes = $filter->filter($bytes); + } + + return $bytes; + } + + /** Just write the bytes to the stream */ + private function _doWrite($bytes) + { + $this->_commit($this->_filter($bytes)); + + foreach ($this->_mirrors as $stream) { + $stream->write($bytes); + } + + $this->_writeBuffer = ''; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php new file mode 100644 index 00000000..ef05a6d5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php @@ -0,0 +1,182 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Allows reading and writing of bytes to and from an array. + * + * @author Chris Corbyn + */ +class Swift_ByteStream_ArrayByteStream implements Swift_InputByteStream, Swift_OutputByteStream +{ + /** + * The internal stack of bytes. + * + * @var string[] + */ + private $_array = array(); + + /** + * The size of the stack. + * + * @var int + */ + private $_arraySize = 0; + + /** + * The internal pointer offset. + * + * @var int + */ + private $_offset = 0; + + /** + * Bound streams. + * + * @var Swift_InputByteStream[] + */ + private $_mirrors = array(); + + /** + * Create a new ArrayByteStream. + * + * If $stack is given the stream will be populated with the bytes it contains. + * + * @param mixed $stack of bytes in string or array form, optional + */ + public function __construct($stack = null) + { + if (is_array($stack)) { + $this->_array = $stack; + $this->_arraySize = count($stack); + } elseif (is_string($stack)) { + $this->write($stack); + } else { + $this->_array = array(); + } + } + + /** + * Reads $length bytes from the stream into a string and moves the pointer + * through the stream by $length. + * + * If less bytes exist than are requested the + * remaining bytes are given instead. If no bytes are remaining at all, boolean + * false is returned. + * + * @param int $length + * + * @return string + */ + public function read($length) + { + if ($this->_offset == $this->_arraySize) { + return false; + } + + // Don't use array slice + $end = $length + $this->_offset; + $end = $this->_arraySize < $end ? $this->_arraySize : $end; + $ret = ''; + for (; $this->_offset < $end; ++$this->_offset) { + $ret .= $this->_array[$this->_offset]; + } + + return $ret; + } + + /** + * Writes $bytes to the end of the stream. + * + * @param string $bytes + */ + public function write($bytes) + { + $to_add = str_split($bytes); + foreach ($to_add as $value) { + $this->_array[] = $value; + } + $this->_arraySize = count($this->_array); + + foreach ($this->_mirrors as $stream) { + $stream->write($bytes); + } + } + + /** + * Not used. + */ + public function commit() + { + } + + /** + * Attach $is to this stream. + * + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + */ + public function bind(Swift_InputByteStream $is) + { + $this->_mirrors[] = $is; + } + + /** + * Remove an already bound stream. + * + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + */ + public function unbind(Swift_InputByteStream $is) + { + foreach ($this->_mirrors as $k => $stream) { + if ($is === $stream) { + unset($this->_mirrors[$k]); + } + } + } + + /** + * Move the internal read pointer to $byteOffset in the stream. + * + * @param int $byteOffset + * + * @return bool + */ + public function setReadPointer($byteOffset) + { + if ($byteOffset > $this->_arraySize) { + $byteOffset = $this->_arraySize; + } elseif ($byteOffset < 0) { + $byteOffset = 0; + } + + $this->_offset = $byteOffset; + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + */ + public function flushBuffers() + { + $this->_offset = 0; + $this->_array = array(); + $this->_arraySize = 0; + + foreach ($this->_mirrors as $stream) { + $stream->flushBuffers(); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php new file mode 100644 index 00000000..9ed85231 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php @@ -0,0 +1,231 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Allows reading and writing of bytes to and from a file. + * + * @author Chris Corbyn + */ +class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterableInputStream implements Swift_FileStream +{ + /** The internal pointer offset */ + private $_offset = 0; + + /** The path to the file */ + private $_path; + + /** The mode this file is opened in for writing */ + private $_mode; + + /** A lazy-loaded resource handle for reading the file */ + private $_reader; + + /** A lazy-loaded resource handle for writing the file */ + private $_writer; + + /** If magic_quotes_runtime is on, this will be true */ + private $_quotes = false; + + /** If stream is seekable true/false, or null if not known */ + private $_seekable = null; + + /** + * Create a new FileByteStream for $path. + * + * @param string $path + * @param bool $writable if true + */ + public function __construct($path, $writable = false) + { + if (empty($path)) { + throw new Swift_IoException('The path cannot be empty'); + } + $this->_path = $path; + $this->_mode = $writable ? 'w+b' : 'rb'; + + if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() == 1) { + $this->_quotes = true; + } + } + + /** + * Get the complete path to the file. + * + * @return string + */ + public function getPath() + { + return $this->_path; + } + + /** + * Reads $length bytes from the stream into a string and moves the pointer + * through the stream by $length. + * + * If less bytes exist than are requested the + * remaining bytes are given instead. If no bytes are remaining at all, boolean + * false is returned. + * + * @param int $length + * + * @throws Swift_IoException + * + * @return string|bool + */ + public function read($length) + { + $fp = $this->_getReadHandle(); + if (!feof($fp)) { + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 0); + } + $bytes = fread($fp, $length); + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 1); + } + $this->_offset = ftell($fp); + + // If we read one byte after reaching the end of the file + // feof() will return false and an empty string is returned + if ($bytes === '' && feof($fp)) { + $this->_resetReadHandle(); + + return false; + } + + return $bytes; + } + + $this->_resetReadHandle(); + + return false; + } + + /** + * Move the internal read pointer to $byteOffset in the stream. + * + * @param int $byteOffset + * + * @return bool + */ + public function setReadPointer($byteOffset) + { + if (isset($this->_reader)) { + $this->_seekReadStreamToPosition($byteOffset); + } + $this->_offset = $byteOffset; + } + + /** Just write the bytes to the file */ + protected function _commit($bytes) + { + fwrite($this->_getWriteHandle(), $bytes); + $this->_resetReadHandle(); + } + + /** Not used */ + protected function _flush() + { + } + + /** Get the resource for reading */ + private function _getReadHandle() + { + if (!isset($this->_reader)) { + $pointer = @fopen($this->_path, 'rb'); + if (!$pointer) { + throw new Swift_IoException( + 'Unable to open file for reading ['.$this->_path.']' + ); + } + $this->_reader = $pointer; + if ($this->_offset != 0) { + $this->_getReadStreamSeekableStatus(); + $this->_seekReadStreamToPosition($this->_offset); + } + } + + return $this->_reader; + } + + /** Get the resource for writing */ + private function _getWriteHandle() + { + if (!isset($this->_writer)) { + if (!$this->_writer = fopen($this->_path, $this->_mode)) { + throw new Swift_IoException( + 'Unable to open file for writing ['.$this->_path.']' + ); + } + } + + return $this->_writer; + } + + /** Force a reload of the resource for reading */ + private function _resetReadHandle() + { + if (isset($this->_reader)) { + fclose($this->_reader); + $this->_reader = null; + } + } + + /** Check if ReadOnly Stream is seekable */ + private function _getReadStreamSeekableStatus() + { + $metas = stream_get_meta_data($this->_reader); + $this->_seekable = $metas['seekable']; + } + + /** Streams in a readOnly stream ensuring copy if needed */ + private function _seekReadStreamToPosition($offset) + { + if ($this->_seekable === null) { + $this->_getReadStreamSeekableStatus(); + } + if ($this->_seekable === false) { + $currentPos = ftell($this->_reader); + if ($currentPos < $offset) { + $toDiscard = $offset - $currentPos; + fread($this->_reader, $toDiscard); + + return; + } + $this->_copyReadStream(); + } + fseek($this->_reader, $offset, SEEK_SET); + } + + /** Copy a readOnly Stream to ensure seekability */ + private function _copyReadStream() + { + if ($tmpFile = fopen('php://temp/maxmemory:4096', 'w+b')) { + /* We have opened a php:// Stream Should work without problem */ + } elseif (function_exists('sys_get_temp_dir') && is_writable(sys_get_temp_dir()) && ($tmpFile = tmpfile())) { + /* We have opened a tmpfile */ + } else { + throw new Swift_IoException('Unable to copy the file to make it seekable, sys_temp_dir is not writable, php://memory not available'); + } + $currentPos = ftell($this->_reader); + fclose($this->_reader); + $source = fopen($this->_path, 'rb'); + if (!$source) { + throw new Swift_IoException('Unable to open file for copying ['.$this->_path.']'); + } + fseek($tmpFile, 0, SEEK_SET); + while (!feof($source)) { + fwrite($tmpFile, fread($source, 4096)); + } + fseek($tmpFile, $currentPos, SEEK_SET); + fclose($source); + $this->_reader = $tmpFile; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/TemporaryFileByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/TemporaryFileByteStream.php new file mode 100644 index 00000000..1c9a80c0 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/TemporaryFileByteStream.php @@ -0,0 +1,42 @@ +<?php + +/* +* This file is part of SwiftMailer. +* (c) 2004-2009 Chris Corbyn +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +/** + * @author Romain-Geissler + */ +class Swift_ByteStream_TemporaryFileByteStream extends Swift_ByteStream_FileByteStream +{ + public function __construct() + { + $filePath = tempnam(sys_get_temp_dir(), 'FileByteStream'); + + if ($filePath === false) { + throw new Swift_IoException('Failed to retrieve temporary file name.'); + } + + parent::__construct($filePath, true); + } + + public function getContent() + { + if (($content = file_get_contents($this->getPath())) === false) { + throw new Swift_IoException('Failed to get temporary file content.'); + } + + return $content; + } + + public function __destruct() + { + if (file_exists($this->getPath())) { + @unlink($this->getPath()); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader.php new file mode 100644 index 00000000..3d5e854a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader.php @@ -0,0 +1,67 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Analyzes characters for a specific character set. + * + * @author Chris Corbyn + * @author Xavier De Cock <xdecock@gmail.com> + */ +interface Swift_CharacterReader +{ + const MAP_TYPE_INVALID = 0x01; + const MAP_TYPE_FIXED_LEN = 0x02; + const MAP_TYPE_POSITIONS = 0x03; + + /** + * Returns the complete character map. + * + * @param string $string + * @param int $startOffset + * @param array $currentMap + * @param mixed $ignoredChars + * + * @return int + */ + public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars); + + /** + * Returns the mapType, see constants. + * + * @return int + */ + public function getMapType(); + + /** + * Returns an integer which specifies how many more bytes to read. + * + * A positive integer indicates the number of more bytes to fetch before invoking + * this method again. + * + * A value of zero means this is already a valid character. + * A value of -1 means this cannot possibly be a valid character. + * + * @param integer[] $bytes + * @param int $size + * + * @return int + */ + public function validateByteSequence($bytes, $size); + + /** + * Returns the number of bytes which should be read to start each character. + * + * For fixed width character sets this should be the number of octets-per-character. + * For multibyte character sets this will probably be 1. + * + * @return int + */ + public function getInitialByteSize(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php new file mode 100644 index 00000000..6a18e1dd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php @@ -0,0 +1,97 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides fixed-width byte sizes for reading fixed-width character sets. + * + * @author Chris Corbyn + * @author Xavier De Cock <xdecock@gmail.com> + */ +class Swift_CharacterReader_GenericFixedWidthReader implements Swift_CharacterReader +{ + /** + * The number of bytes in a single character. + * + * @var int + */ + private $_width; + + /** + * Creates a new GenericFixedWidthReader using $width bytes per character. + * + * @param int $width + */ + public function __construct($width) + { + $this->_width = $width; + } + + /** + * Returns the complete character map. + * + * @param string $string + * @param int $startOffset + * @param array $currentMap + * @param mixed $ignoredChars + * + * @return int + */ + public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars) + { + $strlen = strlen($string); + // % and / are CPU intensive, so, maybe find a better way + $ignored = $strlen % $this->_width; + $ignoredChars = $ignored ? substr($string, -$ignored) : ''; + $currentMap = $this->_width; + + return ($strlen - $ignored) / $this->_width; + } + + /** + * Returns the mapType. + * + * @return int + */ + public function getMapType() + { + return self::MAP_TYPE_FIXED_LEN; + } + + /** + * Returns an integer which specifies how many more bytes to read. + * + * A positive integer indicates the number of more bytes to fetch before invoking + * this method again. + * + * A value of zero means this is already a valid character. + * A value of -1 means this cannot possibly be a valid character. + * + * @param string $bytes + * @param int $size + * + * @return int + */ + public function validateByteSequence($bytes, $size) + { + $needed = $this->_width - $size; + + return $needed > -1 ? $needed : -1; + } + + /** + * Returns the number of bytes which should be read to start each character. + * + * @return int + */ + public function getInitialByteSize() + { + return $this->_width; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/UsAsciiReader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/UsAsciiReader.php new file mode 100644 index 00000000..67da48f6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/UsAsciiReader.php @@ -0,0 +1,84 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Analyzes US-ASCII characters. + * + * @author Chris Corbyn + */ +class Swift_CharacterReader_UsAsciiReader implements Swift_CharacterReader +{ + /** + * Returns the complete character map. + * + * @param string $string + * @param int $startOffset + * @param array $currentMap + * @param string $ignoredChars + * + * @return int + */ + public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars) + { + $strlen = strlen($string); + $ignoredChars = ''; + for ($i = 0; $i < $strlen; ++$i) { + if ($string[$i] > "\x07F") { + // Invalid char + $currentMap[$i + $startOffset] = $string[$i]; + } + } + + return $strlen; + } + + /** + * Returns mapType. + * + * @return int mapType + */ + public function getMapType() + { + return self::MAP_TYPE_INVALID; + } + + /** + * Returns an integer which specifies how many more bytes to read. + * + * A positive integer indicates the number of more bytes to fetch before invoking + * this method again. + * A value of zero means this is already a valid character. + * A value of -1 means this cannot possibly be a valid character. + * + * @param string $bytes + * @param int $size + * + * @return int + */ + public function validateByteSequence($bytes, $size) + { + $byte = reset($bytes); + if (1 == count($bytes) && $byte >= 0x00 && $byte <= 0x7F) { + return 0; + } + + return -1; + } + + /** + * Returns the number of bytes which should be read to start each character. + * + * @return int + */ + public function getInitialByteSize() + { + return 1; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php new file mode 100644 index 00000000..7379bda2 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php @@ -0,0 +1,176 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Analyzes UTF-8 characters. + * + * @author Chris Corbyn + * @author Xavier De Cock <xdecock@gmail.com> + */ +class Swift_CharacterReader_Utf8Reader implements Swift_CharacterReader +{ + /** Pre-computed for optimization */ + private static $length_map = array( + // N=0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x0N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x2N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x3N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x4N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x5N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x6N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x7N + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x8N + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x9N + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xAN + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xBN + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xCN + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xDN + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 0xEN + 4,4,4,4,4,4,4,4,5,5,5,5,6,6,0,0, // 0xFN + ); + + private static $s_length_map = array( + "\x00" => 1, "\x01" => 1, "\x02" => 1, "\x03" => 1, "\x04" => 1, "\x05" => 1, "\x06" => 1, "\x07" => 1, + "\x08" => 1, "\x09" => 1, "\x0a" => 1, "\x0b" => 1, "\x0c" => 1, "\x0d" => 1, "\x0e" => 1, "\x0f" => 1, + "\x10" => 1, "\x11" => 1, "\x12" => 1, "\x13" => 1, "\x14" => 1, "\x15" => 1, "\x16" => 1, "\x17" => 1, + "\x18" => 1, "\x19" => 1, "\x1a" => 1, "\x1b" => 1, "\x1c" => 1, "\x1d" => 1, "\x1e" => 1, "\x1f" => 1, + "\x20" => 1, "\x21" => 1, "\x22" => 1, "\x23" => 1, "\x24" => 1, "\x25" => 1, "\x26" => 1, "\x27" => 1, + "\x28" => 1, "\x29" => 1, "\x2a" => 1, "\x2b" => 1, "\x2c" => 1, "\x2d" => 1, "\x2e" => 1, "\x2f" => 1, + "\x30" => 1, "\x31" => 1, "\x32" => 1, "\x33" => 1, "\x34" => 1, "\x35" => 1, "\x36" => 1, "\x37" => 1, + "\x38" => 1, "\x39" => 1, "\x3a" => 1, "\x3b" => 1, "\x3c" => 1, "\x3d" => 1, "\x3e" => 1, "\x3f" => 1, + "\x40" => 1, "\x41" => 1, "\x42" => 1, "\x43" => 1, "\x44" => 1, "\x45" => 1, "\x46" => 1, "\x47" => 1, + "\x48" => 1, "\x49" => 1, "\x4a" => 1, "\x4b" => 1, "\x4c" => 1, "\x4d" => 1, "\x4e" => 1, "\x4f" => 1, + "\x50" => 1, "\x51" => 1, "\x52" => 1, "\x53" => 1, "\x54" => 1, "\x55" => 1, "\x56" => 1, "\x57" => 1, + "\x58" => 1, "\x59" => 1, "\x5a" => 1, "\x5b" => 1, "\x5c" => 1, "\x5d" => 1, "\x5e" => 1, "\x5f" => 1, + "\x60" => 1, "\x61" => 1, "\x62" => 1, "\x63" => 1, "\x64" => 1, "\x65" => 1, "\x66" => 1, "\x67" => 1, + "\x68" => 1, "\x69" => 1, "\x6a" => 1, "\x6b" => 1, "\x6c" => 1, "\x6d" => 1, "\x6e" => 1, "\x6f" => 1, + "\x70" => 1, "\x71" => 1, "\x72" => 1, "\x73" => 1, "\x74" => 1, "\x75" => 1, "\x76" => 1, "\x77" => 1, + "\x78" => 1, "\x79" => 1, "\x7a" => 1, "\x7b" => 1, "\x7c" => 1, "\x7d" => 1, "\x7e" => 1, "\x7f" => 1, + "\x80" => 0, "\x81" => 0, "\x82" => 0, "\x83" => 0, "\x84" => 0, "\x85" => 0, "\x86" => 0, "\x87" => 0, + "\x88" => 0, "\x89" => 0, "\x8a" => 0, "\x8b" => 0, "\x8c" => 0, "\x8d" => 0, "\x8e" => 0, "\x8f" => 0, + "\x90" => 0, "\x91" => 0, "\x92" => 0, "\x93" => 0, "\x94" => 0, "\x95" => 0, "\x96" => 0, "\x97" => 0, + "\x98" => 0, "\x99" => 0, "\x9a" => 0, "\x9b" => 0, "\x9c" => 0, "\x9d" => 0, "\x9e" => 0, "\x9f" => 0, + "\xa0" => 0, "\xa1" => 0, "\xa2" => 0, "\xa3" => 0, "\xa4" => 0, "\xa5" => 0, "\xa6" => 0, "\xa7" => 0, + "\xa8" => 0, "\xa9" => 0, "\xaa" => 0, "\xab" => 0, "\xac" => 0, "\xad" => 0, "\xae" => 0, "\xaf" => 0, + "\xb0" => 0, "\xb1" => 0, "\xb2" => 0, "\xb3" => 0, "\xb4" => 0, "\xb5" => 0, "\xb6" => 0, "\xb7" => 0, + "\xb8" => 0, "\xb9" => 0, "\xba" => 0, "\xbb" => 0, "\xbc" => 0, "\xbd" => 0, "\xbe" => 0, "\xbf" => 0, + "\xc0" => 2, "\xc1" => 2, "\xc2" => 2, "\xc3" => 2, "\xc4" => 2, "\xc5" => 2, "\xc6" => 2, "\xc7" => 2, + "\xc8" => 2, "\xc9" => 2, "\xca" => 2, "\xcb" => 2, "\xcc" => 2, "\xcd" => 2, "\xce" => 2, "\xcf" => 2, + "\xd0" => 2, "\xd1" => 2, "\xd2" => 2, "\xd3" => 2, "\xd4" => 2, "\xd5" => 2, "\xd6" => 2, "\xd7" => 2, + "\xd8" => 2, "\xd9" => 2, "\xda" => 2, "\xdb" => 2, "\xdc" => 2, "\xdd" => 2, "\xde" => 2, "\xdf" => 2, + "\xe0" => 3, "\xe1" => 3, "\xe2" => 3, "\xe3" => 3, "\xe4" => 3, "\xe5" => 3, "\xe6" => 3, "\xe7" => 3, + "\xe8" => 3, "\xe9" => 3, "\xea" => 3, "\xeb" => 3, "\xec" => 3, "\xed" => 3, "\xee" => 3, "\xef" => 3, + "\xf0" => 4, "\xf1" => 4, "\xf2" => 4, "\xf3" => 4, "\xf4" => 4, "\xf5" => 4, "\xf6" => 4, "\xf7" => 4, + "\xf8" => 5, "\xf9" => 5, "\xfa" => 5, "\xfb" => 5, "\xfc" => 6, "\xfd" => 6, "\xfe" => 0, "\xff" => 0, + ); + + /** + * Returns the complete character map. + * + * @param string $string + * @param int $startOffset + * @param array $currentMap + * @param mixed $ignoredChars + * + * @return int + */ + public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars) + { + if (!isset($currentMap['i']) || !isset($currentMap['p'])) { + $currentMap['p'] = $currentMap['i'] = array(); + } + + $strlen = strlen($string); + $charPos = count($currentMap['p']); + $foundChars = 0; + $invalid = false; + for ($i = 0; $i < $strlen; ++$i) { + $char = $string[$i]; + $size = self::$s_length_map[$char]; + if ($size == 0) { + /* char is invalid, we must wait for a resync */ + $invalid = true; + continue; + } else { + if ($invalid == true) { + /* We mark the chars as invalid and start a new char */ + $currentMap['p'][$charPos + $foundChars] = $startOffset + $i; + $currentMap['i'][$charPos + $foundChars] = true; + ++$foundChars; + $invalid = false; + } + if (($i + $size) > $strlen) { + $ignoredChars = substr($string, $i); + break; + } + for ($j = 1; $j < $size; ++$j) { + $char = $string[$i + $j]; + if ($char > "\x7F" && $char < "\xC0") { + // Valid - continue parsing + } else { + /* char is invalid, we must wait for a resync */ + $invalid = true; + continue 2; + } + } + /* Ok we got a complete char here */ + $currentMap['p'][$charPos + $foundChars] = $startOffset + $i + $size; + $i += $j - 1; + ++$foundChars; + } + } + + return $foundChars; + } + + /** + * Returns mapType. + * + * @return int mapType + */ + public function getMapType() + { + return self::MAP_TYPE_POSITIONS; + } + + /** + * Returns an integer which specifies how many more bytes to read. + * + * A positive integer indicates the number of more bytes to fetch before invoking + * this method again. + * A value of zero means this is already a valid character. + * A value of -1 means this cannot possibly be a valid character. + * + * @param string $bytes + * @param int $size + * + * @return int + */ + public function validateByteSequence($bytes, $size) + { + if ($size < 1) { + return -1; + } + $needed = self::$length_map[$bytes[0]] - $size; + + return $needed > -1 ? $needed : -1; + } + + /** + * Returns the number of bytes which should be read to start each character. + * + * @return int + */ + public function getInitialByteSize() + { + return 1; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory.php new file mode 100644 index 00000000..15b6c692 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory.php @@ -0,0 +1,26 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A factory for creating CharacterReaders. + * + * @author Chris Corbyn + */ +interface Swift_CharacterReaderFactory +{ + /** + * Returns a CharacterReader suitable for the charset applied. + * + * @param string $charset + * + * @return Swift_CharacterReader + */ + public function getReaderFor($charset); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory/SimpleCharacterReaderFactory.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory/SimpleCharacterReaderFactory.php new file mode 100644 index 00000000..9171a0ba --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory/SimpleCharacterReaderFactory.php @@ -0,0 +1,124 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Standard factory for creating CharacterReaders. + * + * @author Chris Corbyn + */ +class Swift_CharacterReaderFactory_SimpleCharacterReaderFactory implements Swift_CharacterReaderFactory +{ + /** + * A map of charset patterns to their implementation classes. + * + * @var array + */ + private static $_map = array(); + + /** + * Factories which have already been loaded. + * + * @var Swift_CharacterReaderFactory[] + */ + private static $_loaded = array(); + + /** + * Creates a new CharacterReaderFactory. + */ + public function __construct() + { + $this->init(); + } + + public function __wakeup() + { + $this->init(); + } + + public function init() + { + if (count(self::$_map) > 0) { + return; + } + + $prefix = 'Swift_CharacterReader_'; + + $singleByte = array( + 'class' => $prefix.'GenericFixedWidthReader', + 'constructor' => array(1), + ); + + $doubleByte = array( + 'class' => $prefix.'GenericFixedWidthReader', + 'constructor' => array(2), + ); + + $fourBytes = array( + 'class' => $prefix.'GenericFixedWidthReader', + 'constructor' => array(4), + ); + + // Utf-8 + self::$_map['utf-?8'] = array( + 'class' => $prefix.'Utf8Reader', + 'constructor' => array(), + ); + + //7-8 bit charsets + self::$_map['(us-)?ascii'] = $singleByte; + self::$_map['(iso|iec)-?8859-?[0-9]+'] = $singleByte; + self::$_map['windows-?125[0-9]'] = $singleByte; + self::$_map['cp-?[0-9]+'] = $singleByte; + self::$_map['ansi'] = $singleByte; + self::$_map['macintosh'] = $singleByte; + self::$_map['koi-?7'] = $singleByte; + self::$_map['koi-?8-?.+'] = $singleByte; + self::$_map['mik'] = $singleByte; + self::$_map['(cork|t1)'] = $singleByte; + self::$_map['v?iscii'] = $singleByte; + + //16 bits + self::$_map['(ucs-?2|utf-?16)'] = $doubleByte; + + //32 bits + self::$_map['(ucs-?4|utf-?32)'] = $fourBytes; + + // Fallback + self::$_map['.*'] = $singleByte; + } + + /** + * Returns a CharacterReader suitable for the charset applied. + * + * @param string $charset + * + * @return Swift_CharacterReader + */ + public function getReaderFor($charset) + { + $charset = trim(strtolower($charset)); + foreach (self::$_map as $pattern => $spec) { + $re = '/^'.$pattern.'$/D'; + if (preg_match($re, $charset)) { + if (!array_key_exists($pattern, self::$_loaded)) { + $reflector = new ReflectionClass($spec['class']); + if ($reflector->getConstructor()) { + $reader = $reflector->newInstanceArgs($spec['constructor']); + } else { + $reader = $reflector->newInstance(); + } + self::$_loaded[$pattern] = $reader; + } + + return self::$_loaded[$pattern]; + } + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php new file mode 100644 index 00000000..717924f5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php @@ -0,0 +1,89 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An abstract means of reading and writing data in terms of characters as opposed + * to bytes. + * + * Classes implementing this interface may use a subsystem which requires less + * memory than working with large strings of data. + * + * @author Chris Corbyn + */ +interface Swift_CharacterStream +{ + /** + * Set the character set used in this CharacterStream. + * + * @param string $charset + */ + public function setCharacterSet($charset); + + /** + * Set the CharacterReaderFactory for multi charset support. + * + * @param Swift_CharacterReaderFactory $factory + */ + public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory); + + /** + * Overwrite this character stream using the byte sequence in the byte stream. + * + * @param Swift_OutputByteStream $os output stream to read from + */ + public function importByteStream(Swift_OutputByteStream $os); + + /** + * Import a string a bytes into this CharacterStream, overwriting any existing + * data in the stream. + * + * @param string $string + */ + public function importString($string); + + /** + * Read $length characters from the stream and move the internal pointer + * $length further into the stream. + * + * @param int $length + * + * @return string + */ + public function read($length); + + /** + * Read $length characters from the stream and return a 1-dimensional array + * containing there octet values. + * + * @param int $length + * + * @return int[] + */ + public function readBytes($length); + + /** + * Write $chars to the end of the stream. + * + * @param string $chars + */ + public function write($chars); + + /** + * Move the internal pointer to $charOffset in the stream. + * + * @param int $charOffset + */ + public function setPointer($charOffset); + + /** + * Empty the stream and reset the internal pointer. + */ + public function flushContents(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/ArrayCharacterStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/ArrayCharacterStream.php new file mode 100644 index 00000000..d695a6e1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/ArrayCharacterStream.php @@ -0,0 +1,293 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A CharacterStream implementation which stores characters in an internal array. + * + * @author Chris Corbyn + */ +class Swift_CharacterStream_ArrayCharacterStream implements Swift_CharacterStream +{ + /** A map of byte values and their respective characters */ + private static $_charMap; + + /** A map of characters and their derivative byte values */ + private static $_byteMap; + + /** The char reader (lazy-loaded) for the current charset */ + private $_charReader; + + /** A factory for creating CharacterReader instances */ + private $_charReaderFactory; + + /** The character set this stream is using */ + private $_charset; + + /** Array of characters */ + private $_array = array(); + + /** Size of the array of character */ + private $_array_size = array(); + + /** The current character offset in the stream */ + private $_offset = 0; + + /** + * Create a new CharacterStream with the given $chars, if set. + * + * @param Swift_CharacterReaderFactory $factory for loading validators + * @param string $charset used in the stream + */ + public function __construct(Swift_CharacterReaderFactory $factory, $charset) + { + self::_initializeMaps(); + $this->setCharacterReaderFactory($factory); + $this->setCharacterSet($charset); + } + + /** + * Set the character set used in this CharacterStream. + * + * @param string $charset + */ + public function setCharacterSet($charset) + { + $this->_charset = $charset; + $this->_charReader = null; + } + + /** + * Set the CharacterReaderFactory for multi charset support. + * + * @param Swift_CharacterReaderFactory $factory + */ + public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory) + { + $this->_charReaderFactory = $factory; + } + + /** + * Overwrite this character stream using the byte sequence in the byte stream. + * + * @param Swift_OutputByteStream $os output stream to read from + */ + public function importByteStream(Swift_OutputByteStream $os) + { + if (!isset($this->_charReader)) { + $this->_charReader = $this->_charReaderFactory + ->getReaderFor($this->_charset); + } + + $startLength = $this->_charReader->getInitialByteSize(); + while (false !== $bytes = $os->read($startLength)) { + $c = array(); + for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) { + $c[] = self::$_byteMap[$bytes[$i]]; + } + $size = count($c); + $need = $this->_charReader + ->validateByteSequence($c, $size); + if ($need > 0 && + false !== $bytes = $os->read($need)) { + for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) { + $c[] = self::$_byteMap[$bytes[$i]]; + } + } + $this->_array[] = $c; + ++$this->_array_size; + } + } + + /** + * Import a string a bytes into this CharacterStream, overwriting any existing + * data in the stream. + * + * @param string $string + */ + public function importString($string) + { + $this->flushContents(); + $this->write($string); + } + + /** + * Read $length characters from the stream and move the internal pointer + * $length further into the stream. + * + * @param int $length + * + * @return string + */ + public function read($length) + { + if ($this->_offset == $this->_array_size) { + return false; + } + + // Don't use array slice + $arrays = array(); + $end = $length + $this->_offset; + for ($i = $this->_offset; $i < $end; ++$i) { + if (!isset($this->_array[$i])) { + break; + } + $arrays[] = $this->_array[$i]; + } + $this->_offset += $i - $this->_offset; // Limit function calls + $chars = false; + foreach ($arrays as $array) { + $chars .= implode('', array_map('chr', $array)); + } + + return $chars; + } + + /** + * Read $length characters from the stream and return a 1-dimensional array + * containing there octet values. + * + * @param int $length + * + * @return integer[] + */ + public function readBytes($length) + { + if ($this->_offset == $this->_array_size) { + return false; + } + $arrays = array(); + $end = $length + $this->_offset; + for ($i = $this->_offset; $i < $end; ++$i) { + if (!isset($this->_array[$i])) { + break; + } + $arrays[] = $this->_array[$i]; + } + $this->_offset += ($i - $this->_offset); // Limit function calls + + return call_user_func_array('array_merge', $arrays); + } + + /** + * Write $chars to the end of the stream. + * + * @param string $chars + */ + public function write($chars) + { + if (!isset($this->_charReader)) { + $this->_charReader = $this->_charReaderFactory->getReaderFor( + $this->_charset); + } + + $startLength = $this->_charReader->getInitialByteSize(); + + $fp = fopen('php://memory', 'w+b'); + fwrite($fp, $chars); + unset($chars); + fseek($fp, 0, SEEK_SET); + + $buffer = array(0); + $buf_pos = 1; + $buf_len = 1; + $has_datas = true; + do { + $bytes = array(); + // Buffer Filing + if ($buf_len - $buf_pos < $startLength) { + $buf = array_splice($buffer, $buf_pos); + $new = $this->_reloadBuffer($fp, 100); + if ($new) { + $buffer = array_merge($buf, $new); + $buf_len = count($buffer); + $buf_pos = 0; + } else { + $has_datas = false; + } + } + if ($buf_len - $buf_pos > 0) { + $size = 0; + for ($i = 0; $i < $startLength && isset($buffer[$buf_pos]); ++$i) { + ++$size; + $bytes[] = $buffer[$buf_pos++]; + } + $need = $this->_charReader->validateByteSequence( + $bytes, $size); + if ($need > 0) { + if ($buf_len - $buf_pos < $need) { + $new = $this->_reloadBuffer($fp, $need); + + if ($new) { + $buffer = array_merge($buffer, $new); + $buf_len = count($buffer); + } + } + for ($i = 0; $i < $need && isset($buffer[$buf_pos]); ++$i) { + $bytes[] = $buffer[$buf_pos++]; + } + } + $this->_array[] = $bytes; + ++$this->_array_size; + } + } while ($has_datas); + + fclose($fp); + } + + /** + * Move the internal pointer to $charOffset in the stream. + * + * @param int $charOffset + */ + public function setPointer($charOffset) + { + if ($charOffset > $this->_array_size) { + $charOffset = $this->_array_size; + } elseif ($charOffset < 0) { + $charOffset = 0; + } + $this->_offset = $charOffset; + } + + /** + * Empty the stream and reset the internal pointer. + */ + public function flushContents() + { + $this->_offset = 0; + $this->_array = array(); + $this->_array_size = 0; + } + + private function _reloadBuffer($fp, $len) + { + if (!feof($fp) && ($bytes = fread($fp, $len)) !== false) { + $buf = array(); + for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) { + $buf[] = self::$_byteMap[$bytes[$i]]; + } + + return $buf; + } + + return false; + } + + private static function _initializeMaps() + { + if (!isset(self::$_charMap)) { + self::$_charMap = array(); + for ($byte = 0; $byte < 256; ++$byte) { + self::$_charMap[$byte] = chr($byte); + } + self::$_byteMap = array_flip(self::$_charMap); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php new file mode 100644 index 00000000..58bd140f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php @@ -0,0 +1,267 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A CharacterStream implementation which stores characters in an internal array. + * + * @author Xavier De Cock <xdecock@gmail.com> + */ +class Swift_CharacterStream_NgCharacterStream implements Swift_CharacterStream +{ + /** + * The char reader (lazy-loaded) for the current charset. + * + * @var Swift_CharacterReader + */ + private $_charReader; + + /** + * A factory for creating CharacterReader instances. + * + * @var Swift_CharacterReaderFactory + */ + private $_charReaderFactory; + + /** + * The character set this stream is using. + * + * @var string + */ + private $_charset; + + /** + * The data's stored as-is. + * + * @var string + */ + private $_datas = ''; + + /** + * Number of bytes in the stream. + * + * @var int + */ + private $_datasSize = 0; + + /** + * Map. + * + * @var mixed + */ + private $_map; + + /** + * Map Type. + * + * @var int + */ + private $_mapType = 0; + + /** + * Number of characters in the stream. + * + * @var int + */ + private $_charCount = 0; + + /** + * Position in the stream. + * + * @var int + */ + private $_currentPos = 0; + + /** + * Constructor. + * + * @param Swift_CharacterReaderFactory $factory + * @param string $charset + */ + public function __construct(Swift_CharacterReaderFactory $factory, $charset) + { + $this->setCharacterReaderFactory($factory); + $this->setCharacterSet($charset); + } + + /* -- Changing parameters of the stream -- */ + + /** + * Set the character set used in this CharacterStream. + * + * @param string $charset + */ + public function setCharacterSet($charset) + { + $this->_charset = $charset; + $this->_charReader = null; + $this->_mapType = 0; + } + + /** + * Set the CharacterReaderFactory for multi charset support. + * + * @param Swift_CharacterReaderFactory $factory + */ + public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory) + { + $this->_charReaderFactory = $factory; + } + + /** + * @see Swift_CharacterStream::flushContents() + */ + public function flushContents() + { + $this->_datas = null; + $this->_map = null; + $this->_charCount = 0; + $this->_currentPos = 0; + $this->_datasSize = 0; + } + + /** + * @see Swift_CharacterStream::importByteStream() + * + * @param Swift_OutputByteStream $os + */ + public function importByteStream(Swift_OutputByteStream $os) + { + $this->flushContents(); + $blocks = 512; + $os->setReadPointer(0); + while (false !== ($read = $os->read($blocks))) { + $this->write($read); + } + } + + /** + * @see Swift_CharacterStream::importString() + * + * @param string $string + */ + public function importString($string) + { + $this->flushContents(); + $this->write($string); + } + + /** + * @see Swift_CharacterStream::read() + * + * @param int $length + * + * @return string + */ + public function read($length) + { + if ($this->_currentPos >= $this->_charCount) { + return false; + } + $ret = false; + $length = $this->_currentPos + $length > $this->_charCount ? $this->_charCount - $this->_currentPos : $length; + switch ($this->_mapType) { + case Swift_CharacterReader::MAP_TYPE_FIXED_LEN: + $len = $length * $this->_map; + $ret = substr($this->_datas, + $this->_currentPos * $this->_map, + $len); + $this->_currentPos += $length; + break; + + case Swift_CharacterReader::MAP_TYPE_INVALID: + $ret = ''; + for (; $this->_currentPos < $length; ++$this->_currentPos) { + if (isset($this->_map[$this->_currentPos])) { + $ret .= '?'; + } else { + $ret .= $this->_datas[$this->_currentPos]; + } + } + break; + + case Swift_CharacterReader::MAP_TYPE_POSITIONS: + $end = $this->_currentPos + $length; + $end = $end > $this->_charCount ? $this->_charCount : $end; + $ret = ''; + $start = 0; + if ($this->_currentPos > 0) { + $start = $this->_map['p'][$this->_currentPos - 1]; + } + $to = $start; + for (; $this->_currentPos < $end; ++$this->_currentPos) { + if (isset($this->_map['i'][$this->_currentPos])) { + $ret .= substr($this->_datas, $start, $to - $start).'?'; + $start = $this->_map['p'][$this->_currentPos]; + } else { + $to = $this->_map['p'][$this->_currentPos]; + } + } + $ret .= substr($this->_datas, $start, $to - $start); + break; + } + + return $ret; + } + + /** + * @see Swift_CharacterStream::readBytes() + * + * @param int $length + * + * @return int[] + */ + public function readBytes($length) + { + $read = $this->read($length); + if ($read !== false) { + $ret = array_map('ord', str_split($read, 1)); + + return $ret; + } + + return false; + } + + /** + * @see Swift_CharacterStream::setPointer() + * + * @param int $charOffset + */ + public function setPointer($charOffset) + { + if ($this->_charCount < $charOffset) { + $charOffset = $this->_charCount; + } + $this->_currentPos = $charOffset; + } + + /** + * @see Swift_CharacterStream::write() + * + * @param string $chars + */ + public function write($chars) + { + if (!isset($this->_charReader)) { + $this->_charReader = $this->_charReaderFactory->getReaderFor( + $this->_charset); + $this->_map = array(); + $this->_mapType = $this->_charReader->getMapType(); + } + $ignored = ''; + $this->_datas .= $chars; + $this->_charCount += $this->_charReader->getCharPositions(substr($this->_datas, $this->_datasSize), $this->_datasSize, $this->_map, $ignored); + if ($ignored !== false) { + $this->_datasSize = strlen($this->_datas) - strlen($ignored); + } else { + $this->_datasSize = strlen($this->_datas); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ConfigurableSpool.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ConfigurableSpool.php new file mode 100644 index 00000000..4ae5bacf --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ConfigurableSpool.php @@ -0,0 +1,63 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2009 Fabien Potencier <fabien.potencier@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Base class for Spools (implements time and message limits). + * + * @author Fabien Potencier + */ +abstract class Swift_ConfigurableSpool implements Swift_Spool +{ + /** The maximum number of messages to send per flush */ + private $_message_limit; + + /** The time limit per flush */ + private $_time_limit; + + /** + * Sets the maximum number of messages to send per flush. + * + * @param int $limit + */ + public function setMessageLimit($limit) + { + $this->_message_limit = (int) $limit; + } + + /** + * Gets the maximum number of messages to send per flush. + * + * @return int The limit + */ + public function getMessageLimit() + { + return $this->_message_limit; + } + + /** + * Sets the time limit (in seconds) per flush. + * + * @param int $limit The limit + */ + public function setTimeLimit($limit) + { + $this->_time_limit = (int) $limit; + } + + /** + * Gets the time limit (in seconds) per flush. + * + * @return int The limit + */ + public function getTimeLimit() + { + return $this->_time_limit; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyContainer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyContainer.php new file mode 100644 index 00000000..8c1074a3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyContainer.php @@ -0,0 +1,373 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Dependency Injection container. + * + * @author Chris Corbyn + */ +class Swift_DependencyContainer +{ + /** Constant for literal value types */ + const TYPE_VALUE = 0x0001; + + /** Constant for new instance types */ + const TYPE_INSTANCE = 0x0010; + + /** Constant for shared instance types */ + const TYPE_SHARED = 0x0100; + + /** Constant for aliases */ + const TYPE_ALIAS = 0x1000; + + /** Singleton instance */ + private static $_instance = null; + + /** The data container */ + private $_store = array(); + + /** The current endpoint in the data container */ + private $_endPoint; + + /** + * Constructor should not be used. + * + * Use {@link getInstance()} instead. + */ + public function __construct() + { + } + + /** + * Returns a singleton of the DependencyContainer. + * + * @return Swift_DependencyContainer + */ + public static function getInstance() + { + if (!isset(self::$_instance)) { + self::$_instance = new self(); + } + + return self::$_instance; + } + + /** + * List the names of all items stored in the Container. + * + * @return array + */ + public function listItems() + { + return array_keys($this->_store); + } + + /** + * Test if an item is registered in this container with the given name. + * + * @see register() + * + * @param string $itemName + * + * @return bool + */ + public function has($itemName) + { + return array_key_exists($itemName, $this->_store) + && isset($this->_store[$itemName]['lookupType']); + } + + /** + * Lookup the item with the given $itemName. + * + * @see register() + * + * @param string $itemName + * + * @throws Swift_DependencyException If the dependency is not found + * + * @return mixed + */ + public function lookup($itemName) + { + if (!$this->has($itemName)) { + throw new Swift_DependencyException( + 'Cannot lookup dependency "'.$itemName.'" since it is not registered.' + ); + } + + switch ($this->_store[$itemName]['lookupType']) { + case self::TYPE_ALIAS: + return $this->_createAlias($itemName); + case self::TYPE_VALUE: + return $this->_getValue($itemName); + case self::TYPE_INSTANCE: + return $this->_createNewInstance($itemName); + case self::TYPE_SHARED: + return $this->_createSharedInstance($itemName); + } + } + + /** + * Create an array of arguments passed to the constructor of $itemName. + * + * @param string $itemName + * + * @return array + */ + public function createDependenciesFor($itemName) + { + $args = array(); + if (isset($this->_store[$itemName]['args'])) { + $args = $this->_resolveArgs($this->_store[$itemName]['args']); + } + + return $args; + } + + /** + * Register a new dependency with $itemName. + * + * This method returns the current DependencyContainer instance because it + * requires the use of the fluid interface to set the specific details for the + * dependency. + * + * @see asNewInstanceOf(), asSharedInstanceOf(), asValue() + * + * @param string $itemName + * + * @return Swift_DependencyContainer + */ + public function register($itemName) + { + $this->_store[$itemName] = array(); + $this->_endPoint = &$this->_store[$itemName]; + + return $this; + } + + /** + * Specify the previously registered item as a literal value. + * + * {@link register()} must be called before this will work. + * + * @param mixed $value + * + * @return Swift_DependencyContainer + */ + public function asValue($value) + { + $endPoint = &$this->_getEndPoint(); + $endPoint['lookupType'] = self::TYPE_VALUE; + $endPoint['value'] = $value; + + return $this; + } + + /** + * Specify the previously registered item as an alias of another item. + * + * @param string $lookup + * + * @return Swift_DependencyContainer + */ + public function asAliasOf($lookup) + { + $endPoint = &$this->_getEndPoint(); + $endPoint['lookupType'] = self::TYPE_ALIAS; + $endPoint['ref'] = $lookup; + + return $this; + } + + /** + * Specify the previously registered item as a new instance of $className. + * + * {@link register()} must be called before this will work. + * Any arguments can be set with {@link withDependencies()}, + * {@link addConstructorValue()} or {@link addConstructorLookup()}. + * + * @see withDependencies(), addConstructorValue(), addConstructorLookup() + * + * @param string $className + * + * @return Swift_DependencyContainer + */ + public function asNewInstanceOf($className) + { + $endPoint = &$this->_getEndPoint(); + $endPoint['lookupType'] = self::TYPE_INSTANCE; + $endPoint['className'] = $className; + + return $this; + } + + /** + * Specify the previously registered item as a shared instance of $className. + * + * {@link register()} must be called before this will work. + * + * @param string $className + * + * @return Swift_DependencyContainer + */ + public function asSharedInstanceOf($className) + { + $endPoint = &$this->_getEndPoint(); + $endPoint['lookupType'] = self::TYPE_SHARED; + $endPoint['className'] = $className; + + return $this; + } + + /** + * Specify a list of injected dependencies for the previously registered item. + * + * This method takes an array of lookup names. + * + * @see addConstructorValue(), addConstructorLookup() + * + * @param array $lookups + * + * @return Swift_DependencyContainer + */ + public function withDependencies(array $lookups) + { + $endPoint = &$this->_getEndPoint(); + $endPoint['args'] = array(); + foreach ($lookups as $lookup) { + $this->addConstructorLookup($lookup); + } + + return $this; + } + + /** + * Specify a literal (non looked up) value for the constructor of the + * previously registered item. + * + * @see withDependencies(), addConstructorLookup() + * + * @param mixed $value + * + * @return Swift_DependencyContainer + */ + public function addConstructorValue($value) + { + $endPoint = &$this->_getEndPoint(); + if (!isset($endPoint['args'])) { + $endPoint['args'] = array(); + } + $endPoint['args'][] = array('type' => 'value', 'item' => $value); + + return $this; + } + + /** + * Specify a dependency lookup for the constructor of the previously + * registered item. + * + * @see withDependencies(), addConstructorValue() + * + * @param string $lookup + * + * @return Swift_DependencyContainer + */ + public function addConstructorLookup($lookup) + { + $endPoint = &$this->_getEndPoint(); + if (!isset($this->_endPoint['args'])) { + $endPoint['args'] = array(); + } + $endPoint['args'][] = array('type' => 'lookup', 'item' => $lookup); + + return $this; + } + + /** Get the literal value with $itemName */ + private function _getValue($itemName) + { + return $this->_store[$itemName]['value']; + } + + /** Resolve an alias to another item */ + private function _createAlias($itemName) + { + return $this->lookup($this->_store[$itemName]['ref']); + } + + /** Create a fresh instance of $itemName */ + private function _createNewInstance($itemName) + { + $reflector = new ReflectionClass($this->_store[$itemName]['className']); + if ($reflector->getConstructor()) { + return $reflector->newInstanceArgs( + $this->createDependenciesFor($itemName) + ); + } + + return $reflector->newInstance(); + } + + /** Create and register a shared instance of $itemName */ + private function _createSharedInstance($itemName) + { + if (!isset($this->_store[$itemName]['instance'])) { + $this->_store[$itemName]['instance'] = $this->_createNewInstance($itemName); + } + + return $this->_store[$itemName]['instance']; + } + + /** Get the current endpoint in the store */ + private function &_getEndPoint() + { + if (!isset($this->_endPoint)) { + throw new BadMethodCallException( + 'Component must first be registered by calling register()' + ); + } + + return $this->_endPoint; + } + + /** Get an argument list with dependencies resolved */ + private function _resolveArgs(array $args) + { + $resolved = array(); + foreach ($args as $argDefinition) { + switch ($argDefinition['type']) { + case 'lookup': + $resolved[] = $this->_lookupRecursive($argDefinition['item']); + break; + case 'value': + $resolved[] = $argDefinition['item']; + break; + } + } + + return $resolved; + } + + /** Resolve a single dependency with an collections */ + private function _lookupRecursive($item) + { + if (is_array($item)) { + $collection = array(); + foreach ($item as $k => $v) { + $collection[$k] = $this->_lookupRecursive($v); + } + + return $collection; + } + + return $this->lookup($item); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyException.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyException.php new file mode 100644 index 00000000..799d38d8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyException.php @@ -0,0 +1,27 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * DependencyException gets thrown when a requested dependency is missing. + * + * @author Chris Corbyn + */ +class Swift_DependencyException extends Swift_SwiftException +{ + /** + * Create a new DependencyException with $message. + * + * @param string $message + */ + public function __construct($message) + { + parent::__construct($message); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/EmbeddedFile.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/EmbeddedFile.php new file mode 100644 index 00000000..d8c72ad4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/EmbeddedFile.php @@ -0,0 +1,69 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An embedded file, in a multipart message. + * + * @author Chris Corbyn + */ +class Swift_EmbeddedFile extends Swift_Mime_EmbeddedFile +{ + /** + * Create a new EmbeddedFile. + * + * Details may be optionally provided to the constructor. + * + * @param string|Swift_OutputByteStream $data + * @param string $filename + * @param string $contentType + */ + public function __construct($data = null, $filename = null, $contentType = null) + { + call_user_func_array( + array($this, 'Swift_Mime_EmbeddedFile::__construct'), + Swift_DependencyContainer::getInstance() + ->createDependenciesFor('mime.embeddedfile') + ); + + $this->setBody($data); + $this->setFilename($filename); + if ($contentType) { + $this->setContentType($contentType); + } + } + + /** + * Create a new EmbeddedFile. + * + * @param string|Swift_OutputByteStream $data + * @param string $filename + * @param string $contentType + * + * @return Swift_Mime_EmbeddedFile + */ + public static function newInstance($data = null, $filename = null, $contentType = null) + { + return new self($data, $filename, $contentType); + } + + /** + * Create a new EmbeddedFile from a filesystem path. + * + * @param string $path + * + * @return Swift_Mime_EmbeddedFile + */ + public static function fromPath($path) + { + return self::newInstance()->setFile( + new Swift_ByteStream_FileByteStream($path) + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder.php new file mode 100644 index 00000000..2073abca --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder.php @@ -0,0 +1,28 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Interface for all Encoder schemes. + * + * @author Chris Corbyn + */ +interface Swift_Encoder extends Swift_Mime_CharsetObserver +{ + /** + * Encode a given string to produce an encoded string. + * + * @param string $string + * @param int $firstLineOffset if first line needs to be shorter + * @param int $maxLineLength - 0 indicates the default length for this encoding + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Base64Encoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Base64Encoder.php new file mode 100644 index 00000000..0e7b2a1b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Base64Encoder.php @@ -0,0 +1,58 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Handles Base 64 Encoding in Swift Mailer. + * + * @author Chris Corbyn + */ +class Swift_Encoder_Base64Encoder implements Swift_Encoder +{ + /** + * Takes an unencoded string and produces a Base64 encoded string from it. + * + * Base64 encoded strings have a maximum line length of 76 characters. + * If the first line needs to be shorter, indicate the difference with + * $firstLineOffset. + * + * @param string $string to encode + * @param int $firstLineOffset + * @param int $maxLineLength optional, 0 indicates the default of 76 bytes + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + if (0 >= $maxLineLength || 76 < $maxLineLength) { + $maxLineLength = 76; + } + + $encodedString = base64_encode($string); + $firstLine = ''; + + if (0 != $firstLineOffset) { + $firstLine = substr( + $encodedString, 0, $maxLineLength - $firstLineOffset + )."\r\n"; + $encodedString = substr( + $encodedString, $maxLineLength - $firstLineOffset + ); + } + + return $firstLine.trim(chunk_split($encodedString, $maxLineLength, "\r\n")); + } + + /** + * Does nothing. + */ + public function charsetChanged($charset) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php new file mode 100644 index 00000000..8a81fe39 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php @@ -0,0 +1,300 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Handles Quoted Printable (QP) Encoding in Swift Mailer. + * + * Possibly the most accurate RFC 2045 QP implementation found in PHP. + * + * @author Chris Corbyn + */ +class Swift_Encoder_QpEncoder implements Swift_Encoder +{ + /** + * The CharacterStream used for reading characters (as opposed to bytes). + * + * @var Swift_CharacterStream + */ + protected $_charStream; + + /** + * A filter used if input should be canonicalized. + * + * @var Swift_StreamFilter + */ + protected $_filter; + + /** + * Pre-computed QP for HUGE optimization. + * + * @var string[] + */ + protected static $_qpMap = array( + 0 => '=00', 1 => '=01', 2 => '=02', 3 => '=03', 4 => '=04', + 5 => '=05', 6 => '=06', 7 => '=07', 8 => '=08', 9 => '=09', + 10 => '=0A', 11 => '=0B', 12 => '=0C', 13 => '=0D', 14 => '=0E', + 15 => '=0F', 16 => '=10', 17 => '=11', 18 => '=12', 19 => '=13', + 20 => '=14', 21 => '=15', 22 => '=16', 23 => '=17', 24 => '=18', + 25 => '=19', 26 => '=1A', 27 => '=1B', 28 => '=1C', 29 => '=1D', + 30 => '=1E', 31 => '=1F', 32 => '=20', 33 => '=21', 34 => '=22', + 35 => '=23', 36 => '=24', 37 => '=25', 38 => '=26', 39 => '=27', + 40 => '=28', 41 => '=29', 42 => '=2A', 43 => '=2B', 44 => '=2C', + 45 => '=2D', 46 => '=2E', 47 => '=2F', 48 => '=30', 49 => '=31', + 50 => '=32', 51 => '=33', 52 => '=34', 53 => '=35', 54 => '=36', + 55 => '=37', 56 => '=38', 57 => '=39', 58 => '=3A', 59 => '=3B', + 60 => '=3C', 61 => '=3D', 62 => '=3E', 63 => '=3F', 64 => '=40', + 65 => '=41', 66 => '=42', 67 => '=43', 68 => '=44', 69 => '=45', + 70 => '=46', 71 => '=47', 72 => '=48', 73 => '=49', 74 => '=4A', + 75 => '=4B', 76 => '=4C', 77 => '=4D', 78 => '=4E', 79 => '=4F', + 80 => '=50', 81 => '=51', 82 => '=52', 83 => '=53', 84 => '=54', + 85 => '=55', 86 => '=56', 87 => '=57', 88 => '=58', 89 => '=59', + 90 => '=5A', 91 => '=5B', 92 => '=5C', 93 => '=5D', 94 => '=5E', + 95 => '=5F', 96 => '=60', 97 => '=61', 98 => '=62', 99 => '=63', + 100 => '=64', 101 => '=65', 102 => '=66', 103 => '=67', 104 => '=68', + 105 => '=69', 106 => '=6A', 107 => '=6B', 108 => '=6C', 109 => '=6D', + 110 => '=6E', 111 => '=6F', 112 => '=70', 113 => '=71', 114 => '=72', + 115 => '=73', 116 => '=74', 117 => '=75', 118 => '=76', 119 => '=77', + 120 => '=78', 121 => '=79', 122 => '=7A', 123 => '=7B', 124 => '=7C', + 125 => '=7D', 126 => '=7E', 127 => '=7F', 128 => '=80', 129 => '=81', + 130 => '=82', 131 => '=83', 132 => '=84', 133 => '=85', 134 => '=86', + 135 => '=87', 136 => '=88', 137 => '=89', 138 => '=8A', 139 => '=8B', + 140 => '=8C', 141 => '=8D', 142 => '=8E', 143 => '=8F', 144 => '=90', + 145 => '=91', 146 => '=92', 147 => '=93', 148 => '=94', 149 => '=95', + 150 => '=96', 151 => '=97', 152 => '=98', 153 => '=99', 154 => '=9A', + 155 => '=9B', 156 => '=9C', 157 => '=9D', 158 => '=9E', 159 => '=9F', + 160 => '=A0', 161 => '=A1', 162 => '=A2', 163 => '=A3', 164 => '=A4', + 165 => '=A5', 166 => '=A6', 167 => '=A7', 168 => '=A8', 169 => '=A9', + 170 => '=AA', 171 => '=AB', 172 => '=AC', 173 => '=AD', 174 => '=AE', + 175 => '=AF', 176 => '=B0', 177 => '=B1', 178 => '=B2', 179 => '=B3', + 180 => '=B4', 181 => '=B5', 182 => '=B6', 183 => '=B7', 184 => '=B8', + 185 => '=B9', 186 => '=BA', 187 => '=BB', 188 => '=BC', 189 => '=BD', + 190 => '=BE', 191 => '=BF', 192 => '=C0', 193 => '=C1', 194 => '=C2', + 195 => '=C3', 196 => '=C4', 197 => '=C5', 198 => '=C6', 199 => '=C7', + 200 => '=C8', 201 => '=C9', 202 => '=CA', 203 => '=CB', 204 => '=CC', + 205 => '=CD', 206 => '=CE', 207 => '=CF', 208 => '=D0', 209 => '=D1', + 210 => '=D2', 211 => '=D3', 212 => '=D4', 213 => '=D5', 214 => '=D6', + 215 => '=D7', 216 => '=D8', 217 => '=D9', 218 => '=DA', 219 => '=DB', + 220 => '=DC', 221 => '=DD', 222 => '=DE', 223 => '=DF', 224 => '=E0', + 225 => '=E1', 226 => '=E2', 227 => '=E3', 228 => '=E4', 229 => '=E5', + 230 => '=E6', 231 => '=E7', 232 => '=E8', 233 => '=E9', 234 => '=EA', + 235 => '=EB', 236 => '=EC', 237 => '=ED', 238 => '=EE', 239 => '=EF', + 240 => '=F0', 241 => '=F1', 242 => '=F2', 243 => '=F3', 244 => '=F4', + 245 => '=F5', 246 => '=F6', 247 => '=F7', 248 => '=F8', 249 => '=F9', + 250 => '=FA', 251 => '=FB', 252 => '=FC', 253 => '=FD', 254 => '=FE', + 255 => '=FF', + ); + + protected static $_safeMapShare = array(); + + /** + * A map of non-encoded ascii characters. + * + * @var string[] + */ + protected $_safeMap = array(); + + /** + * Creates a new QpEncoder for the given CharacterStream. + * + * @param Swift_CharacterStream $charStream to use for reading characters + * @param Swift_StreamFilter $filter if input should be canonicalized + */ + public function __construct(Swift_CharacterStream $charStream, Swift_StreamFilter $filter = null) + { + $this->_charStream = $charStream; + if (!isset(self::$_safeMapShare[$this->getSafeMapShareId()])) { + $this->initSafeMap(); + self::$_safeMapShare[$this->getSafeMapShareId()] = $this->_safeMap; + } else { + $this->_safeMap = self::$_safeMapShare[$this->getSafeMapShareId()]; + } + $this->_filter = $filter; + } + + public function __sleep() + { + return array('_charStream', '_filter'); + } + + public function __wakeup() + { + if (!isset(self::$_safeMapShare[$this->getSafeMapShareId()])) { + $this->initSafeMap(); + self::$_safeMapShare[$this->getSafeMapShareId()] = $this->_safeMap; + } else { + $this->_safeMap = self::$_safeMapShare[$this->getSafeMapShareId()]; + } + } + + protected function getSafeMapShareId() + { + return get_class($this); + } + + protected function initSafeMap() + { + foreach (array_merge( + array(0x09, 0x20), range(0x21, 0x3C), range(0x3E, 0x7E)) as $byte) { + $this->_safeMap[$byte] = chr($byte); + } + } + + /** + * Takes an unencoded string and produces a QP encoded string from it. + * + * QP encoded strings have a maximum line length of 76 characters. + * If the first line needs to be shorter, indicate the difference with + * $firstLineOffset. + * + * @param string $string to encode + * @param int $firstLineOffset, optional + * @param int $maxLineLength, optional 0 indicates the default of 76 chars + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + if ($maxLineLength > 76 || $maxLineLength <= 0) { + $maxLineLength = 76; + } + + $thisLineLength = $maxLineLength - $firstLineOffset; + + $lines = array(); + $lNo = 0; + $lines[$lNo] = ''; + $currentLine = &$lines[$lNo++]; + $size = $lineLen = 0; + + $this->_charStream->flushContents(); + $this->_charStream->importString($string); + + // Fetching more than 4 chars at one is slower, as is fetching fewer bytes + // Conveniently 4 chars is the UTF-8 safe number since UTF-8 has up to 6 + // bytes per char and (6 * 4 * 3 = 72 chars per line) * =NN is 3 bytes + while (false !== $bytes = $this->_nextSequence()) { + // If we're filtering the input + if (isset($this->_filter)) { + // If we can't filter because we need more bytes + while ($this->_filter->shouldBuffer($bytes)) { + // Then collect bytes into the buffer + if (false === $moreBytes = $this->_nextSequence(1)) { + break; + } + + foreach ($moreBytes as $b) { + $bytes[] = $b; + } + } + // And filter them + $bytes = $this->_filter->filter($bytes); + } + + $enc = $this->_encodeByteSequence($bytes, $size); + + $i = strpos($enc, '=0D=0A'); + $newLineLength = $lineLen + ($i === false ? $size : $i); + + if ($currentLine && $newLineLength >= $thisLineLength) { + $lines[$lNo] = ''; + $currentLine = &$lines[$lNo++]; + $thisLineLength = $maxLineLength; + $lineLen = 0; + } + + $currentLine .= $enc; + + if ($i === false) { + $lineLen += $size; + } else { + // 6 is the length of '=0D=0A'. + $lineLen = $size - strrpos($enc, '=0D=0A') - 6; + } + } + + return $this->_standardize(implode("=\r\n", $lines)); + } + + /** + * Updates the charset used. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->_charStream->setCharacterSet($charset); + } + + /** + * Encode the given byte array into a verbatim QP form. + * + * @param integer[] $bytes + * @param int $size + * + * @return string + */ + protected function _encodeByteSequence(array $bytes, &$size) + { + $ret = ''; + $size = 0; + foreach ($bytes as $b) { + if (isset($this->_safeMap[$b])) { + $ret .= $this->_safeMap[$b]; + ++$size; + } else { + $ret .= self::$_qpMap[$b]; + $size += 3; + } + } + + return $ret; + } + + /** + * Get the next sequence of bytes to read from the char stream. + * + * @param int $size number of bytes to read + * + * @return integer[] + */ + protected function _nextSequence($size = 4) + { + return $this->_charStream->readBytes($size); + } + + /** + * Make sure CRLF is correct and HT/SPACE are in valid places. + * + * @param string $string + * + * @return string + */ + protected function _standardize($string) + { + $string = str_replace(array("\t=0D=0A", ' =0D=0A', '=0D=0A'), + array("=09\r\n", "=20\r\n", "\r\n"), $string + ); + switch ($end = ord(substr($string, -1))) { + case 0x09: + case 0x20: + $string = substr_replace($string, self::$_qpMap[$end], -1); + } + + return $string; + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->_charStream = clone $this->_charStream; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php new file mode 100644 index 00000000..b0215e88 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php @@ -0,0 +1,92 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Handles RFC 2231 specified Encoding in Swift Mailer. + * + * @author Chris Corbyn + */ +class Swift_Encoder_Rfc2231Encoder implements Swift_Encoder +{ + /** + * A character stream to use when reading a string as characters instead of bytes. + * + * @var Swift_CharacterStream + */ + private $_charStream; + + /** + * Creates a new Rfc2231Encoder using the given character stream instance. + * + * @param Swift_CharacterStream + */ + public function __construct(Swift_CharacterStream $charStream) + { + $this->_charStream = $charStream; + } + + /** + * Takes an unencoded string and produces a string encoded according to + * RFC 2231 from it. + * + * @param string $string + * @param int $firstLineOffset + * @param int $maxLineLength optional, 0 indicates the default of 75 bytes + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + $lines = array(); + $lineCount = 0; + $lines[] = ''; + $currentLine = &$lines[$lineCount++]; + + if (0 >= $maxLineLength) { + $maxLineLength = 75; + } + + $this->_charStream->flushContents(); + $this->_charStream->importString($string); + + $thisLineLength = $maxLineLength - $firstLineOffset; + + while (false !== $char = $this->_charStream->read(4)) { + $encodedChar = rawurlencode($char); + if (0 != strlen($currentLine) + && strlen($currentLine.$encodedChar) > $thisLineLength) { + $lines[] = ''; + $currentLine = &$lines[$lineCount++]; + $thisLineLength = $maxLineLength; + } + $currentLine .= $encodedChar; + } + + return implode("\r\n", $lines); + } + + /** + * Updates the charset used. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->_charStream->setCharacterSet($charset); + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->_charStream = clone $this->_charStream; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoding.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoding.php new file mode 100644 index 00000000..253977b6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoding.php @@ -0,0 +1,64 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides quick access to each encoding type. + * + * @author Chris Corbyn + */ +class Swift_Encoding +{ + /** + * Get the Encoder that provides 7-bit encoding. + * + * @return Swift_Mime_ContentEncoder + */ + public static function get7BitEncoding() + { + return self::_lookup('mime.7bitcontentencoder'); + } + + /** + * Get the Encoder that provides 8-bit encoding. + * + * @return Swift_Mime_ContentEncoder + */ + public static function get8BitEncoding() + { + return self::_lookup('mime.8bitcontentencoder'); + } + + /** + * Get the Encoder that provides Quoted-Printable (QP) encoding. + * + * @return Swift_Mime_ContentEncoder + */ + public static function getQpEncoding() + { + return self::_lookup('mime.qpcontentencoder'); + } + + /** + * Get the Encoder that provides Base64 encoding. + * + * @return Swift_Mime_ContentEncoder + */ + public static function getBase64Encoding() + { + return self::_lookup('mime.base64contentencoder'); + } + + // -- Private Static Methods + + private static function _lookup($key) + { + return Swift_DependencyContainer::getInstance()->lookup($key); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandEvent.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandEvent.php new file mode 100644 index 00000000..7dc381d9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandEvent.php @@ -0,0 +1,65 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Generated when a command is sent over an SMTP connection. + * + * @author Chris Corbyn + */ +class Swift_Events_CommandEvent extends Swift_Events_EventObject +{ + /** + * The command sent to the server. + * + * @var string + */ + private $_command; + + /** + * An array of codes which a successful response will contain. + * + * @var integer[] + */ + private $_successCodes = array(); + + /** + * Create a new CommandEvent for $source with $command. + * + * @param Swift_Transport $source + * @param string $command + * @param array $successCodes + */ + public function __construct(Swift_Transport $source, $command, $successCodes = array()) + { + parent::__construct($source); + $this->_command = $command; + $this->_successCodes = $successCodes; + } + + /** + * Get the command which was sent to the server. + * + * @return string + */ + public function getCommand() + { + return $this->_command; + } + + /** + * Get the numeric response codes which indicate success for this command. + * + * @return integer[] + */ + public function getSuccessCodes() + { + return $this->_successCodes; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandListener.php new file mode 100644 index 00000000..7545404e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandListener.php @@ -0,0 +1,24 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Listens for Transports to send commands to the server. + * + * @author Chris Corbyn + */ +interface Swift_Events_CommandListener extends Swift_Events_EventListener +{ + /** + * Invoked immediately following a command being sent. + * + * @param Swift_Events_CommandEvent $evt + */ + public function commandSent(Swift_Events_CommandEvent $evt); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/Event.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/Event.php new file mode 100644 index 00000000..720b1563 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/Event.php @@ -0,0 +1,38 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * The minimum interface for an Event. + * + * @author Chris Corbyn + */ +interface Swift_Events_Event +{ + /** + * Get the source object of this event. + * + * @return object + */ + public function getSource(); + + /** + * Prevent this Event from bubbling any further up the stack. + * + * @param bool $cancel, optional + */ + public function cancelBubble($cancel = true); + + /** + * Returns true if this Event will not bubble any further up the stack. + * + * @return bool + */ + public function bubbleCancelled(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventDispatcher.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventDispatcher.php new file mode 100644 index 00000000..aac36aaa --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventDispatcher.php @@ -0,0 +1,83 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Interface for the EventDispatcher which handles the event dispatching layer. + * + * @author Chris Corbyn + */ +interface Swift_Events_EventDispatcher +{ + /** + * Create a new SendEvent for $source and $message. + * + * @param Swift_Transport $source + * @param Swift_Mime_Message + * + * @return Swift_Events_SendEvent + */ + public function createSendEvent(Swift_Transport $source, Swift_Mime_Message $message); + + /** + * Create a new CommandEvent for $source and $command. + * + * @param Swift_Transport $source + * @param string $command That will be executed + * @param array $successCodes That are needed + * + * @return Swift_Events_CommandEvent + */ + public function createCommandEvent(Swift_Transport $source, $command, $successCodes = array()); + + /** + * Create a new ResponseEvent for $source and $response. + * + * @param Swift_Transport $source + * @param string $response + * @param bool $valid If the response is valid + * + * @return Swift_Events_ResponseEvent + */ + public function createResponseEvent(Swift_Transport $source, $response, $valid); + + /** + * Create a new TransportChangeEvent for $source. + * + * @param Swift_Transport $source + * + * @return Swift_Events_TransportChangeEvent + */ + public function createTransportChangeEvent(Swift_Transport $source); + + /** + * Create a new TransportExceptionEvent for $source. + * + * @param Swift_Transport $source + * @param Swift_TransportException $ex + * + * @return Swift_Events_TransportExceptionEvent + */ + public function createTransportExceptionEvent(Swift_Transport $source, Swift_TransportException $ex); + + /** + * Bind an event listener to this dispatcher. + * + * @param Swift_Events_EventListener $listener + */ + public function bindEventListener(Swift_Events_EventListener $listener); + + /** + * Dispatch the given Event to all suitable listeners. + * + * @param Swift_Events_EventObject $evt + * @param string $target method + */ + public function dispatchEvent(Swift_Events_EventObject $evt, $target); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventListener.php new file mode 100644 index 00000000..51290954 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventListener.php @@ -0,0 +1,18 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An identity interface which all EventListeners must extend. + * + * @author Chris Corbyn + */ +interface Swift_Events_EventListener +{ +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventObject.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventObject.php new file mode 100644 index 00000000..90694a9a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventObject.php @@ -0,0 +1,63 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A base Event which all Event classes inherit from. + * + * @author Chris Corbyn + */ +class Swift_Events_EventObject implements Swift_Events_Event +{ + /** The source of this Event */ + private $_source; + + /** The state of this Event (should it bubble up the stack?) */ + private $_bubbleCancelled = false; + + /** + * Create a new EventObject originating at $source. + * + * @param object $source + */ + public function __construct($source) + { + $this->_source = $source; + } + + /** + * Get the source object of this event. + * + * @return object + */ + public function getSource() + { + return $this->_source; + } + + /** + * Prevent this Event from bubbling any further up the stack. + * + * @param bool $cancel, optional + */ + public function cancelBubble($cancel = true) + { + $this->_bubbleCancelled = $cancel; + } + + /** + * Returns true if this Event will not bubble any further up the stack. + * + * @return bool + */ + public function bubbleCancelled() + { + return $this->_bubbleCancelled; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php new file mode 100644 index 00000000..2e92ba94 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php @@ -0,0 +1,65 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Generated when a response is received on a SMTP connection. + * + * @author Chris Corbyn + */ +class Swift_Events_ResponseEvent extends Swift_Events_EventObject +{ + /** + * The overall result. + * + * @var bool + */ + private $_valid; + + /** + * The response received from the server. + * + * @var string + */ + private $_response; + + /** + * Create a new ResponseEvent for $source and $response. + * + * @param Swift_Transport $source + * @param string $response + * @param bool $valid + */ + public function __construct(Swift_Transport $source, $response, $valid = false) + { + parent::__construct($source); + $this->_response = $response; + $this->_valid = $valid; + } + + /** + * Get the response which was received from the server. + * + * @return string + */ + public function getResponse() + { + return $this->_response; + } + + /** + * Get the success status of this Event. + * + * @return bool + */ + public function isValid() + { + return $this->_valid; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseListener.php new file mode 100644 index 00000000..c40919d2 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseListener.php @@ -0,0 +1,24 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Listens for responses from a remote SMTP server. + * + * @author Chris Corbyn + */ +interface Swift_Events_ResponseListener extends Swift_Events_EventListener +{ + /** + * Invoked immediately following a response coming back. + * + * @param Swift_Events_ResponseEvent $evt + */ + public function responseReceived(Swift_Events_ResponseEvent $evt); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendEvent.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendEvent.php new file mode 100644 index 00000000..10da8080 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendEvent.php @@ -0,0 +1,129 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Generated when a message is being sent. + * + * @author Chris Corbyn + */ +class Swift_Events_SendEvent extends Swift_Events_EventObject +{ + /** Sending has yet to occur */ + const RESULT_PENDING = 0x0001; + + /** Email is spooled, ready to be sent */ + const RESULT_SPOOLED = 0x0011; + + /** Sending was successful */ + const RESULT_SUCCESS = 0x0010; + + /** Sending worked, but there were some failures */ + const RESULT_TENTATIVE = 0x0100; + + /** Sending failed */ + const RESULT_FAILED = 0x1000; + + /** + * The Message being sent. + * + * @var Swift_Mime_Message + */ + private $_message; + + /** + * Any recipients which failed after sending. + * + * @var string[] + */ + private $_failedRecipients = array(); + + /** + * The overall result as a bitmask from the class constants. + * + * @var int + */ + private $_result; + + /** + * Create a new SendEvent for $source and $message. + * + * @param Swift_Transport $source + * @param Swift_Mime_Message $message + */ + public function __construct(Swift_Transport $source, Swift_Mime_Message $message) + { + parent::__construct($source); + $this->_message = $message; + $this->_result = self::RESULT_PENDING; + } + + /** + * Get the Transport used to send the Message. + * + * @return Swift_Transport + */ + public function getTransport() + { + return $this->getSource(); + } + + /** + * Get the Message being sent. + * + * @return Swift_Mime_Message + */ + public function getMessage() + { + return $this->_message; + } + + /** + * Set the array of addresses that failed in sending. + * + * @param array $recipients + */ + public function setFailedRecipients($recipients) + { + $this->_failedRecipients = $recipients; + } + + /** + * Get an recipient addresses which were not accepted for delivery. + * + * @return string[] + */ + public function getFailedRecipients() + { + return $this->_failedRecipients; + } + + /** + * Set the result of sending. + * + * @param int $result + */ + public function setResult($result) + { + $this->_result = $result; + } + + /** + * Get the result of this Event. + * + * The return value is a bitmask from + * {@see RESULT_PENDING, RESULT_SUCCESS, RESULT_TENTATIVE, RESULT_FAILED} + * + * @return int + */ + public function getResult() + { + return $this->_result; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendListener.php new file mode 100644 index 00000000..d922e1bf --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendListener.php @@ -0,0 +1,31 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Listens for Messages being sent from within the Transport system. + * + * @author Chris Corbyn + */ +interface Swift_Events_SendListener extends Swift_Events_EventListener +{ + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt); + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SimpleEventDispatcher.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SimpleEventDispatcher.php new file mode 100644 index 00000000..e8aca752 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SimpleEventDispatcher.php @@ -0,0 +1,156 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * The EventDispatcher which handles the event dispatching layer. + * + * @author Chris Corbyn + */ +class Swift_Events_SimpleEventDispatcher implements Swift_Events_EventDispatcher +{ + /** A map of event types to their associated listener types */ + private $_eventMap = array(); + + /** Event listeners bound to this dispatcher */ + private $_listeners = array(); + + /** Listeners queued to have an Event bubbled up the stack to them */ + private $_bubbleQueue = array(); + + /** + * Create a new EventDispatcher. + */ + public function __construct() + { + $this->_eventMap = array( + 'Swift_Events_CommandEvent' => 'Swift_Events_CommandListener', + 'Swift_Events_ResponseEvent' => 'Swift_Events_ResponseListener', + 'Swift_Events_SendEvent' => 'Swift_Events_SendListener', + 'Swift_Events_TransportChangeEvent' => 'Swift_Events_TransportChangeListener', + 'Swift_Events_TransportExceptionEvent' => 'Swift_Events_TransportExceptionListener', + ); + } + + /** + * Create a new SendEvent for $source and $message. + * + * @param Swift_Transport $source + * @param Swift_Mime_Message + * + * @return Swift_Events_SendEvent + */ + public function createSendEvent(Swift_Transport $source, Swift_Mime_Message $message) + { + return new Swift_Events_SendEvent($source, $message); + } + + /** + * Create a new CommandEvent for $source and $command. + * + * @param Swift_Transport $source + * @param string $command That will be executed + * @param array $successCodes That are needed + * + * @return Swift_Events_CommandEvent + */ + public function createCommandEvent(Swift_Transport $source, $command, $successCodes = array()) + { + return new Swift_Events_CommandEvent($source, $command, $successCodes); + } + + /** + * Create a new ResponseEvent for $source and $response. + * + * @param Swift_Transport $source + * @param string $response + * @param bool $valid If the response is valid + * + * @return Swift_Events_ResponseEvent + */ + public function createResponseEvent(Swift_Transport $source, $response, $valid) + { + return new Swift_Events_ResponseEvent($source, $response, $valid); + } + + /** + * Create a new TransportChangeEvent for $source. + * + * @param Swift_Transport $source + * + * @return Swift_Events_TransportChangeEvent + */ + public function createTransportChangeEvent(Swift_Transport $source) + { + return new Swift_Events_TransportChangeEvent($source); + } + + /** + * Create a new TransportExceptionEvent for $source. + * + * @param Swift_Transport $source + * @param Swift_TransportException $ex + * + * @return Swift_Events_TransportExceptionEvent + */ + public function createTransportExceptionEvent(Swift_Transport $source, Swift_TransportException $ex) + { + return new Swift_Events_TransportExceptionEvent($source, $ex); + } + + /** + * Bind an event listener to this dispatcher. + * + * @param Swift_Events_EventListener $listener + */ + public function bindEventListener(Swift_Events_EventListener $listener) + { + foreach ($this->_listeners as $l) { + // Already loaded + if ($l === $listener) { + return; + } + } + $this->_listeners[] = $listener; + } + + /** + * Dispatch the given Event to all suitable listeners. + * + * @param Swift_Events_EventObject $evt + * @param string $target method + */ + public function dispatchEvent(Swift_Events_EventObject $evt, $target) + { + $this->_prepareBubbleQueue($evt); + $this->_bubble($evt, $target); + } + + /** Queue listeners on a stack ready for $evt to be bubbled up it */ + private function _prepareBubbleQueue(Swift_Events_EventObject $evt) + { + $this->_bubbleQueue = array(); + $evtClass = get_class($evt); + foreach ($this->_listeners as $listener) { + if (array_key_exists($evtClass, $this->_eventMap) + && ($listener instanceof $this->_eventMap[$evtClass])) { + $this->_bubbleQueue[] = $listener; + } + } + } + + /** Bubble $evt up the stack calling $target() on each listener */ + private function _bubble(Swift_Events_EventObject $evt, $target) + { + if (!$evt->bubbleCancelled() && $listener = array_shift($this->_bubbleQueue)) { + $listener->$target($evt); + $this->_bubble($evt, $target); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeEvent.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeEvent.php new file mode 100644 index 00000000..a8972fda --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeEvent.php @@ -0,0 +1,27 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Generated when the state of a Transport is changed (i.e. stopped/started). + * + * @author Chris Corbyn + */ +class Swift_Events_TransportChangeEvent extends Swift_Events_EventObject +{ + /** + * Get the Transport. + * + * @return Swift_Transport + */ + public function getTransport() + { + return $this->getSource(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeListener.php new file mode 100644 index 00000000..253165de --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeListener.php @@ -0,0 +1,45 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Listens for changes within the Transport system. + * + * @author Chris Corbyn + */ +interface Swift_Events_TransportChangeListener extends Swift_Events_EventListener +{ + /** + * Invoked just before a Transport is started. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function beforeTransportStarted(Swift_Events_TransportChangeEvent $evt); + + /** + * Invoked immediately after the Transport is started. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function transportStarted(Swift_Events_TransportChangeEvent $evt); + + /** + * Invoked just before a Transport is stopped. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function beforeTransportStopped(Swift_Events_TransportChangeEvent $evt); + + /** + * Invoked immediately after the Transport is stopped. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function transportStopped(Swift_Events_TransportChangeEvent $evt); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionEvent.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionEvent.php new file mode 100644 index 00000000..f87154fb --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionEvent.php @@ -0,0 +1,46 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Generated when a TransportException is thrown from the Transport system. + * + * @author Chris Corbyn + */ +class Swift_Events_TransportExceptionEvent extends Swift_Events_EventObject +{ + /** + * The Exception thrown. + * + * @var Swift_TransportException + */ + private $_exception; + + /** + * Create a new TransportExceptionEvent for $transport. + * + * @param Swift_Transport $transport + * @param Swift_TransportException $ex + */ + public function __construct(Swift_Transport $transport, Swift_TransportException $ex) + { + parent::__construct($transport); + $this->_exception = $ex; + } + + /** + * Get the TransportException thrown. + * + * @return Swift_TransportException + */ + public function getException() + { + return $this->_exception; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionListener.php new file mode 100644 index 00000000..cc3c0993 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionListener.php @@ -0,0 +1,24 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Listens for Exceptions thrown from within the Transport system. + * + * @author Chris Corbyn + */ +interface Swift_Events_TransportExceptionListener extends Swift_Events_EventListener +{ + /** + * Invoked as a TransportException is thrown in the Transport system. + * + * @param Swift_Events_TransportExceptionEvent $evt + */ + public function exceptionThrown(Swift_Events_TransportExceptionEvent $evt); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FailoverTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FailoverTransport.php new file mode 100644 index 00000000..53f277da --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FailoverTransport.php @@ -0,0 +1,45 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Contains a list of redundant Transports so when one fails, the next is used. + * + * @author Chris Corbyn + */ +class Swift_FailoverTransport extends Swift_Transport_FailoverTransport +{ + /** + * Creates a new FailoverTransport with $transports. + * + * @param Swift_Transport[] $transports + */ + public function __construct($transports = array()) + { + call_user_func_array( + array($this, 'Swift_Transport_FailoverTransport::__construct'), + Swift_DependencyContainer::getInstance() + ->createDependenciesFor('transport.failover') + ); + + $this->setTransports($transports); + } + + /** + * Create a new FailoverTransport instance. + * + * @param Swift_Transport[] $transports + * + * @return Swift_FailoverTransport + */ + public static function newInstance($transports = array()) + { + return new self($transports); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileSpool.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileSpool.php new file mode 100644 index 00000000..c82c5dbf --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileSpool.php @@ -0,0 +1,208 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2009 Fabien Potencier <fabien.potencier@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages on the filesystem. + * + * @author Fabien Potencier + * @author Xavier De Cock <xdecock@gmail.com> + */ +class Swift_FileSpool extends Swift_ConfigurableSpool +{ + /** The spool directory */ + private $_path; + + /** + * File WriteRetry Limit. + * + * @var int + */ + private $_retryLimit = 10; + + /** + * Create a new FileSpool. + * + * @param string $path + * + * @throws Swift_IoException + */ + public function __construct($path) + { + $this->_path = $path; + + if (!file_exists($this->_path)) { + if (!mkdir($this->_path, 0777, true)) { + throw new Swift_IoException(sprintf('Unable to create path "%s".', $this->_path)); + } + } + } + + /** + * Tests if this Spool mechanism has started. + * + * @return bool + */ + public function isStarted() + { + return true; + } + + /** + * Starts this Spool mechanism. + */ + public function start() + { + } + + /** + * Stops this Spool mechanism. + */ + public function stop() + { + } + + /** + * Allow to manage the enqueuing retry limit. + * + * Default, is ten and allows over 64^20 different fileNames + * + * @param int $limit + */ + public function setRetryLimit($limit) + { + $this->_retryLimit = $limit; + } + + /** + * Queues a message. + * + * @param Swift_Mime_Message $message The message to store + * + * @throws Swift_IoException + * + * @return bool + */ + public function queueMessage(Swift_Mime_Message $message) + { + $ser = serialize($message); + $fileName = $this->_path.'/'.$this->getRandomString(10); + for ($i = 0; $i < $this->_retryLimit; ++$i) { + /* We try an exclusive creation of the file. This is an atomic operation, it avoid locking mechanism */ + $fp = @fopen($fileName.'.message', 'x'); + if (false !== $fp) { + if (false === fwrite($fp, $ser)) { + return false; + } + + return fclose($fp); + } else { + /* The file already exists, we try a longer fileName */ + $fileName .= $this->getRandomString(1); + } + } + + throw new Swift_IoException(sprintf('Unable to create a file for enqueuing Message in "%s".', $this->_path)); + } + + /** + * Execute a recovery if for any reason a process is sending for too long. + * + * @param int $timeout in second Defaults is for very slow smtp responses + */ + public function recover($timeout = 900) + { + foreach (new DirectoryIterator($this->_path) as $file) { + $file = $file->getRealPath(); + + if (substr($file, -16) == '.message.sending') { + $lockedtime = filectime($file); + if ((time() - $lockedtime) > $timeout) { + rename($file, substr($file, 0, -8)); + } + } + } + } + + /** + * Sends messages using the given transport instance. + * + * @param Swift_Transport $transport A transport instance + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int The number of sent e-mail's + */ + public function flushQueue(Swift_Transport $transport, &$failedRecipients = null) + { + $directoryIterator = new DirectoryIterator($this->_path); + + /* Start the transport only if there are queued files to send */ + if (!$transport->isStarted()) { + foreach ($directoryIterator as $file) { + if (substr($file->getRealPath(), -8) == '.message') { + $transport->start(); + break; + } + } + } + + $failedRecipients = (array) $failedRecipients; + $count = 0; + $time = time(); + foreach ($directoryIterator as $file) { + $file = $file->getRealPath(); + + if (substr($file, -8) != '.message') { + continue; + } + + /* We try a rename, it's an atomic operation, and avoid locking the file */ + if (rename($file, $file.'.sending')) { + $message = unserialize(file_get_contents($file.'.sending')); + + $count += $transport->send($message, $failedRecipients); + + unlink($file.'.sending'); + } else { + /* This message has just been catched by another process */ + continue; + } + + if ($this->getMessageLimit() && $count >= $this->getMessageLimit()) { + break; + } + + if ($this->getTimeLimit() && (time() - $time) >= $this->getTimeLimit()) { + break; + } + } + + return $count; + } + + /** + * Returns a random string needed to generate a fileName for the queue. + * + * @param int $count + * + * @return string + */ + protected function getRandomString($count) + { + // This string MUST stay FS safe, avoid special chars + $base = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-'; + $ret = ''; + $strlen = strlen($base); + for ($i = 0; $i < $count; ++$i) { + $ret .= $base[((int) rand(0, $strlen - 1))]; + } + + return $ret; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileStream.php new file mode 100644 index 00000000..0b24db1c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileStream.php @@ -0,0 +1,24 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An OutputByteStream which specifically reads from a file. + * + * @author Chris Corbyn + */ +interface Swift_FileStream extends Swift_OutputByteStream +{ + /** + * Get the complete path to the file. + * + * @return string + */ + public function getPath(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Filterable.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Filterable.php new file mode 100644 index 00000000..6b75b527 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Filterable.php @@ -0,0 +1,32 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Allows StreamFilters to operate on a stream. + * + * @author Chris Corbyn + */ +interface Swift_Filterable +{ + /** + * Add a new StreamFilter, referenced by $key. + * + * @param Swift_StreamFilter $filter + * @param string $key + */ + public function addFilter(Swift_StreamFilter $filter, $key); + + /** + * Remove an existing filter using $key. + * + * @param string $key + */ + public function removeFilter($key); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Image.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Image.php new file mode 100644 index 00000000..25f5f1b6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Image.php @@ -0,0 +1,61 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An image, embedded in a multipart message. + * + * @author Chris Corbyn + */ +class Swift_Image extends Swift_EmbeddedFile +{ + /** + * Create a new EmbeddedFile. + * + * Details may be optionally provided to the constructor. + * + * @param string|Swift_OutputByteStream $data + * @param string $filename + * @param string $contentType + */ + public function __construct($data = null, $filename = null, $contentType = null) + { + parent::__construct($data, $filename, $contentType); + } + + /** + * Create a new Image. + * + * @param string|Swift_OutputByteStream $data + * @param string $filename + * @param string $contentType + * + * @return Swift_Image + */ + public static function newInstance($data = null, $filename = null, $contentType = null) + { + return new self($data, $filename, $contentType); + } + + /** + * Create a new Image from a filesystem path. + * + * @param string $path + * + * @return Swift_Image + */ + public static function fromPath($path) + { + $image = self::newInstance()->setFile( + new Swift_ByteStream_FileByteStream($path) + ); + + return $image; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php new file mode 100644 index 00000000..56efc759 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php @@ -0,0 +1,75 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An abstract means of writing data. + * + * Classes implementing this interface may use a subsystem which requires less + * memory than working with large strings of data. + * + * @author Chris Corbyn + */ +interface Swift_InputByteStream +{ + /** + * Writes $bytes to the end of the stream. + * + * Writing may not happen immediately if the stream chooses to buffer. If + * you want to write these bytes with immediate effect, call {@link commit()} + * after calling write(). + * + * This method returns the sequence ID of the write (i.e. 1 for first, 2 for + * second, etc etc). + * + * @param string $bytes + * + * @throws Swift_IoException + * + * @return int + */ + public function write($bytes); + + /** + * For any bytes that are currently buffered inside the stream, force them + * off the buffer. + * + * @throws Swift_IoException + */ + public function commit(); + + /** + * Attach $is to this stream. + * + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + */ + public function bind(Swift_InputByteStream $is); + + /** + * Remove an already bound stream. + * + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + */ + public function unbind(Swift_InputByteStream $is); + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + * + * @throws Swift_IoException + */ + public function flushBuffers(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/IoException.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/IoException.php new file mode 100644 index 00000000..c405f352 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/IoException.php @@ -0,0 +1,29 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * I/O Exception class. + * + * @author Chris Corbyn + */ +class Swift_IoException extends Swift_SwiftException +{ + /** + * Create a new IoException with $message. + * + * @param string $message + * @param int $code + * @param Exception $previous + */ + public function __construct($message, $code = 0, Exception $previous = null) + { + parent::__construct($message, $code, $previous); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache.php new file mode 100644 index 00000000..cd6f786d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache.php @@ -0,0 +1,105 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides a mechanism for storing data using two keys. + * + * @author Chris Corbyn + */ +interface Swift_KeyCache +{ + /** Mode for replacing existing cached data */ + const MODE_WRITE = 1; + + /** Mode for appending data to the end of existing cached data */ + const MODE_APPEND = 2; + + /** + * Set a string into the cache under $itemKey for the namespace $nsKey. + * + * @see MODE_WRITE, MODE_APPEND + * + * @param string $nsKey + * @param string $itemKey + * @param string $string + * @param int $mode + */ + public function setString($nsKey, $itemKey, $string, $mode); + + /** + * Set a ByteStream into the cache under $itemKey for the namespace $nsKey. + * + * @see MODE_WRITE, MODE_APPEND + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_OutputByteStream $os + * @param int $mode + */ + public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode); + + /** + * Provides a ByteStream which when written to, writes data to $itemKey. + * + * NOTE: The stream will always write in append mode. + * If the optional third parameter is passed all writes will go through $is. + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $is optional input stream + * + * @return Swift_InputByteStream + */ + public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $is = null); + + /** + * Get data back out of the cache as a string. + * + * @param string $nsKey + * @param string $itemKey + * + * @return string + */ + public function getString($nsKey, $itemKey); + + /** + * Get data back out of the cache as a ByteStream. + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $is stream to write the data to + */ + public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is); + + /** + * Check if the given $itemKey exists in the namespace $nsKey. + * + * @param string $nsKey + * @param string $itemKey + * + * @return bool + */ + public function hasKey($nsKey, $itemKey); + + /** + * Clear data for $itemKey in the namespace $nsKey if it exists. + * + * @param string $nsKey + * @param string $itemKey + */ + public function clearKey($nsKey, $itemKey); + + /** + * Clear all data in the namespace $nsKey if it exists. + * + * @param string $nsKey + */ + public function clearAll($nsKey); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/ArrayKeyCache.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/ArrayKeyCache.php new file mode 100644 index 00000000..b37f07f7 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/ArrayKeyCache.php @@ -0,0 +1,206 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A basic KeyCache backed by an array. + * + * @author Chris Corbyn + */ +class Swift_KeyCache_ArrayKeyCache implements Swift_KeyCache +{ + /** + * Cache contents. + * + * @var array + */ + private $_contents = array(); + + /** + * An InputStream for cloning. + * + * @var Swift_KeyCache_KeyCacheInputStream + */ + private $_stream; + + /** + * Create a new ArrayKeyCache with the given $stream for cloning to make + * InputByteStreams. + * + * @param Swift_KeyCache_KeyCacheInputStream $stream + */ + public function __construct(Swift_KeyCache_KeyCacheInputStream $stream) + { + $this->_stream = $stream; + } + + /** + * Set a string into the cache under $itemKey for the namespace $nsKey. + * + * @see MODE_WRITE, MODE_APPEND + * + * @param string $nsKey + * @param string $itemKey + * @param string $string + * @param int $mode + */ + public function setString($nsKey, $itemKey, $string, $mode) + { + $this->_prepareCache($nsKey); + switch ($mode) { + case self::MODE_WRITE: + $this->_contents[$nsKey][$itemKey] = $string; + break; + case self::MODE_APPEND: + if (!$this->hasKey($nsKey, $itemKey)) { + $this->_contents[$nsKey][$itemKey] = ''; + } + $this->_contents[$nsKey][$itemKey] .= $string; + break; + default: + throw new Swift_SwiftException( + 'Invalid mode ['.$mode.'] used to set nsKey='. + $nsKey.', itemKey='.$itemKey + ); + } + } + + /** + * Set a ByteStream into the cache under $itemKey for the namespace $nsKey. + * + * @see MODE_WRITE, MODE_APPEND + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_OutputByteStream $os + * @param int $mode + */ + public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode) + { + $this->_prepareCache($nsKey); + switch ($mode) { + case self::MODE_WRITE: + $this->clearKey($nsKey, $itemKey); + case self::MODE_APPEND: + if (!$this->hasKey($nsKey, $itemKey)) { + $this->_contents[$nsKey][$itemKey] = ''; + } + while (false !== $bytes = $os->read(8192)) { + $this->_contents[$nsKey][$itemKey] .= $bytes; + } + break; + default: + throw new Swift_SwiftException( + 'Invalid mode ['.$mode.'] used to set nsKey='. + $nsKey.', itemKey='.$itemKey + ); + } + } + + /** + * Provides a ByteStream which when written to, writes data to $itemKey. + * + * NOTE: The stream will always write in append mode. + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $writeThrough + * + * @return Swift_InputByteStream + */ + public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null) + { + $is = clone $this->_stream; + $is->setKeyCache($this); + $is->setNsKey($nsKey); + $is->setItemKey($itemKey); + if (isset($writeThrough)) { + $is->setWriteThroughStream($writeThrough); + } + + return $is; + } + + /** + * Get data back out of the cache as a string. + * + * @param string $nsKey + * @param string $itemKey + * + * @return string + */ + public function getString($nsKey, $itemKey) + { + $this->_prepareCache($nsKey); + if ($this->hasKey($nsKey, $itemKey)) { + return $this->_contents[$nsKey][$itemKey]; + } + } + + /** + * Get data back out of the cache as a ByteStream. + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $is to write the data to + */ + public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is) + { + $this->_prepareCache($nsKey); + $is->write($this->getString($nsKey, $itemKey)); + } + + /** + * Check if the given $itemKey exists in the namespace $nsKey. + * + * @param string $nsKey + * @param string $itemKey + * + * @return bool + */ + public function hasKey($nsKey, $itemKey) + { + $this->_prepareCache($nsKey); + + return array_key_exists($itemKey, $this->_contents[$nsKey]); + } + + /** + * Clear data for $itemKey in the namespace $nsKey if it exists. + * + * @param string $nsKey + * @param string $itemKey + */ + public function clearKey($nsKey, $itemKey) + { + unset($this->_contents[$nsKey][$itemKey]); + } + + /** + * Clear all data in the namespace $nsKey if it exists. + * + * @param string $nsKey + */ + public function clearAll($nsKey) + { + unset($this->_contents[$nsKey]); + } + + /** + * Initialize the namespace of $nsKey if needed. + * + * @param string $nsKey + */ + private function _prepareCache($nsKey) + { + if (!array_key_exists($nsKey, $this->_contents)) { + $this->_contents[$nsKey] = array(); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php new file mode 100644 index 00000000..453f50a1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php @@ -0,0 +1,321 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A KeyCache which streams to and from disk. + * + * @author Chris Corbyn + */ +class Swift_KeyCache_DiskKeyCache implements Swift_KeyCache +{ + /** Signal to place pointer at start of file */ + const POSITION_START = 0; + + /** Signal to place pointer at end of file */ + const POSITION_END = 1; + + /** Signal to leave pointer in whatever position it currently is */ + const POSITION_CURRENT = 2; + + /** + * An InputStream for cloning. + * + * @var Swift_KeyCache_KeyCacheInputStream + */ + private $_stream; + + /** + * A path to write to. + * + * @var string + */ + private $_path; + + /** + * Stored keys. + * + * @var array + */ + private $_keys = array(); + + /** + * Will be true if magic_quotes_runtime is turned on. + * + * @var bool + */ + private $_quotes = false; + + /** + * Create a new DiskKeyCache with the given $stream for cloning to make + * InputByteStreams, and the given $path to save to. + * + * @param Swift_KeyCache_KeyCacheInputStream $stream + * @param string $path to save to + */ + public function __construct(Swift_KeyCache_KeyCacheInputStream $stream, $path) + { + $this->_stream = $stream; + $this->_path = $path; + + if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() == 1) { + $this->_quotes = true; + } + } + + /** + * Set a string into the cache under $itemKey for the namespace $nsKey. + * + * @see MODE_WRITE, MODE_APPEND + * + * @param string $nsKey + * @param string $itemKey + * @param string $string + * @param int $mode + * + * @throws Swift_IoException + */ + public function setString($nsKey, $itemKey, $string, $mode) + { + $this->_prepareCache($nsKey); + switch ($mode) { + case self::MODE_WRITE: + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + break; + case self::MODE_APPEND: + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_END); + break; + default: + throw new Swift_SwiftException( + 'Invalid mode ['.$mode.'] used to set nsKey='. + $nsKey.', itemKey='.$itemKey + ); + break; + } + fwrite($fp, $string); + $this->_freeHandle($nsKey, $itemKey); + } + + /** + * Set a ByteStream into the cache under $itemKey for the namespace $nsKey. + * + * @see MODE_WRITE, MODE_APPEND + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_OutputByteStream $os + * @param int $mode + * + * @throws Swift_IoException + */ + public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode) + { + $this->_prepareCache($nsKey); + switch ($mode) { + case self::MODE_WRITE: + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + break; + case self::MODE_APPEND: + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_END); + break; + default: + throw new Swift_SwiftException( + 'Invalid mode ['.$mode.'] used to set nsKey='. + $nsKey.', itemKey='.$itemKey + ); + break; + } + while (false !== $bytes = $os->read(8192)) { + fwrite($fp, $bytes); + } + $this->_freeHandle($nsKey, $itemKey); + } + + /** + * Provides a ByteStream which when written to, writes data to $itemKey. + * + * NOTE: The stream will always write in append mode. + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $writeThrough + * + * @return Swift_InputByteStream + */ + public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null) + { + $is = clone $this->_stream; + $is->setKeyCache($this); + $is->setNsKey($nsKey); + $is->setItemKey($itemKey); + if (isset($writeThrough)) { + $is->setWriteThroughStream($writeThrough); + } + + return $is; + } + + /** + * Get data back out of the cache as a string. + * + * @param string $nsKey + * @param string $itemKey + * + * @throws Swift_IoException + * + * @return string + */ + public function getString($nsKey, $itemKey) + { + $this->_prepareCache($nsKey); + if ($this->hasKey($nsKey, $itemKey)) { + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 0); + } + $str = ''; + while (!feof($fp) && false !== $bytes = fread($fp, 8192)) { + $str .= $bytes; + } + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 1); + } + $this->_freeHandle($nsKey, $itemKey); + + return $str; + } + } + + /** + * Get data back out of the cache as a ByteStream. + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $is to write the data to + */ + public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is) + { + if ($this->hasKey($nsKey, $itemKey)) { + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 0); + } + while (!feof($fp) && false !== $bytes = fread($fp, 8192)) { + $is->write($bytes); + } + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 1); + } + $this->_freeHandle($nsKey, $itemKey); + } + } + + /** + * Check if the given $itemKey exists in the namespace $nsKey. + * + * @param string $nsKey + * @param string $itemKey + * + * @return bool + */ + public function hasKey($nsKey, $itemKey) + { + return is_file($this->_path.'/'.$nsKey.'/'.$itemKey); + } + + /** + * Clear data for $itemKey in the namespace $nsKey if it exists. + * + * @param string $nsKey + * @param string $itemKey + */ + public function clearKey($nsKey, $itemKey) + { + if ($this->hasKey($nsKey, $itemKey)) { + $this->_freeHandle($nsKey, $itemKey); + unlink($this->_path.'/'.$nsKey.'/'.$itemKey); + } + } + + /** + * Clear all data in the namespace $nsKey if it exists. + * + * @param string $nsKey + */ + public function clearAll($nsKey) + { + if (array_key_exists($nsKey, $this->_keys)) { + foreach ($this->_keys[$nsKey] as $itemKey => $null) { + $this->clearKey($nsKey, $itemKey); + } + if (is_dir($this->_path.'/'.$nsKey)) { + rmdir($this->_path.'/'.$nsKey); + } + unset($this->_keys[$nsKey]); + } + } + + /** + * Initialize the namespace of $nsKey if needed. + * + * @param string $nsKey + */ + private function _prepareCache($nsKey) + { + $cacheDir = $this->_path.'/'.$nsKey; + if (!is_dir($cacheDir)) { + if (!mkdir($cacheDir)) { + throw new Swift_IoException('Failed to create cache directory '.$cacheDir); + } + $this->_keys[$nsKey] = array(); + } + } + + /** + * Get a file handle on the cache item. + * + * @param string $nsKey + * @param string $itemKey + * @param int $position + * + * @return resource + */ + private function _getHandle($nsKey, $itemKey, $position) + { + if (!isset($this->_keys[$nsKey][$itemKey])) { + $openMode = $this->hasKey($nsKey, $itemKey) ? 'r+b' : 'w+b'; + $fp = fopen($this->_path.'/'.$nsKey.'/'.$itemKey, $openMode); + $this->_keys[$nsKey][$itemKey] = $fp; + } + if (self::POSITION_START == $position) { + fseek($this->_keys[$nsKey][$itemKey], 0, SEEK_SET); + } elseif (self::POSITION_END == $position) { + fseek($this->_keys[$nsKey][$itemKey], 0, SEEK_END); + } + + return $this->_keys[$nsKey][$itemKey]; + } + + private function _freeHandle($nsKey, $itemKey) + { + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_CURRENT); + fclose($fp); + $this->_keys[$nsKey][$itemKey] = null; + } + + /** + * Destructor. + */ + public function __destruct() + { + foreach ($this->_keys as $nsKey => $null) { + $this->clearAll($nsKey); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php new file mode 100644 index 00000000..af80bdca --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php @@ -0,0 +1,51 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Writes data to a KeyCache using a stream. + * + * @author Chris Corbyn + */ +interface Swift_KeyCache_KeyCacheInputStream extends Swift_InputByteStream +{ + /** + * Set the KeyCache to wrap. + * + * @param Swift_KeyCache $keyCache + */ + public function setKeyCache(Swift_KeyCache $keyCache); + + /** + * Set the nsKey which will be written to. + * + * @param string $nsKey + */ + public function setNsKey($nsKey); + + /** + * Set the itemKey which will be written to. + * + * @param string $itemKey + */ + public function setItemKey($itemKey); + + /** + * Specify a stream to write through for each write(). + * + * @param Swift_InputByteStream $is + */ + public function setWriteThroughStream(Swift_InputByteStream $is); + + /** + * Any implementation should be cloneable, allowing the clone to access a + * separate $nsKey and $itemKey. + */ + public function __clone(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/NullKeyCache.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/NullKeyCache.php new file mode 100644 index 00000000..4efe7856 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/NullKeyCache.php @@ -0,0 +1,115 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A null KeyCache that does not cache at all. + * + * @author Chris Corbyn + */ +class Swift_KeyCache_NullKeyCache implements Swift_KeyCache +{ + /** + * Set a string into the cache under $itemKey for the namespace $nsKey. + * + * @see MODE_WRITE, MODE_APPEND + * + * @param string $nsKey + * @param string $itemKey + * @param string $string + * @param int $mode + */ + public function setString($nsKey, $itemKey, $string, $mode) + { + } + + /** + * Set a ByteStream into the cache under $itemKey for the namespace $nsKey. + * + * @see MODE_WRITE, MODE_APPEND + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_OutputByteStream $os + * @param int $mode + */ + public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode) + { + } + + /** + * Provides a ByteStream which when written to, writes data to $itemKey. + * + * NOTE: The stream will always write in append mode. + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $writeThrough + * + * @return Swift_InputByteStream + */ + public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null) + { + } + + /** + * Get data back out of the cache as a string. + * + * @param string $nsKey + * @param string $itemKey + * + * @return string + */ + public function getString($nsKey, $itemKey) + { + } + + /** + * Get data back out of the cache as a ByteStream. + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $is to write the data to + */ + public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is) + { + } + + /** + * Check if the given $itemKey exists in the namespace $nsKey. + * + * @param string $nsKey + * @param string $itemKey + * + * @return bool + */ + public function hasKey($nsKey, $itemKey) + { + return false; + } + + /** + * Clear data for $itemKey in the namespace $nsKey if it exists. + * + * @param string $nsKey + * @param string $itemKey + */ + public function clearKey($nsKey, $itemKey) + { + } + + /** + * Clear all data in the namespace $nsKey if it exists. + * + * @param string $nsKey + */ + public function clearAll($nsKey) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/SimpleKeyCacheInputStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/SimpleKeyCacheInputStream.php new file mode 100644 index 00000000..b00d458a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/SimpleKeyCacheInputStream.php @@ -0,0 +1,127 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Writes data to a KeyCache using a stream. + * + * @author Chris Corbyn + */ +class Swift_KeyCache_SimpleKeyCacheInputStream implements Swift_KeyCache_KeyCacheInputStream +{ + /** The KeyCache being written to */ + private $_keyCache; + + /** The nsKey of the KeyCache being written to */ + private $_nsKey; + + /** The itemKey of the KeyCache being written to */ + private $_itemKey; + + /** A stream to write through on each write() */ + private $_writeThrough = null; + + /** + * Set the KeyCache to wrap. + * + * @param Swift_KeyCache $keyCache + */ + public function setKeyCache(Swift_KeyCache $keyCache) + { + $this->_keyCache = $keyCache; + } + + /** + * Specify a stream to write through for each write(). + * + * @param Swift_InputByteStream $is + */ + public function setWriteThroughStream(Swift_InputByteStream $is) + { + $this->_writeThrough = $is; + } + + /** + * Writes $bytes to the end of the stream. + * + * @param string $bytes + * @param Swift_InputByteStream $is optional + */ + public function write($bytes, Swift_InputByteStream $is = null) + { + $this->_keyCache->setString( + $this->_nsKey, $this->_itemKey, $bytes, Swift_KeyCache::MODE_APPEND + ); + if (isset($is)) { + $is->write($bytes); + } + if (isset($this->_writeThrough)) { + $this->_writeThrough->write($bytes); + } + } + + /** + * Not used. + */ + public function commit() + { + } + + /** + * Not used. + */ + public function bind(Swift_InputByteStream $is) + { + } + + /** + * Not used. + */ + public function unbind(Swift_InputByteStream $is) + { + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + */ + public function flushBuffers() + { + $this->_keyCache->clearKey($this->_nsKey, $this->_itemKey); + } + + /** + * Set the nsKey which will be written to. + * + * @param string $nsKey + */ + public function setNsKey($nsKey) + { + $this->_nsKey = $nsKey; + } + + /** + * Set the itemKey which will be written to. + * + * @param string $itemKey + */ + public function setItemKey($itemKey) + { + $this->_itemKey = $itemKey; + } + + /** + * Any implementation should be cloneable, allowing the clone to access a + * separate $nsKey and $itemKey. + */ + public function __clone() + { + $this->_writeThrough = null; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php new file mode 100644 index 00000000..fdba9df5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php @@ -0,0 +1,45 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Redundantly and rotationally uses several Transport implementations when sending. + * + * @author Chris Corbyn + */ +class Swift_LoadBalancedTransport extends Swift_Transport_LoadBalancedTransport +{ + /** + * Creates a new LoadBalancedTransport with $transports. + * + * @param array $transports + */ + public function __construct($transports = array()) + { + call_user_func_array( + array($this, 'Swift_Transport_LoadBalancedTransport::__construct'), + Swift_DependencyContainer::getInstance() + ->createDependenciesFor('transport.loadbalanced') + ); + + $this->setTransports($transports); + } + + /** + * Create a new LoadBalancedTransport instance. + * + * @param array $transports + * + * @return Swift_LoadBalancedTransport + */ + public static function newInstance($transports = array()) + { + return new self($transports); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MailTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MailTransport.php new file mode 100644 index 00000000..994df3ec --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MailTransport.php @@ -0,0 +1,47 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Sends Messages using the mail() function. + * + * @author Chris Corbyn + * + * @deprecated since 5.4.5 (to be removed in 6.0) + */ +class Swift_MailTransport extends Swift_Transport_MailTransport +{ + /** + * Create a new MailTransport, optionally specifying $extraParams. + * + * @param string $extraParams + */ + public function __construct($extraParams = '-f%s') + { + call_user_func_array( + array($this, 'Swift_Transport_MailTransport::__construct'), + Swift_DependencyContainer::getInstance() + ->createDependenciesFor('transport.mail') + ); + + $this->setExtraParams($extraParams); + } + + /** + * Create a new MailTransport instance. + * + * @param string $extraParams To be passed to mail() + * + * @return Swift_MailTransport + */ + public static function newInstance($extraParams = '-f%s') + { + return new self($extraParams); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php new file mode 100644 index 00000000..64115462 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php @@ -0,0 +1,114 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Swift Mailer class. + * + * @author Chris Corbyn + */ +class Swift_Mailer +{ + /** The Transport used to send messages */ + private $_transport; + + /** + * Create a new Mailer using $transport for delivery. + * + * @param Swift_Transport $transport + */ + public function __construct(Swift_Transport $transport) + { + $this->_transport = $transport; + } + + /** + * Create a new Mailer instance. + * + * @param Swift_Transport $transport + * + * @return Swift_Mailer + */ + public static function newInstance(Swift_Transport $transport) + { + return new self($transport); + } + + /** + * Create a new class instance of one of the message services. + * + * For example 'mimepart' would create a 'message.mimepart' instance + * + * @param string $service + * + * @return object + */ + public function createMessage($service = 'message') + { + return Swift_DependencyContainer::getInstance() + ->lookup('message.'.$service); + } + + /** + * Send the given Message like it would be sent in a mail client. + * + * All recipients (with the exception of Bcc) will be able to see the other + * recipients this message was sent to. + * + * Recipient/sender data will be retrieved from the Message object. + * + * The return value is the number of recipients who were accepted for + * delivery. + * + * @param Swift_Mime_Message $message + * @param array $failedRecipients An array of failures by-reference + * + * @return int The number of successful recipients. Can be 0 which indicates failure + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $failedRecipients = (array) $failedRecipients; + + if (!$this->_transport->isStarted()) { + $this->_transport->start(); + } + + $sent = 0; + + try { + $sent = $this->_transport->send($message, $failedRecipients); + } catch (Swift_RfcComplianceException $e) { + foreach ($message->getTo() as $address => $name) { + $failedRecipients[] = $address; + } + } + + return $sent; + } + + /** + * Register a plugin using a known unique key (e.g. myPlugin). + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_transport->registerPlugin($plugin); + } + + /** + * The Transport used to send messages. + * + * @return Swift_Transport + */ + public function getTransport() + { + return $this->_transport; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php new file mode 100644 index 00000000..e3e6cad0 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php @@ -0,0 +1,55 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Wraps a standard PHP array in an iterator. + * + * @author Chris Corbyn + */ +class Swift_Mailer_ArrayRecipientIterator implements Swift_Mailer_RecipientIterator +{ + /** + * The list of recipients. + * + * @var array + */ + private $_recipients = array(); + + /** + * Create a new ArrayRecipientIterator from $recipients. + * + * @param array $recipients + */ + public function __construct(array $recipients) + { + $this->_recipients = $recipients; + } + + /** + * Returns true only if there are more recipients to send to. + * + * @return bool + */ + public function hasNext() + { + return !empty($this->_recipients); + } + + /** + * Returns an array where the keys are the addresses of recipients and the + * values are the names. e.g. ('foo@bar' => 'Foo') or ('foo@bar' => NULL). + * + * @return array + */ + public function nextRecipient() + { + return array_splice($this->_recipients, 0, 1); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/RecipientIterator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/RecipientIterator.php new file mode 100644 index 00000000..650f3ec3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/RecipientIterator.php @@ -0,0 +1,32 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides an abstract way of specifying recipients for batch sending. + * + * @author Chris Corbyn + */ +interface Swift_Mailer_RecipientIterator +{ + /** + * Returns true only if there are more recipients to send to. + * + * @return bool + */ + public function hasNext(); + + /** + * Returns an array where the keys are the addresses of recipients and the + * values are the names. e.g. ('foo@bar' => 'Foo') or ('foo@bar' => NULL). + * + * @return array + */ + public function nextRecipient(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MemorySpool.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MemorySpool.php new file mode 100644 index 00000000..2cafb675 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MemorySpool.php @@ -0,0 +1,110 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2011 Fabien Potencier <fabien.potencier@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages in memory. + * + * @author Fabien Potencier + */ +class Swift_MemorySpool implements Swift_Spool +{ + protected $messages = array(); + private $flushRetries = 3; + + /** + * Tests if this Transport mechanism has started. + * + * @return bool + */ + public function isStarted() + { + return true; + } + + /** + * Starts this Transport mechanism. + */ + public function start() + { + } + + /** + * Stops this Transport mechanism. + */ + public function stop() + { + } + + /** + * @param int $retries + */ + public function setFlushRetries($retries) + { + $this->flushRetries = $retries; + } + + /** + * Stores a message in the queue. + * + * @param Swift_Mime_Message $message The message to store + * + * @return bool Whether the operation has succeeded + */ + public function queueMessage(Swift_Mime_Message $message) + { + //clone the message to make sure it is not changed while in the queue + $this->messages[] = clone $message; + + return true; + } + + /** + * Sends messages using the given transport instance. + * + * @param Swift_Transport $transport A transport instance + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int The number of sent emails + */ + public function flushQueue(Swift_Transport $transport, &$failedRecipients = null) + { + if (!$this->messages) { + return 0; + } + + if (!$transport->isStarted()) { + $transport->start(); + } + + $count = 0; + $retries = $this->flushRetries; + while ($retries--) { + try { + while ($message = array_pop($this->messages)) { + $count += $transport->send($message, $failedRecipients); + } + } catch (Swift_TransportException $exception) { + if ($retries) { + // re-queue the message at the end of the queue to give a chance + // to the other messages to be sent, in case the failure was due to + // this message and not just the transport failing + array_unshift($this->messages, $message); + + // wait half a second before we try again + usleep(500000); + } else { + throw $exception; + } + } + } + + return $count; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php new file mode 100644 index 00000000..a6bb6593 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php @@ -0,0 +1,291 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * The Message class for building emails. + * + * @author Chris Corbyn + */ +class Swift_Message extends Swift_Mime_SimpleMessage +{ + /** + * @var Swift_Signers_HeaderSigner[] + */ + private $headerSigners = array(); + + /** + * @var Swift_Signers_BodySigner[] + */ + private $bodySigners = array(); + + /** + * @var array + */ + private $savedMessage = array(); + + /** + * Create a new Message. + * + * Details may be optionally passed into the constructor. + * + * @param string $subject + * @param string $body + * @param string $contentType + * @param string $charset + */ + public function __construct($subject = null, $body = null, $contentType = null, $charset = null) + { + call_user_func_array( + array($this, 'Swift_Mime_SimpleMessage::__construct'), + Swift_DependencyContainer::getInstance() + ->createDependenciesFor('mime.message') + ); + + if (!isset($charset)) { + $charset = Swift_DependencyContainer::getInstance() + ->lookup('properties.charset'); + } + $this->setSubject($subject); + $this->setBody($body); + $this->setCharset($charset); + if ($contentType) { + $this->setContentType($contentType); + } + } + + /** + * Create a new Message. + * + * @param string $subject + * @param string $body + * @param string $contentType + * @param string $charset + * + * @return $this + */ + public static function newInstance($subject = null, $body = null, $contentType = null, $charset = null) + { + return new self($subject, $body, $contentType, $charset); + } + + /** + * Add a MimePart to this Message. + * + * @param string|Swift_OutputByteStream $body + * @param string $contentType + * @param string $charset + * + * @return $this + */ + public function addPart($body, $contentType = null, $charset = null) + { + return $this->attach(Swift_MimePart::newInstance( + $body, $contentType, $charset + )); + } + + /** + * Detach a signature handler from a message. + * + * @param Swift_Signer $signer + * + * @return $this + */ + public function attachSigner(Swift_Signer $signer) + { + if ($signer instanceof Swift_Signers_HeaderSigner) { + $this->headerSigners[] = $signer; + } elseif ($signer instanceof Swift_Signers_BodySigner) { + $this->bodySigners[] = $signer; + } + + return $this; + } + + /** + * Attach a new signature handler to the message. + * + * @param Swift_Signer $signer + * + * @return $this + */ + public function detachSigner(Swift_Signer $signer) + { + if ($signer instanceof Swift_Signers_HeaderSigner) { + foreach ($this->headerSigners as $k => $headerSigner) { + if ($headerSigner === $signer) { + unset($this->headerSigners[$k]); + + return $this; + } + } + } elseif ($signer instanceof Swift_Signers_BodySigner) { + foreach ($this->bodySigners as $k => $bodySigner) { + if ($bodySigner === $signer) { + unset($this->bodySigners[$k]); + + return $this; + } + } + } + + return $this; + } + + /** + * Get this message as a complete string. + * + * @return string + */ + public function toString() + { + if (empty($this->headerSigners) && empty($this->bodySigners)) { + return parent::toString(); + } + + $this->saveMessage(); + + $this->doSign(); + + $string = parent::toString(); + + $this->restoreMessage(); + + return $string; + } + + /** + * Write this message to a {@link Swift_InputByteStream}. + * + * @param Swift_InputByteStream $is + */ + public function toByteStream(Swift_InputByteStream $is) + { + if (empty($this->headerSigners) && empty($this->bodySigners)) { + parent::toByteStream($is); + + return; + } + + $this->saveMessage(); + + $this->doSign(); + + parent::toByteStream($is); + + $this->restoreMessage(); + } + + public function __wakeup() + { + Swift_DependencyContainer::getInstance()->createDependenciesFor('mime.message'); + } + + /** + * loops through signers and apply the signatures. + */ + protected function doSign() + { + foreach ($this->bodySigners as $signer) { + $altered = $signer->getAlteredHeaders(); + $this->saveHeaders($altered); + $signer->signMessage($this); + } + + foreach ($this->headerSigners as $signer) { + $altered = $signer->getAlteredHeaders(); + $this->saveHeaders($altered); + $signer->reset(); + + $signer->setHeaders($this->getHeaders()); + + $signer->startBody(); + $this->_bodyToByteStream($signer); + $signer->endBody(); + + $signer->addSignature($this->getHeaders()); + } + } + + /** + * save the message before any signature is applied. + */ + protected function saveMessage() + { + $this->savedMessage = array('headers' => array()); + $this->savedMessage['body'] = $this->getBody(); + $this->savedMessage['children'] = $this->getChildren(); + if (count($this->savedMessage['children']) > 0 && $this->getBody() != '') { + $this->setChildren(array_merge(array($this->_becomeMimePart()), $this->savedMessage['children'])); + $this->setBody(''); + } + } + + /** + * save the original headers. + * + * @param array $altered + */ + protected function saveHeaders(array $altered) + { + foreach ($altered as $head) { + $lc = strtolower($head); + + if (!isset($this->savedMessage['headers'][$lc])) { + $this->savedMessage['headers'][$lc] = $this->getHeaders()->getAll($head); + } + } + } + + /** + * Remove or restore altered headers. + */ + protected function restoreHeaders() + { + foreach ($this->savedMessage['headers'] as $name => $savedValue) { + $headers = $this->getHeaders()->getAll($name); + + foreach ($headers as $key => $value) { + if (!isset($savedValue[$key])) { + $this->getHeaders()->remove($name, $key); + } + } + } + } + + /** + * Restore message body. + */ + protected function restoreMessage() + { + $this->setBody($this->savedMessage['body']); + $this->setChildren($this->savedMessage['children']); + + $this->restoreHeaders(); + $this->savedMessage = array(); + } + + /** + * Clone Message Signers. + * + * @see Swift_Mime_SimpleMimeEntity::__clone() + */ + public function __clone() + { + parent::__clone(); + foreach ($this->bodySigners as $key => $bodySigner) { + $this->bodySigners[$key] = clone $bodySigner; + } + + foreach ($this->headerSigners as $key => $headerSigner) { + $this->headerSigners[$key] = clone $headerSigner; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Attachment.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Attachment.php new file mode 100644 index 00000000..46a5e8da --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Attachment.php @@ -0,0 +1,149 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An attachment, in a multipart message. + * + * @author Chris Corbyn + */ +class Swift_Mime_Attachment extends Swift_Mime_SimpleMimeEntity +{ + /** Recognized MIME types */ + private $_mimeTypes = array(); + + /** + * Create a new Attachment with $headers, $encoder and $cache. + * + * @param Swift_Mime_HeaderSet $headers + * @param Swift_Mime_ContentEncoder $encoder + * @param Swift_KeyCache $cache + * @param Swift_Mime_Grammar $grammar + * @param array $mimeTypes optional + */ + public function __construct(Swift_Mime_HeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_Mime_Grammar $grammar, $mimeTypes = array()) + { + parent::__construct($headers, $encoder, $cache, $grammar); + $this->setDisposition('attachment'); + $this->setContentType('application/octet-stream'); + $this->_mimeTypes = $mimeTypes; + } + + /** + * Get the nesting level used for this attachment. + * + * Always returns {@link LEVEL_MIXED}. + * + * @return int + */ + public function getNestingLevel() + { + return self::LEVEL_MIXED; + } + + /** + * Get the Content-Disposition of this attachment. + * + * By default attachments have a disposition of "attachment". + * + * @return string + */ + public function getDisposition() + { + return $this->_getHeaderFieldModel('Content-Disposition'); + } + + /** + * Set the Content-Disposition of this attachment. + * + * @param string $disposition + * + * @return Swift_Mime_Attachment + */ + public function setDisposition($disposition) + { + if (!$this->_setHeaderFieldModel('Content-Disposition', $disposition)) { + $this->getHeaders()->addParameterizedHeader('Content-Disposition', $disposition); + } + + return $this; + } + + /** + * Get the filename of this attachment when downloaded. + * + * @return string + */ + public function getFilename() + { + return $this->_getHeaderParameter('Content-Disposition', 'filename'); + } + + /** + * Set the filename of this attachment. + * + * @param string $filename + * + * @return Swift_Mime_Attachment + */ + public function setFilename($filename) + { + $this->_setHeaderParameter('Content-Disposition', 'filename', $filename); + $this->_setHeaderParameter('Content-Type', 'name', $filename); + + return $this; + } + + /** + * Get the file size of this attachment. + * + * @return int + */ + public function getSize() + { + return $this->_getHeaderParameter('Content-Disposition', 'size'); + } + + /** + * Set the file size of this attachment. + * + * @param int $size + * + * @return Swift_Mime_Attachment + */ + public function setSize($size) + { + $this->_setHeaderParameter('Content-Disposition', 'size', $size); + + return $this; + } + + /** + * Set the file that this attachment is for. + * + * @param Swift_FileStream $file + * @param string $contentType optional + * + * @return Swift_Mime_Attachment + */ + public function setFile(Swift_FileStream $file, $contentType = null) + { + $this->setFilename(basename($file->getPath())); + $this->setBody($file, $contentType); + if (!isset($contentType)) { + $extension = strtolower(substr($file->getPath(), strrpos($file->getPath(), '.') + 1)); + + if (array_key_exists($extension, $this->_mimeTypes)) { + $this->setContentType($this->_mimeTypes[$extension]); + } + } + + return $this; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/CharsetObserver.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/CharsetObserver.php new file mode 100644 index 00000000..b49c3a87 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/CharsetObserver.php @@ -0,0 +1,24 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Observes changes in an Mime entity's character set. + * + * @author Chris Corbyn + */ +interface Swift_Mime_CharsetObserver +{ + /** + * Notify this observer that the entity's charset has changed. + * + * @param string $charset + */ + public function charsetChanged($charset); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder.php new file mode 100644 index 00000000..d43ea9f4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder.php @@ -0,0 +1,34 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Interface for all Transfer Encoding schemes. + * + * @author Chris Corbyn + */ +interface Swift_Mime_ContentEncoder extends Swift_Encoder +{ + /** + * Encode $in to $out. + * + * @param Swift_OutputByteStream $os to read from + * @param Swift_InputByteStream $is to write to + * @param int $firstLineOffset + * @param int $maxLineLength - 0 indicates the default length for this encoding + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0); + + /** + * Get the MIME name of this content encoding scheme. + * + * @return string + */ + public function getName(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/Base64ContentEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/Base64ContentEncoder.php new file mode 100644 index 00000000..8f76d70f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/Base64ContentEncoder.php @@ -0,0 +1,104 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Handles Base 64 Transfer Encoding in Swift Mailer. + * + * @author Chris Corbyn + */ +class Swift_Mime_ContentEncoder_Base64ContentEncoder extends Swift_Encoder_Base64Encoder implements Swift_Mime_ContentEncoder +{ + /** + * Encode stream $in to stream $out. + * + * @param Swift_OutputByteStream $os + * @param Swift_InputByteStream $is + * @param int $firstLineOffset + * @param int $maxLineLength, optional, 0 indicates the default of 76 bytes + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + if (0 >= $maxLineLength || 76 < $maxLineLength) { + $maxLineLength = 76; + } + + $remainder = 0; + $base64ReadBufferRemainderBytes = null; + + // To reduce memory usage, the output buffer is streamed to the input buffer like so: + // Output Stream => base64encode => wrap line length => Input Stream + // HOWEVER it's important to note that base64_encode() should only be passed whole triplets of data (except for the final chunk of data) + // otherwise it will assume the input data has *ended* and it will incorrectly pad/terminate the base64 data mid-stream. + // We use $base64ReadBufferRemainderBytes to carry over 1-2 "remainder" bytes from the each chunk from OutputStream and pre-pend those onto the + // chunk of bytes read in the next iteration. + // When the OutputStream is empty, we must flush any remainder bytes. + while (true) { + $readBytes = $os->read(8192); + $atEOF = ($readBytes === false); + + if ($atEOF) { + $streamTheseBytes = $base64ReadBufferRemainderBytes; + } else { + $streamTheseBytes = $base64ReadBufferRemainderBytes.$readBytes; + } + $base64ReadBufferRemainderBytes = null; + $bytesLength = strlen($streamTheseBytes); + + if ($bytesLength === 0) { // no data left to encode + break; + } + + // if we're not on the last block of the ouput stream, make sure $streamTheseBytes ends with a complete triplet of data + // and carry over remainder 1-2 bytes to the next loop iteration + if (!$atEOF) { + $excessBytes = $bytesLength % 3; + if ($excessBytes !== 0) { + $base64ReadBufferRemainderBytes = substr($streamTheseBytes, -$excessBytes); + $streamTheseBytes = substr($streamTheseBytes, 0, $bytesLength - $excessBytes); + } + } + + $encoded = base64_encode($streamTheseBytes); + $encodedTransformed = ''; + $thisMaxLineLength = $maxLineLength - $remainder - $firstLineOffset; + + while ($thisMaxLineLength < strlen($encoded)) { + $encodedTransformed .= substr($encoded, 0, $thisMaxLineLength)."\r\n"; + $firstLineOffset = 0; + $encoded = substr($encoded, $thisMaxLineLength); + $thisMaxLineLength = $maxLineLength; + $remainder = 0; + } + + if (0 < $remainingLength = strlen($encoded)) { + $remainder += $remainingLength; + $encodedTransformed .= $encoded; + $encoded = null; + } + + $is->write($encodedTransformed); + + if ($atEOF) { + break; + } + } + } + + /** + * Get the name of this encoding scheme. + * Returns the string 'base64'. + * + * @return string + */ + public function getName() + { + return 'base64'; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php new file mode 100644 index 00000000..710b5ac9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php @@ -0,0 +1,123 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Handles Quoted Printable (QP) Transfer Encoding in Swift Mailer using the PHP core function. + * + * @author Lars Strojny + */ +class Swift_Mime_ContentEncoder_NativeQpContentEncoder implements Swift_Mime_ContentEncoder +{ + /** + * @var null|string + */ + private $charset; + + /** + * @param null|string $charset + */ + public function __construct($charset = null) + { + $this->charset = $charset ? $charset : 'utf-8'; + } + + /** + * Notify this observer that the entity's charset has changed. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->charset = $charset; + } + + /** + * Encode $in to $out. + * + * @param Swift_OutputByteStream $os to read from + * @param Swift_InputByteStream $is to write to + * @param int $firstLineOffset + * @param int $maxLineLength 0 indicates the default length for this encoding + * + * @throws RuntimeException + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + if ($this->charset !== 'utf-8') { + throw new RuntimeException( + sprintf('Charset "%s" not supported. NativeQpContentEncoder only supports "utf-8"', $this->charset)); + } + + $string = ''; + + while (false !== $bytes = $os->read(8192)) { + $string .= $bytes; + } + + $is->write($this->encodeString($string)); + } + + /** + * Get the MIME name of this content encoding scheme. + * + * @return string + */ + public function getName() + { + return 'quoted-printable'; + } + + /** + * Encode a given string to produce an encoded string. + * + * @param string $string + * @param int $firstLineOffset if first line needs to be shorter + * @param int $maxLineLength 0 indicates the default length for this encoding + * + * @throws RuntimeException + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + if ($this->charset !== 'utf-8') { + throw new RuntimeException( + sprintf('Charset "%s" not supported. NativeQpContentEncoder only supports "utf-8"', $this->charset)); + } + + return $this->_standardize(quoted_printable_encode($string)); + } + + /** + * Make sure CRLF is correct and HT/SPACE are in valid places. + * + * @param string $string + * + * @return string + */ + protected function _standardize($string) + { + // transform CR or LF to CRLF + $string = preg_replace('~=0D(?!=0A)|(?<!=0D)=0A~', '=0D=0A', $string); + // transform =0D=0A to CRLF + $string = str_replace(array("\t=0D=0A", ' =0D=0A', '=0D=0A'), array("=09\r\n", "=20\r\n", "\r\n"), $string); + + switch ($end = ord(substr($string, -1))) { + case 0x09: + $string = substr_replace($string, '=09', -1); + break; + case 0x20: + $string = substr_replace($string, '=20', -1); + break; + } + + return $string; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/PlainContentEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/PlainContentEncoder.php new file mode 100644 index 00000000..219f482c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/PlainContentEncoder.php @@ -0,0 +1,162 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Handles binary/7/8-bit Transfer Encoding in Swift Mailer. + * + * @author Chris Corbyn + */ +class Swift_Mime_ContentEncoder_PlainContentEncoder implements Swift_Mime_ContentEncoder +{ + /** + * The name of this encoding scheme (probably 7bit or 8bit). + * + * @var string + */ + private $_name; + + /** + * True if canonical transformations should be done. + * + * @var bool + */ + private $_canonical; + + /** + * Creates a new PlainContentEncoder with $name (probably 7bit or 8bit). + * + * @param string $name + * @param bool $canonical If canonicalization transformation should be done. + */ + public function __construct($name, $canonical = false) + { + $this->_name = $name; + $this->_canonical = $canonical; + } + + /** + * Encode a given string to produce an encoded string. + * + * @param string $string + * @param int $firstLineOffset ignored + * @param int $maxLineLength - 0 means no wrapping will occur + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + if ($this->_canonical) { + $string = $this->_canonicalize($string); + } + + return $this->_safeWordWrap($string, $maxLineLength, "\r\n"); + } + + /** + * Encode stream $in to stream $out. + * + * @param Swift_OutputByteStream $os + * @param Swift_InputByteStream $is + * @param int $firstLineOffset ignored + * @param int $maxLineLength optional, 0 means no wrapping will occur + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + $leftOver = ''; + while (false !== $bytes = $os->read(8192)) { + $toencode = $leftOver.$bytes; + if ($this->_canonical) { + $toencode = $this->_canonicalize($toencode); + } + $wrapped = $this->_safeWordWrap($toencode, $maxLineLength, "\r\n"); + $lastLinePos = strrpos($wrapped, "\r\n"); + $leftOver = substr($wrapped, $lastLinePos); + $wrapped = substr($wrapped, 0, $lastLinePos); + + $is->write($wrapped); + } + if (strlen($leftOver)) { + $is->write($leftOver); + } + } + + /** + * Get the name of this encoding scheme. + * + * @return string + */ + public function getName() + { + return $this->_name; + } + + /** + * Not used. + */ + public function charsetChanged($charset) + { + } + + /** + * A safer (but weaker) wordwrap for unicode. + * + * @param string $string + * @param int $length + * @param string $le + * + * @return string + */ + private function _safeWordwrap($string, $length = 75, $le = "\r\n") + { + if (0 >= $length) { + return $string; + } + + $originalLines = explode($le, $string); + + $lines = array(); + $lineCount = 0; + + foreach ($originalLines as $originalLine) { + $lines[] = ''; + $currentLine = &$lines[$lineCount++]; + + //$chunks = preg_split('/(?<=[\ \t,\.!\?\-&\+\/])/', $originalLine); + $chunks = preg_split('/(?<=\s)/', $originalLine); + + foreach ($chunks as $chunk) { + if (0 != strlen($currentLine) + && strlen($currentLine.$chunk) > $length) { + $lines[] = ''; + $currentLine = &$lines[$lineCount++]; + } + $currentLine .= $chunk; + } + } + + return implode("\r\n", $lines); + } + + /** + * Canonicalize string input (fix CRLF). + * + * @param string $string + * + * @return string + */ + private function _canonicalize($string) + { + return str_replace( + array("\r\n", "\r", "\n"), + array("\n", "\n", "\r\n"), + $string + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php new file mode 100644 index 00000000..5cc907b8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php @@ -0,0 +1,134 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Handles Quoted Printable (QP) Transfer Encoding in Swift Mailer. + * + * @author Chris Corbyn + */ +class Swift_Mime_ContentEncoder_QpContentEncoder extends Swift_Encoder_QpEncoder implements Swift_Mime_ContentEncoder +{ + protected $_dotEscape; + + /** + * Creates a new QpContentEncoder for the given CharacterStream. + * + * @param Swift_CharacterStream $charStream to use for reading characters + * @param Swift_StreamFilter $filter if canonicalization should occur + * @param bool $dotEscape if dot stuffing workaround must be enabled + */ + public function __construct(Swift_CharacterStream $charStream, Swift_StreamFilter $filter = null, $dotEscape = false) + { + $this->_dotEscape = $dotEscape; + parent::__construct($charStream, $filter); + } + + public function __sleep() + { + return array('_charStream', '_filter', '_dotEscape'); + } + + protected function getSafeMapShareId() + { + return get_class($this).($this->_dotEscape ? '.dotEscape' : ''); + } + + protected function initSafeMap() + { + parent::initSafeMap(); + if ($this->_dotEscape) { + /* Encode . as =2e for buggy remote servers */ + unset($this->_safeMap[0x2e]); + } + } + + /** + * Encode stream $in to stream $out. + * + * QP encoded strings have a maximum line length of 76 characters. + * If the first line needs to be shorter, indicate the difference with + * $firstLineOffset. + * + * @param Swift_OutputByteStream $os output stream + * @param Swift_InputByteStream $is input stream + * @param int $firstLineOffset + * @param int $maxLineLength + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + if ($maxLineLength > 76 || $maxLineLength <= 0) { + $maxLineLength = 76; + } + + $thisLineLength = $maxLineLength - $firstLineOffset; + + $this->_charStream->flushContents(); + $this->_charStream->importByteStream($os); + + $currentLine = ''; + $prepend = ''; + $size = $lineLen = 0; + + while (false !== $bytes = $this->_nextSequence()) { + // If we're filtering the input + if (isset($this->_filter)) { + // If we can't filter because we need more bytes + while ($this->_filter->shouldBuffer($bytes)) { + // Then collect bytes into the buffer + if (false === $moreBytes = $this->_nextSequence(1)) { + break; + } + + foreach ($moreBytes as $b) { + $bytes[] = $b; + } + } + // And filter them + $bytes = $this->_filter->filter($bytes); + } + + $enc = $this->_encodeByteSequence($bytes, $size); + + $i = strpos($enc, '=0D=0A'); + $newLineLength = $lineLen + ($i === false ? $size : $i); + + if ($currentLine && $newLineLength >= $thisLineLength) { + $is->write($prepend.$this->_standardize($currentLine)); + $currentLine = ''; + $prepend = "=\r\n"; + $thisLineLength = $maxLineLength; + $lineLen = 0; + } + + $currentLine .= $enc; + + if ($i === false) { + $lineLen += $size; + } else { + // 6 is the length of '=0D=0A'. + $lineLen = $size - strrpos($enc, '=0D=0A') - 6; + } + } + if (strlen($currentLine)) { + $is->write($prepend.$this->_standardize($currentLine)); + } + } + + /** + * Get the name of this encoding scheme. + * Returns the string 'quoted-printable'. + * + * @return string + */ + public function getName() + { + return 'quoted-printable'; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoderProxy.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoderProxy.php new file mode 100644 index 00000000..3214e1cf --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoderProxy.php @@ -0,0 +1,98 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Proxy for quoted-printable content encoders. + * + * Switches on the best QP encoder implementation for current charset. + * + * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> + */ +class Swift_Mime_ContentEncoder_QpContentEncoderProxy implements Swift_Mime_ContentEncoder +{ + /** + * @var Swift_Mime_ContentEncoder_QpContentEncoder + */ + private $safeEncoder; + + /** + * @var Swift_Mime_ContentEncoder_NativeQpContentEncoder + */ + private $nativeEncoder; + + /** + * @var null|string + */ + private $charset; + + /** + * Constructor. + * + * @param Swift_Mime_ContentEncoder_QpContentEncoder $safeEncoder + * @param Swift_Mime_ContentEncoder_NativeQpContentEncoder $nativeEncoder + * @param string|null $charset + */ + public function __construct(Swift_Mime_ContentEncoder_QpContentEncoder $safeEncoder, Swift_Mime_ContentEncoder_NativeQpContentEncoder $nativeEncoder, $charset) + { + $this->safeEncoder = $safeEncoder; + $this->nativeEncoder = $nativeEncoder; + $this->charset = $charset; + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->safeEncoder = clone $this->safeEncoder; + $this->nativeEncoder = clone $this->nativeEncoder; + } + + /** + * {@inheritdoc} + */ + public function charsetChanged($charset) + { + $this->charset = $charset; + $this->safeEncoder->charsetChanged($charset); + } + + /** + * {@inheritdoc} + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + $this->getEncoder()->encodeByteStream($os, $is, $firstLineOffset, $maxLineLength); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'quoted-printable'; + } + + /** + * {@inheritdoc} + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + return $this->getEncoder()->encodeString($string, $firstLineOffset, $maxLineLength); + } + + /** + * @return Swift_Mime_ContentEncoder + */ + private function getEncoder() + { + return 'utf-8' === $this->charset ? $this->nativeEncoder : $this->safeEncoder; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/RawContentEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/RawContentEncoder.php new file mode 100644 index 00000000..0b8526e3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/RawContentEncoder.php @@ -0,0 +1,64 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Handles raw Transfer Encoding in Swift Mailer. + * + * + * @author Sebastiaan Stok <s.stok@rollerscapes.net> + */ +class Swift_Mime_ContentEncoder_RawContentEncoder implements Swift_Mime_ContentEncoder +{ + /** + * Encode a given string to produce an encoded string. + * + * @param string $string + * @param int $firstLineOffset ignored + * @param int $maxLineLength ignored + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + return $string; + } + + /** + * Encode stream $in to stream $out. + * + * @param Swift_OutputByteStream $in + * @param Swift_InputByteStream $out + * @param int $firstLineOffset ignored + * @param int $maxLineLength ignored + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + while (false !== ($bytes = $os->read(8192))) { + $is->write($bytes); + } + } + + /** + * Get the name of this encoding scheme. + * + * @return string + */ + public function getName() + { + return 'raw'; + } + + /** + * Not used. + */ + public function charsetChanged($charset) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php new file mode 100644 index 00000000..6af75712 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php @@ -0,0 +1,45 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An embedded file, in a multipart message. + * + * @author Chris Corbyn + */ +class Swift_Mime_EmbeddedFile extends Swift_Mime_Attachment +{ + /** + * Creates a new Attachment with $headers and $encoder. + * + * @param Swift_Mime_HeaderSet $headers + * @param Swift_Mime_ContentEncoder $encoder + * @param Swift_KeyCache $cache + * @param Swift_Mime_Grammar $grammar + * @param array $mimeTypes optional + */ + public function __construct(Swift_Mime_HeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_Mime_Grammar $grammar, $mimeTypes = array()) + { + parent::__construct($headers, $encoder, $cache, $grammar, $mimeTypes); + $this->setDisposition('inline'); + $this->setId($this->getId()); + } + + /** + * Get the nesting level of this EmbeddedFile. + * + * Returns {@see LEVEL_RELATED}. + * + * @return int + */ + public function getNestingLevel() + { + return self::LEVEL_RELATED; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php new file mode 100644 index 00000000..cc44a6ef --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php @@ -0,0 +1,24 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Observes changes for a Mime entity's ContentEncoder. + * + * @author Chris Corbyn + */ +interface Swift_Mime_EncodingObserver +{ + /** + * Notify this observer that the observed entity's ContentEncoder has changed. + * + * @param Swift_Mime_ContentEncoder $encoder + */ + public function encoderChanged(Swift_Mime_ContentEncoder $encoder); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Grammar.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Grammar.php new file mode 100644 index 00000000..a09f3383 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Grammar.php @@ -0,0 +1,176 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Defines the grammar to use for validation, implements the RFC 2822 (and friends) ABNF grammar definitions. + * + * @author Fabien Potencier + * @author Chris Corbyn + */ +class Swift_Mime_Grammar +{ + /** + * Special characters used in the syntax which need to be escaped. + * + * @var string[] + */ + private static $_specials = array(); + + /** + * Tokens defined in RFC 2822 (and some related RFCs). + * + * @var string[] + */ + private static $_grammar = array(); + + /** + * Initialize some RFC 2822 (and friends) ABNF grammar definitions. + */ + public function __construct() + { + $this->init(); + } + + public function __wakeup() + { + $this->init(); + } + + protected function init() + { + if (count(self::$_specials) > 0) { + return; + } + + self::$_specials = array( + '(', ')', '<', '>', '[', ']', + ':', ';', '@', ',', '.', '"', + ); + + /*** Refer to RFC 2822 for ABNF grammar ***/ + + // All basic building blocks + self::$_grammar['NO-WS-CTL'] = '[\x01-\x08\x0B\x0C\x0E-\x19\x7F]'; + self::$_grammar['WSP'] = '[ \t]'; + self::$_grammar['CRLF'] = '(?:\r\n)'; + self::$_grammar['FWS'] = '(?:(?:'.self::$_grammar['WSP'].'*'. + self::$_grammar['CRLF'].')?'.self::$_grammar['WSP'].')'; + self::$_grammar['text'] = '[\x00-\x08\x0B\x0C\x0E-\x7F]'; + self::$_grammar['quoted-pair'] = '(?:\\\\'.self::$_grammar['text'].')'; + self::$_grammar['ctext'] = '(?:'.self::$_grammar['NO-WS-CTL']. + '|[\x21-\x27\x2A-\x5B\x5D-\x7E])'; + // Uses recursive PCRE (?1) -- could be a weak point?? + self::$_grammar['ccontent'] = '(?:'.self::$_grammar['ctext'].'|'. + self::$_grammar['quoted-pair'].'|(?1))'; + self::$_grammar['comment'] = '(\((?:'.self::$_grammar['FWS'].'|'. + self::$_grammar['ccontent'].')*'.self::$_grammar['FWS'].'?\))'; + self::$_grammar['CFWS'] = '(?:(?:'.self::$_grammar['FWS'].'?'. + self::$_grammar['comment'].')*(?:(?:'.self::$_grammar['FWS'].'?'. + self::$_grammar['comment'].')|'.self::$_grammar['FWS'].'))'; + self::$_grammar['qtext'] = '(?:'.self::$_grammar['NO-WS-CTL']. + '|[\x21\x23-\x5B\x5D-\x7E])'; + self::$_grammar['qcontent'] = '(?:'.self::$_grammar['qtext'].'|'. + self::$_grammar['quoted-pair'].')'; + self::$_grammar['quoted-string'] = '(?:'.self::$_grammar['CFWS'].'?"'. + '('.self::$_grammar['FWS'].'?'.self::$_grammar['qcontent'].')*'. + self::$_grammar['FWS'].'?"'.self::$_grammar['CFWS'].'?)'; + self::$_grammar['atext'] = '[a-zA-Z0-9!#\$%&\'\*\+\-\/=\?\^_`\{\}\|~]'; + self::$_grammar['atom'] = '(?:'.self::$_grammar['CFWS'].'?'. + self::$_grammar['atext'].'+'.self::$_grammar['CFWS'].'?)'; + self::$_grammar['dot-atom-text'] = '(?:'.self::$_grammar['atext'].'+'. + '(\.'.self::$_grammar['atext'].'+)*)'; + self::$_grammar['dot-atom'] = '(?:'.self::$_grammar['CFWS'].'?'. + self::$_grammar['dot-atom-text'].'+'.self::$_grammar['CFWS'].'?)'; + self::$_grammar['word'] = '(?:'.self::$_grammar['atom'].'|'. + self::$_grammar['quoted-string'].')'; + self::$_grammar['phrase'] = '(?:'.self::$_grammar['word'].'+?)'; + self::$_grammar['no-fold-quote'] = '(?:"(?:'.self::$_grammar['qtext']. + '|'.self::$_grammar['quoted-pair'].')*")'; + self::$_grammar['dtext'] = '(?:'.self::$_grammar['NO-WS-CTL']. + '|[\x21-\x5A\x5E-\x7E])'; + self::$_grammar['no-fold-literal'] = '(?:\[(?:'.self::$_grammar['dtext']. + '|'.self::$_grammar['quoted-pair'].')*\])'; + + // Message IDs + self::$_grammar['id-left'] = '(?:'.self::$_grammar['dot-atom-text'].'|'. + self::$_grammar['no-fold-quote'].')'; + self::$_grammar['id-right'] = '(?:'.self::$_grammar['dot-atom-text'].'|'. + self::$_grammar['no-fold-literal'].')'; + + // Addresses, mailboxes and paths + self::$_grammar['local-part'] = '(?:'.self::$_grammar['dot-atom'].'|'. + self::$_grammar['quoted-string'].')'; + self::$_grammar['dcontent'] = '(?:'.self::$_grammar['dtext'].'|'. + self::$_grammar['quoted-pair'].')'; + self::$_grammar['domain-literal'] = '(?:'.self::$_grammar['CFWS'].'?\[('. + self::$_grammar['FWS'].'?'.self::$_grammar['dcontent'].')*?'. + self::$_grammar['FWS'].'?\]'.self::$_grammar['CFWS'].'?)'; + self::$_grammar['domain'] = '(?:'.self::$_grammar['dot-atom'].'|'. + self::$_grammar['domain-literal'].')'; + self::$_grammar['addr-spec'] = '(?:'.self::$_grammar['local-part'].'@'. + self::$_grammar['domain'].')'; + } + + /** + * Get the grammar defined for $name token. + * + * @param string $name exactly as written in the RFC + * + * @return string + */ + public function getDefinition($name) + { + if (array_key_exists($name, self::$_grammar)) { + return self::$_grammar[$name]; + } + + throw new Swift_RfcComplianceException( + "No such grammar '".$name."' defined." + ); + } + + /** + * Returns the tokens defined in RFC 2822 (and some related RFCs). + * + * @return array + */ + public function getGrammarDefinitions() + { + return self::$_grammar; + } + + /** + * Returns the current special characters used in the syntax which need to be escaped. + * + * @return array + */ + public function getSpecials() + { + return self::$_specials; + } + + /** + * Escape special characters in a string (convert to quoted-pairs). + * + * @param string $token + * @param string[] $include additional chars to escape + * @param string[] $exclude chars from escaping + * + * @return string + */ + public function escapeSpecials($token, $include = array(), $exclude = array()) + { + foreach (array_merge(array('\\'), array_diff(self::$_specials, $exclude), $include) as $char) { + $token = str_replace($char, '\\'.$char, $token); + } + + return $token; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php new file mode 100644 index 00000000..a8ddd272 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php @@ -0,0 +1,93 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A MIME Header. + * + * @author Chris Corbyn + */ +interface Swift_Mime_Header +{ + /** Text headers */ + const TYPE_TEXT = 2; + + /** headers (text + params) */ + const TYPE_PARAMETERIZED = 6; + + /** Mailbox and address headers */ + const TYPE_MAILBOX = 8; + + /** Date and time headers */ + const TYPE_DATE = 16; + + /** Identification headers */ + const TYPE_ID = 32; + + /** Address path headers */ + const TYPE_PATH = 64; + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType(); + + /** + * Set the model for the field body. + * + * The actual types needed will vary depending upon the type of Header. + * + * @param mixed $model + */ + public function setFieldBodyModel($model); + + /** + * Set the charset used when rendering the Header. + * + * @param string $charset + */ + public function setCharset($charset); + + /** + * Get the model for the field body. + * + * The return type depends on the specifics of the Header. + * + * @return mixed + */ + public function getFieldBodyModel(); + + /** + * Get the name of this header (e.g. Subject). + * + * The name is an identifier and as such will be immutable. + * + * @return string + */ + public function getFieldName(); + + /** + * Get the field body, prepared for folding into a final header value. + * + * @return string + */ + public function getFieldBody(); + + /** + * Get this Header rendered as a compliant string. + * + * @return string + */ + public function toString(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder.php new file mode 100644 index 00000000..08fd453d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder.php @@ -0,0 +1,24 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Interface for all Header Encoding schemes. + * + * @author Chris Corbyn + */ +interface Swift_Mime_HeaderEncoder extends Swift_Encoder +{ + /** + * Get the MIME name of this content encoding scheme. + * + * @return string + */ + public function getName(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/Base64HeaderEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/Base64HeaderEncoder.php new file mode 100644 index 00000000..83a4f2f3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/Base64HeaderEncoder.php @@ -0,0 +1,55 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Handles Base64 (B) Header Encoding in Swift Mailer. + * + * @author Chris Corbyn + */ +class Swift_Mime_HeaderEncoder_Base64HeaderEncoder extends Swift_Encoder_Base64Encoder implements Swift_Mime_HeaderEncoder +{ + /** + * Get the name of this encoding scheme. + * Returns the string 'B'. + * + * @return string + */ + public function getName() + { + return 'B'; + } + + /** + * Takes an unencoded string and produces a Base64 encoded string from it. + * + * If the charset is iso-2022-jp, it uses mb_encode_mimeheader instead of + * default encodeString, otherwise pass to the parent method. + * + * @param string $string string to encode + * @param int $firstLineOffset + * @param int $maxLineLength optional, 0 indicates the default of 76 bytes + * @param string $charset + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0, $charset = 'utf-8') + { + if (strtolower($charset) === 'iso-2022-jp') { + $old = mb_internal_encoding(); + mb_internal_encoding('utf-8'); + $newstring = mb_encode_mimeheader($string, $charset, $this->getName(), "\r\n"); + mb_internal_encoding($old); + + return $newstring; + } + + return parent::encodeString($string, $firstLineOffset, $maxLineLength); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php new file mode 100644 index 00000000..510dd663 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php @@ -0,0 +1,65 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Handles Quoted Printable (Q) Header Encoding in Swift Mailer. + * + * @author Chris Corbyn + */ +class Swift_Mime_HeaderEncoder_QpHeaderEncoder extends Swift_Encoder_QpEncoder implements Swift_Mime_HeaderEncoder +{ + /** + * Creates a new QpHeaderEncoder for the given CharacterStream. + * + * @param Swift_CharacterStream $charStream to use for reading characters + */ + public function __construct(Swift_CharacterStream $charStream) + { + parent::__construct($charStream); + } + + protected function initSafeMap() + { + foreach (array_merge( + range(0x61, 0x7A), range(0x41, 0x5A), + range(0x30, 0x39), array(0x20, 0x21, 0x2A, 0x2B, 0x2D, 0x2F) + ) as $byte) { + $this->_safeMap[$byte] = chr($byte); + } + } + + /** + * Get the name of this encoding scheme. + * + * Returns the string 'Q'. + * + * @return string + */ + public function getName() + { + return 'Q'; + } + + /** + * Takes an unencoded string and produces a QP encoded string from it. + * + * @param string $string string to encode + * @param int $firstLineOffset optional + * @param int $maxLineLength optional, 0 indicates the default of 76 chars + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + return str_replace(array(' ', '=20', "=\r\n"), array('_', '_', "\r\n"), + parent::encodeString($string, $firstLineOffset, $maxLineLength) + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php new file mode 100644 index 00000000..c65f26d7 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php @@ -0,0 +1,78 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Creates MIME headers. + * + * @author Chris Corbyn + */ +interface Swift_Mime_HeaderFactory extends Swift_Mime_CharsetObserver +{ + /** + * Create a new Mailbox Header with a list of $addresses. + * + * @param string $name + * @param array|string $addresses + * + * @return Swift_Mime_Header + */ + public function createMailboxHeader($name, $addresses = null); + + /** + * Create a new Date header using $timestamp (UNIX time). + * + * @param string $name + * @param int $timestamp + * + * @return Swift_Mime_Header + */ + public function createDateHeader($name, $timestamp = null); + + /** + * Create a new basic text header with $name and $value. + * + * @param string $name + * @param string $value + * + * @return Swift_Mime_Header + */ + public function createTextHeader($name, $value = null); + + /** + * Create a new ParameterizedHeader with $name, $value and $params. + * + * @param string $name + * @param string $value + * @param array $params + * + * @return Swift_Mime_ParameterizedHeader + */ + public function createParameterizedHeader($name, $value = null, $params = array()); + + /** + * Create a new ID header for Message-ID or Content-ID. + * + * @param string $name + * @param string|array $ids + * + * @return Swift_Mime_Header + */ + public function createIdHeader($name, $ids = null); + + /** + * Create a new Path header with an address (path) in it. + * + * @param string $name + * @param string $path + * + * @return Swift_Mime_Header + */ + public function createPathHeader($name, $path = null); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderSet.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderSet.php new file mode 100644 index 00000000..7390d2c8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderSet.php @@ -0,0 +1,169 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A collection of MIME headers. + * + * @author Chris Corbyn + */ +interface Swift_Mime_HeaderSet extends Swift_Mime_CharsetObserver +{ + /** + * Add a new Mailbox Header with a list of $addresses. + * + * @param string $name + * @param array|string $addresses + */ + public function addMailboxHeader($name, $addresses = null); + + /** + * Add a new Date header using $timestamp (UNIX time). + * + * @param string $name + * @param int $timestamp + */ + public function addDateHeader($name, $timestamp = null); + + /** + * Add a new basic text header with $name and $value. + * + * @param string $name + * @param string $value + */ + public function addTextHeader($name, $value = null); + + /** + * Add a new ParameterizedHeader with $name, $value and $params. + * + * @param string $name + * @param string $value + * @param array $params + */ + public function addParameterizedHeader($name, $value = null, $params = array()); + + /** + * Add a new ID header for Message-ID or Content-ID. + * + * @param string $name + * @param string|array $ids + */ + public function addIdHeader($name, $ids = null); + + /** + * Add a new Path header with an address (path) in it. + * + * @param string $name + * @param string $path + */ + public function addPathHeader($name, $path = null); + + /** + * Returns true if at least one header with the given $name exists. + * + * If multiple headers match, the actual one may be specified by $index. + * + * @param string $name + * @param int $index + * + * @return bool + */ + public function has($name, $index = 0); + + /** + * Set a header in the HeaderSet. + * + * The header may be a previously fetched header via {@link get()} or it may + * be one that has been created separately. + * + * If $index is specified, the header will be inserted into the set at this + * offset. + * + * @param Swift_Mime_Header $header + * @param int $index + */ + public function set(Swift_Mime_Header $header, $index = 0); + + /** + * Get the header with the given $name. + * If multiple headers match, the actual one may be specified by $index. + * Returns NULL if none present. + * + * @param string $name + * @param int $index + * + * @return Swift_Mime_Header + */ + public function get($name, $index = 0); + + /** + * Get all headers with the given $name. + * + * @param string $name + * + * @return array + */ + public function getAll($name = null); + + /** + * Return the name of all Headers. + * + * @return array + */ + public function listAll(); + + /** + * Remove the header with the given $name if it's set. + * + * If multiple headers match, the actual one may be specified by $index. + * + * @param string $name + * @param int $index + */ + public function remove($name, $index = 0); + + /** + * Remove all headers with the given $name. + * + * @param string $name + */ + public function removeAll($name); + + /** + * Create a new instance of this HeaderSet. + * + * @return Swift_Mime_HeaderSet + */ + public function newInstance(); + + /** + * Define a list of Header names as an array in the correct order. + * + * These Headers will be output in the given order where present. + * + * @param array $sequence + */ + public function defineOrdering(array $sequence); + + /** + * Set a list of header names which must always be displayed when set. + * + * Usually headers without a field value won't be output unless set here. + * + * @param array $names + */ + public function setAlwaysDisplayed(array $names); + + /** + * Returns a string with a representation of all headers. + * + * @return string + */ + public function toString(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/AbstractHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/AbstractHeader.php new file mode 100644 index 00000000..fbd9258a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/AbstractHeader.php @@ -0,0 +1,503 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An abstract base MIME Header. + * + * @author Chris Corbyn + */ +abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header +{ + /** + * The name of this Header. + * + * @var string + */ + private $_name; + + /** + * The Grammar used for this Header. + * + * @var Swift_Mime_Grammar + */ + private $_grammar; + + /** + * The Encoder used to encode this Header. + * + * @var Swift_Encoder + */ + private $_encoder; + + /** + * The maximum length of a line in the header. + * + * @var int + */ + private $_lineLength = 78; + + /** + * The language used in this Header. + * + * @var string + */ + private $_lang; + + /** + * The character set of the text in this Header. + * + * @var string + */ + private $_charset = 'utf-8'; + + /** + * The value of this Header, cached. + * + * @var string + */ + private $_cachedValue = null; + + /** + * Creates a new Header. + * + * @param Swift_Mime_Grammar $grammar + */ + public function __construct(Swift_Mime_Grammar $grammar) + { + $this->setGrammar($grammar); + } + + /** + * Set the character set used in this Header. + * + * @param string $charset + */ + public function setCharset($charset) + { + $this->clearCachedValueIf($charset != $this->_charset); + $this->_charset = $charset; + if (isset($this->_encoder)) { + $this->_encoder->charsetChanged($charset); + } + } + + /** + * Get the character set used in this Header. + * + * @return string + */ + public function getCharset() + { + return $this->_charset; + } + + /** + * Set the language used in this Header. + * + * For example, for US English, 'en-us'. + * This can be unspecified. + * + * @param string $lang + */ + public function setLanguage($lang) + { + $this->clearCachedValueIf($this->_lang != $lang); + $this->_lang = $lang; + } + + /** + * Get the language used in this Header. + * + * @return string + */ + public function getLanguage() + { + return $this->_lang; + } + + /** + * Set the encoder used for encoding the header. + * + * @param Swift_Mime_HeaderEncoder $encoder + */ + public function setEncoder(Swift_Mime_HeaderEncoder $encoder) + { + $this->_encoder = $encoder; + $this->setCachedValue(null); + } + + /** + * Get the encoder used for encoding this Header. + * + * @return Swift_Mime_HeaderEncoder + */ + public function getEncoder() + { + return $this->_encoder; + } + + /** + * Set the grammar used for the header. + * + * @param Swift_Mime_Grammar $grammar + */ + public function setGrammar(Swift_Mime_Grammar $grammar) + { + $this->_grammar = $grammar; + $this->setCachedValue(null); + } + + /** + * Get the grammar used for this Header. + * + * @return Swift_Mime_Grammar + */ + public function getGrammar() + { + return $this->_grammar; + } + + /** + * Get the name of this header (e.g. charset). + * + * @return string + */ + public function getFieldName() + { + return $this->_name; + } + + /** + * Set the maximum length of lines in the header (excluding EOL). + * + * @param int $lineLength + */ + public function setMaxLineLength($lineLength) + { + $this->clearCachedValueIf($this->_lineLength != $lineLength); + $this->_lineLength = $lineLength; + } + + /** + * Get the maximum permitted length of lines in this Header. + * + * @return int + */ + public function getMaxLineLength() + { + return $this->_lineLength; + } + + /** + * Get this Header rendered as a RFC 2822 compliant string. + * + * @throws Swift_RfcComplianceException + * + * @return string + */ + public function toString() + { + return $this->_tokensToString($this->toTokens()); + } + + /** + * Returns a string representation of this object. + * + * @return string + * + * @see toString() + */ + public function __toString() + { + return $this->toString(); + } + + // -- Points of extension + + /** + * Set the name of this Header field. + * + * @param string $name + */ + protected function setFieldName($name) + { + $this->_name = $name; + } + + /** + * Produces a compliant, formatted RFC 2822 'phrase' based on the string given. + * + * @param Swift_Mime_Header $header + * @param string $string as displayed + * @param string $charset of the text + * @param Swift_Mime_HeaderEncoder $encoder + * @param bool $shorten the first line to make remove for header name + * + * @return string + */ + protected function createPhrase(Swift_Mime_Header $header, $string, $charset, Swift_Mime_HeaderEncoder $encoder = null, $shorten = false) + { + // Treat token as exactly what was given + $phraseStr = $string; + // If it's not valid + if (!preg_match('/^'.$this->getGrammar()->getDefinition('phrase').'$/D', $phraseStr)) { + // .. but it is just ascii text, try escaping some characters + // and make it a quoted-string + if (preg_match('/^'.$this->getGrammar()->getDefinition('text').'*$/D', $phraseStr)) { + $phraseStr = $this->getGrammar()->escapeSpecials( + $phraseStr, array('"'), $this->getGrammar()->getSpecials() + ); + $phraseStr = '"'.$phraseStr.'"'; + } else { + // ... otherwise it needs encoding + // Determine space remaining on line if first line + if ($shorten) { + $usedLength = strlen($header->getFieldName().': '); + } else { + $usedLength = 0; + } + $phraseStr = $this->encodeWords($header, $string, $usedLength); + } + } + + return $phraseStr; + } + + /** + * Encode needed word tokens within a string of input. + * + * @param Swift_Mime_Header $header + * @param string $input + * @param string $usedLength optional + * + * @return string + */ + protected function encodeWords(Swift_Mime_Header $header, $input, $usedLength = -1) + { + $value = ''; + + $tokens = $this->getEncodableWordTokens($input); + + foreach ($tokens as $token) { + // See RFC 2822, Sect 2.2 (really 2.2 ??) + if ($this->tokenNeedsEncoding($token)) { + // Don't encode starting WSP + $firstChar = substr($token, 0, 1); + switch ($firstChar) { + case ' ': + case "\t": + $value .= $firstChar; + $token = substr($token, 1); + } + + if (-1 == $usedLength) { + $usedLength = strlen($header->getFieldName().': ') + strlen($value); + } + $value .= $this->getTokenAsEncodedWord($token, $usedLength); + + $header->setMaxLineLength(76); // Forcefully override + } else { + $value .= $token; + } + } + + return $value; + } + + /** + * Test if a token needs to be encoded or not. + * + * @param string $token + * + * @return bool + */ + protected function tokenNeedsEncoding($token) + { + return preg_match('~[\x00-\x08\x10-\x19\x7F-\xFF\r\n]~', $token); + } + + /** + * Splits a string into tokens in blocks of words which can be encoded quickly. + * + * @param string $string + * + * @return string[] + */ + protected function getEncodableWordTokens($string) + { + $tokens = array(); + + $encodedToken = ''; + // Split at all whitespace boundaries + foreach (preg_split('~(?=[\t ])~', $string) as $token) { + if ($this->tokenNeedsEncoding($token)) { + $encodedToken .= $token; + } else { + if (strlen($encodedToken) > 0) { + $tokens[] = $encodedToken; + $encodedToken = ''; + } + $tokens[] = $token; + } + } + if (strlen($encodedToken)) { + $tokens[] = $encodedToken; + } + + return $tokens; + } + + /** + * Get a token as an encoded word for safe insertion into headers. + * + * @param string $token token to encode + * @param int $firstLineOffset optional + * + * @return string + */ + protected function getTokenAsEncodedWord($token, $firstLineOffset = 0) + { + // Adjust $firstLineOffset to account for space needed for syntax + $charsetDecl = $this->_charset; + if (isset($this->_lang)) { + $charsetDecl .= '*'.$this->_lang; + } + $encodingWrapperLength = strlen( + '=?'.$charsetDecl.'?'.$this->_encoder->getName().'??=' + ); + + if ($firstLineOffset >= 75) { + //Does this logic need to be here? + $firstLineOffset = 0; + } + + $encodedTextLines = explode("\r\n", + $this->_encoder->encodeString( + $token, $firstLineOffset, 75 - $encodingWrapperLength, $this->_charset + ) + ); + + if (strtolower($this->_charset) !== 'iso-2022-jp') { + // special encoding for iso-2022-jp using mb_encode_mimeheader + foreach ($encodedTextLines as $lineNum => $line) { + $encodedTextLines[$lineNum] = '=?'.$charsetDecl. + '?'.$this->_encoder->getName(). + '?'.$line.'?='; + } + } + + return implode("\r\n ", $encodedTextLines); + } + + /** + * Generates tokens from the given string which include CRLF as individual tokens. + * + * @param string $token + * + * @return string[] + */ + protected function generateTokenLines($token) + { + return preg_split('~(\r\n)~', $token, -1, PREG_SPLIT_DELIM_CAPTURE); + } + + /** + * Set a value into the cache. + * + * @param string $value + */ + protected function setCachedValue($value) + { + $this->_cachedValue = $value; + } + + /** + * Get the value in the cache. + * + * @return string + */ + protected function getCachedValue() + { + return $this->_cachedValue; + } + + /** + * Clear the cached value if $condition is met. + * + * @param bool $condition + */ + protected function clearCachedValueIf($condition) + { + if ($condition) { + $this->setCachedValue(null); + } + } + + /** + * Generate a list of all tokens in the final header. + * + * @param string $string The string to tokenize + * + * @return array An array of tokens as strings + */ + protected function toTokens($string = null) + { + if (is_null($string)) { + $string = $this->getFieldBody(); + } + + $tokens = array(); + + // Generate atoms; split at all invisible boundaries followed by WSP + foreach (preg_split('~(?=[ \t])~', $string) as $token) { + $newTokens = $this->generateTokenLines($token); + foreach ($newTokens as $newToken) { + $tokens[] = $newToken; + } + } + + return $tokens; + } + + /** + * Takes an array of tokens which appear in the header and turns them into + * an RFC 2822 compliant string, adding FWSP where needed. + * + * @param string[] $tokens + * + * @return string + */ + private function _tokensToString(array $tokens) + { + $lineCount = 0; + $headerLines = array(); + $headerLines[] = $this->_name.': '; + $currentLine = &$headerLines[$lineCount++]; + + // Build all tokens back into compliant header + foreach ($tokens as $i => $token) { + // Line longer than specified maximum or token was just a new line + if (("\r\n" == $token) || + ($i > 0 && strlen($currentLine.$token) > $this->_lineLength) + && 0 < strlen($currentLine)) { + $headerLines[] = ''; + $currentLine = &$headerLines[$lineCount++]; + } + + // Append token to the line + if ("\r\n" != $token) { + $currentLine .= $token; + } + } + + // Implode with FWS (RFC 2822, 2.2.3) + return implode("\r\n", $headerLines)."\r\n"; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php new file mode 100644 index 00000000..4fd66742 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php @@ -0,0 +1,125 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A Date MIME Header for Swift Mailer. + * + * @author Chris Corbyn + */ +class Swift_Mime_Headers_DateHeader extends Swift_Mime_Headers_AbstractHeader +{ + /** + * The UNIX timestamp value of this Header. + * + * @var int + */ + private $_timestamp; + + /** + * Creates a new DateHeader with $name and $timestamp. + * + * Example: + * <code> + * <?php + * $header = new Swift_Mime_Headers_DateHeader('Date', time()); + * ?> + * </code> + * + * @param string $name of Header + * @param Swift_Mime_Grammar $grammar + */ + public function __construct($name, Swift_Mime_Grammar $grammar) + { + $this->setFieldName($name); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_DATE; + } + + /** + * Set the model for the field body. + * + * This method takes a UNIX timestamp. + * + * @param int $model + */ + public function setFieldBodyModel($model) + { + $this->setTimestamp($model); + } + + /** + * Get the model for the field body. + * + * This method returns a UNIX timestamp. + * + * @return mixed + */ + public function getFieldBodyModel() + { + return $this->getTimestamp(); + } + + /** + * Get the UNIX timestamp of the Date in this Header. + * + * @return int + */ + public function getTimestamp() + { + return $this->_timestamp; + } + + /** + * Set the UNIX timestamp of the Date in this Header. + * + * @param int $timestamp + */ + public function setTimestamp($timestamp) + { + if (!is_null($timestamp)) { + $timestamp = (int) $timestamp; + } + $this->clearCachedValueIf($this->_timestamp != $timestamp); + $this->_timestamp = $timestamp; + } + + /** + * Get the string value of the body in this Header. + * + * This is not necessarily RFC 2822 compliant since folding white space will + * not be added at this stage (see {@link toString()} for that). + * + * @see toString() + * + * @return string + */ + public function getFieldBody() + { + if (!$this->getCachedValue()) { + if (isset($this->_timestamp)) { + $this->setCachedValue(date('r', $this->_timestamp)); + } + } + + return $this->getCachedValue(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php new file mode 100644 index 00000000..b114506b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php @@ -0,0 +1,180 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An ID MIME Header for something like Message-ID or Content-ID. + * + * @author Chris Corbyn + */ +class Swift_Mime_Headers_IdentificationHeader extends Swift_Mime_Headers_AbstractHeader +{ + /** + * The IDs used in the value of this Header. + * + * This may hold multiple IDs or just a single ID. + * + * @var string[] + */ + private $_ids = array(); + + /** + * Creates a new IdentificationHeader with the given $name and $id. + * + * @param string $name + * @param Swift_Mime_Grammar $grammar + */ + public function __construct($name, Swift_Mime_Grammar $grammar) + { + $this->setFieldName($name); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_ID; + } + + /** + * Set the model for the field body. + * + * This method takes a string ID, or an array of IDs. + * + * @param mixed $model + * + * @throws Swift_RfcComplianceException + */ + public function setFieldBodyModel($model) + { + $this->setId($model); + } + + /** + * Get the model for the field body. + * + * This method returns an array of IDs + * + * @return array + */ + public function getFieldBodyModel() + { + return $this->getIds(); + } + + /** + * Set the ID used in the value of this header. + * + * @param string|array $id + * + * @throws Swift_RfcComplianceException + */ + public function setId($id) + { + $this->setIds(is_array($id) ? $id : array($id)); + } + + /** + * Get the ID used in the value of this Header. + * + * If multiple IDs are set only the first is returned. + * + * @return string + */ + public function getId() + { + if (count($this->_ids) > 0) { + return $this->_ids[0]; + } + } + + /** + * Set a collection of IDs to use in the value of this Header. + * + * @param string[] $ids + * + * @throws Swift_RfcComplianceException + */ + public function setIds(array $ids) + { + $actualIds = array(); + + foreach ($ids as $id) { + $this->_assertValidId($id); + $actualIds[] = $id; + } + + $this->clearCachedValueIf($this->_ids != $actualIds); + $this->_ids = $actualIds; + } + + /** + * Get the list of IDs used in this Header. + * + * @return string[] + */ + public function getIds() + { + return $this->_ids; + } + + /** + * Get the string value of the body in this Header. + * + * This is not necessarily RFC 2822 compliant since folding white space will + * not be added at this stage (see {@see toString()} for that). + * + * @see toString() + * + * @throws Swift_RfcComplianceException + * + * @return string + */ + public function getFieldBody() + { + if (!$this->getCachedValue()) { + $angleAddrs = array(); + + foreach ($this->_ids as $id) { + $angleAddrs[] = '<'.$id.'>'; + } + + $this->setCachedValue(implode(' ', $angleAddrs)); + } + + return $this->getCachedValue(); + } + + /** + * Throws an Exception if the id passed does not comply with RFC 2822. + * + * @param string $id + * + * @throws Swift_RfcComplianceException + */ + private function _assertValidId($id) + { + if (!preg_match( + '/^'.$this->getGrammar()->getDefinition('id-left').'@'. + $this->getGrammar()->getDefinition('id-right').'$/D', + $id + )) { + throw new Swift_RfcComplianceException( + 'Invalid ID given <'.$id.'>' + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php new file mode 100644 index 00000000..e54b1c66 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php @@ -0,0 +1,353 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A Mailbox Address MIME Header for something like From or Sender. + * + * @author Chris Corbyn + */ +class Swift_Mime_Headers_MailboxHeader extends Swift_Mime_Headers_AbstractHeader +{ + /** + * The mailboxes used in this Header. + * + * @var string[] + */ + private $_mailboxes = array(); + + /** + * Creates a new MailboxHeader with $name. + * + * @param string $name of Header + * @param Swift_Mime_HeaderEncoder $encoder + * @param Swift_Mime_Grammar $grammar + */ + public function __construct($name, Swift_Mime_HeaderEncoder $encoder, Swift_Mime_Grammar $grammar) + { + $this->setFieldName($name); + $this->setEncoder($encoder); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_MAILBOX; + } + + /** + * Set the model for the field body. + * + * This method takes a string, or an array of addresses. + * + * @param mixed $model + * + * @throws Swift_RfcComplianceException + */ + public function setFieldBodyModel($model) + { + $this->setNameAddresses($model); + } + + /** + * Get the model for the field body. + * + * This method returns an associative array like {@link getNameAddresses()} + * + * @throws Swift_RfcComplianceException + * + * @return array + */ + public function getFieldBodyModel() + { + return $this->getNameAddresses(); + } + + /** + * Set a list of mailboxes to be shown in this Header. + * + * The mailboxes can be a simple array of addresses, or an array of + * key=>value pairs where (email => personalName). + * Example: + * <code> + * <?php + * //Sets two mailboxes in the Header, one with a personal name + * $header->setNameAddresses(array( + * 'chris@swiftmailer.org' => 'Chris Corbyn', + * 'mark@swiftmailer.org' //No associated personal name + * )); + * ?> + * </code> + * + * @see __construct() + * @see setAddresses() + * @see setValue() + * + * @param string|string[] $mailboxes + * + * @throws Swift_RfcComplianceException + */ + public function setNameAddresses($mailboxes) + { + $this->_mailboxes = $this->normalizeMailboxes((array) $mailboxes); + $this->setCachedValue(null); //Clear any cached value + } + + /** + * Get the full mailbox list of this Header as an array of valid RFC 2822 strings. + * + * Example: + * <code> + * <?php + * $header = new Swift_Mime_Headers_MailboxHeader('From', + * array('chris@swiftmailer.org' => 'Chris Corbyn', + * 'mark@swiftmailer.org' => 'Mark Corbyn') + * ); + * print_r($header->getNameAddressStrings()); + * // array ( + * // 0 => Chris Corbyn <chris@swiftmailer.org>, + * // 1 => Mark Corbyn <mark@swiftmailer.org> + * // ) + * ?> + * </code> + * + * @see getNameAddresses() + * @see toString() + * + * @throws Swift_RfcComplianceException + * + * @return string[] + */ + public function getNameAddressStrings() + { + return $this->_createNameAddressStrings($this->getNameAddresses()); + } + + /** + * Get all mailboxes in this Header as key=>value pairs. + * + * The key is the address and the value is the name (or null if none set). + * Example: + * <code> + * <?php + * $header = new Swift_Mime_Headers_MailboxHeader('From', + * array('chris@swiftmailer.org' => 'Chris Corbyn', + * 'mark@swiftmailer.org' => 'Mark Corbyn') + * ); + * print_r($header->getNameAddresses()); + * // array ( + * // chris@swiftmailer.org => Chris Corbyn, + * // mark@swiftmailer.org => Mark Corbyn + * // ) + * ?> + * </code> + * + * @see getAddresses() + * @see getNameAddressStrings() + * + * @return string[] + */ + public function getNameAddresses() + { + return $this->_mailboxes; + } + + /** + * Makes this Header represent a list of plain email addresses with no names. + * + * Example: + * <code> + * <?php + * //Sets three email addresses as the Header data + * $header->setAddresses( + * array('one@domain.tld', 'two@domain.tld', 'three@domain.tld') + * ); + * ?> + * </code> + * + * @see setNameAddresses() + * @see setValue() + * + * @param string[] $addresses + * + * @throws Swift_RfcComplianceException + */ + public function setAddresses($addresses) + { + $this->setNameAddresses(array_values((array) $addresses)); + } + + /** + * Get all email addresses in this Header. + * + * @see getNameAddresses() + * + * @return string[] + */ + public function getAddresses() + { + return array_keys($this->_mailboxes); + } + + /** + * Remove one or more addresses from this Header. + * + * @param string|string[] $addresses + */ + public function removeAddresses($addresses) + { + $this->setCachedValue(null); + foreach ((array) $addresses as $address) { + unset($this->_mailboxes[$address]); + } + } + + /** + * Get the string value of the body in this Header. + * + * This is not necessarily RFC 2822 compliant since folding white space will + * not be added at this stage (see {@link toString()} for that). + * + * @see toString() + * + * @throws Swift_RfcComplianceException + * + * @return string + */ + public function getFieldBody() + { + // Compute the string value of the header only if needed + if (is_null($this->getCachedValue())) { + $this->setCachedValue($this->createMailboxListString($this->_mailboxes)); + } + + return $this->getCachedValue(); + } + + // -- Points of extension + + /** + * Normalizes a user-input list of mailboxes into consistent key=>value pairs. + * + * @param string[] $mailboxes + * + * @return string[] + */ + protected function normalizeMailboxes(array $mailboxes) + { + $actualMailboxes = array(); + + foreach ($mailboxes as $key => $value) { + if (is_string($key)) { + //key is email addr + $address = $key; + $name = $value; + } else { + $address = $value; + $name = null; + } + $this->_assertValidAddress($address); + $actualMailboxes[$address] = $name; + } + + return $actualMailboxes; + } + + /** + * Produces a compliant, formatted display-name based on the string given. + * + * @param string $displayName as displayed + * @param bool $shorten the first line to make remove for header name + * + * @return string + */ + protected function createDisplayNameString($displayName, $shorten = false) + { + return $this->createPhrase($this, $displayName, $this->getCharset(), $this->getEncoder(), $shorten); + } + + /** + * Creates a string form of all the mailboxes in the passed array. + * + * @param string[] $mailboxes + * + * @throws Swift_RfcComplianceException + * + * @return string + */ + protected function createMailboxListString(array $mailboxes) + { + return implode(', ', $this->_createNameAddressStrings($mailboxes)); + } + + /** + * Redefine the encoding requirements for mailboxes. + * + * All "specials" must be encoded as the full header value will not be quoted + * + * @see RFC 2822 3.2.1 + * + * @param string $token + * + * @return bool + */ + protected function tokenNeedsEncoding($token) + { + return preg_match('/[()<>\[\]:;@\,."]/', $token) || parent::tokenNeedsEncoding($token); + } + + /** + * Return an array of strings conforming the the name-addr spec of RFC 2822. + * + * @param string[] $mailboxes + * + * @return string[] + */ + private function _createNameAddressStrings(array $mailboxes) + { + $strings = array(); + + foreach ($mailboxes as $email => $name) { + $mailboxStr = $email; + if (!is_null($name)) { + $nameStr = $this->createDisplayNameString($name, empty($strings)); + $mailboxStr = $nameStr.' <'.$mailboxStr.'>'; + } + $strings[] = $mailboxStr; + } + + return $strings; + } + + /** + * Throws an Exception if the address passed does not comply with RFC 2822. + * + * @param string $address + * + * @throws Swift_RfcComplianceException If invalid. + */ + private function _assertValidAddress($address) + { + if (!preg_match('/^'.$this->getGrammar()->getDefinition('addr-spec').'$/D', + $address)) { + throw new Swift_RfcComplianceException( + 'Address in mailbox given ['.$address. + '] does not comply with RFC 2822, 3.6.2.' + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/OpenDKIMHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/OpenDKIMHeader.php new file mode 100644 index 00000000..d7495500 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/OpenDKIMHeader.php @@ -0,0 +1,133 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An OpenDKIM Specific Header using only raw header datas without encoding. + * + * @author De Cock Xavier <xdecock@gmail.com> + */ +class Swift_Mime_Headers_OpenDKIMHeader implements Swift_Mime_Header +{ + /** + * The value of this Header. + * + * @var string + */ + private $_value; + + /** + * The name of this Header. + * + * @var string + */ + private $_fieldName; + + /** + * @param string $name + */ + public function __construct($name) + { + $this->_fieldName = $name; + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_TEXT; + } + + /** + * Set the model for the field body. + * + * This method takes a string for the field value. + * + * @param string $model + */ + public function setFieldBodyModel($model) + { + $this->setValue($model); + } + + /** + * Get the model for the field body. + * + * This method returns a string. + * + * @return string + */ + public function getFieldBodyModel() + { + return $this->getValue(); + } + + /** + * Get the (unencoded) value of this header. + * + * @return string + */ + public function getValue() + { + return $this->_value; + } + + /** + * Set the (unencoded) value of this header. + * + * @param string $value + */ + public function setValue($value) + { + $this->_value = $value; + } + + /** + * Get the value of this header prepared for rendering. + * + * @return string + */ + public function getFieldBody() + { + return $this->_value; + } + + /** + * Get this Header rendered as a RFC 2822 compliant string. + * + * @return string + */ + public function toString() + { + return $this->_fieldName.': '.$this->_value; + } + + /** + * Set the Header FieldName. + * + * @see Swift_Mime_Header::getFieldName() + */ + public function getFieldName() + { + return $this->_fieldName; + } + + /** + * Ignored. + */ + public function setCharset($charset) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php new file mode 100644 index 00000000..c506daec --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php @@ -0,0 +1,258 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An abstract base MIME Header. + * + * @author Chris Corbyn + */ +class Swift_Mime_Headers_ParameterizedHeader extends Swift_Mime_Headers_UnstructuredHeader implements Swift_Mime_ParameterizedHeader +{ + /** + * RFC 2231's definition of a token. + * + * @var string + */ + const TOKEN_REGEX = '(?:[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+)'; + + /** + * The Encoder used to encode the parameters. + * + * @var Swift_Encoder + */ + private $_paramEncoder; + + /** + * The parameters as an associative array. + * + * @var string[] + */ + private $_params = array(); + + /** + * Creates a new ParameterizedHeader with $name. + * + * @param string $name + * @param Swift_Mime_HeaderEncoder $encoder + * @param Swift_Encoder $paramEncoder, optional + * @param Swift_Mime_Grammar $grammar + */ + public function __construct($name, Swift_Mime_HeaderEncoder $encoder, Swift_Encoder $paramEncoder = null, Swift_Mime_Grammar $grammar) + { + parent::__construct($name, $encoder, $grammar); + $this->_paramEncoder = $paramEncoder; + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_PARAMETERIZED; + } + + /** + * Set the character set used in this Header. + * + * @param string $charset + */ + public function setCharset($charset) + { + parent::setCharset($charset); + if (isset($this->_paramEncoder)) { + $this->_paramEncoder->charsetChanged($charset); + } + } + + /** + * Set the value of $parameter. + * + * @param string $parameter + * @param string $value + */ + public function setParameter($parameter, $value) + { + $this->setParameters(array_merge($this->getParameters(), array($parameter => $value))); + } + + /** + * Get the value of $parameter. + * + * @param string $parameter + * + * @return string + */ + public function getParameter($parameter) + { + $params = $this->getParameters(); + + return array_key_exists($parameter, $params) ? $params[$parameter] : null; + } + + /** + * Set an associative array of parameter names mapped to values. + * + * @param string[] $parameters + */ + public function setParameters(array $parameters) + { + $this->clearCachedValueIf($this->_params != $parameters); + $this->_params = $parameters; + } + + /** + * Returns an associative array of parameter names mapped to values. + * + * @return string[] + */ + public function getParameters() + { + return $this->_params; + } + + /** + * Get the value of this header prepared for rendering. + * + * @return string + */ + public function getFieldBody() //TODO: Check caching here + { + $body = parent::getFieldBody(); + foreach ($this->_params as $name => $value) { + if (!is_null($value)) { + // Add the parameter + $body .= '; '.$this->_createParameter($name, $value); + } + } + + return $body; + } + + /** + * Generate a list of all tokens in the final header. + * + * This doesn't need to be overridden in theory, but it is for implementation + * reasons to prevent potential breakage of attributes. + * + * @param string $string The string to tokenize + * + * @return array An array of tokens as strings + */ + protected function toTokens($string = null) + { + $tokens = parent::toTokens(parent::getFieldBody()); + + // Try creating any parameters + foreach ($this->_params as $name => $value) { + if (!is_null($value)) { + // Add the semi-colon separator + $tokens[count($tokens) - 1] .= ';'; + $tokens = array_merge($tokens, $this->generateTokenLines( + ' '.$this->_createParameter($name, $value) + )); + } + } + + return $tokens; + } + + /** + * Render a RFC 2047 compliant header parameter from the $name and $value. + * + * @param string $name + * @param string $value + * + * @return string + */ + private function _createParameter($name, $value) + { + $origValue = $value; + + $encoded = false; + // Allow room for parameter name, indices, "=" and DQUOTEs + $maxValueLength = $this->getMaxLineLength() - strlen($name.'=*N"";') - 1; + $firstLineOffset = 0; + + // If it's not already a valid parameter value... + if (!preg_match('/^'.self::TOKEN_REGEX.'$/D', $value)) { + // TODO: text, or something else?? + // ... and it's not ascii + if (!preg_match('/^'.$this->getGrammar()->getDefinition('text').'*$/D', $value)) { + $encoded = true; + // Allow space for the indices, charset and language + $maxValueLength = $this->getMaxLineLength() - strlen($name.'*N*="";') - 1; + $firstLineOffset = strlen( + $this->getCharset()."'".$this->getLanguage()."'" + ); + } + } + + // Encode if we need to + if ($encoded || strlen($value) > $maxValueLength) { + if (isset($this->_paramEncoder)) { + $value = $this->_paramEncoder->encodeString( + $origValue, $firstLineOffset, $maxValueLength, $this->getCharset() + ); + } else { + // We have to go against RFC 2183/2231 in some areas for interoperability + $value = $this->getTokenAsEncodedWord($origValue); + $encoded = false; + } + } + + $valueLines = isset($this->_paramEncoder) ? explode("\r\n", $value) : array($value); + + // Need to add indices + if (count($valueLines) > 1) { + $paramLines = array(); + foreach ($valueLines as $i => $line) { + $paramLines[] = $name.'*'.$i. + $this->_getEndOfParameterValue($line, true, $i == 0); + } + + return implode(";\r\n ", $paramLines); + } else { + return $name.$this->_getEndOfParameterValue( + $valueLines[0], $encoded, true + ); + } + } + + /** + * Returns the parameter value from the "=" and beyond. + * + * @param string $value to append + * @param bool $encoded + * @param bool $firstLine + * + * @return string + */ + private function _getEndOfParameterValue($value, $encoded = false, $firstLine = false) + { + if (!preg_match('/^'.self::TOKEN_REGEX.'$/D', $value)) { + $value = '"'.$value.'"'; + } + $prepend = '='; + if ($encoded) { + $prepend = '*='; + if ($firstLine) { + $prepend = '*='.$this->getCharset()."'".$this->getLanguage(). + "'"; + } + } + + return $prepend.$value; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php new file mode 100644 index 00000000..2fffc7b4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php @@ -0,0 +1,143 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A Path Header in Swift Mailer, such a Return-Path. + * + * @author Chris Corbyn + */ +class Swift_Mime_Headers_PathHeader extends Swift_Mime_Headers_AbstractHeader +{ + /** + * The address in this Header (if specified). + * + * @var string + */ + private $_address; + + /** + * Creates a new PathHeader with the given $name. + * + * @param string $name + * @param Swift_Mime_Grammar $grammar + */ + public function __construct($name, Swift_Mime_Grammar $grammar) + { + $this->setFieldName($name); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_PATH; + } + + /** + * Set the model for the field body. + * This method takes a string for an address. + * + * @param string $model + * + * @throws Swift_RfcComplianceException + */ + public function setFieldBodyModel($model) + { + $this->setAddress($model); + } + + /** + * Get the model for the field body. + * This method returns a string email address. + * + * @return mixed + */ + public function getFieldBodyModel() + { + return $this->getAddress(); + } + + /** + * Set the Address which should appear in this Header. + * + * @param string $address + * + * @throws Swift_RfcComplianceException + */ + public function setAddress($address) + { + if (is_null($address)) { + $this->_address = null; + } elseif ('' == $address) { + $this->_address = ''; + } else { + $this->_assertValidAddress($address); + $this->_address = $address; + } + $this->setCachedValue(null); + } + + /** + * Get the address which is used in this Header (if any). + * + * Null is returned if no address is set. + * + * @return string + */ + public function getAddress() + { + return $this->_address; + } + + /** + * Get the string value of the body in this Header. + * + * This is not necessarily RFC 2822 compliant since folding white space will + * not be added at this stage (see {@link toString()} for that). + * + * @see toString() + * + * @return string + */ + public function getFieldBody() + { + if (!$this->getCachedValue()) { + if (isset($this->_address)) { + $this->setCachedValue('<'.$this->_address.'>'); + } + } + + return $this->getCachedValue(); + } + + /** + * Throws an Exception if the address passed does not comply with RFC 2822. + * + * @param string $address + * + * @throws Swift_RfcComplianceException If address is invalid + */ + private function _assertValidAddress($address) + { + if (!preg_match('/^'.$this->getGrammar()->getDefinition('addr-spec').'$/D', + $address)) { + throw new Swift_RfcComplianceException( + 'Address set in PathHeader does not comply with addr-spec of RFC 2822.' + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php new file mode 100644 index 00000000..86177f14 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php @@ -0,0 +1,112 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A Simple MIME Header. + * + * @author Chris Corbyn + */ +class Swift_Mime_Headers_UnstructuredHeader extends Swift_Mime_Headers_AbstractHeader +{ + /** + * The value of this Header. + * + * @var string + */ + private $_value; + + /** + * Creates a new SimpleHeader with $name. + * + * @param string $name + * @param Swift_Mime_HeaderEncoder $encoder + * @param Swift_Mime_Grammar $grammar + */ + public function __construct($name, Swift_Mime_HeaderEncoder $encoder, Swift_Mime_Grammar $grammar) + { + $this->setFieldName($name); + $this->setEncoder($encoder); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_TEXT; + } + + /** + * Set the model for the field body. + * + * This method takes a string for the field value. + * + * @param string $model + */ + public function setFieldBodyModel($model) + { + $this->setValue($model); + } + + /** + * Get the model for the field body. + * + * This method returns a string. + * + * @return string + */ + public function getFieldBodyModel() + { + return $this->getValue(); + } + + /** + * Get the (unencoded) value of this header. + * + * @return string + */ + public function getValue() + { + return $this->_value; + } + + /** + * Set the (unencoded) value of this header. + * + * @param string $value + */ + public function setValue($value) + { + $this->clearCachedValueIf($this->_value != $value); + $this->_value = $value; + } + + /** + * Get the value of this header prepared for rendering. + * + * @return string + */ + public function getFieldBody() + { + if (!$this->getCachedValue()) { + $this->setCachedValue( + $this->encodeWords($this, $this->_value) + ); + } + + return $this->getCachedValue(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Message.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Message.php new file mode 100644 index 00000000..9b36d216 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Message.php @@ -0,0 +1,223 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A Message (RFC 2822) object. + * + * @author Chris Corbyn + */ +interface Swift_Mime_Message extends Swift_Mime_MimeEntity +{ + /** + * Generates a valid Message-ID and switches to it. + * + * @return string + */ + public function generateId(); + + /** + * Set the subject of the message. + * + * @param string $subject + */ + public function setSubject($subject); + + /** + * Get the subject of the message. + * + * @return string + */ + public function getSubject(); + + /** + * Set the origination date of the message as a UNIX timestamp. + * + * @param int $date + */ + public function setDate($date); + + /** + * Get the origination date of the message as a UNIX timestamp. + * + * @return int + */ + public function getDate(); + + /** + * Set the return-path (bounce-detect) address. + * + * @param string $address + */ + public function setReturnPath($address); + + /** + * Get the return-path (bounce-detect) address. + * + * @return string + */ + public function getReturnPath(); + + /** + * Set the sender of this message. + * + * If multiple addresses are present in the From field, this SHOULD be set. + * + * According to RFC 2822 it is a requirement when there are multiple From + * addresses, but Swift itself does not require it directly. + * + * An associative array (with one element!) can be used to provide a display- + * name: i.e. array('email@address' => 'Real Name'). + * + * If the second parameter is provided and the first is a string, then $name + * is associated with the address. + * + * @param mixed $address + * @param string $name optional + */ + public function setSender($address, $name = null); + + /** + * Get the sender address for this message. + * + * This has a higher significance than the From address. + * + * @return string + */ + public function getSender(); + + /** + * Set the From address of this message. + * + * It is permissible for multiple From addresses to be set using an array. + * + * If multiple From addresses are used, you SHOULD set the Sender address and + * according to RFC 2822, MUST set the sender address. + * + * An array can be used if display names are to be provided: i.e. + * array('email@address.com' => 'Real Name'). + * + * If the second parameter is provided and the first is a string, then $name + * is associated with the address. + * + * @param mixed $addresses + * @param string $name optional + */ + public function setFrom($addresses, $name = null); + + /** + * Get the From address(es) of this message. + * + * This method always returns an associative array where the keys are the + * addresses. + * + * @return string[] + */ + public function getFrom(); + + /** + * Set the Reply-To address(es). + * + * Any replies from the receiver will be sent to this address. + * + * It is permissible for multiple reply-to addresses to be set using an array. + * + * This method has the same synopsis as {@link setFrom()} and {@link setTo()}. + * + * If the second parameter is provided and the first is a string, then $name + * is associated with the address. + * + * @param mixed $addresses + * @param string $name optional + */ + public function setReplyTo($addresses, $name = null); + + /** + * Get the Reply-To addresses for this message. + * + * This method always returns an associative array where the keys provide the + * email addresses. + * + * @return string[] + */ + public function getReplyTo(); + + /** + * Set the To address(es). + * + * Recipients set in this field will receive a copy of this message. + * + * This method has the same synopsis as {@link setFrom()} and {@link setCc()}. + * + * If the second parameter is provided and the first is a string, then $name + * is associated with the address. + * + * @param mixed $addresses + * @param string $name optional + */ + public function setTo($addresses, $name = null); + + /** + * Get the To addresses for this message. + * + * This method always returns an associative array, whereby the keys provide + * the actual email addresses. + * + * @return string[] + */ + public function getTo(); + + /** + * Set the Cc address(es). + * + * Recipients set in this field will receive a 'carbon-copy' of this message. + * + * This method has the same synopsis as {@link setFrom()} and {@link setTo()}. + * + * @param mixed $addresses + * @param string $name optional + */ + public function setCc($addresses, $name = null); + + /** + * Get the Cc addresses for this message. + * + * This method always returns an associative array, whereby the keys provide + * the actual email addresses. + * + * @return string[] + */ + public function getCc(); + + /** + * Set the Bcc address(es). + * + * Recipients set in this field will receive a 'blind-carbon-copy' of this + * message. + * + * In other words, they will get the message, but any other recipients of the + * message will have no such knowledge of their receipt of it. + * + * This method has the same synopsis as {@link setFrom()} and {@link setTo()}. + * + * @param mixed $addresses + * @param string $name optional + */ + public function setBcc($addresses, $name = null); + + /** + * Get the Bcc addresses for this message. + * + * This method always returns an associative array, whereby the keys provide + * the actual email addresses. + * + * @return string[] + */ + public function getBcc(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimeEntity.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimeEntity.php new file mode 100644 index 00000000..30f460cd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimeEntity.php @@ -0,0 +1,117 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A MIME entity, such as an attachment. + * + * @author Chris Corbyn + */ +interface Swift_Mime_MimeEntity extends Swift_Mime_CharsetObserver, Swift_Mime_EncodingObserver +{ + /** Main message document; there can only be one of these */ + const LEVEL_TOP = 16; + + /** An entity which nests with the same precedence as an attachment */ + const LEVEL_MIXED = 256; + + /** An entity which nests with the same precedence as a mime part */ + const LEVEL_ALTERNATIVE = 4096; + + /** An entity which nests with the same precedence as embedded content */ + const LEVEL_RELATED = 65536; + + /** + * Get the level at which this entity shall be nested in final document. + * + * The lower the value, the more outermost the entity will be nested. + * + * @see LEVEL_TOP, LEVEL_MIXED, LEVEL_RELATED, LEVEL_ALTERNATIVE + * + * @return int + */ + public function getNestingLevel(); + + /** + * Get the qualified content-type of this mime entity. + * + * @return string + */ + public function getContentType(); + + /** + * Returns a unique ID for this entity. + * + * For most entities this will likely be the Content-ID, though it has + * no explicit semantic meaning and can be considered an identifier for + * programming logic purposes. + * + * If a Content-ID header is present, this value SHOULD match the value of + * the header. + * + * @return string + */ + public function getId(); + + /** + * Get all children nested inside this entity. + * + * These are not just the immediate children, but all children. + * + * @return Swift_Mime_MimeEntity[] + */ + public function getChildren(); + + /** + * Set all children nested inside this entity. + * + * This includes grandchildren. + * + * @param Swift_Mime_MimeEntity[] $children + */ + public function setChildren(array $children); + + /** + * Get the collection of Headers in this Mime entity. + * + * @return Swift_Mime_HeaderSet + */ + public function getHeaders(); + + /** + * Get the body content of this entity as a string. + * + * Returns NULL if no body has been set. + * + * @return string|null + */ + public function getBody(); + + /** + * Set the body content of this entity as a string. + * + * @param string $body + * @param string $contentType optional + */ + public function setBody($body, $contentType = null); + + /** + * Get this entire entity in its string form. + * + * @return string + */ + public function toString(); + + /** + * Get this entire entity as a ByteStream. + * + * @param Swift_InputByteStream $is to write to + */ + public function toByteStream(Swift_InputByteStream $is); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimePart.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimePart.php new file mode 100644 index 00000000..50fadfb7 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimePart.php @@ -0,0 +1,212 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A MIME part, in a multipart message. + * + * @author Chris Corbyn + */ +class Swift_Mime_MimePart extends Swift_Mime_SimpleMimeEntity +{ + /** The format parameter last specified by the user */ + protected $_userFormat; + + /** The charset last specified by the user */ + protected $_userCharset; + + /** The delsp parameter last specified by the user */ + protected $_userDelSp; + + /** The nesting level of this MimePart */ + private $_nestingLevel = self::LEVEL_ALTERNATIVE; + + /** + * Create a new MimePart with $headers, $encoder and $cache. + * + * @param Swift_Mime_HeaderSet $headers + * @param Swift_Mime_ContentEncoder $encoder + * @param Swift_KeyCache $cache + * @param Swift_Mime_Grammar $grammar + * @param string $charset + */ + public function __construct(Swift_Mime_HeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_Mime_Grammar $grammar, $charset = null) + { + parent::__construct($headers, $encoder, $cache, $grammar); + $this->setContentType('text/plain'); + if (!is_null($charset)) { + $this->setCharset($charset); + } + } + + /** + * Set the body of this entity, either as a string, or as an instance of + * {@link Swift_OutputByteStream}. + * + * @param mixed $body + * @param string $contentType optional + * @param string $charset optional + * + * @return Swift_Mime_MimePart + */ + public function setBody($body, $contentType = null, $charset = null) + { + if (isset($charset)) { + $this->setCharset($charset); + } + $body = $this->_convertString($body); + + parent::setBody($body, $contentType); + + return $this; + } + + /** + * Get the character set of this entity. + * + * @return string + */ + public function getCharset() + { + return $this->_getHeaderParameter('Content-Type', 'charset'); + } + + /** + * Set the character set of this entity. + * + * @param string $charset + * + * @return Swift_Mime_MimePart + */ + public function setCharset($charset) + { + $this->_setHeaderParameter('Content-Type', 'charset', $charset); + if ($charset !== $this->_userCharset) { + $this->_clearCache(); + } + $this->_userCharset = $charset; + parent::charsetChanged($charset); + + return $this; + } + + /** + * Get the format of this entity (i.e. flowed or fixed). + * + * @return string + */ + public function getFormat() + { + return $this->_getHeaderParameter('Content-Type', 'format'); + } + + /** + * Set the format of this entity (flowed or fixed). + * + * @param string $format + * + * @return Swift_Mime_MimePart + */ + public function setFormat($format) + { + $this->_setHeaderParameter('Content-Type', 'format', $format); + $this->_userFormat = $format; + + return $this; + } + + /** + * Test if delsp is being used for this entity. + * + * @return bool + */ + public function getDelSp() + { + return 'yes' == $this->_getHeaderParameter('Content-Type', 'delsp') ? true : false; + } + + /** + * Turn delsp on or off for this entity. + * + * @param bool $delsp + * + * @return Swift_Mime_MimePart + */ + public function setDelSp($delsp = true) + { + $this->_setHeaderParameter('Content-Type', 'delsp', $delsp ? 'yes' : null); + $this->_userDelSp = $delsp; + + return $this; + } + + /** + * Get the nesting level of this entity. + * + * @see LEVEL_TOP, LEVEL_ALTERNATIVE, LEVEL_MIXED, LEVEL_RELATED + * + * @return int + */ + public function getNestingLevel() + { + return $this->_nestingLevel; + } + + /** + * Receive notification that the charset has changed on this document, or a + * parent document. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->setCharset($charset); + } + + /** Fix the content-type and encoding of this entity */ + protected function _fixHeaders() + { + parent::_fixHeaders(); + if (count($this->getChildren())) { + $this->_setHeaderParameter('Content-Type', 'charset', null); + $this->_setHeaderParameter('Content-Type', 'format', null); + $this->_setHeaderParameter('Content-Type', 'delsp', null); + } else { + $this->setCharset($this->_userCharset); + $this->setFormat($this->_userFormat); + $this->setDelSp($this->_userDelSp); + } + } + + /** Set the nesting level of this entity */ + protected function _setNestingLevel($level) + { + $this->_nestingLevel = $level; + } + + /** Encode charset when charset is not utf-8 */ + protected function _convertString($string) + { + $charset = strtolower($this->getCharset()); + if (!in_array($charset, array('utf-8', 'iso-8859-1', 'iso-8859-15', ''))) { + // mb_convert_encoding must be the first one to check, since iconv cannot convert some words. + if (function_exists('mb_convert_encoding')) { + $string = mb_convert_encoding($string, $charset, 'utf-8'); + } elseif (function_exists('iconv')) { + $string = iconv('utf-8//TRANSLIT//IGNORE', $charset, $string); + } else { + throw new Swift_SwiftException('No suitable convert encoding function (use UTF-8 as your charset or install the mbstring or iconv extension).'); + } + + return $string; + } + + return $string; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ParameterizedHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ParameterizedHeader.php new file mode 100644 index 00000000..e15c6ef9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ParameterizedHeader.php @@ -0,0 +1,34 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A MIME Header with parameters. + * + * @author Chris Corbyn + */ +interface Swift_Mime_ParameterizedHeader extends Swift_Mime_Header +{ + /** + * Set the value of $parameter. + * + * @param string $parameter + * @param string $value + */ + public function setParameter($parameter, $value); + + /** + * Get the value of $parameter. + * + * @param string $parameter + * + * @return string + */ + public function getParameter($parameter); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderFactory.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderFactory.php new file mode 100644 index 00000000..1ca504e7 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderFactory.php @@ -0,0 +1,193 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Creates MIME headers. + * + * @author Chris Corbyn + */ +class Swift_Mime_SimpleHeaderFactory implements Swift_Mime_HeaderFactory +{ + /** The HeaderEncoder used by these headers */ + private $_encoder; + + /** The Encoder used by parameters */ + private $_paramEncoder; + + /** The Grammar */ + private $_grammar; + + /** The charset of created Headers */ + private $_charset; + + /** + * Creates a new SimpleHeaderFactory using $encoder and $paramEncoder. + * + * @param Swift_Mime_HeaderEncoder $encoder + * @param Swift_Encoder $paramEncoder + * @param Swift_Mime_Grammar $grammar + * @param string|null $charset + */ + public function __construct(Swift_Mime_HeaderEncoder $encoder, Swift_Encoder $paramEncoder, Swift_Mime_Grammar $grammar, $charset = null) + { + $this->_encoder = $encoder; + $this->_paramEncoder = $paramEncoder; + $this->_grammar = $grammar; + $this->_charset = $charset; + } + + /** + * Create a new Mailbox Header with a list of $addresses. + * + * @param string $name + * @param array|string|null $addresses + * + * @return Swift_Mime_Header + */ + public function createMailboxHeader($name, $addresses = null) + { + $header = new Swift_Mime_Headers_MailboxHeader($name, $this->_encoder, $this->_grammar); + if (isset($addresses)) { + $header->setFieldBodyModel($addresses); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Create a new Date header using $timestamp (UNIX time). + * + * @param string $name + * @param int|null $timestamp + * + * @return Swift_Mime_Header + */ + public function createDateHeader($name, $timestamp = null) + { + $header = new Swift_Mime_Headers_DateHeader($name, $this->_grammar); + if (isset($timestamp)) { + $header->setFieldBodyModel($timestamp); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Create a new basic text header with $name and $value. + * + * @param string $name + * @param string $value + * + * @return Swift_Mime_Header + */ + public function createTextHeader($name, $value = null) + { + $header = new Swift_Mime_Headers_UnstructuredHeader($name, $this->_encoder, $this->_grammar); + if (isset($value)) { + $header->setFieldBodyModel($value); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Create a new ParameterizedHeader with $name, $value and $params. + * + * @param string $name + * @param string $value + * @param array $params + * + * @return Swift_Mime_ParameterizedHeader + */ + public function createParameterizedHeader($name, $value = null, + $params = array()) + { + $header = new Swift_Mime_Headers_ParameterizedHeader($name, $this->_encoder, strtolower($name) == 'content-disposition' ? $this->_paramEncoder : null, $this->_grammar); + if (isset($value)) { + $header->setFieldBodyModel($value); + } + foreach ($params as $k => $v) { + $header->setParameter($k, $v); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Create a new ID header for Message-ID or Content-ID. + * + * @param string $name + * @param string|array $ids + * + * @return Swift_Mime_Header + */ + public function createIdHeader($name, $ids = null) + { + $header = new Swift_Mime_Headers_IdentificationHeader($name, $this->_grammar); + if (isset($ids)) { + $header->setFieldBodyModel($ids); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Create a new Path header with an address (path) in it. + * + * @param string $name + * @param string $path + * + * @return Swift_Mime_Header + */ + public function createPathHeader($name, $path = null) + { + $header = new Swift_Mime_Headers_PathHeader($name, $this->_grammar); + if (isset($path)) { + $header->setFieldBodyModel($path); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Notify this observer that the entity's charset has changed. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->_charset = $charset; + $this->_encoder->charsetChanged($charset); + $this->_paramEncoder->charsetChanged($charset); + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->_encoder = clone $this->_encoder; + $this->_paramEncoder = clone $this->_paramEncoder; + } + + /** Apply the charset to the Header */ + private function _setHeaderCharset(Swift_Mime_Header $header) + { + if (isset($this->_charset)) { + $header->setCharset($this->_charset); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php new file mode 100644 index 00000000..e2d0e874 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php @@ -0,0 +1,414 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A collection of MIME headers. + * + * @author Chris Corbyn + */ +class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet +{ + /** HeaderFactory */ + private $_factory; + + /** Collection of set Headers */ + private $_headers = array(); + + /** Field ordering details */ + private $_order = array(); + + /** List of fields which are required to be displayed */ + private $_required = array(); + + /** The charset used by Headers */ + private $_charset; + + /** + * Create a new SimpleHeaderSet with the given $factory. + * + * @param Swift_Mime_HeaderFactory $factory + * @param string $charset + */ + public function __construct(Swift_Mime_HeaderFactory $factory, $charset = null) + { + $this->_factory = $factory; + if (isset($charset)) { + $this->setCharset($charset); + } + } + + /** + * Set the charset used by these headers. + * + * @param string $charset + */ + public function setCharset($charset) + { + $this->_charset = $charset; + $this->_factory->charsetChanged($charset); + $this->_notifyHeadersOfCharset($charset); + } + + /** + * Add a new Mailbox Header with a list of $addresses. + * + * @param string $name + * @param array|string $addresses + */ + public function addMailboxHeader($name, $addresses = null) + { + $this->_storeHeader($name, + $this->_factory->createMailboxHeader($name, $addresses)); + } + + /** + * Add a new Date header using $timestamp (UNIX time). + * + * @param string $name + * @param int $timestamp + */ + public function addDateHeader($name, $timestamp = null) + { + $this->_storeHeader($name, + $this->_factory->createDateHeader($name, $timestamp)); + } + + /** + * Add a new basic text header with $name and $value. + * + * @param string $name + * @param string $value + */ + public function addTextHeader($name, $value = null) + { + $this->_storeHeader($name, + $this->_factory->createTextHeader($name, $value)); + } + + /** + * Add a new ParameterizedHeader with $name, $value and $params. + * + * @param string $name + * @param string $value + * @param array $params + */ + public function addParameterizedHeader($name, $value = null, $params = array()) + { + $this->_storeHeader($name, $this->_factory->createParameterizedHeader($name, $value, $params)); + } + + /** + * Add a new ID header for Message-ID or Content-ID. + * + * @param string $name + * @param string|array $ids + */ + public function addIdHeader($name, $ids = null) + { + $this->_storeHeader($name, $this->_factory->createIdHeader($name, $ids)); + } + + /** + * Add a new Path header with an address (path) in it. + * + * @param string $name + * @param string $path + */ + public function addPathHeader($name, $path = null) + { + $this->_storeHeader($name, $this->_factory->createPathHeader($name, $path)); + } + + /** + * Returns true if at least one header with the given $name exists. + * + * If multiple headers match, the actual one may be specified by $index. + * + * @param string $name + * @param int $index + * + * @return bool + */ + public function has($name, $index = 0) + { + $lowerName = strtolower($name); + + if (!array_key_exists($lowerName, $this->_headers)) { + return false; + } + + if (func_num_args() < 2) { + // index was not specified, so we only need to check that there is at least one header value set + return (bool) count($this->_headers[$lowerName]); + } + + return array_key_exists($index, $this->_headers[$lowerName]); + } + + /** + * Set a header in the HeaderSet. + * + * The header may be a previously fetched header via {@link get()} or it may + * be one that has been created separately. + * + * If $index is specified, the header will be inserted into the set at this + * offset. + * + * @param Swift_Mime_Header $header + * @param int $index + */ + public function set(Swift_Mime_Header $header, $index = 0) + { + $this->_storeHeader($header->getFieldName(), $header, $index); + } + + /** + * Get the header with the given $name. + * + * If multiple headers match, the actual one may be specified by $index. + * Returns NULL if none present. + * + * @param string $name + * @param int $index + * + * @return Swift_Mime_Header + */ + public function get($name, $index = 0) + { + $name = strtolower($name); + + if (func_num_args() < 2) { + if ($this->has($name)) { + $values = array_values($this->_headers[$name]); + + return array_shift($values); + } + } else { + if ($this->has($name, $index)) { + return $this->_headers[$name][$index]; + } + } + } + + /** + * Get all headers with the given $name. + * + * @param string $name + * + * @return array + */ + public function getAll($name = null) + { + if (!isset($name)) { + $headers = array(); + foreach ($this->_headers as $collection) { + $headers = array_merge($headers, $collection); + } + + return $headers; + } + + $lowerName = strtolower($name); + if (!array_key_exists($lowerName, $this->_headers)) { + return array(); + } + + return $this->_headers[$lowerName]; + } + + /** + * Return the name of all Headers. + * + * @return array + */ + public function listAll() + { + $headers = $this->_headers; + if ($this->_canSort()) { + uksort($headers, array($this, '_sortHeaders')); + } + + return array_keys($headers); + } + + /** + * Remove the header with the given $name if it's set. + * + * If multiple headers match, the actual one may be specified by $index. + * + * @param string $name + * @param int $index + */ + public function remove($name, $index = 0) + { + $lowerName = strtolower($name); + unset($this->_headers[$lowerName][$index]); + } + + /** + * Remove all headers with the given $name. + * + * @param string $name + */ + public function removeAll($name) + { + $lowerName = strtolower($name); + unset($this->_headers[$lowerName]); + } + + /** + * Create a new instance of this HeaderSet. + * + * @return Swift_Mime_HeaderSet + */ + public function newInstance() + { + return new self($this->_factory); + } + + /** + * Define a list of Header names as an array in the correct order. + * + * These Headers will be output in the given order where present. + * + * @param array $sequence + */ + public function defineOrdering(array $sequence) + { + $this->_order = array_flip(array_map('strtolower', $sequence)); + } + + /** + * Set a list of header names which must always be displayed when set. + * + * Usually headers without a field value won't be output unless set here. + * + * @param array $names + */ + public function setAlwaysDisplayed(array $names) + { + $this->_required = array_flip(array_map('strtolower', $names)); + } + + /** + * Notify this observer that the entity's charset has changed. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->setCharset($charset); + } + + /** + * Returns a string with a representation of all headers. + * + * @return string + */ + public function toString() + { + $string = ''; + $headers = $this->_headers; + if ($this->_canSort()) { + uksort($headers, array($this, '_sortHeaders')); + } + foreach ($headers as $collection) { + foreach ($collection as $header) { + if ($this->_isDisplayed($header) || $header->getFieldBody() != '') { + $string .= $header->toString(); + } + } + } + + return $string; + } + + /** + * Returns a string representation of this object. + * + * @return string + * + * @see toString() + */ + public function __toString() + { + return $this->toString(); + } + + /** Save a Header to the internal collection */ + private function _storeHeader($name, Swift_Mime_Header $header, $offset = null) + { + if (!isset($this->_headers[strtolower($name)])) { + $this->_headers[strtolower($name)] = array(); + } + if (!isset($offset)) { + $this->_headers[strtolower($name)][] = $header; + } else { + $this->_headers[strtolower($name)][$offset] = $header; + } + } + + /** Test if the headers can be sorted */ + private function _canSort() + { + return count($this->_order) > 0; + } + + /** uksort() algorithm for Header ordering */ + private function _sortHeaders($a, $b) + { + $lowerA = strtolower($a); + $lowerB = strtolower($b); + $aPos = array_key_exists($lowerA, $this->_order) ? $this->_order[$lowerA] : -1; + $bPos = array_key_exists($lowerB, $this->_order) ? $this->_order[$lowerB] : -1; + + if (-1 === $aPos && -1 === $bPos) { + // just be sure to be determinist here + return $a > $b ? -1 : 1; + } + + if ($aPos == -1) { + return 1; + } elseif ($bPos == -1) { + return -1; + } + + return $aPos < $bPos ? -1 : 1; + } + + /** Test if the given Header is always displayed */ + private function _isDisplayed(Swift_Mime_Header $header) + { + return array_key_exists(strtolower($header->getFieldName()), $this->_required); + } + + /** Notify all Headers of the new charset */ + private function _notifyHeadersOfCharset($charset) + { + foreach ($this->_headers as $headerGroup) { + foreach ($headerGroup as $header) { + $header->setCharset($charset); + } + } + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->_factory = clone $this->_factory; + foreach ($this->_headers as $groupKey => $headerGroup) { + foreach ($headerGroup as $key => $header) { + $this->_headers[$groupKey][$key] = clone $header; + } + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php new file mode 100644 index 00000000..72d40cee --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php @@ -0,0 +1,655 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * The default email message class. + * + * @author Chris Corbyn + */ +class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime_Message +{ + const PRIORITY_HIGHEST = 1; + const PRIORITY_HIGH = 2; + const PRIORITY_NORMAL = 3; + const PRIORITY_LOW = 4; + const PRIORITY_LOWEST = 5; + + /** + * Create a new SimpleMessage with $headers, $encoder and $cache. + * + * @param Swift_Mime_HeaderSet $headers + * @param Swift_Mime_ContentEncoder $encoder + * @param Swift_KeyCache $cache + * @param Swift_Mime_Grammar $grammar + * @param string $charset + */ + public function __construct(Swift_Mime_HeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_Mime_Grammar $grammar, $charset = null) + { + parent::__construct($headers, $encoder, $cache, $grammar, $charset); + $this->getHeaders()->defineOrdering(array( + 'Return-Path', + 'Received', + 'DKIM-Signature', + 'DomainKey-Signature', + 'Sender', + 'Message-ID', + 'Date', + 'Subject', + 'From', + 'Reply-To', + 'To', + 'Cc', + 'Bcc', + 'MIME-Version', + 'Content-Type', + 'Content-Transfer-Encoding', + )); + $this->getHeaders()->setAlwaysDisplayed(array('Date', 'Message-ID', 'From')); + $this->getHeaders()->addTextHeader('MIME-Version', '1.0'); + $this->setDate(time()); + $this->setId($this->getId()); + $this->getHeaders()->addMailboxHeader('From'); + } + + /** + * Always returns {@link LEVEL_TOP} for a message instance. + * + * @return int + */ + public function getNestingLevel() + { + return self::LEVEL_TOP; + } + + /** + * Set the subject of this message. + * + * @param string $subject + * + * @return $this + */ + public function setSubject($subject) + { + if (!$this->_setHeaderFieldModel('Subject', $subject)) { + $this->getHeaders()->addTextHeader('Subject', $subject); + } + + return $this; + } + + /** + * Get the subject of this message. + * + * @return string + */ + public function getSubject() + { + return $this->_getHeaderFieldModel('Subject'); + } + + /** + * Set the date at which this message was created. + * + * @param int $date + * + * @return $this + */ + public function setDate($date) + { + if (!$this->_setHeaderFieldModel('Date', $date)) { + $this->getHeaders()->addDateHeader('Date', $date); + } + + return $this; + } + + /** + * Get the date at which this message was created. + * + * @return int + */ + public function getDate() + { + return $this->_getHeaderFieldModel('Date'); + } + + /** + * Set the return-path (the bounce address) of this message. + * + * @param string $address + * + * @return $this + */ + public function setReturnPath($address) + { + if (!$this->_setHeaderFieldModel('Return-Path', $address)) { + $this->getHeaders()->addPathHeader('Return-Path', $address); + } + + return $this; + } + + /** + * Get the return-path (bounce address) of this message. + * + * @return string + */ + public function getReturnPath() + { + return $this->_getHeaderFieldModel('Return-Path'); + } + + /** + * Set the sender of this message. + * + * This does not override the From field, but it has a higher significance. + * + * @param string $address + * @param string $name optional + * + * @return $this + */ + public function setSender($address, $name = null) + { + if (!is_array($address) && isset($name)) { + $address = array($address => $name); + } + + if (!$this->_setHeaderFieldModel('Sender', (array) $address)) { + $this->getHeaders()->addMailboxHeader('Sender', (array) $address); + } + + return $this; + } + + /** + * Get the sender of this message. + * + * @return string + */ + public function getSender() + { + return $this->_getHeaderFieldModel('Sender'); + } + + /** + * Add a From: address to this message. + * + * If $name is passed this name will be associated with the address. + * + * @param string $address + * @param string $name optional + * + * @return $this + */ + public function addFrom($address, $name = null) + { + $current = $this->getFrom(); + $current[$address] = $name; + + return $this->setFrom($current); + } + + /** + * Set the from address of this message. + * + * You may pass an array of addresses if this message is from multiple people. + * + * If $name is passed and the first parameter is a string, this name will be + * associated with the address. + * + * @param string|array $addresses + * @param string $name optional + * + * @return $this + */ + public function setFrom($addresses, $name = null) + { + if (!is_array($addresses) && isset($name)) { + $addresses = array($addresses => $name); + } + + if (!$this->_setHeaderFieldModel('From', (array) $addresses)) { + $this->getHeaders()->addMailboxHeader('From', (array) $addresses); + } + + return $this; + } + + /** + * Get the from address of this message. + * + * @return mixed + */ + public function getFrom() + { + return $this->_getHeaderFieldModel('From'); + } + + /** + * Add a Reply-To: address to this message. + * + * If $name is passed this name will be associated with the address. + * + * @param string $address + * @param string $name optional + * + * @return $this + */ + public function addReplyTo($address, $name = null) + { + $current = $this->getReplyTo(); + $current[$address] = $name; + + return $this->setReplyTo($current); + } + + /** + * Set the reply-to address of this message. + * + * You may pass an array of addresses if replies will go to multiple people. + * + * If $name is passed and the first parameter is a string, this name will be + * associated with the address. + * + * @param mixed $addresses + * @param string $name optional + * + * @return $this + */ + public function setReplyTo($addresses, $name = null) + { + if (!is_array($addresses) && isset($name)) { + $addresses = array($addresses => $name); + } + + if (!$this->_setHeaderFieldModel('Reply-To', (array) $addresses)) { + $this->getHeaders()->addMailboxHeader('Reply-To', (array) $addresses); + } + + return $this; + } + + /** + * Get the reply-to address of this message. + * + * @return string + */ + public function getReplyTo() + { + return $this->_getHeaderFieldModel('Reply-To'); + } + + /** + * Add a To: address to this message. + * + * If $name is passed this name will be associated with the address. + * + * @param string $address + * @param string $name optional + * + * @return $this + */ + public function addTo($address, $name = null) + { + $current = $this->getTo(); + $current[$address] = $name; + + return $this->setTo($current); + } + + /** + * Set the to addresses of this message. + * + * If multiple recipients will receive the message an array should be used. + * Example: array('receiver@domain.org', 'other@domain.org' => 'A name') + * + * If $name is passed and the first parameter is a string, this name will be + * associated with the address. + * + * @param mixed $addresses + * @param string $name optional + * + * @return $this + */ + public function setTo($addresses, $name = null) + { + if (!is_array($addresses) && isset($name)) { + $addresses = array($addresses => $name); + } + + if (!$this->_setHeaderFieldModel('To', (array) $addresses)) { + $this->getHeaders()->addMailboxHeader('To', (array) $addresses); + } + + return $this; + } + + /** + * Get the To addresses of this message. + * + * @return array + */ + public function getTo() + { + return $this->_getHeaderFieldModel('To'); + } + + /** + * Add a Cc: address to this message. + * + * If $name is passed this name will be associated with the address. + * + * @param string $address + * @param string $name optional + * + * @return $this + */ + public function addCc($address, $name = null) + { + $current = $this->getCc(); + $current[$address] = $name; + + return $this->setCc($current); + } + + /** + * Set the Cc addresses of this message. + * + * If $name is passed and the first parameter is a string, this name will be + * associated with the address. + * + * @param mixed $addresses + * @param string $name optional + * + * @return $this + */ + public function setCc($addresses, $name = null) + { + if (!is_array($addresses) && isset($name)) { + $addresses = array($addresses => $name); + } + + if (!$this->_setHeaderFieldModel('Cc', (array) $addresses)) { + $this->getHeaders()->addMailboxHeader('Cc', (array) $addresses); + } + + return $this; + } + + /** + * Get the Cc address of this message. + * + * @return array + */ + public function getCc() + { + return $this->_getHeaderFieldModel('Cc'); + } + + /** + * Add a Bcc: address to this message. + * + * If $name is passed this name will be associated with the address. + * + * @param string $address + * @param string $name optional + * + * @return $this + */ + public function addBcc($address, $name = null) + { + $current = $this->getBcc(); + $current[$address] = $name; + + return $this->setBcc($current); + } + + /** + * Set the Bcc addresses of this message. + * + * If $name is passed and the first parameter is a string, this name will be + * associated with the address. + * + * @param mixed $addresses + * @param string $name optional + * + * @return $this + */ + public function setBcc($addresses, $name = null) + { + if (!is_array($addresses) && isset($name)) { + $addresses = array($addresses => $name); + } + + if (!$this->_setHeaderFieldModel('Bcc', (array) $addresses)) { + $this->getHeaders()->addMailboxHeader('Bcc', (array) $addresses); + } + + return $this; + } + + /** + * Get the Bcc addresses of this message. + * + * @return array + */ + public function getBcc() + { + return $this->_getHeaderFieldModel('Bcc'); + } + + /** + * Set the priority of this message. + * + * The value is an integer where 1 is the highest priority and 5 is the lowest. + * + * @param int $priority + * + * @return $this + */ + public function setPriority($priority) + { + $priorityMap = array( + self::PRIORITY_HIGHEST => 'Highest', + self::PRIORITY_HIGH => 'High', + self::PRIORITY_NORMAL => 'Normal', + self::PRIORITY_LOW => 'Low', + self::PRIORITY_LOWEST => 'Lowest', + ); + $pMapKeys = array_keys($priorityMap); + if ($priority > max($pMapKeys)) { + $priority = max($pMapKeys); + } elseif ($priority < min($pMapKeys)) { + $priority = min($pMapKeys); + } + if (!$this->_setHeaderFieldModel('X-Priority', + sprintf('%d (%s)', $priority, $priorityMap[$priority]))) { + $this->getHeaders()->addTextHeader('X-Priority', + sprintf('%d (%s)', $priority, $priorityMap[$priority])); + } + + return $this; + } + + /** + * Get the priority of this message. + * + * The returned value is an integer where 1 is the highest priority and 5 + * is the lowest. + * + * @return int + */ + public function getPriority() + { + list($priority) = sscanf($this->_getHeaderFieldModel('X-Priority'), + '%[1-5]' + ); + + return isset($priority) ? $priority : 3; + } + + /** + * Ask for a delivery receipt from the recipient to be sent to $addresses. + * + * @param array $addresses + * + * @return $this + */ + public function setReadReceiptTo($addresses) + { + if (!$this->_setHeaderFieldModel('Disposition-Notification-To', $addresses)) { + $this->getHeaders() + ->addMailboxHeader('Disposition-Notification-To', $addresses); + } + + return $this; + } + + /** + * Get the addresses to which a read-receipt will be sent. + * + * @return string + */ + public function getReadReceiptTo() + { + return $this->_getHeaderFieldModel('Disposition-Notification-To'); + } + + /** + * Attach a {@link Swift_Mime_MimeEntity} such as an Attachment or MimePart. + * + * @param Swift_Mime_MimeEntity $entity + * + * @return $this + */ + public function attach(Swift_Mime_MimeEntity $entity) + { + $this->setChildren(array_merge($this->getChildren(), array($entity))); + + return $this; + } + + /** + * Remove an already attached entity. + * + * @param Swift_Mime_MimeEntity $entity + * + * @return $this + */ + public function detach(Swift_Mime_MimeEntity $entity) + { + $newChildren = array(); + foreach ($this->getChildren() as $child) { + if ($entity !== $child) { + $newChildren[] = $child; + } + } + $this->setChildren($newChildren); + + return $this; + } + + /** + * Attach a {@link Swift_Mime_MimeEntity} and return it's CID source. + * This method should be used when embedding images or other data in a message. + * + * @param Swift_Mime_MimeEntity $entity + * + * @return string + */ + public function embed(Swift_Mime_MimeEntity $entity) + { + $this->attach($entity); + + return 'cid:'.$entity->getId(); + } + + /** + * Get this message as a complete string. + * + * @return string + */ + public function toString() + { + if (count($children = $this->getChildren()) > 0 && $this->getBody() != '') { + $this->setChildren(array_merge(array($this->_becomeMimePart()), $children)); + $string = parent::toString(); + $this->setChildren($children); + } else { + $string = parent::toString(); + } + + return $string; + } + + /** + * Returns a string representation of this object. + * + * @see toString() + * + * @return string + */ + public function __toString() + { + return $this->toString(); + } + + /** + * Write this message to a {@link Swift_InputByteStream}. + * + * @param Swift_InputByteStream $is + */ + public function toByteStream(Swift_InputByteStream $is) + { + if (count($children = $this->getChildren()) > 0 && $this->getBody() != '') { + $this->setChildren(array_merge(array($this->_becomeMimePart()), $children)); + parent::toByteStream($is); + $this->setChildren($children); + } else { + parent::toByteStream($is); + } + } + + /** @see Swift_Mime_SimpleMimeEntity::_getIdField() */ + protected function _getIdField() + { + return 'Message-ID'; + } + + /** Turn the body of this message into a child of itself if needed */ + protected function _becomeMimePart() + { + $part = new parent($this->getHeaders()->newInstance(), $this->getEncoder(), + $this->_getCache(), $this->_getGrammar(), $this->_userCharset + ); + $part->setContentType($this->_userContentType); + $part->setBody($this->getBody()); + $part->setFormat($this->_userFormat); + $part->setDelSp($this->_userDelSp); + $part->_setNestingLevel($this->_getTopNestingLevel()); + + return $part; + } + + /** Get the highest nesting level nested inside this message */ + private function _getTopNestingLevel() + { + $highestLevel = $this->getNestingLevel(); + foreach ($this->getChildren() as $child) { + $childLevel = $child->getNestingLevel(); + if ($highestLevel < $childLevel) { + $highestLevel = $childLevel; + } + } + + return $highestLevel; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php new file mode 100644 index 00000000..f169584d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php @@ -0,0 +1,843 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A MIME entity, in a multipart message. + * + * @author Chris Corbyn + */ +class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity +{ + /** A collection of Headers for this mime entity */ + private $_headers; + + /** The body as a string, or a stream */ + private $_body; + + /** The encoder that encodes the body into a streamable format */ + private $_encoder; + + /** The grammar to use for id validation */ + private $_grammar; + + /** A mime boundary, if any is used */ + private $_boundary; + + /** Mime types to be used based on the nesting level */ + private $_compositeRanges = array( + 'multipart/mixed' => array(self::LEVEL_TOP, self::LEVEL_MIXED), + 'multipart/alternative' => array(self::LEVEL_MIXED, self::LEVEL_ALTERNATIVE), + 'multipart/related' => array(self::LEVEL_ALTERNATIVE, self::LEVEL_RELATED), + ); + + /** A set of filter rules to define what level an entity should be nested at */ + private $_compoundLevelFilters = array(); + + /** The nesting level of this entity */ + private $_nestingLevel = self::LEVEL_ALTERNATIVE; + + /** A KeyCache instance used during encoding and streaming */ + private $_cache; + + /** Direct descendants of this entity */ + private $_immediateChildren = array(); + + /** All descendants of this entity */ + private $_children = array(); + + /** The maximum line length of the body of this entity */ + private $_maxLineLength = 78; + + /** The order in which alternative mime types should appear */ + private $_alternativePartOrder = array( + 'text/plain' => 1, + 'text/html' => 2, + 'multipart/related' => 3, + ); + + /** The CID of this entity */ + private $_id; + + /** The key used for accessing the cache */ + private $_cacheKey; + + protected $_userContentType; + + /** + * Create a new SimpleMimeEntity with $headers, $encoder and $cache. + * + * @param Swift_Mime_HeaderSet $headers + * @param Swift_Mime_ContentEncoder $encoder + * @param Swift_KeyCache $cache + * @param Swift_Mime_Grammar $grammar + */ + public function __construct(Swift_Mime_HeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_Mime_Grammar $grammar) + { + $this->_cacheKey = md5(uniqid(getmypid().mt_rand(), true)); + $this->_cache = $cache; + $this->_headers = $headers; + $this->_grammar = $grammar; + $this->setEncoder($encoder); + $this->_headers->defineOrdering(array('Content-Type', 'Content-Transfer-Encoding')); + + // This array specifies that, when the entire MIME document contains + // $compoundLevel, then for each child within $level, if its Content-Type + // is $contentType then it should be treated as if it's level is + // $neededLevel instead. I tried to write that unambiguously! :-\ + // Data Structure: + // array ( + // $compoundLevel => array( + // $level => array( + // $contentType => $neededLevel + // ) + // ) + // ) + + $this->_compoundLevelFilters = array( + (self::LEVEL_ALTERNATIVE + self::LEVEL_RELATED) => array( + self::LEVEL_ALTERNATIVE => array( + 'text/plain' => self::LEVEL_ALTERNATIVE, + 'text/html' => self::LEVEL_RELATED, + ), + ), + ); + + $this->_id = $this->getRandomId(); + } + + /** + * Generate a new Content-ID or Message-ID for this MIME entity. + * + * @return string + */ + public function generateId() + { + $this->setId($this->getRandomId()); + + return $this->_id; + } + + /** + * Get the {@link Swift_Mime_HeaderSet} for this entity. + * + * @return Swift_Mime_HeaderSet + */ + public function getHeaders() + { + return $this->_headers; + } + + /** + * Get the nesting level of this entity. + * + * @see LEVEL_TOP, LEVEL_MIXED, LEVEL_RELATED, LEVEL_ALTERNATIVE + * + * @return int + */ + public function getNestingLevel() + { + return $this->_nestingLevel; + } + + /** + * Get the Content-type of this entity. + * + * @return string + */ + public function getContentType() + { + return $this->_getHeaderFieldModel('Content-Type'); + } + + /** + * Set the Content-type of this entity. + * + * @param string $type + * + * @return $this + */ + public function setContentType($type) + { + $this->_setContentTypeInHeaders($type); + // Keep track of the value so that if the content-type changes automatically + // due to added child entities, it can be restored if they are later removed + $this->_userContentType = $type; + + return $this; + } + + /** + * Get the CID of this entity. + * + * The CID will only be present in headers if a Content-ID header is present. + * + * @return string + */ + public function getId() + { + $tmp = (array) $this->_getHeaderFieldModel($this->_getIdField()); + + return $this->_headers->has($this->_getIdField()) ? current($tmp) : $this->_id; + } + + /** + * Set the CID of this entity. + * + * @param string $id + * + * @return $this + */ + public function setId($id) + { + if (!$this->_setHeaderFieldModel($this->_getIdField(), $id)) { + $this->_headers->addIdHeader($this->_getIdField(), $id); + } + $this->_id = $id; + + return $this; + } + + /** + * Get the description of this entity. + * + * This value comes from the Content-Description header if set. + * + * @return string + */ + public function getDescription() + { + return $this->_getHeaderFieldModel('Content-Description'); + } + + /** + * Set the description of this entity. + * + * This method sets a value in the Content-ID header. + * + * @param string $description + * + * @return $this + */ + public function setDescription($description) + { + if (!$this->_setHeaderFieldModel('Content-Description', $description)) { + $this->_headers->addTextHeader('Content-Description', $description); + } + + return $this; + } + + /** + * Get the maximum line length of the body of this entity. + * + * @return int + */ + public function getMaxLineLength() + { + return $this->_maxLineLength; + } + + /** + * Set the maximum line length of lines in this body. + * + * Though not enforced by the library, lines should not exceed 1000 chars. + * + * @param int $length + * + * @return $this + */ + public function setMaxLineLength($length) + { + $this->_maxLineLength = $length; + + return $this; + } + + /** + * Get all children added to this entity. + * + * @return Swift_Mime_MimeEntity[] + */ + public function getChildren() + { + return $this->_children; + } + + /** + * Set all children of this entity. + * + * @param Swift_Mime_MimeEntity[] $children + * @param int $compoundLevel For internal use only + * + * @return $this + */ + public function setChildren(array $children, $compoundLevel = null) + { + // TODO: Try to refactor this logic + + $compoundLevel = isset($compoundLevel) ? $compoundLevel : $this->_getCompoundLevel($children); + $immediateChildren = array(); + $grandchildren = array(); + $newContentType = $this->_userContentType; + + foreach ($children as $child) { + $level = $this->_getNeededChildLevel($child, $compoundLevel); + if (empty($immediateChildren)) { + //first iteration + $immediateChildren = array($child); + } else { + $nextLevel = $this->_getNeededChildLevel($immediateChildren[0], $compoundLevel); + if ($nextLevel == $level) { + $immediateChildren[] = $child; + } elseif ($level < $nextLevel) { + // Re-assign immediateChildren to grandchildren + $grandchildren = array_merge($grandchildren, $immediateChildren); + // Set new children + $immediateChildren = array($child); + } else { + $grandchildren[] = $child; + } + } + } + + if ($immediateChildren) { + $lowestLevel = $this->_getNeededChildLevel($immediateChildren[0], $compoundLevel); + + // Determine which composite media type is needed to accommodate the + // immediate children + foreach ($this->_compositeRanges as $mediaType => $range) { + if ($lowestLevel > $range[0] && $lowestLevel <= $range[1]) { + $newContentType = $mediaType; + + break; + } + } + + // Put any grandchildren in a subpart + if (!empty($grandchildren)) { + $subentity = $this->_createChild(); + $subentity->_setNestingLevel($lowestLevel); + $subentity->setChildren($grandchildren, $compoundLevel); + array_unshift($immediateChildren, $subentity); + } + } + + $this->_immediateChildren = $immediateChildren; + $this->_children = $children; + $this->_setContentTypeInHeaders($newContentType); + $this->_fixHeaders(); + $this->_sortChildren(); + + return $this; + } + + /** + * Get the body of this entity as a string. + * + * @return string + */ + public function getBody() + { + return $this->_body instanceof Swift_OutputByteStream ? $this->_readStream($this->_body) : $this->_body; + } + + /** + * Set the body of this entity, either as a string, or as an instance of + * {@link Swift_OutputByteStream}. + * + * @param mixed $body + * @param string $contentType optional + * + * @return $this + */ + public function setBody($body, $contentType = null) + { + if ($body !== $this->_body) { + $this->_clearCache(); + } + + $this->_body = $body; + if (isset($contentType)) { + $this->setContentType($contentType); + } + + return $this; + } + + /** + * Get the encoder used for the body of this entity. + * + * @return Swift_Mime_ContentEncoder + */ + public function getEncoder() + { + return $this->_encoder; + } + + /** + * Set the encoder used for the body of this entity. + * + * @param Swift_Mime_ContentEncoder $encoder + * + * @return $this + */ + public function setEncoder(Swift_Mime_ContentEncoder $encoder) + { + if ($encoder !== $this->_encoder) { + $this->_clearCache(); + } + + $this->_encoder = $encoder; + $this->_setEncoding($encoder->getName()); + $this->_notifyEncoderChanged($encoder); + + return $this; + } + + /** + * Get the boundary used to separate children in this entity. + * + * @return string + */ + public function getBoundary() + { + if (!isset($this->_boundary)) { + $this->_boundary = '_=_swift_v4_'.time().'_'.md5(getmypid().mt_rand().uniqid('', true)).'_=_'; + } + + return $this->_boundary; + } + + /** + * Set the boundary used to separate children in this entity. + * + * @param string $boundary + * + * @throws Swift_RfcComplianceException + * + * @return $this + */ + public function setBoundary($boundary) + { + $this->_assertValidBoundary($boundary); + $this->_boundary = $boundary; + + return $this; + } + + /** + * Receive notification that the charset of this entity, or a parent entity + * has changed. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->_notifyCharsetChanged($charset); + } + + /** + * Receive notification that the encoder of this entity or a parent entity + * has changed. + * + * @param Swift_Mime_ContentEncoder $encoder + */ + public function encoderChanged(Swift_Mime_ContentEncoder $encoder) + { + $this->_notifyEncoderChanged($encoder); + } + + /** + * Get this entire entity as a string. + * + * @return string + */ + public function toString() + { + $string = $this->_headers->toString(); + $string .= $this->_bodyToString(); + + return $string; + } + + /** + * Get this entire entity as a string. + * + * @return string + */ + protected function _bodyToString() + { + $string = ''; + + if (isset($this->_body) && empty($this->_immediateChildren)) { + if ($this->_cache->hasKey($this->_cacheKey, 'body')) { + $body = $this->_cache->getString($this->_cacheKey, 'body'); + } else { + $body = "\r\n".$this->_encoder->encodeString($this->getBody(), 0, $this->getMaxLineLength()); + $this->_cache->setString($this->_cacheKey, 'body', $body, Swift_KeyCache::MODE_WRITE); + } + $string .= $body; + } + + if (!empty($this->_immediateChildren)) { + foreach ($this->_immediateChildren as $child) { + $string .= "\r\n\r\n--".$this->getBoundary()."\r\n"; + $string .= $child->toString(); + } + $string .= "\r\n\r\n--".$this->getBoundary()."--\r\n"; + } + + return $string; + } + + /** + * Returns a string representation of this object. + * + * @see toString() + * + * @return string + */ + public function __toString() + { + return $this->toString(); + } + + /** + * Write this entire entity to a {@see Swift_InputByteStream}. + * + * @param Swift_InputByteStream + */ + public function toByteStream(Swift_InputByteStream $is) + { + $is->write($this->_headers->toString()); + $is->commit(); + + $this->_bodyToByteStream($is); + } + + /** + * Write this entire entity to a {@link Swift_InputByteStream}. + * + * @param Swift_InputByteStream + */ + protected function _bodyToByteStream(Swift_InputByteStream $is) + { + if (empty($this->_immediateChildren)) { + if (isset($this->_body)) { + if ($this->_cache->hasKey($this->_cacheKey, 'body')) { + $this->_cache->exportToByteStream($this->_cacheKey, 'body', $is); + } else { + $cacheIs = $this->_cache->getInputByteStream($this->_cacheKey, 'body'); + if ($cacheIs) { + $is->bind($cacheIs); + } + + $is->write("\r\n"); + + if ($this->_body instanceof Swift_OutputByteStream) { + $this->_body->setReadPointer(0); + + $this->_encoder->encodeByteStream($this->_body, $is, 0, $this->getMaxLineLength()); + } else { + $is->write($this->_encoder->encodeString($this->getBody(), 0, $this->getMaxLineLength())); + } + + if ($cacheIs) { + $is->unbind($cacheIs); + } + } + } + } + + if (!empty($this->_immediateChildren)) { + foreach ($this->_immediateChildren as $child) { + $is->write("\r\n\r\n--".$this->getBoundary()."\r\n"); + $child->toByteStream($is); + } + $is->write("\r\n\r\n--".$this->getBoundary()."--\r\n"); + } + } + + /** + * Get the name of the header that provides the ID of this entity. + */ + protected function _getIdField() + { + return 'Content-ID'; + } + + /** + * Get the model data (usually an array or a string) for $field. + */ + protected function _getHeaderFieldModel($field) + { + if ($this->_headers->has($field)) { + return $this->_headers->get($field)->getFieldBodyModel(); + } + } + + /** + * Set the model data for $field. + */ + protected function _setHeaderFieldModel($field, $model) + { + if ($this->_headers->has($field)) { + $this->_headers->get($field)->setFieldBodyModel($model); + + return true; + } + + return false; + } + + /** + * Get the parameter value of $parameter on $field header. + */ + protected function _getHeaderParameter($field, $parameter) + { + if ($this->_headers->has($field)) { + return $this->_headers->get($field)->getParameter($parameter); + } + } + + /** + * Set the parameter value of $parameter on $field header. + */ + protected function _setHeaderParameter($field, $parameter, $value) + { + if ($this->_headers->has($field)) { + $this->_headers->get($field)->setParameter($parameter, $value); + + return true; + } + + return false; + } + + /** + * Re-evaluate what content type and encoding should be used on this entity. + */ + protected function _fixHeaders() + { + if (count($this->_immediateChildren)) { + $this->_setHeaderParameter('Content-Type', 'boundary', + $this->getBoundary() + ); + $this->_headers->remove('Content-Transfer-Encoding'); + } else { + $this->_setHeaderParameter('Content-Type', 'boundary', null); + $this->_setEncoding($this->_encoder->getName()); + } + } + + /** + * Get the KeyCache used in this entity. + * + * @return Swift_KeyCache + */ + protected function _getCache() + { + return $this->_cache; + } + + /** + * Get the grammar used for validation. + * + * @return Swift_Mime_Grammar + */ + protected function _getGrammar() + { + return $this->_grammar; + } + + /** + * Empty the KeyCache for this entity. + */ + protected function _clearCache() + { + $this->_cache->clearKey($this->_cacheKey, 'body'); + } + + /** + * Returns a random Content-ID or Message-ID. + * + * @return string + */ + protected function getRandomId() + { + $idLeft = md5(getmypid().'.'.time().'.'.uniqid(mt_rand(), true)); + $idRight = !empty($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'swift.generated'; + $id = $idLeft.'@'.$idRight; + + try { + $this->_assertValidId($id); + } catch (Swift_RfcComplianceException $e) { + $id = $idLeft.'@swift.generated'; + } + + return $id; + } + + private function _readStream(Swift_OutputByteStream $os) + { + $string = ''; + while (false !== $bytes = $os->read(8192)) { + $string .= $bytes; + } + + $os->setReadPointer(0); + + return $string; + } + + private function _setEncoding($encoding) + { + if (!$this->_setHeaderFieldModel('Content-Transfer-Encoding', $encoding)) { + $this->_headers->addTextHeader('Content-Transfer-Encoding', $encoding); + } + } + + private function _assertValidBoundary($boundary) + { + if (!preg_match('/^[a-z0-9\'\(\)\+_\-,\.\/:=\?\ ]{0,69}[a-z0-9\'\(\)\+_\-,\.\/:=\?]$/Di', $boundary)) { + throw new Swift_RfcComplianceException('Mime boundary set is not RFC 2046 compliant.'); + } + } + + private function _setContentTypeInHeaders($type) + { + if (!$this->_setHeaderFieldModel('Content-Type', $type)) { + $this->_headers->addParameterizedHeader('Content-Type', $type); + } + } + + private function _setNestingLevel($level) + { + $this->_nestingLevel = $level; + } + + private function _getCompoundLevel($children) + { + $level = 0; + foreach ($children as $child) { + $level |= $child->getNestingLevel(); + } + + return $level; + } + + private function _getNeededChildLevel($child, $compoundLevel) + { + $filter = array(); + foreach ($this->_compoundLevelFilters as $bitmask => $rules) { + if (($compoundLevel & $bitmask) === $bitmask) { + $filter = $rules + $filter; + } + } + + $realLevel = $child->getNestingLevel(); + $lowercaseType = strtolower($child->getContentType()); + + if (isset($filter[$realLevel]) && isset($filter[$realLevel][$lowercaseType])) { + return $filter[$realLevel][$lowercaseType]; + } + + return $realLevel; + } + + private function _createChild() + { + return new self($this->_headers->newInstance(), $this->_encoder, $this->_cache, $this->_grammar); + } + + private function _notifyEncoderChanged(Swift_Mime_ContentEncoder $encoder) + { + foreach ($this->_immediateChildren as $child) { + $child->encoderChanged($encoder); + } + } + + private function _notifyCharsetChanged($charset) + { + $this->_encoder->charsetChanged($charset); + $this->_headers->charsetChanged($charset); + foreach ($this->_immediateChildren as $child) { + $child->charsetChanged($charset); + } + } + + private function _sortChildren() + { + $shouldSort = false; + foreach ($this->_immediateChildren as $child) { + // NOTE: This include alternative parts moved into a related part + if ($child->getNestingLevel() == self::LEVEL_ALTERNATIVE) { + $shouldSort = true; + break; + } + } + + // Sort in order of preference, if there is one + if ($shouldSort) { + usort($this->_immediateChildren, array($this, '_childSortAlgorithm')); + } + } + + private function _childSortAlgorithm($a, $b) + { + $typePrefs = array(); + $types = array(strtolower($a->getContentType()), strtolower($b->getContentType())); + + foreach ($types as $type) { + $typePrefs[] = array_key_exists($type, $this->_alternativePartOrder) ? $this->_alternativePartOrder[$type] : max($this->_alternativePartOrder) + 1; + } + + return $typePrefs[0] >= $typePrefs[1] ? 1 : -1; + } + + // -- Destructor + + /** + * Empties it's own contents from the cache. + */ + public function __destruct() + { + $this->_cache->clearAll($this->_cacheKey); + } + + /** + * Throws an Exception if the id passed does not comply with RFC 2822. + * + * @param string $id + * + * @throws Swift_RfcComplianceException + */ + private function _assertValidId($id) + { + if (!preg_match('/^'.$this->_grammar->getDefinition('id-left').'@'.$this->_grammar->getDefinition('id-right').'$/D', $id)) { + throw new Swift_RfcComplianceException('Invalid ID given <'.$id.'>'); + } + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->_headers = clone $this->_headers; + $this->_encoder = clone $this->_encoder; + $this->_cacheKey = md5(uniqid(getmypid().mt_rand(), true)); + $children = array(); + foreach ($this->_children as $pos => $child) { + $children[$pos] = clone $child; + } + $this->setChildren($children); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MimePart.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MimePart.php new file mode 100644 index 00000000..215f8db3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MimePart.php @@ -0,0 +1,59 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A MIME part, in a multipart message. + * + * @author Chris Corbyn + */ +class Swift_MimePart extends Swift_Mime_MimePart +{ + /** + * Create a new MimePart. + * + * Details may be optionally passed into the constructor. + * + * @param string $body + * @param string $contentType + * @param string $charset + */ + public function __construct($body = null, $contentType = null, $charset = null) + { + call_user_func_array( + array($this, 'Swift_Mime_MimePart::__construct'), + Swift_DependencyContainer::getInstance() + ->createDependenciesFor('mime.part') + ); + + if (!isset($charset)) { + $charset = Swift_DependencyContainer::getInstance() + ->lookup('properties.charset'); + } + $this->setBody($body); + $this->setCharset($charset); + if ($contentType) { + $this->setContentType($contentType); + } + } + + /** + * Create a new MimePart. + * + * @param string $body + * @param string $contentType + * @param string $charset + * + * @return Swift_Mime_MimePart + */ + public static function newInstance($body = null, $contentType = null, $charset = null) + { + return new self($body, $contentType, $charset); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/NullTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/NullTransport.php new file mode 100644 index 00000000..b38e1cf7 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/NullTransport.php @@ -0,0 +1,39 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2009 Fabien Potencier <fabien.potencier@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Pretends messages have been sent, but just ignores them. + * + * @author Fabien Potencier + */ +class Swift_NullTransport extends Swift_Transport_NullTransport +{ + /** + * Create a new NullTransport. + */ + public function __construct() + { + call_user_func_array( + array($this, 'Swift_Transport_NullTransport::__construct'), + Swift_DependencyContainer::getInstance() + ->createDependenciesFor('transport.null') + ); + } + + /** + * Create a new NullTransport instance. + * + * @return Swift_NullTransport + */ + public static function newInstance() + { + return new self(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/OutputByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/OutputByteStream.php new file mode 100644 index 00000000..1f26f9be --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/OutputByteStream.php @@ -0,0 +1,46 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An abstract means of reading data. + * + * Classes implementing this interface may use a subsystem which requires less + * memory than working with large strings of data. + * + * @author Chris Corbyn + */ +interface Swift_OutputByteStream +{ + /** + * Reads $length bytes from the stream into a string and moves the pointer + * through the stream by $length. + * + * If less bytes exist than are requested the remaining bytes are given instead. + * If no bytes are remaining at all, boolean false is returned. + * + * @param int $length + * + * @throws Swift_IoException + * + * @return string|bool + */ + public function read($length); + + /** + * Move the internal read pointer to $byteOffset in the stream. + * + * @param int $byteOffset + * + * @throws Swift_IoException + * + * @return bool + */ + public function setReadPointer($byteOffset); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/AntiFloodPlugin.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/AntiFloodPlugin.php new file mode 100644 index 00000000..a2ec2abc --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/AntiFloodPlugin.php @@ -0,0 +1,141 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Reduces network flooding when sending large amounts of mail. + * + * @author Chris Corbyn + */ +class Swift_Plugins_AntiFloodPlugin implements Swift_Events_SendListener, Swift_Plugins_Sleeper +{ + /** + * The number of emails to send before restarting Transport. + * + * @var int + */ + private $_threshold; + + /** + * The number of seconds to sleep for during a restart. + * + * @var int + */ + private $_sleep; + + /** + * The internal counter. + * + * @var int + */ + private $_counter = 0; + + /** + * The Sleeper instance for sleeping. + * + * @var Swift_Plugins_Sleeper + */ + private $_sleeper; + + /** + * Create a new AntiFloodPlugin with $threshold and $sleep time. + * + * @param int $threshold + * @param int $sleep time + * @param Swift_Plugins_Sleeper $sleeper (not needed really) + */ + public function __construct($threshold = 99, $sleep = 0, Swift_Plugins_Sleeper $sleeper = null) + { + $this->setThreshold($threshold); + $this->setSleepTime($sleep); + $this->_sleeper = $sleeper; + } + + /** + * Set the number of emails to send before restarting. + * + * @param int $threshold + */ + public function setThreshold($threshold) + { + $this->_threshold = $threshold; + } + + /** + * Get the number of emails to send before restarting. + * + * @return int + */ + public function getThreshold() + { + return $this->_threshold; + } + + /** + * Set the number of seconds to sleep for during a restart. + * + * @param int $sleep time + */ + public function setSleepTime($sleep) + { + $this->_sleep = $sleep; + } + + /** + * Get the number of seconds to sleep for during a restart. + * + * @return int + */ + public function getSleepTime() + { + return $this->_sleep; + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + ++$this->_counter; + if ($this->_counter >= $this->_threshold) { + $transport = $evt->getTransport(); + $transport->stop(); + if ($this->_sleep) { + $this->sleep($this->_sleep); + } + $transport->start(); + $this->_counter = 0; + } + } + + /** + * Sleep for $seconds. + * + * @param int $seconds + */ + public function sleep($seconds) + { + if (isset($this->_sleeper)) { + $this->_sleeper->sleep($seconds); + } else { + sleep($seconds); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php new file mode 100644 index 00000000..f7e18d0e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php @@ -0,0 +1,164 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Reduces network flooding when sending large amounts of mail. + * + * @author Chris Corbyn + */ +class Swift_Plugins_BandwidthMonitorPlugin implements Swift_Events_SendListener, Swift_Events_CommandListener, Swift_Events_ResponseListener, Swift_InputByteStream +{ + /** + * The outgoing traffic counter. + * + * @var int + */ + private $_out = 0; + + /** + * The incoming traffic counter. + * + * @var int + */ + private $_in = 0; + + /** Bound byte streams */ + private $_mirrors = array(); + + /** + * Not used. + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + $message->toByteStream($this); + } + + /** + * Invoked immediately following a command being sent. + * + * @param Swift_Events_CommandEvent $evt + */ + public function commandSent(Swift_Events_CommandEvent $evt) + { + $command = $evt->getCommand(); + $this->_out += strlen($command); + } + + /** + * Invoked immediately following a response coming back. + * + * @param Swift_Events_ResponseEvent $evt + */ + public function responseReceived(Swift_Events_ResponseEvent $evt) + { + $response = $evt->getResponse(); + $this->_in += strlen($response); + } + + /** + * Called when a message is sent so that the outgoing counter can be increased. + * + * @param string $bytes + */ + public function write($bytes) + { + $this->_out += strlen($bytes); + foreach ($this->_mirrors as $stream) { + $stream->write($bytes); + } + } + + /** + * Not used. + */ + public function commit() + { + } + + /** + * Attach $is to this stream. + * + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + */ + public function bind(Swift_InputByteStream $is) + { + $this->_mirrors[] = $is; + } + + /** + * Remove an already bound stream. + * + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + */ + public function unbind(Swift_InputByteStream $is) + { + foreach ($this->_mirrors as $k => $stream) { + if ($is === $stream) { + unset($this->_mirrors[$k]); + } + } + } + + /** + * Not used. + */ + public function flushBuffers() + { + foreach ($this->_mirrors as $stream) { + $stream->flushBuffers(); + } + } + + /** + * Get the total number of bytes sent to the server. + * + * @return int + */ + public function getBytesOut() + { + return $this->_out; + } + + /** + * Get the total number of bytes received from the server. + * + * @return int + */ + public function getBytesIn() + { + return $this->_in; + } + + /** + * Reset the internal counters to zero. + */ + public function reset() + { + $this->_out = 0; + $this->_in = 0; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Decorator/Replacements.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Decorator/Replacements.php new file mode 100644 index 00000000..9f9f08b5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Decorator/Replacements.php @@ -0,0 +1,31 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Allows customization of Messages on-the-fly. + * + * @author Chris Corbyn + */ +interface Swift_Plugins_Decorator_Replacements +{ + /** + * Return the array of replacements for $address. + * + * This method is invoked once for every single recipient of a message. + * + * If no replacements can be found, an empty value (NULL) should be returned + * and no replacements will then be made on the message. + * + * @param string $address + * + * @return array + */ + public function getReplacementsFor($address); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/DecoratorPlugin.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/DecoratorPlugin.php new file mode 100644 index 00000000..0762b36d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/DecoratorPlugin.php @@ -0,0 +1,204 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Allows customization of Messages on-the-fly. + * + * @author Chris Corbyn + * @author Fabien Potencier + */ +class Swift_Plugins_DecoratorPlugin implements Swift_Events_SendListener, Swift_Plugins_Decorator_Replacements +{ + /** The replacement map */ + private $_replacements; + + /** The body as it was before replacements */ + private $_originalBody; + + /** The original headers of the message, before replacements */ + private $_originalHeaders = array(); + + /** Bodies of children before they are replaced */ + private $_originalChildBodies = array(); + + /** The Message that was last replaced */ + private $_lastMessage; + + /** + * Create a new DecoratorPlugin with $replacements. + * + * The $replacements can either be an associative array, or an implementation + * of {@link Swift_Plugins_Decorator_Replacements}. + * + * When using an array, it should be of the form: + * <code> + * $replacements = array( + * "address1@domain.tld" => array("{a}" => "b", "{c}" => "d"), + * "address2@domain.tld" => array("{a}" => "x", "{c}" => "y") + * ) + * </code> + * + * When using an instance of {@link Swift_Plugins_Decorator_Replacements}, + * the object should return just the array of replacements for the address + * given to {@link Swift_Plugins_Decorator_Replacements::getReplacementsFor()}. + * + * @param mixed $replacements Array or Swift_Plugins_Decorator_Replacements + */ + public function __construct($replacements) + { + $this->setReplacements($replacements); + } + + /** + * Sets replacements. + * + * @param mixed $replacements Array or Swift_Plugins_Decorator_Replacements + * + * @see __construct() + */ + public function setReplacements($replacements) + { + if (!($replacements instanceof Swift_Plugins_Decorator_Replacements)) { + $this->_replacements = (array) $replacements; + } else { + $this->_replacements = $replacements; + } + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + $this->_restoreMessage($message); + $to = array_keys($message->getTo()); + $address = array_shift($to); + if ($replacements = $this->getReplacementsFor($address)) { + $body = $message->getBody(); + $search = array_keys($replacements); + $replace = array_values($replacements); + $bodyReplaced = str_replace( + $search, $replace, $body + ); + if ($body != $bodyReplaced) { + $this->_originalBody = $body; + $message->setBody($bodyReplaced); + } + + foreach ($message->getHeaders()->getAll() as $header) { + $body = $header->getFieldBodyModel(); + $count = 0; + if (is_array($body)) { + $bodyReplaced = array(); + foreach ($body as $key => $value) { + $count1 = 0; + $count2 = 0; + $key = is_string($key) ? str_replace($search, $replace, $key, $count1) : $key; + $value = is_string($value) ? str_replace($search, $replace, $value, $count2) : $value; + $bodyReplaced[$key] = $value; + + if (!$count && ($count1 || $count2)) { + $count = 1; + } + } + } else { + $bodyReplaced = str_replace($search, $replace, $body, $count); + } + + if ($count) { + $this->_originalHeaders[$header->getFieldName()] = $body; + $header->setFieldBodyModel($bodyReplaced); + } + } + + $children = (array) $message->getChildren(); + foreach ($children as $child) { + list($type) = sscanf($child->getContentType(), '%[^/]/%s'); + if ('text' == $type) { + $body = $child->getBody(); + $bodyReplaced = str_replace( + $search, $replace, $body + ); + if ($body != $bodyReplaced) { + $child->setBody($bodyReplaced); + $this->_originalChildBodies[$child->getId()] = $body; + } + } + } + $this->_lastMessage = $message; + } + } + + /** + * Find a map of replacements for the address. + * + * If this plugin was provided with a delegate instance of + * {@link Swift_Plugins_Decorator_Replacements} then the call will be + * delegated to it. Otherwise, it will attempt to find the replacements + * from the array provided in the constructor. + * + * If no replacements can be found, an empty value (NULL) is returned. + * + * @param string $address + * + * @return array + */ + public function getReplacementsFor($address) + { + if ($this->_replacements instanceof Swift_Plugins_Decorator_Replacements) { + return $this->_replacements->getReplacementsFor($address); + } + + return isset($this->_replacements[$address]) ? $this->_replacements[$address] : null; + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + $this->_restoreMessage($evt->getMessage()); + } + + /** Restore a changed message back to its original state */ + private function _restoreMessage(Swift_Mime_Message $message) + { + if ($this->_lastMessage === $message) { + if (isset($this->_originalBody)) { + $message->setBody($this->_originalBody); + $this->_originalBody = null; + } + if (!empty($this->_originalHeaders)) { + foreach ($message->getHeaders()->getAll() as $header) { + if (array_key_exists($header->getFieldName(), $this->_originalHeaders)) { + $header->setFieldBodyModel($this->_originalHeaders[$header->getFieldName()]); + } + } + $this->_originalHeaders = array(); + } + if (!empty($this->_originalChildBodies)) { + $children = (array) $message->getChildren(); + foreach ($children as $child) { + $id = $child->getId(); + if (array_key_exists($id, $this->_originalChildBodies)) { + $child->setBody($this->_originalChildBodies[$id]); + } + } + $this->_originalChildBodies = array(); + } + $this->_lastMessage = null; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php new file mode 100644 index 00000000..7552b67a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php @@ -0,0 +1,69 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2009 Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Replaces the sender of a message. + * + * @author Arjen Brouwer + */ +class Swift_Plugins_ImpersonatePlugin implements Swift_Events_SendListener +{ + /** + * The sender to impersonate. + * + * @var String + */ + private $_sender; + + /** + * Create a new ImpersonatePlugin to impersonate $sender. + * + * @param string $sender address + */ + public function __construct($sender) + { + $this->_sender = $sender; + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + $headers = $message->getHeaders(); + + // save current recipients + $headers->addPathHeader('X-Swift-Return-Path', $message->getReturnPath()); + + // replace them with the one to send to + $message->setReturnPath($this->_sender); + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + + // restore original headers + $headers = $message->getHeaders(); + + if ($headers->has('X-Swift-Return-Path')) { + $message->setReturnPath($headers->get('X-Swift-Return-Path')->getAddress()); + $headers->removeAll('X-Swift-Return-Path'); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Logger.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Logger.php new file mode 100644 index 00000000..d9bce893 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Logger.php @@ -0,0 +1,36 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Logs events in the Transport system. + * + * @author Chris Corbyn + */ +interface Swift_Plugins_Logger +{ + /** + * Add a log entry. + * + * @param string $entry + */ + public function add($entry); + + /** + * Clear the log contents. + */ + public function clear(); + + /** + * Get this log as a string. + * + * @return string + */ + public function dump(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/LoggerPlugin.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/LoggerPlugin.php new file mode 100644 index 00000000..64db4384 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/LoggerPlugin.php @@ -0,0 +1,142 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Does real time logging of Transport level information. + * + * @author Chris Corbyn + */ +class Swift_Plugins_LoggerPlugin implements Swift_Events_CommandListener, Swift_Events_ResponseListener, Swift_Events_TransportChangeListener, Swift_Events_TransportExceptionListener, Swift_Plugins_Logger +{ + /** The logger which is delegated to */ + private $_logger; + + /** + * Create a new LoggerPlugin using $logger. + * + * @param Swift_Plugins_Logger $logger + */ + public function __construct(Swift_Plugins_Logger $logger) + { + $this->_logger = $logger; + } + + /** + * Add a log entry. + * + * @param string $entry + */ + public function add($entry) + { + $this->_logger->add($entry); + } + + /** + * Clear the log contents. + */ + public function clear() + { + $this->_logger->clear(); + } + + /** + * Get this log as a string. + * + * @return string + */ + public function dump() + { + return $this->_logger->dump(); + } + + /** + * Invoked immediately following a command being sent. + * + * @param Swift_Events_CommandEvent $evt + */ + public function commandSent(Swift_Events_CommandEvent $evt) + { + $command = $evt->getCommand(); + $this->_logger->add(sprintf('>> %s', $command)); + } + + /** + * Invoked immediately following a response coming back. + * + * @param Swift_Events_ResponseEvent $evt + */ + public function responseReceived(Swift_Events_ResponseEvent $evt) + { + $response = $evt->getResponse(); + $this->_logger->add(sprintf('<< %s', $response)); + } + + /** + * Invoked just before a Transport is started. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function beforeTransportStarted(Swift_Events_TransportChangeEvent $evt) + { + $transportName = get_class($evt->getSource()); + $this->_logger->add(sprintf('++ Starting %s', $transportName)); + } + + /** + * Invoked immediately after the Transport is started. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function transportStarted(Swift_Events_TransportChangeEvent $evt) + { + $transportName = get_class($evt->getSource()); + $this->_logger->add(sprintf('++ %s started', $transportName)); + } + + /** + * Invoked just before a Transport is stopped. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function beforeTransportStopped(Swift_Events_TransportChangeEvent $evt) + { + $transportName = get_class($evt->getSource()); + $this->_logger->add(sprintf('++ Stopping %s', $transportName)); + } + + /** + * Invoked immediately after the Transport is stopped. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function transportStopped(Swift_Events_TransportChangeEvent $evt) + { + $transportName = get_class($evt->getSource()); + $this->_logger->add(sprintf('++ %s stopped', $transportName)); + } + + /** + * Invoked as a TransportException is thrown in the Transport system. + * + * @param Swift_Events_TransportExceptionEvent $evt + */ + public function exceptionThrown(Swift_Events_TransportExceptionEvent $evt) + { + $e = $evt->getException(); + $message = $e->getMessage(); + $code = $e->getCode(); + $this->_logger->add(sprintf('!! %s (code: %s)', $message, $code)); + $message .= PHP_EOL; + $message .= 'Log data:'.PHP_EOL; + $message .= $this->_logger->dump(); + $evt->cancelBubble(); + throw new Swift_TransportException($message, $code, $e->getPrevious()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php new file mode 100644 index 00000000..865bb0aa --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php @@ -0,0 +1,72 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Logs to an Array backend. + * + * @author Chris Corbyn + */ +class Swift_Plugins_Loggers_ArrayLogger implements Swift_Plugins_Logger +{ + /** + * The log contents. + * + * @var array + */ + private $_log = array(); + + /** + * Max size of the log. + * + * @var int + */ + private $_size = 0; + + /** + * Create a new ArrayLogger with a maximum of $size entries. + * + * @var int + */ + public function __construct($size = 50) + { + $this->_size = $size; + } + + /** + * Add a log entry. + * + * @param string $entry + */ + public function add($entry) + { + $this->_log[] = $entry; + while (count($this->_log) > $this->_size) { + array_shift($this->_log); + } + } + + /** + * Clear the log contents. + */ + public function clear() + { + $this->_log = array(); + } + + /** + * Get this log as a string. + * + * @return string + */ + public function dump() + { + return implode(PHP_EOL, $this->_log); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php new file mode 100644 index 00000000..3583297a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php @@ -0,0 +1,58 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Prints all log messages in real time. + * + * @author Chris Corbyn + */ +class Swift_Plugins_Loggers_EchoLogger implements Swift_Plugins_Logger +{ + /** Whether or not HTML should be output */ + private $_isHtml; + + /** + * Create a new EchoLogger. + * + * @param bool $isHtml + */ + public function __construct($isHtml = true) + { + $this->_isHtml = $isHtml; + } + + /** + * Add a log entry. + * + * @param string $entry + */ + public function add($entry) + { + if ($this->_isHtml) { + printf('%s%s%s', htmlspecialchars($entry, ENT_QUOTES), '<br />', PHP_EOL); + } else { + printf('%s%s', $entry, PHP_EOL); + } + } + + /** + * Not implemented. + */ + public function clear() + { + } + + /** + * Not implemented. + */ + public function dump() + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php new file mode 100644 index 00000000..e622cb37 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php @@ -0,0 +1,74 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2011 Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores all sent emails for further usage. + * + * @author Fabien Potencier + */ +class Swift_Plugins_MessageLogger implements Swift_Events_SendListener +{ + /** + * @var array + */ + private $messages; + + public function __construct() + { + $this->messages = array(); + } + + /** + * Get the message list. + * + * @return array + */ + public function getMessages() + { + return $this->messages; + } + + /** + * Get the message count. + * + * @return int count + */ + public function countMessages() + { + return count($this->messages); + } + + /** + * Empty the message list. + */ + public function clear() + { + $this->messages = array(); + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + $this->messages[] = clone $evt->getMessage(); + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Connection.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Connection.php new file mode 100644 index 00000000..fb99e4c9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Connection.php @@ -0,0 +1,31 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Pop3Connection interface for connecting and disconnecting to a POP3 host. + * + * @author Chris Corbyn + */ +interface Swift_Plugins_Pop_Pop3Connection +{ + /** + * Connect to the POP3 host and throw an Exception if it fails. + * + * @throws Swift_Plugins_Pop_Pop3Exception + */ + public function connect(); + + /** + * Disconnect from the POP3 host and throw an Exception if it fails. + * + * @throws Swift_Plugins_Pop_Pop3Exception + */ + public function disconnect(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Exception.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Exception.php new file mode 100644 index 00000000..dc7be0c4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Exception.php @@ -0,0 +1,27 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Pop3Exception thrown when an error occurs connecting to a POP3 host. + * + * @author Chris Corbyn + */ +class Swift_Plugins_Pop_Pop3Exception extends Swift_IoException +{ + /** + * Create a new Pop3Exception with $message. + * + * @param string $message + */ + public function __construct($message) + { + parent::__construct($message); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/PopBeforeSmtpPlugin.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/PopBeforeSmtpPlugin.php new file mode 100644 index 00000000..18abb770 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/PopBeforeSmtpPlugin.php @@ -0,0 +1,273 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Makes sure a connection to a POP3 host has been established prior to connecting to SMTP. + * + * @author Chris Corbyn + */ +class Swift_Plugins_PopBeforeSmtpPlugin implements Swift_Events_TransportChangeListener, Swift_Plugins_Pop_Pop3Connection +{ + /** A delegate connection to use (mostly a test hook) */ + private $_connection; + + /** Hostname of the POP3 server */ + private $_host; + + /** Port number to connect on */ + private $_port; + + /** Encryption type to use (if any) */ + private $_crypto; + + /** Username to use (if any) */ + private $_username; + + /** Password to use (if any) */ + private $_password; + + /** Established connection via TCP socket */ + private $_socket; + + /** Connect timeout in seconds */ + private $_timeout = 10; + + /** SMTP Transport to bind to */ + private $_transport; + + /** + * Create a new PopBeforeSmtpPlugin for $host and $port. + * + * @param string $host + * @param int $port + * @param string $crypto as "tls" or "ssl" + */ + public function __construct($host, $port = 110, $crypto = null) + { + $this->_host = $host; + $this->_port = $port; + $this->_crypto = $crypto; + } + + /** + * Create a new PopBeforeSmtpPlugin for $host and $port. + * + * @param string $host + * @param int $port + * @param string $crypto as "tls" or "ssl" + * + * @return Swift_Plugins_PopBeforeSmtpPlugin + */ + public static function newInstance($host, $port = 110, $crypto = null) + { + return new self($host, $port, $crypto); + } + + /** + * Set a Pop3Connection to delegate to instead of connecting directly. + * + * @param Swift_Plugins_Pop_Pop3Connection $connection + * + * @return Swift_Plugins_PopBeforeSmtpPlugin + */ + public function setConnection(Swift_Plugins_Pop_Pop3Connection $connection) + { + $this->_connection = $connection; + + return $this; + } + + /** + * Bind this plugin to a specific SMTP transport instance. + * + * @param Swift_Transport + */ + public function bindSmtp(Swift_Transport $smtp) + { + $this->_transport = $smtp; + } + + /** + * Set the connection timeout in seconds (default 10). + * + * @param int $timeout + * + * @return Swift_Plugins_PopBeforeSmtpPlugin + */ + public function setTimeout($timeout) + { + $this->_timeout = (int) $timeout; + + return $this; + } + + /** + * Set the username to use when connecting (if needed). + * + * @param string $username + * + * @return Swift_Plugins_PopBeforeSmtpPlugin + */ + public function setUsername($username) + { + $this->_username = $username; + + return $this; + } + + /** + * Set the password to use when connecting (if needed). + * + * @param string $password + * + * @return Swift_Plugins_PopBeforeSmtpPlugin + */ + public function setPassword($password) + { + $this->_password = $password; + + return $this; + } + + /** + * Connect to the POP3 host and authenticate. + * + * @throws Swift_Plugins_Pop_Pop3Exception if connection fails + */ + public function connect() + { + if (isset($this->_connection)) { + $this->_connection->connect(); + } else { + if (!isset($this->_socket)) { + if (!$socket = fsockopen( + $this->_getHostString(), $this->_port, $errno, $errstr, $this->_timeout)) { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('Failed to connect to POP3 host [%s]: %s', $this->_host, $errstr) + ); + } + $this->_socket = $socket; + + if (false === $greeting = fgets($this->_socket)) { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('Failed to connect to POP3 host [%s]', trim($greeting)) + ); + } + + $this->_assertOk($greeting); + + if ($this->_username) { + $this->_command(sprintf("USER %s\r\n", $this->_username)); + $this->_command(sprintf("PASS %s\r\n", $this->_password)); + } + } + } + } + + /** + * Disconnect from the POP3 host. + */ + public function disconnect() + { + if (isset($this->_connection)) { + $this->_connection->disconnect(); + } else { + $this->_command("QUIT\r\n"); + if (!fclose($this->_socket)) { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('POP3 host [%s] connection could not be stopped', $this->_host) + ); + } + $this->_socket = null; + } + } + + /** + * Invoked just before a Transport is started. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function beforeTransportStarted(Swift_Events_TransportChangeEvent $evt) + { + if (isset($this->_transport)) { + if ($this->_transport !== $evt->getTransport()) { + return; + } + } + + $this->connect(); + $this->disconnect(); + } + + /** + * Not used. + */ + public function transportStarted(Swift_Events_TransportChangeEvent $evt) + { + } + + /** + * Not used. + */ + public function beforeTransportStopped(Swift_Events_TransportChangeEvent $evt) + { + } + + /** + * Not used. + */ + public function transportStopped(Swift_Events_TransportChangeEvent $evt) + { + } + + private function _command($command) + { + if (!fwrite($this->_socket, $command)) { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('Failed to write command [%s] to POP3 host', trim($command)) + ); + } + + if (false === $response = fgets($this->_socket)) { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('Failed to read from POP3 host after command [%s]', trim($command)) + ); + } + + $this->_assertOk($response); + + return $response; + } + + private function _assertOk($response) + { + if (substr($response, 0, 3) != '+OK') { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('POP3 command failed [%s]', trim($response)) + ); + } + } + + private function _getHostString() + { + $host = $this->_host; + switch (strtolower($this->_crypto)) { + case 'ssl': + $host = 'ssl://'.$host; + break; + + case 'tls': + $host = 'tls://'.$host; + break; + } + + return $host; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php new file mode 100644 index 00000000..c3a1f868 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php @@ -0,0 +1,213 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2009 Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Redirects all email to a single recipient. + * + * @author Fabien Potencier + */ +class Swift_Plugins_RedirectingPlugin implements Swift_Events_SendListener +{ + /** + * The recipient who will receive all messages. + * + * @var mixed + */ + private $_recipient; + + /** + * List of regular expression for recipient whitelisting. + * + * @var array + */ + private $_whitelist = array(); + + /** + * Create a new RedirectingPlugin. + * + * @param mixed $recipient + * @param array $whitelist + */ + public function __construct($recipient, array $whitelist = array()) + { + $this->_recipient = $recipient; + $this->_whitelist = $whitelist; + } + + /** + * Set the recipient of all messages. + * + * @param mixed $recipient + */ + public function setRecipient($recipient) + { + $this->_recipient = $recipient; + } + + /** + * Get the recipient of all messages. + * + * @return mixed + */ + public function getRecipient() + { + return $this->_recipient; + } + + /** + * Set a list of regular expressions to whitelist certain recipients. + * + * @param array $whitelist + */ + public function setWhitelist(array $whitelist) + { + $this->_whitelist = $whitelist; + } + + /** + * Get the whitelist. + * + * @return array + */ + public function getWhitelist() + { + return $this->_whitelist; + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + $headers = $message->getHeaders(); + + // conditionally save current recipients + + if ($headers->has('to')) { + $headers->addMailboxHeader('X-Swift-To', $message->getTo()); + } + + if ($headers->has('cc')) { + $headers->addMailboxHeader('X-Swift-Cc', $message->getCc()); + } + + if ($headers->has('bcc')) { + $headers->addMailboxHeader('X-Swift-Bcc', $message->getBcc()); + } + + // Filter remaining headers against whitelist + $this->_filterHeaderSet($headers, 'To'); + $this->_filterHeaderSet($headers, 'Cc'); + $this->_filterHeaderSet($headers, 'Bcc'); + + // Add each hard coded recipient + $to = $message->getTo(); + if (null === $to) { + $to = array(); + } + + foreach ((array) $this->_recipient as $recipient) { + if (!array_key_exists($recipient, $to)) { + $message->addTo($recipient); + } + } + } + + /** + * Filter header set against a whitelist of regular expressions. + * + * @param Swift_Mime_HeaderSet $headerSet + * @param string $type + */ + private function _filterHeaderSet(Swift_Mime_HeaderSet $headerSet, $type) + { + foreach ($headerSet->getAll($type) as $headers) { + $headers->setNameAddresses($this->_filterNameAddresses($headers->getNameAddresses())); + } + } + + /** + * Filtered list of addresses => name pairs. + * + * @param array $recipients + * + * @return array + */ + private function _filterNameAddresses(array $recipients) + { + $filtered = array(); + + foreach ($recipients as $address => $name) { + if ($this->_isWhitelisted($address)) { + $filtered[$address] = $name; + } + } + + return $filtered; + } + + /** + * Matches address against whitelist of regular expressions. + * + * @param $recipient + * + * @return bool + */ + protected function _isWhitelisted($recipient) + { + if (in_array($recipient, (array) $this->_recipient)) { + return true; + } + + foreach ($this->_whitelist as $pattern) { + if (preg_match($pattern, $recipient)) { + return true; + } + } + + return false; + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + $this->_restoreMessage($evt->getMessage()); + } + + private function _restoreMessage(Swift_Mime_Message $message) + { + // restore original headers + $headers = $message->getHeaders(); + + if ($headers->has('X-Swift-To')) { + $message->setTo($headers->get('X-Swift-To')->getNameAddresses()); + $headers->removeAll('X-Swift-To'); + } else { + $message->setTo(null); + } + + if ($headers->has('X-Swift-Cc')) { + $message->setCc($headers->get('X-Swift-Cc')->getNameAddresses()); + $headers->removeAll('X-Swift-Cc'); + } + + if ($headers->has('X-Swift-Bcc')) { + $message->setBcc($headers->get('X-Swift-Bcc')->getNameAddresses()); + $headers->removeAll('X-Swift-Bcc'); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporter.php new file mode 100644 index 00000000..0f21b7d6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporter.php @@ -0,0 +1,32 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * The Reporter plugin sends pass/fail notification to a Reporter. + * + * @author Chris Corbyn + */ +interface Swift_Plugins_Reporter +{ + /** The recipient was accepted for delivery */ + const RESULT_PASS = 0x01; + + /** The recipient could not be accepted */ + const RESULT_FAIL = 0x10; + + /** + * Notifies this ReportNotifier that $address failed or succeeded. + * + * @param Swift_Mime_Message $message + * @param string $address + * @param int $result from {@link RESULT_PASS, RESULT_FAIL} + */ + public function notify(Swift_Mime_Message $message, $address, $result); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ReporterPlugin.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ReporterPlugin.php new file mode 100644 index 00000000..a37901ff --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ReporterPlugin.php @@ -0,0 +1,61 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Does real time reporting of pass/fail for each recipient. + * + * @author Chris Corbyn + */ +class Swift_Plugins_ReporterPlugin implements Swift_Events_SendListener +{ + /** + * The reporter backend which takes notifications. + * + * @var Swift_Plugins_Reporter + */ + private $_reporter; + + /** + * Create a new ReporterPlugin using $reporter. + * + * @param Swift_Plugins_Reporter $reporter + */ + public function __construct(Swift_Plugins_Reporter $reporter) + { + $this->_reporter = $reporter; + } + + /** + * Not used. + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + $failures = array_flip($evt->getFailedRecipients()); + foreach ((array) $message->getTo() as $address => $null) { + $this->_reporter->notify($message, $address, array_key_exists($address, $failures) ? Swift_Plugins_Reporter::RESULT_FAIL : Swift_Plugins_Reporter::RESULT_PASS); + } + foreach ((array) $message->getCc() as $address => $null) { + $this->_reporter->notify($message, $address, array_key_exists($address, $failures) ? Swift_Plugins_Reporter::RESULT_FAIL : Swift_Plugins_Reporter::RESULT_PASS); + } + foreach ((array) $message->getBcc() as $address => $null) { + $this->_reporter->notify($message, $address, array_key_exists($address, $failures) ? Swift_Plugins_Reporter::RESULT_FAIL : Swift_Plugins_Reporter::RESULT_PASS); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php new file mode 100644 index 00000000..cad9d168 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php @@ -0,0 +1,59 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A reporter which "collects" failures for the Reporter plugin. + * + * @author Chris Corbyn + */ +class Swift_Plugins_Reporters_HitReporter implements Swift_Plugins_Reporter +{ + /** + * The list of failures. + * + * @var array + */ + private $_failures = array(); + + private $_failures_cache = array(); + + /** + * Notifies this ReportNotifier that $address failed or succeeded. + * + * @param Swift_Mime_Message $message + * @param string $address + * @param int $result from {@link RESULT_PASS, RESULT_FAIL} + */ + public function notify(Swift_Mime_Message $message, $address, $result) + { + if (self::RESULT_FAIL == $result && !isset($this->_failures_cache[$address])) { + $this->_failures[] = $address; + $this->_failures_cache[$address] = true; + } + } + + /** + * Get an array of addresses for which delivery failed. + * + * @return array + */ + public function getFailedRecipients() + { + return $this->_failures; + } + + /** + * Clear the buffer (empty the list). + */ + public function clear() + { + $this->_failures = $this->_failures_cache = array(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php new file mode 100644 index 00000000..c6259355 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php @@ -0,0 +1,39 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A HTML output reporter for the Reporter plugin. + * + * @author Chris Corbyn + */ +class Swift_Plugins_Reporters_HtmlReporter implements Swift_Plugins_Reporter +{ + /** + * Notifies this ReportNotifier that $address failed or succeeded. + * + * @param Swift_Mime_Message $message + * @param string $address + * @param int $result from {@see RESULT_PASS, RESULT_FAIL} + */ + public function notify(Swift_Mime_Message $message, $address, $result) + { + if (self::RESULT_PASS == $result) { + echo '<div style="color: #fff; background: #006600; padding: 2px; margin: 2px;">'.PHP_EOL; + echo 'PASS '.$address.PHP_EOL; + echo '</div>'.PHP_EOL; + flush(); + } else { + echo '<div style="color: #fff; background: #880000; padding: 2px; margin: 2px;">'.PHP_EOL; + echo 'FAIL '.$address.PHP_EOL; + echo '</div>'.PHP_EOL; + flush(); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Sleeper.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Sleeper.php new file mode 100644 index 00000000..595c0f60 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Sleeper.php @@ -0,0 +1,24 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Sleeps for a duration of time. + * + * @author Chris Corbyn + */ +interface Swift_Plugins_Sleeper +{ + /** + * Sleep for $seconds. + * + * @param int $seconds + */ + public function sleep($seconds); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ThrottlerPlugin.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ThrottlerPlugin.php new file mode 100644 index 00000000..2f4b9a75 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ThrottlerPlugin.php @@ -0,0 +1,200 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Throttles the rate at which emails are sent. + * + * @author Chris Corbyn + */ +class Swift_Plugins_ThrottlerPlugin extends Swift_Plugins_BandwidthMonitorPlugin implements Swift_Plugins_Sleeper, Swift_Plugins_Timer +{ + /** Flag for throttling in bytes per minute */ + const BYTES_PER_MINUTE = 0x01; + + /** Flag for throttling in emails per second (Amazon SES) */ + const MESSAGES_PER_SECOND = 0x11; + + /** Flag for throttling in emails per minute */ + const MESSAGES_PER_MINUTE = 0x10; + + /** + * The Sleeper instance for sleeping. + * + * @var Swift_Plugins_Sleeper + */ + private $_sleeper; + + /** + * The Timer instance which provides the timestamp. + * + * @var Swift_Plugins_Timer + */ + private $_timer; + + /** + * The time at which the first email was sent. + * + * @var int + */ + private $_start; + + /** + * The rate at which messages should be sent. + * + * @var int + */ + private $_rate; + + /** + * The mode for throttling. + * + * This is {@link BYTES_PER_MINUTE} or {@link MESSAGES_PER_MINUTE} + * + * @var int + */ + private $_mode; + + /** + * An internal counter of the number of messages sent. + * + * @var int + */ + private $_messages = 0; + + /** + * Create a new ThrottlerPlugin. + * + * @param int $rate + * @param int $mode, defaults to {@link BYTES_PER_MINUTE} + * @param Swift_Plugins_Sleeper $sleeper (only needed in testing) + * @param Swift_Plugins_Timer $timer (only needed in testing) + */ + public function __construct($rate, $mode = self::BYTES_PER_MINUTE, Swift_Plugins_Sleeper $sleeper = null, Swift_Plugins_Timer $timer = null) + { + $this->_rate = $rate; + $this->_mode = $mode; + $this->_sleeper = $sleeper; + $this->_timer = $timer; + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + $time = $this->getTimestamp(); + if (!isset($this->_start)) { + $this->_start = $time; + } + $duration = $time - $this->_start; + + switch ($this->_mode) { + case self::BYTES_PER_MINUTE: + $sleep = $this->_throttleBytesPerMinute($duration); + break; + case self::MESSAGES_PER_SECOND: + $sleep = $this->_throttleMessagesPerSecond($duration); + break; + case self::MESSAGES_PER_MINUTE: + $sleep = $this->_throttleMessagesPerMinute($duration); + break; + default: + $sleep = 0; + break; + } + + if ($sleep > 0) { + $this->sleep($sleep); + } + } + + /** + * Invoked when a Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + parent::sendPerformed($evt); + ++$this->_messages; + } + + /** + * Sleep for $seconds. + * + * @param int $seconds + */ + public function sleep($seconds) + { + if (isset($this->_sleeper)) { + $this->_sleeper->sleep($seconds); + } else { + sleep($seconds); + } + } + + /** + * Get the current UNIX timestamp. + * + * @return int + */ + public function getTimestamp() + { + if (isset($this->_timer)) { + return $this->_timer->getTimestamp(); + } + + return time(); + } + + /** + * Get a number of seconds to sleep for. + * + * @param int $timePassed + * + * @return int + */ + private function _throttleBytesPerMinute($timePassed) + { + $expectedDuration = $this->getBytesOut() / ($this->_rate / 60); + + return (int) ceil($expectedDuration - $timePassed); + } + + /** + * Get a number of seconds to sleep for. + * + * @param int $timePassed + * + * @return int + */ + private function _throttleMessagesPerSecond($timePassed) + { + $expectedDuration = $this->_messages / ($this->_rate); + + return (int) ceil($expectedDuration - $timePassed); + } + + /** + * Get a number of seconds to sleep for. + * + * @param int $timePassed + * + * @return int + */ + private function _throttleMessagesPerMinute($timePassed) + { + $expectedDuration = $this->_messages / ($this->_rate / 60); + + return (int) ceil($expectedDuration - $timePassed); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Timer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Timer.php new file mode 100644 index 00000000..9c8deb38 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Timer.php @@ -0,0 +1,24 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides timestamp data. + * + * @author Chris Corbyn + */ +interface Swift_Plugins_Timer +{ + /** + * Get the current UNIX timestamp. + * + * @return int + */ + public function getTimestamp(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Preferences.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Preferences.php new file mode 100644 index 00000000..503db84d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Preferences.php @@ -0,0 +1,103 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Changes some global preference settings in Swift Mailer. + * + * @author Chris Corbyn + */ +class Swift_Preferences +{ + /** Singleton instance */ + private static $_instance = null; + + /** Constructor not to be used */ + private function __construct() + { + } + + /** + * Gets the instance of Preferences. + * + * @return Swift_Preferences + */ + public static function getInstance() + { + if (!isset(self::$_instance)) { + self::$_instance = new self(); + } + + return self::$_instance; + } + + /** + * Set the default charset used. + * + * @param string $charset + * + * @return Swift_Preferences + */ + public function setCharset($charset) + { + Swift_DependencyContainer::getInstance() + ->register('properties.charset')->asValue($charset); + + return $this; + } + + /** + * Set the directory where temporary files can be saved. + * + * @param string $dir + * + * @return Swift_Preferences + */ + public function setTempDir($dir) + { + Swift_DependencyContainer::getInstance() + ->register('tempdir')->asValue($dir); + + return $this; + } + + /** + * Set the type of cache to use (i.e. "disk" or "array"). + * + * @param string $type + * + * @return Swift_Preferences + */ + public function setCacheType($type) + { + Swift_DependencyContainer::getInstance() + ->register('cache')->asAliasOf(sprintf('cache.%s', $type)); + + return $this; + } + + /** + * Set the QuotedPrintable dot escaper preference. + * + * @param bool $dotEscape + * + * @return Swift_Preferences + */ + public function setQPDotEscape($dotEscape) + { + $dotEscape = !empty($dotEscape); + Swift_DependencyContainer::getInstance() + ->register('mime.qpcontentencoder') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_QpContentEncoder') + ->withDependencies(array('mime.charstream', 'mime.bytecanonicalizer')) + ->addConstructorValue($dotEscape); + + return $this; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ReplacementFilterFactory.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ReplacementFilterFactory.php new file mode 100644 index 00000000..2897474e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ReplacementFilterFactory.php @@ -0,0 +1,27 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Creates StreamFilters. + * + * @author Chris Corbyn + */ +interface Swift_ReplacementFilterFactory +{ + /** + * Create a filter to replace $search with $replace. + * + * @param mixed $search + * @param mixed $replace + * + * @return Swift_StreamFilter + */ + public function createFilter($search, $replace); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/RfcComplianceException.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/RfcComplianceException.php new file mode 100644 index 00000000..81bc4031 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/RfcComplianceException.php @@ -0,0 +1,27 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * RFC Compliance Exception class. + * + * @author Chris Corbyn + */ +class Swift_RfcComplianceException extends Swift_SwiftException +{ + /** + * Create a new RfcComplianceException with $message. + * + * @param string $message + */ + public function __construct($message) + { + parent::__construct($message); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SendmailTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SendmailTransport.php new file mode 100644 index 00000000..974b24f2 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SendmailTransport.php @@ -0,0 +1,45 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * SendmailTransport for sending mail through a Sendmail/Postfix (etc..) binary. + * + * @author Chris Corbyn + */ +class Swift_SendmailTransport extends Swift_Transport_SendmailTransport +{ + /** + * Create a new SendmailTransport, optionally using $command for sending. + * + * @param string $command + */ + public function __construct($command = '/usr/sbin/sendmail -bs') + { + call_user_func_array( + array($this, 'Swift_Transport_SendmailTransport::__construct'), + Swift_DependencyContainer::getInstance() + ->createDependenciesFor('transport.sendmail') + ); + + $this->setCommand($command); + } + + /** + * Create a new SendmailTransport instance. + * + * @param string $command + * + * @return Swift_SendmailTransport + */ + public static function newInstance($command = '/usr/sbin/sendmail -bs') + { + return new self($command); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SignedMessage.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SignedMessage.php new file mode 100644 index 00000000..2e7a8726 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SignedMessage.php @@ -0,0 +1,23 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Signed Message, message that can be signed using a signer. + * + * This class is only kept for compatibility + * + * + * @author Xavier De Cock <xdecock@gmail.com> + * + * @deprecated + */ +class Swift_SignedMessage extends Swift_Message +{ +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signer.php new file mode 100644 index 00000000..2d8176d9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signer.php @@ -0,0 +1,20 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Base Class of Signer Infrastructure. + * + * + * @author Xavier De Cock <xdecock@gmail.com> + */ +interface Swift_Signer +{ + public function reset(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/BodySigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/BodySigner.php new file mode 100644 index 00000000..9ffcef39 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/BodySigner.php @@ -0,0 +1,33 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Body Signer Interface used to apply Body-Based Signature to a message. + * + * @author Xavier De Cock <xdecock@gmail.com> + */ +interface Swift_Signers_BodySigner extends Swift_Signer +{ + /** + * Change the Swift_Signed_Message to apply the singing. + * + * @param Swift_Message $message + * + * @return Swift_Signers_BodySigner + */ + public function signMessage(Swift_Message $message); + + /** + * Return the list of header a signer might tamper. + * + * @return array + */ + public function getAlteredHeaders(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php new file mode 100644 index 00000000..6ddd4f92 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php @@ -0,0 +1,698 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * DKIM Signer used to apply DKIM Signature to a message. + * + * @author Xavier De Cock <xdecock@gmail.com> + */ +class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner +{ + /** + * PrivateKey. + * + * @var string + */ + protected $_privateKey; + + /** + * DomainName. + * + * @var string + */ + protected $_domainName; + + /** + * Selector. + * + * @var string + */ + protected $_selector; + + /** + * Hash algorithm used. + * + * @var string + */ + protected $_hashAlgorithm = 'rsa-sha1'; + + /** + * Body canon method. + * + * @var string + */ + protected $_bodyCanon = 'simple'; + + /** + * Header canon method. + * + * @var string + */ + protected $_headerCanon = 'simple'; + + /** + * Headers not being signed. + * + * @var array + */ + protected $_ignoredHeaders = array('return-path' => true); + + /** + * Signer identity. + * + * @var string + */ + protected $_signerIdentity; + + /** + * BodyLength. + * + * @var int + */ + protected $_bodyLen = 0; + + /** + * Maximum signedLen. + * + * @var int + */ + protected $_maxLen = PHP_INT_MAX; + + /** + * Embbed bodyLen in signature. + * + * @var bool + */ + protected $_showLen = false; + + /** + * When the signature has been applied (true means time()), false means not embedded. + * + * @var mixed + */ + protected $_signatureTimestamp = true; + + /** + * When will the signature expires false means not embedded, if sigTimestamp is auto + * Expiration is relative, otherwhise it's absolute. + * + * @var int + */ + protected $_signatureExpiration = false; + + /** + * Must we embed signed headers? + * + * @var bool + */ + protected $_debugHeaders = false; + + // work variables + /** + * Headers used to generate hash. + * + * @var array + */ + protected $_signedHeaders = array(); + + /** + * If debugHeaders is set store debugDatas here. + * + * @var string + */ + private $_debugHeadersData = ''; + + /** + * Stores the bodyHash. + * + * @var string + */ + private $_bodyHash = ''; + + /** + * Stores the signature header. + * + * @var Swift_Mime_Headers_ParameterizedHeader + */ + protected $_dkimHeader; + + private $_bodyHashHandler; + + private $_headerHash; + + private $_headerCanonData = ''; + + private $_bodyCanonEmptyCounter = 0; + + private $_bodyCanonIgnoreStart = 2; + + private $_bodyCanonSpace = false; + + private $_bodyCanonLastChar = null; + + private $_bodyCanonLine = ''; + + private $_bound = array(); + + /** + * Constructor. + * + * @param string $privateKey + * @param string $domainName + * @param string $selector + */ + public function __construct($privateKey, $domainName, $selector) + { + $this->_privateKey = $privateKey; + $this->_domainName = $domainName; + $this->_signerIdentity = '@'.$domainName; + $this->_selector = $selector; + } + + /** + * Instanciate DKIMSigner. + * + * @param string $privateKey + * @param string $domainName + * @param string $selector + * + * @return Swift_Signers_DKIMSigner + */ + public static function newInstance($privateKey, $domainName, $selector) + { + return new static($privateKey, $domainName, $selector); + } + + /** + * Reset the Signer. + * + * @see Swift_Signer::reset() + */ + public function reset() + { + $this->_headerHash = null; + $this->_signedHeaders = array(); + $this->_bodyHash = null; + $this->_bodyHashHandler = null; + $this->_bodyCanonIgnoreStart = 2; + $this->_bodyCanonEmptyCounter = 0; + $this->_bodyCanonLastChar = null; + $this->_bodyCanonSpace = false; + } + + /** + * Writes $bytes to the end of the stream. + * + * Writing may not happen immediately if the stream chooses to buffer. If + * you want to write these bytes with immediate effect, call {@link commit()} + * after calling write(). + * + * This method returns the sequence ID of the write (i.e. 1 for first, 2 for + * second, etc etc). + * + * @param string $bytes + * + * @throws Swift_IoException + * + * @return int + */ + public function write($bytes) + { + $this->_canonicalizeBody($bytes); + foreach ($this->_bound as $is) { + $is->write($bytes); + } + } + + /** + * For any bytes that are currently buffered inside the stream, force them + * off the buffer. + * + * @throws Swift_IoException + */ + public function commit() + { + // Nothing to do + return; + } + + /** + * Attach $is to this stream. + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + */ + public function bind(Swift_InputByteStream $is) + { + // Don't have to mirror anything + $this->_bound[] = $is; + + return; + } + + /** + * Remove an already bound stream. + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + */ + public function unbind(Swift_InputByteStream $is) + { + // Don't have to mirror anything + foreach ($this->_bound as $k => $stream) { + if ($stream === $is) { + unset($this->_bound[$k]); + + return; + } + } + + return; + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + * + * @throws Swift_IoException + */ + public function flushBuffers() + { + $this->reset(); + } + + /** + * Set hash_algorithm, must be one of rsa-sha256 | rsa-sha1 defaults to rsa-sha256. + * + * @param string $hash + * + * @return Swift_Signers_DKIMSigner + */ + public function setHashAlgorithm($hash) + { + // Unable to sign with rsa-sha256 + if ($hash == 'rsa-sha1') { + $this->_hashAlgorithm = 'rsa-sha1'; + } else { + $this->_hashAlgorithm = 'rsa-sha256'; + } + + return $this; + } + + /** + * Set the body canonicalization algorithm. + * + * @param string $canon + * + * @return Swift_Signers_DKIMSigner + */ + public function setBodyCanon($canon) + { + if ($canon == 'relaxed') { + $this->_bodyCanon = 'relaxed'; + } else { + $this->_bodyCanon = 'simple'; + } + + return $this; + } + + /** + * Set the header canonicalization algorithm. + * + * @param string $canon + * + * @return Swift_Signers_DKIMSigner + */ + public function setHeaderCanon($canon) + { + if ($canon == 'relaxed') { + $this->_headerCanon = 'relaxed'; + } else { + $this->_headerCanon = 'simple'; + } + + return $this; + } + + /** + * Set the signer identity. + * + * @param string $identity + * + * @return Swift_Signers_DKIMSigner + */ + public function setSignerIdentity($identity) + { + $this->_signerIdentity = $identity; + + return $this; + } + + /** + * Set the length of the body to sign. + * + * @param mixed $len (bool or int) + * + * @return Swift_Signers_DKIMSigner + */ + public function setBodySignedLen($len) + { + if ($len === true) { + $this->_showLen = true; + $this->_maxLen = PHP_INT_MAX; + } elseif ($len === false) { + $this->_showLen = false; + $this->_maxLen = PHP_INT_MAX; + } else { + $this->_showLen = true; + $this->_maxLen = (int) $len; + } + + return $this; + } + + /** + * Set the signature timestamp. + * + * @param int $time A timestamp + * + * @return Swift_Signers_DKIMSigner + */ + public function setSignatureTimestamp($time) + { + $this->_signatureTimestamp = $time; + + return $this; + } + + /** + * Set the signature expiration timestamp. + * + * @param int $time A timestamp + * + * @return Swift_Signers_DKIMSigner + */ + public function setSignatureExpiration($time) + { + $this->_signatureExpiration = $time; + + return $this; + } + + /** + * Enable / disable the DebugHeaders. + * + * @param bool $debug + * + * @return Swift_Signers_DKIMSigner + */ + public function setDebugHeaders($debug) + { + $this->_debugHeaders = (bool) $debug; + + return $this; + } + + /** + * Start Body. + */ + public function startBody() + { + // Init + switch ($this->_hashAlgorithm) { + case 'rsa-sha256': + $this->_bodyHashHandler = hash_init('sha256'); + break; + case 'rsa-sha1': + $this->_bodyHashHandler = hash_init('sha1'); + break; + } + $this->_bodyCanonLine = ''; + } + + /** + * End Body. + */ + public function endBody() + { + $this->_endOfBody(); + } + + /** + * Returns the list of Headers Tampered by this plugin. + * + * @return array + */ + public function getAlteredHeaders() + { + if ($this->_debugHeaders) { + return array('DKIM-Signature', 'X-DebugHash'); + } else { + return array('DKIM-Signature'); + } + } + + /** + * Adds an ignored Header. + * + * @param string $header_name + * + * @return Swift_Signers_DKIMSigner + */ + public function ignoreHeader($header_name) + { + $this->_ignoredHeaders[strtolower($header_name)] = true; + + return $this; + } + + /** + * Set the headers to sign. + * + * @param Swift_Mime_HeaderSet $headers + * + * @return Swift_Signers_DKIMSigner + */ + public function setHeaders(Swift_Mime_HeaderSet $headers) + { + $this->_headerCanonData = ''; + // Loop through Headers + $listHeaders = $headers->listAll(); + foreach ($listHeaders as $hName) { + // Check if we need to ignore Header + if (!isset($this->_ignoredHeaders[strtolower($hName)])) { + if ($headers->has($hName)) { + $tmp = $headers->getAll($hName); + foreach ($tmp as $header) { + if ($header->getFieldBody() != '') { + $this->_addHeader($header->toString()); + $this->_signedHeaders[] = $header->getFieldName(); + } + } + } + } + } + + return $this; + } + + /** + * Add the signature to the given Headers. + * + * @param Swift_Mime_HeaderSet $headers + * + * @return Swift_Signers_DKIMSigner + */ + public function addSignature(Swift_Mime_HeaderSet $headers) + { + // Prepare the DKIM-Signature + $params = array('v' => '1', 'a' => $this->_hashAlgorithm, 'bh' => base64_encode($this->_bodyHash), 'd' => $this->_domainName, 'h' => implode(': ', $this->_signedHeaders), 'i' => $this->_signerIdentity, 's' => $this->_selector); + if ($this->_bodyCanon != 'simple') { + $params['c'] = $this->_headerCanon.'/'.$this->_bodyCanon; + } elseif ($this->_headerCanon != 'simple') { + $params['c'] = $this->_headerCanon; + } + if ($this->_showLen) { + $params['l'] = $this->_bodyLen; + } + if ($this->_signatureTimestamp === true) { + $params['t'] = time(); + if ($this->_signatureExpiration !== false) { + $params['x'] = $params['t'] + $this->_signatureExpiration; + } + } else { + if ($this->_signatureTimestamp !== false) { + $params['t'] = $this->_signatureTimestamp; + } + if ($this->_signatureExpiration !== false) { + $params['x'] = $this->_signatureExpiration; + } + } + if ($this->_debugHeaders) { + $params['z'] = implode('|', $this->_debugHeadersData); + } + $string = ''; + foreach ($params as $k => $v) { + $string .= $k.'='.$v.'; '; + } + $string = trim($string); + $headers->addTextHeader('DKIM-Signature', $string); + // Add the last DKIM-Signature + $tmp = $headers->getAll('DKIM-Signature'); + $this->_dkimHeader = end($tmp); + $this->_addHeader(trim($this->_dkimHeader->toString())."\r\n b=", true); + $this->_endOfHeaders(); + if ($this->_debugHeaders) { + $headers->addTextHeader('X-DebugHash', base64_encode($this->_headerHash)); + } + $this->_dkimHeader->setValue($string.' b='.trim(chunk_split(base64_encode($this->_getEncryptedHash()), 73, ' '))); + + return $this; + } + + /* Private helpers */ + + protected function _addHeader($header, $is_sig = false) + { + switch ($this->_headerCanon) { + case 'relaxed': + // Prepare Header and cascade + $exploded = explode(':', $header, 2); + $name = strtolower(trim($exploded[0])); + $value = str_replace("\r\n", '', $exploded[1]); + $value = preg_replace("/[ \t][ \t]+/", ' ', $value); + $header = $name.':'.trim($value).($is_sig ? '' : "\r\n"); + case 'simple': + // Nothing to do + } + $this->_addToHeaderHash($header); + } + + /** + * @deprecated This method is currently useless in this class but it must be + * kept for BC reasons due to its "protected" scope. This method + * might be overriden by custom client code. + */ + protected function _endOfHeaders() + { + } + + protected function _canonicalizeBody($string) + { + $len = strlen($string); + $canon = ''; + $method = ($this->_bodyCanon == 'relaxed'); + for ($i = 0; $i < $len; ++$i) { + if ($this->_bodyCanonIgnoreStart > 0) { + --$this->_bodyCanonIgnoreStart; + continue; + } + switch ($string[$i]) { + case "\r": + $this->_bodyCanonLastChar = "\r"; + break; + case "\n": + if ($this->_bodyCanonLastChar == "\r") { + if ($method) { + $this->_bodyCanonSpace = false; + } + if ($this->_bodyCanonLine == '') { + ++$this->_bodyCanonEmptyCounter; + } else { + $this->_bodyCanonLine = ''; + $canon .= "\r\n"; + } + } else { + // Wooops Error + // todo handle it but should never happen + } + break; + case ' ': + case "\t": + if ($method) { + $this->_bodyCanonSpace = true; + break; + } + default: + if ($this->_bodyCanonEmptyCounter > 0) { + $canon .= str_repeat("\r\n", $this->_bodyCanonEmptyCounter); + $this->_bodyCanonEmptyCounter = 0; + } + if ($this->_bodyCanonSpace) { + $this->_bodyCanonLine .= ' '; + $canon .= ' '; + $this->_bodyCanonSpace = false; + } + $this->_bodyCanonLine .= $string[$i]; + $canon .= $string[$i]; + } + } + $this->_addToBodyHash($canon); + } + + protected function _endOfBody() + { + // Add trailing Line return if last line is non empty + if (strlen($this->_bodyCanonLine) > 0) { + $this->_addToBodyHash("\r\n"); + } + $this->_bodyHash = hash_final($this->_bodyHashHandler, true); + } + + private function _addToBodyHash($string) + { + $len = strlen($string); + if ($len > ($new_len = ($this->_maxLen - $this->_bodyLen))) { + $string = substr($string, 0, $new_len); + $len = $new_len; + } + hash_update($this->_bodyHashHandler, $string); + $this->_bodyLen += $len; + } + + private function _addToHeaderHash($header) + { + if ($this->_debugHeaders) { + $this->_debugHeadersData[] = trim($header); + } + $this->_headerCanonData .= $header; + } + + /** + * @throws Swift_SwiftException + * + * @return string + */ + private function _getEncryptedHash() + { + $signature = ''; + switch ($this->_hashAlgorithm) { + case 'rsa-sha1': + $algorithm = OPENSSL_ALGO_SHA1; + break; + case 'rsa-sha256': + $algorithm = OPENSSL_ALGO_SHA256; + break; + } + $pkeyId = openssl_get_privatekey($this->_privateKey); + if (!$pkeyId) { + throw new Swift_SwiftException('Unable to load DKIM Private Key ['.openssl_error_string().']'); + } + if (openssl_sign($this->_headerCanonData, $signature, $pkeyId, $algorithm)) { + return $signature; + } + throw new Swift_SwiftException('Unable to sign DKIM Hash ['.openssl_error_string().']'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php new file mode 100644 index 00000000..786cee7b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php @@ -0,0 +1,525 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * DomainKey Signer used to apply DomainKeys Signature to a message. + * + * @author Xavier De Cock <xdecock@gmail.com> + */ +class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner +{ + /** + * PrivateKey. + * + * @var string + */ + protected $_privateKey; + + /** + * DomainName. + * + * @var string + */ + protected $_domainName; + + /** + * Selector. + * + * @var string + */ + protected $_selector; + + /** + * Hash algorithm used. + * + * @var string + */ + protected $_hashAlgorithm = 'rsa-sha1'; + + /** + * Canonisation method. + * + * @var string + */ + protected $_canon = 'simple'; + + /** + * Headers not being signed. + * + * @var array + */ + protected $_ignoredHeaders = array(); + + /** + * Signer identity. + * + * @var string + */ + protected $_signerIdentity; + + /** + * Must we embed signed headers? + * + * @var bool + */ + protected $_debugHeaders = false; + + // work variables + /** + * Headers used to generate hash. + * + * @var array + */ + private $_signedHeaders = array(); + + /** + * Stores the signature header. + * + * @var Swift_Mime_Headers_ParameterizedHeader + */ + protected $_domainKeyHeader; + + /** + * Hash Handler. + * + * @var resource|null + */ + private $_hashHandler; + + private $_hash; + + private $_canonData = ''; + + private $_bodyCanonEmptyCounter = 0; + + private $_bodyCanonIgnoreStart = 2; + + private $_bodyCanonSpace = false; + + private $_bodyCanonLastChar = null; + + private $_bodyCanonLine = ''; + + private $_bound = array(); + + /** + * Constructor. + * + * @param string $privateKey + * @param string $domainName + * @param string $selector + */ + public function __construct($privateKey, $domainName, $selector) + { + $this->_privateKey = $privateKey; + $this->_domainName = $domainName; + $this->_signerIdentity = '@'.$domainName; + $this->_selector = $selector; + } + + /** + * Instanciate DomainKeySigner. + * + * @param string $privateKey + * @param string $domainName + * @param string $selector + * + * @return Swift_Signers_DomainKeySigner + */ + public static function newInstance($privateKey, $domainName, $selector) + { + return new static($privateKey, $domainName, $selector); + } + + /** + * Resets internal states. + * + * @return Swift_Signers_DomainKeySigner + */ + public function reset() + { + $this->_hash = null; + $this->_hashHandler = null; + $this->_bodyCanonIgnoreStart = 2; + $this->_bodyCanonEmptyCounter = 0; + $this->_bodyCanonLastChar = null; + $this->_bodyCanonSpace = false; + + return $this; + } + + /** + * Writes $bytes to the end of the stream. + * + * Writing may not happen immediately if the stream chooses to buffer. If + * you want to write these bytes with immediate effect, call {@link commit()} + * after calling write(). + * + * This method returns the sequence ID of the write (i.e. 1 for first, 2 for + * second, etc etc). + * + * @param string $bytes + * + * @throws Swift_IoException + * + * @return int + * @return Swift_Signers_DomainKeySigner + */ + public function write($bytes) + { + $this->_canonicalizeBody($bytes); + foreach ($this->_bound as $is) { + $is->write($bytes); + } + + return $this; + } + + /** + * For any bytes that are currently buffered inside the stream, force them + * off the buffer. + * + * @throws Swift_IoException + * + * @return Swift_Signers_DomainKeySigner + */ + public function commit() + { + // Nothing to do + return $this; + } + + /** + * Attach $is to this stream. + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + * + * @return Swift_Signers_DomainKeySigner + */ + public function bind(Swift_InputByteStream $is) + { + // Don't have to mirror anything + $this->_bound[] = $is; + + return $this; + } + + /** + * Remove an already bound stream. + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + * + * @return Swift_Signers_DomainKeySigner + */ + public function unbind(Swift_InputByteStream $is) + { + // Don't have to mirror anything + foreach ($this->_bound as $k => $stream) { + if ($stream === $is) { + unset($this->_bound[$k]); + + return; + } + } + + return $this; + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + * + * @throws Swift_IoException + * + * @return Swift_Signers_DomainKeySigner + */ + public function flushBuffers() + { + $this->reset(); + + return $this; + } + + /** + * Set hash_algorithm, must be one of rsa-sha256 | rsa-sha1 defaults to rsa-sha256. + * + * @param string $hash + * + * @return Swift_Signers_DomainKeySigner + */ + public function setHashAlgorithm($hash) + { + $this->_hashAlgorithm = 'rsa-sha1'; + + return $this; + } + + /** + * Set the canonicalization algorithm. + * + * @param string $canon simple | nofws defaults to simple + * + * @return Swift_Signers_DomainKeySigner + */ + public function setCanon($canon) + { + if ($canon == 'nofws') { + $this->_canon = 'nofws'; + } else { + $this->_canon = 'simple'; + } + + return $this; + } + + /** + * Set the signer identity. + * + * @param string $identity + * + * @return Swift_Signers_DomainKeySigner + */ + public function setSignerIdentity($identity) + { + $this->_signerIdentity = $identity; + + return $this; + } + + /** + * Enable / disable the DebugHeaders. + * + * @param bool $debug + * + * @return Swift_Signers_DomainKeySigner + */ + public function setDebugHeaders($debug) + { + $this->_debugHeaders = (bool) $debug; + + return $this; + } + + /** + * Start Body. + */ + public function startBody() + { + } + + /** + * End Body. + */ + public function endBody() + { + $this->_endOfBody(); + } + + /** + * Returns the list of Headers Tampered by this plugin. + * + * @return array + */ + public function getAlteredHeaders() + { + if ($this->_debugHeaders) { + return array('DomainKey-Signature', 'X-DebugHash'); + } + + return array('DomainKey-Signature'); + } + + /** + * Adds an ignored Header. + * + * @param string $header_name + * + * @return Swift_Signers_DomainKeySigner + */ + public function ignoreHeader($header_name) + { + $this->_ignoredHeaders[strtolower($header_name)] = true; + + return $this; + } + + /** + * Set the headers to sign. + * + * @param Swift_Mime_HeaderSet $headers + * + * @return Swift_Signers_DomainKeySigner + */ + public function setHeaders(Swift_Mime_HeaderSet $headers) + { + $this->_startHash(); + $this->_canonData = ''; + // Loop through Headers + $listHeaders = $headers->listAll(); + foreach ($listHeaders as $hName) { + // Check if we need to ignore Header + if (!isset($this->_ignoredHeaders[strtolower($hName)])) { + if ($headers->has($hName)) { + $tmp = $headers->getAll($hName); + foreach ($tmp as $header) { + if ($header->getFieldBody() != '') { + $this->_addHeader($header->toString()); + $this->_signedHeaders[] = $header->getFieldName(); + } + } + } + } + } + $this->_endOfHeaders(); + + return $this; + } + + /** + * Add the signature to the given Headers. + * + * @param Swift_Mime_HeaderSet $headers + * + * @return Swift_Signers_DomainKeySigner + */ + public function addSignature(Swift_Mime_HeaderSet $headers) + { + // Prepare the DomainKey-Signature Header + $params = array('a' => $this->_hashAlgorithm, 'b' => chunk_split(base64_encode($this->_getEncryptedHash()), 73, ' '), 'c' => $this->_canon, 'd' => $this->_domainName, 'h' => implode(': ', $this->_signedHeaders), 'q' => 'dns', 's' => $this->_selector); + $string = ''; + foreach ($params as $k => $v) { + $string .= $k.'='.$v.'; '; + } + $string = trim($string); + $headers->addTextHeader('DomainKey-Signature', $string); + + return $this; + } + + /* Private helpers */ + + protected function _addHeader($header) + { + switch ($this->_canon) { + case 'nofws': + // Prepare Header and cascade + $exploded = explode(':', $header, 2); + $name = strtolower(trim($exploded[0])); + $value = str_replace("\r\n", '', $exploded[1]); + $value = preg_replace("/[ \t][ \t]+/", ' ', $value); + $header = $name.':'.trim($value)."\r\n"; + case 'simple': + // Nothing to do + } + $this->_addToHash($header); + } + + protected function _endOfHeaders() + { + $this->_bodyCanonEmptyCounter = 1; + } + + protected function _canonicalizeBody($string) + { + $len = strlen($string); + $canon = ''; + $nofws = ($this->_canon == 'nofws'); + for ($i = 0; $i < $len; ++$i) { + if ($this->_bodyCanonIgnoreStart > 0) { + --$this->_bodyCanonIgnoreStart; + continue; + } + switch ($string[$i]) { + case "\r": + $this->_bodyCanonLastChar = "\r"; + break; + case "\n": + if ($this->_bodyCanonLastChar == "\r") { + if ($nofws) { + $this->_bodyCanonSpace = false; + } + if ($this->_bodyCanonLine == '') { + ++$this->_bodyCanonEmptyCounter; + } else { + $this->_bodyCanonLine = ''; + $canon .= "\r\n"; + } + } else { + // Wooops Error + throw new Swift_SwiftException('Invalid new line sequence in mail found \n without preceding \r'); + } + break; + case ' ': + case "\t": + case "\x09": //HTAB + if ($nofws) { + $this->_bodyCanonSpace = true; + break; + } + default: + if ($this->_bodyCanonEmptyCounter > 0) { + $canon .= str_repeat("\r\n", $this->_bodyCanonEmptyCounter); + $this->_bodyCanonEmptyCounter = 0; + } + $this->_bodyCanonLine .= $string[$i]; + $canon .= $string[$i]; + } + } + $this->_addToHash($canon); + } + + protected function _endOfBody() + { + if (strlen($this->_bodyCanonLine) > 0) { + $this->_addToHash("\r\n"); + } + $this->_hash = hash_final($this->_hashHandler, true); + } + + private function _addToHash($string) + { + $this->_canonData .= $string; + hash_update($this->_hashHandler, $string); + } + + private function _startHash() + { + // Init + switch ($this->_hashAlgorithm) { + case 'rsa-sha1': + $this->_hashHandler = hash_init('sha1'); + break; + } + $this->_bodyCanonLine = ''; + } + + /** + * @throws Swift_SwiftException + * + * @return string + */ + private function _getEncryptedHash() + { + $signature = ''; + $pkeyId = openssl_get_privatekey($this->_privateKey); + if (!$pkeyId) { + throw new Swift_SwiftException('Unable to load DomainKey Private Key ['.openssl_error_string().']'); + } + if (openssl_sign($this->_canonData, $signature, $pkeyId, OPENSSL_ALGO_SHA1)) { + return $signature; + } + throw new Swift_SwiftException('Unable to sign DomainKey Hash ['.openssl_error_string().']'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php new file mode 100644 index 00000000..c75cb08a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php @@ -0,0 +1,65 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Header Signer Interface used to apply Header-Based Signature to a message. + * + * @author Xavier De Cock <xdecock@gmail.com> + */ +interface Swift_Signers_HeaderSigner extends Swift_Signer, Swift_InputByteStream +{ + /** + * Exclude an header from the signed headers. + * + * @param string $header_name + * + * @return Swift_Signers_HeaderSigner + */ + public function ignoreHeader($header_name); + + /** + * Prepare the Signer to get a new Body. + * + * @return Swift_Signers_HeaderSigner + */ + public function startBody(); + + /** + * Give the signal that the body has finished streaming. + * + * @return Swift_Signers_HeaderSigner + */ + public function endBody(); + + /** + * Give the headers already given. + * + * @param Swift_Mime_SimpleHeaderSet $headers + * + * @return Swift_Signers_HeaderSigner + */ + public function setHeaders(Swift_Mime_HeaderSet $headers); + + /** + * Add the header(s) to the headerSet. + * + * @param Swift_Mime_HeaderSet $headers + * + * @return Swift_Signers_HeaderSigner + */ + public function addSignature(Swift_Mime_HeaderSet $headers); + + /** + * Return the list of header a signer might tamper. + * + * @return array + */ + public function getAlteredHeaders(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/OpenDKIMSigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/OpenDKIMSigner.php new file mode 100644 index 00000000..3a35ad55 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/OpenDKIMSigner.php @@ -0,0 +1,190 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * DKIM Signer used to apply DKIM Signature to a message + * Takes advantage of pecl extension. + * + * @author Xavier De Cock <xdecock@gmail.com> + */ +class Swift_Signers_OpenDKIMSigner extends Swift_Signers_DKIMSigner +{ + private $_peclLoaded = false; + + private $_dkimHandler = null; + + private $dropFirstLF = true; + + const CANON_RELAXED = 1; + const CANON_SIMPLE = 2; + const SIG_RSA_SHA1 = 3; + const SIG_RSA_SHA256 = 4; + + public function __construct($privateKey, $domainName, $selector) + { + if (!extension_loaded('opendkim')) { + throw new Swift_SwiftException('php-opendkim extension not found'); + } + + $this->_peclLoaded = true; + + parent::__construct($privateKey, $domainName, $selector); + } + + public static function newInstance($privateKey, $domainName, $selector) + { + return new static($privateKey, $domainName, $selector); + } + + public function addSignature(Swift_Mime_HeaderSet $headers) + { + $header = new Swift_Mime_Headers_OpenDKIMHeader('DKIM-Signature'); + $headerVal = $this->_dkimHandler->getSignatureHeader(); + if (!$headerVal) { + throw new Swift_SwiftException('OpenDKIM Error: '.$this->_dkimHandler->getError()); + } + $header->setValue($headerVal); + $headers->set($header); + + return $this; + } + + public function setHeaders(Swift_Mime_HeaderSet $headers) + { + $bodyLen = $this->_bodyLen; + if (is_bool($bodyLen)) { + $bodyLen = -1; + } + $hash = $this->_hashAlgorithm == 'rsa-sha1' ? OpenDKIMSign::ALG_RSASHA1 : OpenDKIMSign::ALG_RSASHA256; + $bodyCanon = $this->_bodyCanon == 'simple' ? OpenDKIMSign::CANON_SIMPLE : OpenDKIMSign::CANON_RELAXED; + $headerCanon = $this->_headerCanon == 'simple' ? OpenDKIMSign::CANON_SIMPLE : OpenDKIMSign::CANON_RELAXED; + $this->_dkimHandler = new OpenDKIMSign($this->_privateKey, $this->_selector, $this->_domainName, $headerCanon, $bodyCanon, $hash, $bodyLen); + // Hardcode signature Margin for now + $this->_dkimHandler->setMargin(78); + + if (!is_numeric($this->_signatureTimestamp)) { + OpenDKIM::setOption(OpenDKIM::OPTS_FIXEDTIME, time()); + } else { + if (!OpenDKIM::setOption(OpenDKIM::OPTS_FIXEDTIME, $this->_signatureTimestamp)) { + throw new Swift_SwiftException('Unable to force signature timestamp ['.openssl_error_string().']'); + } + } + if (isset($this->_signerIdentity)) { + $this->_dkimHandler->setSigner($this->_signerIdentity); + } + $listHeaders = $headers->listAll(); + foreach ($listHeaders as $hName) { + // Check if we need to ignore Header + if (!isset($this->_ignoredHeaders[strtolower($hName)])) { + $tmp = $headers->getAll($hName); + if ($headers->has($hName)) { + foreach ($tmp as $header) { + if ($header->getFieldBody() != '') { + $htosign = $header->toString(); + $this->_dkimHandler->header($htosign); + $this->_signedHeaders[] = $header->getFieldName(); + } + } + } + } + } + + return $this; + } + + public function startBody() + { + if (!$this->_peclLoaded) { + return parent::startBody(); + } + $this->dropFirstLF = true; + $this->_dkimHandler->eoh(); + + return $this; + } + + public function endBody() + { + if (!$this->_peclLoaded) { + return parent::endBody(); + } + $this->_dkimHandler->eom(); + + return $this; + } + + public function reset() + { + $this->_dkimHandler = null; + parent::reset(); + + return $this; + } + + /** + * Set the signature timestamp. + * + * @param int $time + * + * @return Swift_Signers_DKIMSigner + */ + public function setSignatureTimestamp($time) + { + $this->_signatureTimestamp = $time; + + return $this; + } + + /** + * Set the signature expiration timestamp. + * + * @param int $time + * + * @return Swift_Signers_DKIMSigner + */ + public function setSignatureExpiration($time) + { + $this->_signatureExpiration = $time; + + return $this; + } + + /** + * Enable / disable the DebugHeaders. + * + * @param bool $debug + * + * @return Swift_Signers_DKIMSigner + */ + public function setDebugHeaders($debug) + { + $this->_debugHeaders = (bool) $debug; + + return $this; + } + + // Protected + + protected function _canonicalizeBody($string) + { + if (!$this->_peclLoaded) { + return parent::_canonicalizeBody($string); + } + if (false && $this->dropFirstLF === true) { + if ($string[0] == "\r" && $string[1] == "\n") { + $string = substr($string, 2); + } + } + $this->dropFirstLF = false; + if (strlen($string)) { + $this->_dkimHandler->body($string); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php new file mode 100644 index 00000000..b267099a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php @@ -0,0 +1,436 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * MIME Message Signer used to apply S/MIME Signature/Encryption to a message. + * + * + * @author Romain-Geissler + * @author Sebastiaan Stok <s.stok@rollerscapes.net> + */ +class Swift_Signers_SMimeSigner implements Swift_Signers_BodySigner +{ + protected $signCertificate; + protected $signPrivateKey; + protected $encryptCert; + protected $signThenEncrypt = true; + protected $signLevel; + protected $encryptLevel; + protected $signOptions; + protected $encryptOptions; + protected $encryptCipher; + protected $extraCerts = null; + + /** + * @var Swift_StreamFilters_StringReplacementFilterFactory + */ + protected $replacementFactory; + + /** + * @var Swift_Mime_HeaderFactory + */ + protected $headerFactory; + + /** + * Constructor. + * + * @param string|null $signCertificate + * @param string|null $signPrivateKey + * @param string|null $encryptCertificate + */ + public function __construct($signCertificate = null, $signPrivateKey = null, $encryptCertificate = null) + { + if (null !== $signPrivateKey) { + $this->setSignCertificate($signCertificate, $signPrivateKey); + } + + if (null !== $encryptCertificate) { + $this->setEncryptCertificate($encryptCertificate); + } + + $this->replacementFactory = Swift_DependencyContainer::getInstance() + ->lookup('transport.replacementfactory'); + + $this->signOptions = PKCS7_DETACHED; + + // Supported since php5.4 + if (defined('OPENSSL_CIPHER_AES_128_CBC')) { + $this->encryptCipher = OPENSSL_CIPHER_AES_128_CBC; + } else { + $this->encryptCipher = OPENSSL_CIPHER_RC2_128; + } + } + + /** + * Returns an new Swift_Signers_SMimeSigner instance. + * + * @param string $certificate + * @param string $privateKey + * + * @return Swift_Signers_SMimeSigner + */ + public static function newInstance($certificate = null, $privateKey = null) + { + return new self($certificate, $privateKey); + } + + /** + * Set the certificate location to use for signing. + * + * @link http://www.php.net/manual/en/openssl.pkcs7.flags.php + * + * @param string $certificate + * @param string|array $privateKey If the key needs an passphrase use array('file-location', 'passphrase') instead + * @param int $signOptions Bitwise operator options for openssl_pkcs7_sign() + * @param string $extraCerts A file containing intermediate certificates needed by the signing certificate + * + * @return Swift_Signers_SMimeSigner + */ + public function setSignCertificate($certificate, $privateKey = null, $signOptions = PKCS7_DETACHED, $extraCerts = null) + { + $this->signCertificate = 'file://'.str_replace('\\', '/', realpath($certificate)); + + if (null !== $privateKey) { + if (is_array($privateKey)) { + $this->signPrivateKey = $privateKey; + $this->signPrivateKey[0] = 'file://'.str_replace('\\', '/', realpath($privateKey[0])); + } else { + $this->signPrivateKey = 'file://'.str_replace('\\', '/', realpath($privateKey)); + } + } + + $this->signOptions = $signOptions; + if (null !== $extraCerts) { + $this->extraCerts = str_replace('\\', '/', realpath($extraCerts)); + } + + return $this; + } + + /** + * Set the certificate location to use for encryption. + * + * @link http://www.php.net/manual/en/openssl.pkcs7.flags.php + * @link http://nl3.php.net/manual/en/openssl.ciphers.php + * + * @param string|array $recipientCerts Either an single X.509 certificate, or an assoc array of X.509 certificates. + * @param int $cipher + * + * @return Swift_Signers_SMimeSigner + */ + public function setEncryptCertificate($recipientCerts, $cipher = null) + { + if (is_array($recipientCerts)) { + $this->encryptCert = array(); + + foreach ($recipientCerts as $cert) { + $this->encryptCert[] = 'file://'.str_replace('\\', '/', realpath($cert)); + } + } else { + $this->encryptCert = 'file://'.str_replace('\\', '/', realpath($recipientCerts)); + } + + if (null !== $cipher) { + $this->encryptCipher = $cipher; + } + + return $this; + } + + /** + * @return string + */ + public function getSignCertificate() + { + return $this->signCertificate; + } + + /** + * @return string + */ + public function getSignPrivateKey() + { + return $this->signPrivateKey; + } + + /** + * Set perform signing before encryption. + * + * The default is to first sign the message and then encrypt. + * But some older mail clients, namely Microsoft Outlook 2000 will work when the message first encrypted. + * As this goes against the official specs, its recommended to only use 'encryption -> signing' when specifically targeting these 'broken' clients. + * + * @param bool $signThenEncrypt + * + * @return Swift_Signers_SMimeSigner + */ + public function setSignThenEncrypt($signThenEncrypt = true) + { + $this->signThenEncrypt = $signThenEncrypt; + + return $this; + } + + /** + * @return bool + */ + public function isSignThenEncrypt() + { + return $this->signThenEncrypt; + } + + /** + * Resets internal states. + * + * @return Swift_Signers_SMimeSigner + */ + public function reset() + { + return $this; + } + + /** + * Change the Swift_Message to apply the signing. + * + * @param Swift_Message $message + * + * @return Swift_Signers_SMimeSigner + */ + public function signMessage(Swift_Message $message) + { + if (null === $this->signCertificate && null === $this->encryptCert) { + return $this; + } + + // Store the message using ByteStream to a file{1} + // Remove all Children + // Sign file{1}, parse the new MIME headers and set them on the primary MimeEntity + // Set the singed-body as the new body (without boundary) + + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + $this->toSMimeByteStream($messageStream, $message); + $message->setEncoder(Swift_DependencyContainer::getInstance()->lookup('mime.rawcontentencoder')); + + $message->setChildren(array()); + $this->streamToMime($messageStream, $message); + } + + /** + * Return the list of header a signer might tamper. + * + * @return array + */ + public function getAlteredHeaders() + { + return array('Content-Type', 'Content-Transfer-Encoding', 'Content-Disposition'); + } + + /** + * @param Swift_InputByteStream $inputStream + * @param Swift_Message $mimeEntity + */ + protected function toSMimeByteStream(Swift_InputByteStream $inputStream, Swift_Message $message) + { + $mimeEntity = $this->createMessage($message); + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + $mimeEntity->toByteStream($messageStream); + $messageStream->commit(); + + if (null !== $this->signCertificate && null !== $this->encryptCert) { + $temporaryStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if ($this->signThenEncrypt) { + $this->messageStreamToSignedByteStream($messageStream, $temporaryStream); + $this->messageStreamToEncryptedByteStream($temporaryStream, $inputStream); + } else { + $this->messageStreamToEncryptedByteStream($messageStream, $temporaryStream); + $this->messageStreamToSignedByteStream($temporaryStream, $inputStream); + } + } elseif ($this->signCertificate !== null) { + $this->messageStreamToSignedByteStream($messageStream, $inputStream); + } else { + $this->messageStreamToEncryptedByteStream($messageStream, $inputStream); + } + } + + /** + * @param Swift_Message $message + * + * @return Swift_Message + */ + protected function createMessage(Swift_Message $message) + { + $mimeEntity = new Swift_Message('', $message->getBody(), $message->getContentType(), $message->getCharset()); + $mimeEntity->setChildren($message->getChildren()); + + $messageHeaders = $mimeEntity->getHeaders(); + $messageHeaders->remove('Message-ID'); + $messageHeaders->remove('Date'); + $messageHeaders->remove('Subject'); + $messageHeaders->remove('MIME-Version'); + $messageHeaders->remove('To'); + $messageHeaders->remove('From'); + + return $mimeEntity; + } + + /** + * @param Swift_FileStream $outputStream + * @param Swift_InputByteStream $inputStream + * + * @throws Swift_IoException + */ + protected function messageStreamToSignedByteStream(Swift_FileStream $outputStream, Swift_InputByteStream $inputStream) + { + $signedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + $args = array($outputStream->getPath(), $signedMessageStream->getPath(), $this->signCertificate, $this->signPrivateKey, array(), $this->signOptions); + if (null !== $this->extraCerts) { + $args[] = $this->extraCerts; + } + + if (!call_user_func_array('openssl_pkcs7_sign', $args)) { + throw new Swift_IoException(sprintf('Failed to sign S/Mime message. Error: "%s".', openssl_error_string())); + } + + $this->copyFromOpenSSLOutput($signedMessageStream, $inputStream); + } + + /** + * @param Swift_FileStream $outputStream + * @param Swift_InputByteStream $is + * + * @throws Swift_IoException + */ + protected function messageStreamToEncryptedByteStream(Swift_FileStream $outputStream, Swift_InputByteStream $is) + { + $encryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if (!openssl_pkcs7_encrypt($outputStream->getPath(), $encryptedMessageStream->getPath(), $this->encryptCert, array(), 0, $this->encryptCipher)) { + throw new Swift_IoException(sprintf('Failed to encrypt S/Mime message. Error: "%s".', openssl_error_string())); + } + + $this->copyFromOpenSSLOutput($encryptedMessageStream, $is); + } + + /** + * @param Swift_OutputByteStream $fromStream + * @param Swift_InputByteStream $toStream + */ + protected function copyFromOpenSSLOutput(Swift_OutputByteStream $fromStream, Swift_InputByteStream $toStream) + { + $bufferLength = 4096; + $filteredStream = new Swift_ByteStream_TemporaryFileByteStream(); + $filteredStream->addFilter($this->replacementFactory->createFilter("\r\n", "\n"), 'CRLF to LF'); + $filteredStream->addFilter($this->replacementFactory->createFilter("\n", "\r\n"), 'LF to CRLF'); + + while (false !== ($buffer = $fromStream->read($bufferLength))) { + $filteredStream->write($buffer); + } + + $filteredStream->flushBuffers(); + + while (false !== ($buffer = $filteredStream->read($bufferLength))) { + $toStream->write($buffer); + } + + $toStream->commit(); + } + + /** + * Merges an OutputByteStream to Swift_Message. + * + * @param Swift_OutputByteStream $fromStream + * @param Swift_Message $message + */ + protected function streamToMime(Swift_OutputByteStream $fromStream, Swift_Message $message) + { + $bufferLength = 78; + $headerData = ''; + + $fromStream->setReadPointer(0); + + while (($buffer = $fromStream->read($bufferLength)) !== false) { + $headerData .= $buffer; + + if (false !== strpos($buffer, "\r\n\r\n")) { + break; + } + } + + $headersPosEnd = strpos($headerData, "\r\n\r\n"); + $headerData = trim($headerData); + $headerData = substr($headerData, 0, $headersPosEnd); + $headerLines = explode("\r\n", $headerData); + unset($headerData); + + $headers = array(); + $currentHeaderName = ''; + + foreach ($headerLines as $headerLine) { + // Line separated + if (ctype_space($headerLines[0]) || false === strpos($headerLine, ':')) { + $headers[$currentHeaderName] .= ' '.trim($headerLine); + continue; + } + + $header = explode(':', $headerLine, 2); + $currentHeaderName = strtolower($header[0]); + $headers[$currentHeaderName] = trim($header[1]); + } + + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + $messageStream->addFilter($this->replacementFactory->createFilter("\r\n", "\n"), 'CRLF to LF'); + $messageStream->addFilter($this->replacementFactory->createFilter("\n", "\r\n"), 'LF to CRLF'); + + $messageHeaders = $message->getHeaders(); + + // No need to check for 'application/pkcs7-mime', as this is always base64 + if ('multipart/signed;' === substr($headers['content-type'], 0, 17)) { + if (!preg_match('/boundary=("[^"]+"|(?:[^\s]+|$))/is', $headers['content-type'], $contentTypeData)) { + throw new Swift_SwiftException('Failed to find Boundary parameter'); + } + + $boundary = trim($contentTypeData['1'], '"'); + + // Skip the header and CRLF CRLF + $fromStream->setReadPointer($headersPosEnd + 4); + + while (false !== ($buffer = $fromStream->read($bufferLength))) { + $messageStream->write($buffer); + } + + $messageStream->commit(); + + $messageHeaders->remove('Content-Transfer-Encoding'); + $message->setContentType($headers['content-type']); + $message->setBoundary($boundary); + $message->setBody($messageStream); + } else { + $fromStream->setReadPointer($headersPosEnd + 4); + + if (null === $this->headerFactory) { + $this->headerFactory = Swift_DependencyContainer::getInstance()->lookup('mime.headerfactory'); + } + + $message->setContentType($headers['content-type']); + $messageHeaders->set($this->headerFactory->createTextHeader('Content-Transfer-Encoding', $headers['content-transfer-encoding'])); + $messageHeaders->set($this->headerFactory->createTextHeader('Content-Disposition', $headers['content-disposition'])); + + while (false !== ($buffer = $fromStream->read($bufferLength))) { + $messageStream->write($buffer); + } + + $messageStream->commit(); + $message->setBody($messageStream); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SmtpTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SmtpTransport.php new file mode 100644 index 00000000..62516114 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SmtpTransport.php @@ -0,0 +1,58 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Sends Messages over SMTP with ESMTP support. + * + * @author Chris Corbyn + * + * @method Swift_SmtpTransport setUsername(string $username) Set the username to authenticate with. + * @method string getUsername() Get the username to authenticate with. + * @method Swift_SmtpTransport setPassword(string $password) Set the password to authenticate with. + * @method string getPassword() Get the password to authenticate with. + * @method Swift_SmtpTransport setAuthMode(string $mode) Set the auth mode to use to authenticate. + * @method string getAuthMode() Get the auth mode to use to authenticate. + */ +class Swift_SmtpTransport extends Swift_Transport_EsmtpTransport +{ + /** + * Create a new SmtpTransport, optionally with $host, $port and $security. + * + * @param string $host + * @param int $port + * @param string $security + */ + public function __construct($host = 'localhost', $port = 25, $security = null) + { + call_user_func_array( + array($this, 'Swift_Transport_EsmtpTransport::__construct'), + Swift_DependencyContainer::getInstance() + ->createDependenciesFor('transport.smtp') + ); + + $this->setHost($host); + $this->setPort($port); + $this->setEncryption($security); + } + + /** + * Create a new SmtpTransport instance. + * + * @param string $host + * @param int $port + * @param string $security + * + * @return Swift_SmtpTransport + */ + public static function newInstance($host = 'localhost', $port = 25, $security = null) + { + return new self($host, $port, $security); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Spool.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Spool.php new file mode 100644 index 00000000..c16ab4b3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Spool.php @@ -0,0 +1,53 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2009 Fabien Potencier <fabien.potencier@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Interface for spools. + * + * @author Fabien Potencier + */ +interface Swift_Spool +{ + /** + * Starts this Spool mechanism. + */ + public function start(); + + /** + * Stops this Spool mechanism. + */ + public function stop(); + + /** + * Tests if this Spool mechanism has started. + * + * @return bool + */ + public function isStarted(); + + /** + * Queues a message. + * + * @param Swift_Mime_Message $message The message to store + * + * @return bool Whether the operation has succeeded + */ + public function queueMessage(Swift_Mime_Message $message); + + /** + * Sends messages using the given transport instance. + * + * @param Swift_Transport $transport A transport instance + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int The number of sent emails + */ + public function flushQueue(Swift_Transport $transport, &$failedRecipients = null); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SpoolTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SpoolTransport.php new file mode 100644 index 00000000..cf9bf78f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SpoolTransport.php @@ -0,0 +1,47 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2009 Fabien Potencier <fabien.potencier@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages in a queue. + * + * @author Fabien Potencier + */ +class Swift_SpoolTransport extends Swift_Transport_SpoolTransport +{ + /** + * Create a new SpoolTransport. + * + * @param Swift_Spool $spool + */ + public function __construct(Swift_Spool $spool) + { + $arguments = Swift_DependencyContainer::getInstance() + ->createDependenciesFor('transport.spool'); + + $arguments[] = $spool; + + call_user_func_array( + array($this, 'Swift_Transport_SpoolTransport::__construct'), + $arguments + ); + } + + /** + * Create a new SpoolTransport instance. + * + * @param Swift_Spool $spool + * + * @return Swift_SpoolTransport + */ + public static function newInstance(Swift_Spool $spool) + { + return new self($spool); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilter.php new file mode 100644 index 00000000..362be2e8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilter.php @@ -0,0 +1,35 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Processes bytes as they pass through a stream and performs filtering. + * + * @author Chris Corbyn + */ +interface Swift_StreamFilter +{ + /** + * Based on the buffer given, this returns true if more buffering is needed. + * + * @param mixed $buffer + * + * @return bool + */ + public function shouldBuffer($buffer); + + /** + * Filters $buffer and returns the changes. + * + * @param mixed $buffer + * + * @return mixed + */ + public function filter($buffer); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/ByteArrayReplacementFilter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/ByteArrayReplacementFilter.php new file mode 100644 index 00000000..0418750f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/ByteArrayReplacementFilter.php @@ -0,0 +1,170 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Processes bytes as they pass through a buffer and replaces sequences in it. + * + * This stream filter deals with Byte arrays rather than simple strings. + * + * @author Chris Corbyn + */ +class Swift_StreamFilters_ByteArrayReplacementFilter implements Swift_StreamFilter +{ + /** The needle(s) to search for */ + private $_search; + + /** The replacement(s) to make */ + private $_replace; + + /** The Index for searching */ + private $_index; + + /** The Search Tree */ + private $_tree = array(); + + /** Gives the size of the largest search */ + private $_treeMaxLen = 0; + + private $_repSize; + + /** + * Create a new ByteArrayReplacementFilter with $search and $replace. + * + * @param array $search + * @param array $replace + */ + public function __construct($search, $replace) + { + $this->_search = $search; + $this->_index = array(); + $this->_tree = array(); + $this->_replace = array(); + $this->_repSize = array(); + + $tree = null; + $i = null; + $last_size = $size = 0; + foreach ($search as $i => $search_element) { + if ($tree !== null) { + $tree[-1] = min(count($replace) - 1, $i - 1); + $tree[-2] = $last_size; + } + $tree = &$this->_tree; + if (is_array($search_element)) { + foreach ($search_element as $k => $char) { + $this->_index[$char] = true; + if (!isset($tree[$char])) { + $tree[$char] = array(); + } + $tree = &$tree[$char]; + } + $last_size = $k + 1; + $size = max($size, $last_size); + } else { + $last_size = 1; + if (!isset($tree[$search_element])) { + $tree[$search_element] = array(); + } + $tree = &$tree[$search_element]; + $size = max($last_size, $size); + $this->_index[$search_element] = true; + } + } + if ($i !== null) { + $tree[-1] = min(count($replace) - 1, $i); + $tree[-2] = $last_size; + $this->_treeMaxLen = $size; + } + foreach ($replace as $rep) { + if (!is_array($rep)) { + $rep = array($rep); + } + $this->_replace[] = $rep; + } + for ($i = count($this->_replace) - 1; $i >= 0; --$i) { + $this->_replace[$i] = $rep = $this->filter($this->_replace[$i], $i); + $this->_repSize[$i] = count($rep); + } + } + + /** + * Returns true if based on the buffer passed more bytes should be buffered. + * + * @param array $buffer + * + * @return bool + */ + public function shouldBuffer($buffer) + { + $endOfBuffer = end($buffer); + + return isset($this->_index[$endOfBuffer]); + } + + /** + * Perform the actual replacements on $buffer and return the result. + * + * @param array $buffer + * @param int $_minReplaces + * + * @return array + */ + public function filter($buffer, $_minReplaces = -1) + { + if ($this->_treeMaxLen == 0) { + return $buffer; + } + + $newBuffer = array(); + $buf_size = count($buffer); + $last_size = 0; + for ($i = 0; $i < $buf_size; ++$i) { + $search_pos = $this->_tree; + $last_found = PHP_INT_MAX; + // We try to find if the next byte is part of a search pattern + for ($j = 0; $j <= $this->_treeMaxLen; ++$j) { + // We have a new byte for a search pattern + if (isset($buffer [$p = $i + $j]) && isset($search_pos[$buffer[$p]])) { + $search_pos = $search_pos[$buffer[$p]]; + // We have a complete pattern, save, in case we don't find a better match later + if (isset($search_pos[-1]) && $search_pos[-1] < $last_found + && $search_pos[-1] > $_minReplaces) { + $last_found = $search_pos[-1]; + $last_size = $search_pos[-2]; + } + } + // We got a complete pattern + elseif ($last_found !== PHP_INT_MAX) { + // Adding replacement datas to output buffer + $rep_size = $this->_repSize[$last_found]; + for ($j = 0; $j < $rep_size; ++$j) { + $newBuffer[] = $this->_replace[$last_found][$j]; + } + // We Move cursor forward + $i += $last_size - 1; + // Edge Case, last position in buffer + if ($i >= $buf_size) { + $newBuffer[] = $buffer[$i]; + } + + // We start the next loop + continue 2; + } else { + // this byte is not in a pattern and we haven't found another pattern + break; + } + } + // Normal byte, move it to output buffer + $newBuffer[] = $buffer[$i]; + } + + return $newBuffer; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php new file mode 100644 index 00000000..d0db8b96 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php @@ -0,0 +1,66 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Processes bytes as they pass through a buffer and replaces sequences in it. + * + * @author Chris Corbyn + */ +class Swift_StreamFilters_StringReplacementFilter implements Swift_StreamFilter +{ + /** The needle(s) to search for */ + private $_search; + + /** The replacement(s) to make */ + private $_replace; + + /** + * Create a new StringReplacementFilter with $search and $replace. + * + * @param string|array $search + * @param string|array $replace + */ + public function __construct($search, $replace) + { + $this->_search = $search; + $this->_replace = $replace; + } + + /** + * Returns true if based on the buffer passed more bytes should be buffered. + * + * @param string $buffer + * + * @return bool + */ + public function shouldBuffer($buffer) + { + $endOfBuffer = substr($buffer, -1); + foreach ((array) $this->_search as $needle) { + if (false !== strpos($needle, $endOfBuffer)) { + return true; + } + } + + return false; + } + + /** + * Perform the actual replacements on $buffer and return the result. + * + * @param string $buffer + * + * @return string + */ + public function filter($buffer) + { + return str_replace($this->_search, $this->_replace, $buffer); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php new file mode 100644 index 00000000..e98240b5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php @@ -0,0 +1,45 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Creates filters for replacing needles in a string buffer. + * + * @author Chris Corbyn + */ +class Swift_StreamFilters_StringReplacementFilterFactory implements Swift_ReplacementFilterFactory +{ + /** Lazy-loaded filters */ + private $_filters = array(); + + /** + * Create a new StreamFilter to replace $search with $replace in a string. + * + * @param string $search + * @param string $replace + * + * @return Swift_StreamFilter + */ + public function createFilter($search, $replace) + { + if (!isset($this->_filters[$search][$replace])) { + if (!isset($this->_filters[$search])) { + $this->_filters[$search] = array(); + } + + if (!isset($this->_filters[$search][$replace])) { + $this->_filters[$search][$replace] = array(); + } + + $this->_filters[$search][$replace] = new Swift_StreamFilters_StringReplacementFilter($search, $replace); + } + + return $this->_filters[$search][$replace]; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SwiftException.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SwiftException.php new file mode 100644 index 00000000..db3d3109 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SwiftException.php @@ -0,0 +1,29 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Base Exception class. + * + * @author Chris Corbyn + */ +class Swift_SwiftException extends Exception +{ + /** + * Create a new SwiftException with $message. + * + * @param string $message + * @param int $code + * @param Exception $previous + */ + public function __construct($message, $code = 0, Exception $previous = null) + { + parent::__construct($message, $code, $previous); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport.php new file mode 100644 index 00000000..6535eadf --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport.php @@ -0,0 +1,54 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Sends Messages via an abstract Transport subsystem. + * + * @author Chris Corbyn + */ +interface Swift_Transport +{ + /** + * Test if this Transport mechanism has started. + * + * @return bool + */ + public function isStarted(); + + /** + * Start this Transport mechanism. + */ + public function start(); + + /** + * Stop this Transport mechanism. + */ + public function stop(); + + /** + * Send the given Message. + * + * Recipient/sender data will be retrieved from the Message API. + * The return value is the number of recipients who were accepted for delivery. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null); + + /** + * Register a plugin in the Transport. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php new file mode 100644 index 00000000..f4d9967a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php @@ -0,0 +1,496 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Sends Messages over SMTP. + * + * @author Chris Corbyn + */ +abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport +{ + /** Input-Output buffer for sending/receiving SMTP commands and responses */ + protected $_buffer; + + /** Connection status */ + protected $_started = false; + + /** The domain name to use in HELO command */ + protected $_domain = '[127.0.0.1]'; + + /** The event dispatching layer */ + protected $_eventDispatcher; + + /** Source Ip */ + protected $_sourceIp; + + /** Return an array of params for the Buffer */ + abstract protected function _getBufferParams(); + + /** + * Creates a new EsmtpTransport using the given I/O buffer. + * + * @param Swift_Transport_IoBuffer $buf + * @param Swift_Events_EventDispatcher $dispatcher + */ + public function __construct(Swift_Transport_IoBuffer $buf, Swift_Events_EventDispatcher $dispatcher) + { + $this->_eventDispatcher = $dispatcher; + $this->_buffer = $buf; + $this->_lookupHostname(); + } + + /** + * Set the name of the local domain which Swift will identify itself as. + * + * This should be a fully-qualified domain name and should be truly the domain + * you're using. + * + * If your server doesn't have a domain name, use the IP in square + * brackets (i.e. [127.0.0.1]). + * + * @param string $domain + * + * @return Swift_Transport_AbstractSmtpTransport + */ + public function setLocalDomain($domain) + { + $this->_domain = $domain; + + return $this; + } + + /** + * Get the name of the domain Swift will identify as. + * + * @return string + */ + public function getLocalDomain() + { + return $this->_domain; + } + + /** + * Sets the source IP. + * + * @param string $source + */ + public function setSourceIp($source) + { + $this->_sourceIp = $source; + } + + /** + * Returns the IP used to connect to the destination. + * + * @return string + */ + public function getSourceIp() + { + return $this->_sourceIp; + } + + /** + * Start the SMTP connection. + */ + public function start() + { + if (!$this->_started) { + if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStarted'); + if ($evt->bubbleCancelled()) { + return; + } + } + + try { + $this->_buffer->initialize($this->_getBufferParams()); + } catch (Swift_TransportException $e) { + $this->_throwException($e); + } + $this->_readGreeting(); + $this->_doHeloCommand(); + + if ($evt) { + $this->_eventDispatcher->dispatchEvent($evt, 'transportStarted'); + } + + $this->_started = true; + } + } + + /** + * Test if an SMTP connection has been established. + * + * @return bool + */ + public function isStarted() + { + return $this->_started; + } + + /** + * Send the given Message. + * + * Recipient/sender data will be retrieved from the Message API. + * The return value is the number of recipients who were accepted for delivery. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $sent = 0; + $failedRecipients = (array) $failedRecipients; + + if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt->bubbleCancelled()) { + return 0; + } + } + + if (!$reversePath = $this->_getReversePath($message)) { + $this->_throwException(new Swift_TransportException( + 'Cannot send message without a sender address' + ) + ); + } + + $to = (array) $message->getTo(); + $cc = (array) $message->getCc(); + $tos = array_merge($to, $cc); + $bcc = (array) $message->getBcc(); + + $message->setBcc(array()); + + try { + $sent += $this->_sendTo($message, $reversePath, $tos, $failedRecipients); + $sent += $this->_sendBcc($message, $reversePath, $bcc, $failedRecipients); + } catch (Exception $e) { + $message->setBcc($bcc); + throw $e; + } + + $message->setBcc($bcc); + + if ($evt) { + if ($sent == count($to) + count($cc) + count($bcc)) { + $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS); + } elseif ($sent > 0) { + $evt->setResult(Swift_Events_SendEvent::RESULT_TENTATIVE); + } else { + $evt->setResult(Swift_Events_SendEvent::RESULT_FAILED); + } + $evt->setFailedRecipients($failedRecipients); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + $message->generateId(); //Make sure a new Message ID is used + + return $sent; + } + + /** + * Stop the SMTP connection. + */ + public function stop() + { + if ($this->_started) { + if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStopped'); + if ($evt->bubbleCancelled()) { + return; + } + } + + try { + $this->executeCommand("QUIT\r\n", array(221)); + } catch (Swift_TransportException $e) { + } + + try { + $this->_buffer->terminate(); + + if ($evt) { + $this->_eventDispatcher->dispatchEvent($evt, 'transportStopped'); + } + } catch (Swift_TransportException $e) { + $this->_throwException($e); + } + } + $this->_started = false; + } + + /** + * Register a plugin. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_eventDispatcher->bindEventListener($plugin); + } + + /** + * Reset the current mail transaction. + */ + public function reset() + { + $this->executeCommand("RSET\r\n", array(250)); + } + + /** + * Get the IoBuffer where read/writes are occurring. + * + * @return Swift_Transport_IoBuffer + */ + public function getBuffer() + { + return $this->_buffer; + } + + /** + * Run a command against the buffer, expecting the given response codes. + * + * If no response codes are given, the response will not be validated. + * If codes are given, an exception will be thrown on an invalid response. + * + * @param string $command + * @param int[] $codes + * @param string[] $failures An array of failures by-reference + * + * @return string + */ + public function executeCommand($command, $codes = array(), &$failures = null) + { + $failures = (array) $failures; + $seq = $this->_buffer->write($command); + $response = $this->_getFullResponse($seq); + if ($evt = $this->_eventDispatcher->createCommandEvent($this, $command, $codes)) { + $this->_eventDispatcher->dispatchEvent($evt, 'commandSent'); + } + $this->_assertResponseCode($response, $codes); + + return $response; + } + + /** Read the opening SMTP greeting */ + protected function _readGreeting() + { + $this->_assertResponseCode($this->_getFullResponse(0), array(220)); + } + + /** Send the HELO welcome */ + protected function _doHeloCommand() + { + $this->executeCommand( + sprintf("HELO %s\r\n", $this->_domain), array(250) + ); + } + + /** Send the MAIL FROM command */ + protected function _doMailFromCommand($address) + { + $this->executeCommand( + sprintf("MAIL FROM:<%s>\r\n", $address), array(250) + ); + } + + /** Send the RCPT TO command */ + protected function _doRcptToCommand($address) + { + $this->executeCommand( + sprintf("RCPT TO:<%s>\r\n", $address), array(250, 251, 252) + ); + } + + /** Send the DATA command */ + protected function _doDataCommand() + { + $this->executeCommand("DATA\r\n", array(354)); + } + + /** Stream the contents of the message over the buffer */ + protected function _streamMessage(Swift_Mime_Message $message) + { + $this->_buffer->setWriteTranslations(array("\r\n." => "\r\n..")); + try { + $message->toByteStream($this->_buffer); + $this->_buffer->flushBuffers(); + } catch (Swift_TransportException $e) { + $this->_throwException($e); + } + $this->_buffer->setWriteTranslations(array()); + $this->executeCommand("\r\n.\r\n", array(250)); + } + + /** Determine the best-use reverse path for this message */ + protected function _getReversePath(Swift_Mime_Message $message) + { + $return = $message->getReturnPath(); + $sender = $message->getSender(); + $from = $message->getFrom(); + $path = null; + if (!empty($return)) { + $path = $return; + } elseif (!empty($sender)) { + // Don't use array_keys + reset($sender); // Reset Pointer to first pos + $path = key($sender); // Get key + } elseif (!empty($from)) { + reset($from); // Reset Pointer to first pos + $path = key($from); // Get key + } + + return $path; + } + + /** Throw a TransportException, first sending it to any listeners */ + protected function _throwException(Swift_TransportException $e) + { + if ($evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e)) { + $this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown'); + if (!$evt->bubbleCancelled()) { + throw $e; + } + } else { + throw $e; + } + } + + /** Throws an Exception if a response code is incorrect */ + protected function _assertResponseCode($response, $wanted) + { + list($code) = sscanf($response, '%3d'); + $valid = (empty($wanted) || in_array($code, $wanted)); + + if ($evt = $this->_eventDispatcher->createResponseEvent($this, $response, + $valid)) { + $this->_eventDispatcher->dispatchEvent($evt, 'responseReceived'); + } + + if (!$valid) { + $this->_throwException( + new Swift_TransportException( + 'Expected response code '.implode('/', $wanted).' but got code '. + '"'.$code.'", with message "'.$response.'"', + $code) + ); + } + } + + /** Get an entire multi-line response using its sequence number */ + protected function _getFullResponse($seq) + { + $response = ''; + try { + do { + $line = $this->_buffer->readLine($seq); + $response .= $line; + } while (null !== $line && false !== $line && ' ' != $line{3}); + } catch (Swift_TransportException $e) { + $this->_throwException($e); + } catch (Swift_IoException $e) { + $this->_throwException( + new Swift_TransportException( + $e->getMessage()) + ); + } + + return $response; + } + + /** Send an email to the given recipients from the given reverse path */ + private function _doMailTransaction($message, $reversePath, array $recipients, array &$failedRecipients) + { + $sent = 0; + $this->_doMailFromCommand($reversePath); + foreach ($recipients as $forwardPath) { + try { + $this->_doRcptToCommand($forwardPath); + ++$sent; + } catch (Swift_TransportException $e) { + $failedRecipients[] = $forwardPath; + } + } + + if ($sent != 0) { + $this->_doDataCommand(); + $this->_streamMessage($message); + } else { + $this->reset(); + } + + return $sent; + } + + /** Send a message to the given To: recipients */ + private function _sendTo(Swift_Mime_Message $message, $reversePath, array $to, array &$failedRecipients) + { + if (empty($to)) { + return 0; + } + + return $this->_doMailTransaction($message, $reversePath, array_keys($to), + $failedRecipients); + } + + /** Send a message to all Bcc: recipients */ + private function _sendBcc(Swift_Mime_Message $message, $reversePath, array $bcc, array &$failedRecipients) + { + $sent = 0; + foreach ($bcc as $forwardPath => $name) { + $message->setBcc(array($forwardPath => $name)); + $sent += $this->_doMailTransaction( + $message, $reversePath, array($forwardPath), $failedRecipients + ); + } + + return $sent; + } + + /** Try to determine the hostname of the server this is run on */ + private function _lookupHostname() + { + if (!empty($_SERVER['SERVER_NAME']) && $this->_isFqdn($_SERVER['SERVER_NAME'])) { + $this->_domain = $_SERVER['SERVER_NAME']; + } elseif (!empty($_SERVER['SERVER_ADDR'])) { + // Set the address literal tag (See RFC 5321, section: 4.1.3) + if (false === strpos($_SERVER['SERVER_ADDR'], ':')) { + $prefix = ''; // IPv4 addresses are not tagged. + } else { + $prefix = 'IPv6:'; // Adding prefix in case of IPv6. + } + + $this->_domain = sprintf('[%s%s]', $prefix, $_SERVER['SERVER_ADDR']); + } + } + + /** Determine is the $hostname is a fully-qualified name */ + private function _isFqdn($hostname) + { + // We could do a really thorough check, but there's really no point + if (false !== $dotPos = strpos($hostname, '.')) { + return ($dotPos > 0) && ($dotPos != strlen($hostname) - 1); + } + + return false; + } + + /** + * Destructor. + */ + public function __destruct() + { + $this->stop(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php new file mode 100644 index 00000000..53f721d0 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php @@ -0,0 +1,81 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Handles CRAM-MD5 authentication. + * + * @author Chris Corbyn + */ +class Swift_Transport_Esmtp_Auth_CramMd5Authenticator implements Swift_Transport_Esmtp_Authenticator +{ + /** + * Get the name of the AUTH mechanism this Authenticator handles. + * + * @return string + */ + public function getAuthKeyword() + { + return 'CRAM-MD5'; + } + + /** + * Try to authenticate the user with $username and $password. + * + * @param Swift_Transport_SmtpAgent $agent + * @param string $username + * @param string $password + * + * @return bool + */ + public function authenticate(Swift_Transport_SmtpAgent $agent, $username, $password) + { + try { + $challenge = $agent->executeCommand("AUTH CRAM-MD5\r\n", array(334)); + $challenge = base64_decode(substr($challenge, 4)); + $message = base64_encode( + $username.' '.$this->_getResponse($password, $challenge) + ); + $agent->executeCommand(sprintf("%s\r\n", $message), array(235)); + + return true; + } catch (Swift_TransportException $e) { + $agent->executeCommand("RSET\r\n", array(250)); + + return false; + } + } + + /** + * Generate a CRAM-MD5 response from a server challenge. + * + * @param string $secret + * @param string $challenge + * + * @return string + */ + private function _getResponse($secret, $challenge) + { + if (strlen($secret) > 64) { + $secret = pack('H32', md5($secret)); + } + + if (strlen($secret) < 64) { + $secret = str_pad($secret, 64, chr(0)); + } + + $k_ipad = substr($secret, 0, 64) ^ str_repeat(chr(0x36), 64); + $k_opad = substr($secret, 0, 64) ^ str_repeat(chr(0x5C), 64); + + $inner = pack('H32', md5($k_ipad.$challenge)); + $digest = md5($k_opad.$inner); + + return $digest; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/LoginAuthenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/LoginAuthenticator.php new file mode 100644 index 00000000..6ab6e333 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/LoginAuthenticator.php @@ -0,0 +1,51 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Handles LOGIN authentication. + * + * @author Chris Corbyn + */ +class Swift_Transport_Esmtp_Auth_LoginAuthenticator implements Swift_Transport_Esmtp_Authenticator +{ + /** + * Get the name of the AUTH mechanism this Authenticator handles. + * + * @return string + */ + public function getAuthKeyword() + { + return 'LOGIN'; + } + + /** + * Try to authenticate the user with $username and $password. + * + * @param Swift_Transport_SmtpAgent $agent + * @param string $username + * @param string $password + * + * @return bool + */ + public function authenticate(Swift_Transport_SmtpAgent $agent, $username, $password) + { + try { + $agent->executeCommand("AUTH LOGIN\r\n", array(334)); + $agent->executeCommand(sprintf("%s\r\n", base64_encode($username)), array(334)); + $agent->executeCommand(sprintf("%s\r\n", base64_encode($password)), array(235)); + + return true; + } catch (Swift_TransportException $e) { + $agent->executeCommand("RSET\r\n", array(250)); + + return false; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php new file mode 100644 index 00000000..eb04acf0 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php @@ -0,0 +1,720 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * This authentication is for Exchange servers. We support version 1 & 2. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Handles NTLM authentication. + * + * @author Ward Peeters <ward@coding-tech.com> + */ +class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Esmtp_Authenticator +{ + const NTLMSIG = "NTLMSSP\x00"; + const DESCONST = 'KGS!@#$%'; + + /** + * Get the name of the AUTH mechanism this Authenticator handles. + * + * @return string + */ + public function getAuthKeyword() + { + return 'NTLM'; + } + + /** + * Try to authenticate the user with $username and $password. + * + * @param Swift_Transport_SmtpAgent $agent + * @param string $username + * @param string $password + * + * @return bool + */ + public function authenticate(Swift_Transport_SmtpAgent $agent, $username, $password) + { + if (!function_exists('openssl_random_pseudo_bytes') || !function_exists('openssl_encrypt')) { + throw new LogicException('The OpenSSL extension must be enabled to use the NTLM authenticator.'); + } + + if (!function_exists('bcmul')) { + throw new LogicException('The BCMath functions must be enabled to use the NTLM authenticator.'); + } + + try { + // execute AUTH command and filter out the code at the beginning + // AUTH NTLM xxxx + $response = base64_decode(substr(trim($this->sendMessage1($agent)), 4)); + + // extra parameters for our unit cases + $timestamp = func_num_args() > 3 ? func_get_arg(3) : $this->getCorrectTimestamp(bcmul(microtime(true), '1000')); + $client = func_num_args() > 4 ? func_get_arg(4) : $this->getRandomBytes(8); + + // Message 3 response + $this->sendMessage3($response, $username, $password, $timestamp, $client, $agent); + + return true; + } catch (Swift_TransportException $e) { + $agent->executeCommand("RSET\r\n", array(250)); + + return false; + } + } + + protected function si2bin($si, $bits = 32) + { + $bin = null; + if ($si >= -pow(2, $bits - 1) && ($si <= pow(2, $bits - 1))) { + // positive or zero + if ($si >= 0) { + $bin = base_convert($si, 10, 2); + // pad to $bits bit + $bin_length = strlen($bin); + if ($bin_length < $bits) { + $bin = str_repeat('0', $bits - $bin_length).$bin; + } + } else { + // negative + $si = -$si - pow(2, $bits); + $bin = base_convert($si, 10, 2); + $bin_length = strlen($bin); + if ($bin_length > $bits) { + $bin = str_repeat('1', $bits - $bin_length).$bin; + } + } + } + + return $bin; + } + + /** + * Send our auth message and returns the response. + * + * @param Swift_Transport_SmtpAgent $agent + * + * @return string SMTP Response + */ + protected function sendMessage1(Swift_Transport_SmtpAgent $agent) + { + $message = $this->createMessage1(); + + return $agent->executeCommand(sprintf("AUTH %s %s\r\n", $this->getAuthKeyword(), base64_encode($message)), array(334)); + } + + /** + * Fetch all details of our response (message 2). + * + * @param string $response + * + * @return array our response parsed + */ + protected function parseMessage2($response) + { + $responseHex = bin2hex($response); + $length = floor(hexdec(substr($responseHex, 28, 4)) / 256) * 2; + $offset = floor(hexdec(substr($responseHex, 32, 4)) / 256) * 2; + $challenge = $this->hex2bin(substr($responseHex, 48, 16)); + $context = $this->hex2bin(substr($responseHex, 64, 16)); + $targetInfoH = $this->hex2bin(substr($responseHex, 80, 16)); + $targetName = $this->hex2bin(substr($responseHex, $offset, $length)); + $offset = floor(hexdec(substr($responseHex, 88, 4)) / 256) * 2; + $targetInfoBlock = substr($responseHex, $offset); + list($domainName, $serverName, $DNSDomainName, $DNSServerName, $terminatorByte) = $this->readSubBlock($targetInfoBlock); + + return array( + $challenge, + $context, + $targetInfoH, + $targetName, + $domainName, + $serverName, + $DNSDomainName, + $DNSServerName, + $this->hex2bin($targetInfoBlock), + $terminatorByte, + ); + } + + /** + * Read the blob information in from message2. + * + * @param $block + * + * @return array + */ + protected function readSubBlock($block) + { + // remove terminatorByte cause it's always the same + $block = substr($block, 0, -8); + + $length = strlen($block); + $offset = 0; + $data = array(); + while ($offset < $length) { + $blockLength = hexdec(substr(substr($block, $offset, 8), -4)) / 256; + $offset += 8; + $data[] = $this->hex2bin(substr($block, $offset, $blockLength * 2)); + $offset += $blockLength * 2; + } + + if (count($data) == 3) { + $data[] = $data[2]; + $data[2] = ''; + } + + $data[] = $this->createByte('00'); + + return $data; + } + + /** + * Send our final message with all our data. + * + * @param string $response Message 1 response (message 2) + * @param string $username + * @param string $password + * @param string $timestamp + * @param string $client + * @param Swift_Transport_SmtpAgent $agent + * @param bool $v2 Use version2 of the protocol + * + * @return string + */ + protected function sendMessage3($response, $username, $password, $timestamp, $client, Swift_Transport_SmtpAgent $agent, $v2 = true) + { + list($domain, $username) = $this->getDomainAndUsername($username); + //$challenge, $context, $targetInfoH, $targetName, $domainName, $workstation, $DNSDomainName, $DNSServerName, $blob, $ter + list($challenge, , , , , $workstation, , , $blob) = $this->parseMessage2($response); + + if (!$v2) { + // LMv1 + $lmResponse = $this->createLMPassword($password, $challenge); + // NTLMv1 + $ntlmResponse = $this->createNTLMPassword($password, $challenge); + } else { + // LMv2 + $lmResponse = $this->createLMv2Password($password, $username, $domain, $challenge, $client); + // NTLMv2 + $ntlmResponse = $this->createNTLMv2Hash($password, $username, $domain, $challenge, $blob, $timestamp, $client); + } + + $message = $this->createMessage3($domain, $username, $workstation, $lmResponse, $ntlmResponse); + + return $agent->executeCommand(sprintf("%s\r\n", base64_encode($message)), array(235)); + } + + /** + * Create our message 1. + * + * @return string + */ + protected function createMessage1() + { + return self::NTLMSIG + .$this->createByte('01') // Message 1 +.$this->createByte('0702'); // Flags + } + + /** + * Create our message 3. + * + * @param string $domain + * @param string $username + * @param string $workstation + * @param string $lmResponse + * @param string $ntlmResponse + * + * @return string + */ + protected function createMessage3($domain, $username, $workstation, $lmResponse, $ntlmResponse) + { + // Create security buffers + $domainSec = $this->createSecurityBuffer($domain, 64); + $domainInfo = $this->readSecurityBuffer(bin2hex($domainSec)); + $userSec = $this->createSecurityBuffer($username, ($domainInfo[0] + $domainInfo[1]) / 2); + $userInfo = $this->readSecurityBuffer(bin2hex($userSec)); + $workSec = $this->createSecurityBuffer($workstation, ($userInfo[0] + $userInfo[1]) / 2); + $workInfo = $this->readSecurityBuffer(bin2hex($workSec)); + $lmSec = $this->createSecurityBuffer($lmResponse, ($workInfo[0] + $workInfo[1]) / 2, true); + $lmInfo = $this->readSecurityBuffer(bin2hex($lmSec)); + $ntlmSec = $this->createSecurityBuffer($ntlmResponse, ($lmInfo[0] + $lmInfo[1]) / 2, true); + + return self::NTLMSIG + .$this->createByte('03') // TYPE 3 message +.$lmSec // LM response header +.$ntlmSec // NTLM response header +.$domainSec // Domain header +.$userSec // User header +.$workSec // Workstation header +.$this->createByte('000000009a', 8) // session key header (empty) +.$this->createByte('01020000') // FLAGS +.$this->convertTo16bit($domain) // domain name +.$this->convertTo16bit($username) // username +.$this->convertTo16bit($workstation) // workstation +.$lmResponse + .$ntlmResponse; + } + + /** + * @param string $timestamp Epoch timestamp in microseconds + * @param string $client Random bytes + * @param string $targetInfo + * + * @return string + */ + protected function createBlob($timestamp, $client, $targetInfo) + { + return $this->createByte('0101') + .$this->createByte('00') + .$timestamp + .$client + .$this->createByte('00') + .$targetInfo + .$this->createByte('00'); + } + + /** + * Get domain and username from our username. + * + * @example DOMAIN\username + * + * @param string $name + * + * @return array + */ + protected function getDomainAndUsername($name) + { + if (strpos($name, '\\') !== false) { + return explode('\\', $name); + } + + list($user, $domain) = explode('@', $name); + + return array($domain, $user); + } + + /** + * Create LMv1 response. + * + * @param string $password + * @param string $challenge + * + * @return string + */ + protected function createLMPassword($password, $challenge) + { + // FIRST PART + $password = $this->createByte(strtoupper($password), 14, false); + list($key1, $key2) = str_split($password, 7); + + $desKey1 = $this->createDesKey($key1); + $desKey2 = $this->createDesKey($key2); + + $constantDecrypt = $this->createByte($this->desEncrypt(self::DESCONST, $desKey1).$this->desEncrypt(self::DESCONST, $desKey2), 21, false); + + // SECOND PART + list($key1, $key2, $key3) = str_split($constantDecrypt, 7); + + $desKey1 = $this->createDesKey($key1); + $desKey2 = $this->createDesKey($key2); + $desKey3 = $this->createDesKey($key3); + + return $this->desEncrypt($challenge, $desKey1).$this->desEncrypt($challenge, $desKey2).$this->desEncrypt($challenge, $desKey3); + } + + /** + * Create NTLMv1 response. + * + * @param string $password + * @param string $challenge + * + * @return string + */ + protected function createNTLMPassword($password, $challenge) + { + // FIRST PART + $ntlmHash = $this->createByte($this->md4Encrypt($password), 21, false); + list($key1, $key2, $key3) = str_split($ntlmHash, 7); + + $desKey1 = $this->createDesKey($key1); + $desKey2 = $this->createDesKey($key2); + $desKey3 = $this->createDesKey($key3); + + return $this->desEncrypt($challenge, $desKey1).$this->desEncrypt($challenge, $desKey2).$this->desEncrypt($challenge, $desKey3); + } + + /** + * Convert a normal timestamp to a tenth of a microtime epoch time. + * + * @param string $time + * + * @return string + */ + protected function getCorrectTimestamp($time) + { + // Get our timestamp (tricky!) + bcscale(0); + + $time = number_format($time, 0, '.', ''); // save microtime to string + $time = bcadd($time, '11644473600000'); // add epoch time + $time = bcmul($time, 10000); // tenths of a microsecond. + + $binary = $this->si2bin($time, 64); // create 64 bit binary string + $timestamp = ''; + for ($i = 0; $i < 8; ++$i) { + $timestamp .= chr(bindec(substr($binary, -(($i + 1) * 8), 8))); + } + + return $timestamp; + } + + /** + * Create LMv2 response. + * + * @param string $password + * @param string $username + * @param string $domain + * @param string $challenge NTLM Challenge + * @param string $client Random string + * + * @return string + */ + protected function createLMv2Password($password, $username, $domain, $challenge, $client) + { + $lmPass = '00'; // by default 00 + // if $password > 15 than we can't use this method + if (strlen($password) <= 15) { + $ntlmHash = $this->md4Encrypt($password); + $ntml2Hash = $this->md5Encrypt($ntlmHash, $this->convertTo16bit(strtoupper($username).$domain)); + + $lmPass = bin2hex($this->md5Encrypt($ntml2Hash, $challenge.$client).$client); + } + + return $this->createByte($lmPass, 24); + } + + /** + * Create NTLMv2 response. + * + * @param string $password + * @param string $username + * @param string $domain + * @param string $challenge Hex values + * @param string $targetInfo Hex values + * @param string $timestamp + * @param string $client Random bytes + * + * @return string + * + * @see http://davenport.sourceforge.net/ntlm.html#theNtlmResponse + */ + protected function createNTLMv2Hash($password, $username, $domain, $challenge, $targetInfo, $timestamp, $client) + { + $ntlmHash = $this->md4Encrypt($password); + $ntml2Hash = $this->md5Encrypt($ntlmHash, $this->convertTo16bit(strtoupper($username).$domain)); + + // create blob + $blob = $this->createBlob($timestamp, $client, $targetInfo); + + $ntlmv2Response = $this->md5Encrypt($ntml2Hash, $challenge.$blob); + + return $ntlmv2Response.$blob; + } + + protected function createDesKey($key) + { + $material = array(bin2hex($key[0])); + $len = strlen($key); + for ($i = 1; $i < $len; ++$i) { + list($high, $low) = str_split(bin2hex($key[$i])); + $v = $this->castToByte(ord($key[$i - 1]) << (7 + 1 - $i) | $this->uRShift(hexdec(dechex(hexdec($high) & 0xf).dechex(hexdec($low) & 0xf)), $i)); + $material[] = str_pad(substr(dechex($v), -2), 2, '0', STR_PAD_LEFT); // cast to byte + } + $material[] = str_pad(substr(dechex($this->castToByte(ord($key[6]) << 1)), -2), 2, '0'); + + // odd parity + foreach ($material as $k => $v) { + $b = $this->castToByte(hexdec($v)); + $needsParity = (($this->uRShift($b, 7) ^ $this->uRShift($b, 6) ^ $this->uRShift($b, 5) + ^ $this->uRShift($b, 4) ^ $this->uRShift($b, 3) ^ $this->uRShift($b, 2) + ^ $this->uRShift($b, 1)) & 0x01) == 0; + + list($high, $low) = str_split($v); + if ($needsParity) { + $material[$k] = dechex(hexdec($high) | 0x0).dechex(hexdec($low) | 0x1); + } else { + $material[$k] = dechex(hexdec($high) & 0xf).dechex(hexdec($low) & 0xe); + } + } + + return $this->hex2bin(implode('', $material)); + } + + /** HELPER FUNCTIONS */ + /** + * Create our security buffer depending on length and offset. + * + * @param string $value Value we want to put in + * @param int $offset start of value + * @param bool $is16 Do we 16bit string or not? + * + * @return string + */ + protected function createSecurityBuffer($value, $offset, $is16 = false) + { + $length = strlen(bin2hex($value)); + $length = $is16 ? $length / 2 : $length; + $length = $this->createByte(str_pad(dechex($length), 2, '0', STR_PAD_LEFT), 2); + + return $length.$length.$this->createByte(dechex($offset), 4); + } + + /** + * Read our security buffer to fetch length and offset of our value. + * + * @param string $value Securitybuffer in hex + * + * @return array array with length and offset + */ + protected function readSecurityBuffer($value) + { + $length = floor(hexdec(substr($value, 0, 4)) / 256) * 2; + $offset = floor(hexdec(substr($value, 8, 4)) / 256) * 2; + + return array($length, $offset); + } + + /** + * Cast to byte java equivalent to (byte). + * + * @param int $v + * + * @return int + */ + protected function castToByte($v) + { + return (($v + 128) % 256) - 128; + } + + /** + * Java unsigned right bitwise + * $a >>> $b. + * + * @param int $a + * @param int $b + * + * @return int + */ + protected function uRShift($a, $b) + { + if ($b == 0) { + return $a; + } + + return ($a >> $b) & ~(1 << (8 * PHP_INT_SIZE - 1) >> ($b - 1)); + } + + /** + * Right padding with 0 to certain length. + * + * @param string $input + * @param int $bytes Length of bytes + * @param bool $isHex Did we provided hex value + * + * @return string + */ + protected function createByte($input, $bytes = 4, $isHex = true) + { + if ($isHex) { + $byte = $this->hex2bin(str_pad($input, $bytes * 2, '00')); + } else { + $byte = str_pad($input, $bytes, "\x00"); + } + + return $byte; + } + + /** + * Create random bytes. + * + * @param $length + * + * @return string + */ + protected function getRandomBytes($length) + { + $bytes = openssl_random_pseudo_bytes($length, $strong); + + if (false !== $bytes && true === $strong) { + return $bytes; + } + + throw new RuntimeException('OpenSSL did not produce a secure random number.'); + } + + /** ENCRYPTION ALGORITHMS */ + /** + * DES Encryption. + * + * @param string $value An 8-byte string + * @param string $key + * + * @return string + */ + protected function desEncrypt($value, $key) + { + // 1 == OPENSSL_RAW_DATA - but constant is only available as of PHP 5.4. + return substr(openssl_encrypt($value, 'DES-ECB', $key, 1), 0, 8); + } + + /** + * MD5 Encryption. + * + * @param string $key Encryption key + * @param string $msg Message to encrypt + * + * @return string + */ + protected function md5Encrypt($key, $msg) + { + $blocksize = 64; + if (strlen($key) > $blocksize) { + $key = pack('H*', md5($key)); + } + + $key = str_pad($key, $blocksize, "\0"); + $ipadk = $key ^ str_repeat("\x36", $blocksize); + $opadk = $key ^ str_repeat("\x5c", $blocksize); + + return pack('H*', md5($opadk.pack('H*', md5($ipadk.$msg)))); + } + + /** + * MD4 Encryption. + * + * @param string $input + * + * @return string + * + * @see http://php.net/manual/en/ref.hash.php + */ + protected function md4Encrypt($input) + { + $input = $this->convertTo16bit($input); + + return function_exists('hash') ? $this->hex2bin(hash('md4', $input)) : mhash(MHASH_MD4, $input); + } + + /** + * Convert UTF-8 to UTF-16. + * + * @param string $input + * + * @return string + */ + protected function convertTo16bit($input) + { + return iconv('UTF-8', 'UTF-16LE', $input); + } + + /** + * Hex2bin replacement for < PHP 5.4. + * + * @param string $hex + * + * @return string Binary + */ + protected function hex2bin($hex) + { + if (function_exists('hex2bin')) { + return hex2bin($hex); + } else { + return pack('H*', $hex); + } + } + + /** + * @param string $message + */ + protected function debug($message) + { + $message = bin2hex($message); + $messageId = substr($message, 16, 8); + echo substr($message, 0, 16)." NTLMSSP Signature<br />\n"; + echo $messageId." Type Indicator<br />\n"; + + if ($messageId == '02000000') { + $map = array( + 'Challenge', + 'Context', + 'Target Information Security Buffer', + 'Target Name Data', + 'NetBIOS Domain Name', + 'NetBIOS Server Name', + 'DNS Domain Name', + 'DNS Server Name', + 'BLOB', + 'Target Information Terminator', + ); + + $data = $this->parseMessage2($this->hex2bin($message)); + + foreach ($map as $key => $value) { + echo bin2hex($data[$key]).' - '.$data[$key].' ||| '.$value."<br />\n"; + } + } elseif ($messageId == '03000000') { + $i = 0; + $data[$i++] = substr($message, 24, 16); + list($lmLength, $lmOffset) = $this->readSecurityBuffer($data[$i - 1]); + + $data[$i++] = substr($message, 40, 16); + list($ntmlLength, $ntmlOffset) = $this->readSecurityBuffer($data[$i - 1]); + + $data[$i++] = substr($message, 56, 16); + list($targetLength, $targetOffset) = $this->readSecurityBuffer($data[$i - 1]); + + $data[$i++] = substr($message, 72, 16); + list($userLength, $userOffset) = $this->readSecurityBuffer($data[$i - 1]); + + $data[$i++] = substr($message, 88, 16); + list($workLength, $workOffset) = $this->readSecurityBuffer($data[$i - 1]); + + $data[$i++] = substr($message, 104, 16); + $data[$i++] = substr($message, 120, 8); + $data[$i++] = substr($message, $targetOffset, $targetLength); + $data[$i++] = substr($message, $userOffset, $userLength); + $data[$i++] = substr($message, $workOffset, $workLength); + $data[$i++] = substr($message, $lmOffset, $lmLength); + $data[$i] = substr($message, $ntmlOffset, $ntmlLength); + + $map = array( + 'LM Response Security Buffer', + 'NTLM Response Security Buffer', + 'Target Name Security Buffer', + 'User Name Security Buffer', + 'Workstation Name Security Buffer', + 'Session Key Security Buffer', + 'Flags', + 'Target Name Data', + 'User Name Data', + 'Workstation Name Data', + 'LM Response Data', + 'NTLM Response Data', + ); + + foreach ($map as $key => $value) { + echo $data[$key].' - '.$this->hex2bin($data[$key]).' ||| '.$value."<br />\n"; + } + } + + echo '<br /><br />'; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/PlainAuthenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/PlainAuthenticator.php new file mode 100644 index 00000000..43219f93 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/PlainAuthenticator.php @@ -0,0 +1,50 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Handles PLAIN authentication. + * + * @author Chris Corbyn + */ +class Swift_Transport_Esmtp_Auth_PlainAuthenticator implements Swift_Transport_Esmtp_Authenticator +{ + /** + * Get the name of the AUTH mechanism this Authenticator handles. + * + * @return string + */ + public function getAuthKeyword() + { + return 'PLAIN'; + } + + /** + * Try to authenticate the user with $username and $password. + * + * @param Swift_Transport_SmtpAgent $agent + * @param string $username + * @param string $password + * + * @return bool + */ + public function authenticate(Swift_Transport_SmtpAgent $agent, $username, $password) + { + try { + $message = base64_encode($username.chr(0).$username.chr(0).$password); + $agent->executeCommand(sprintf("AUTH PLAIN %s\r\n", $message), array(235)); + + return true; + } catch (Swift_TransportException $e) { + $agent->executeCommand("RSET\r\n", array(250)); + + return false; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php new file mode 100644 index 00000000..ca35e7b8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php @@ -0,0 +1,70 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Handles XOAUTH2 authentication. + * + * Example: + * <code> + * $transport = Swift_SmtpTransport::newInstance('smtp.gmail.com', 587, 'tls') + * ->setAuthMode('XOAUTH2') + * ->setUsername('YOUR_EMAIL_ADDRESS') + * ->setPassword('YOUR_ACCESS_TOKEN'); + * </code> + * + * @author xu.li<AthenaLightenedMyPath@gmail.com> + * + * @see https://developers.google.com/google-apps/gmail/xoauth2_protocol + */ +class Swift_Transport_Esmtp_Auth_XOAuth2Authenticator implements Swift_Transport_Esmtp_Authenticator +{ + /** + * Get the name of the AUTH mechanism this Authenticator handles. + * + * @return string + */ + public function getAuthKeyword() + { + return 'XOAUTH2'; + } + + /** + * Try to authenticate the user with $email and $token. + * + * @param Swift_Transport_SmtpAgent $agent + * @param string $email + * @param string $token + * + * @return bool + */ + public function authenticate(Swift_Transport_SmtpAgent $agent, $email, $token) + { + try { + $param = $this->constructXOAuth2Params($email, $token); + $agent->executeCommand('AUTH XOAUTH2 '.$param."\r\n", array(235)); + + return true; + } catch (Swift_TransportException $e) { + $agent->executeCommand("RSET\r\n", array(250)); + + return false; + } + } + + /** + * Construct the auth parameter. + * + * @see https://developers.google.com/google-apps/gmail/xoauth2_protocol#the_sasl_xoauth2_mechanism + */ + protected function constructXOAuth2Params($email, $token) + { + return base64_encode("user=$email\1auth=Bearer $token\1\1"); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php new file mode 100644 index 00000000..cb36133c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php @@ -0,0 +1,263 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An ESMTP handler for AUTH support. + * + * @author Chris Corbyn + */ +class Swift_Transport_Esmtp_AuthHandler implements Swift_Transport_EsmtpHandler +{ + /** + * Authenticators available to process the request. + * + * @var Swift_Transport_Esmtp_Authenticator[] + */ + private $_authenticators = array(); + + /** + * The username for authentication. + * + * @var string + */ + private $_username; + + /** + * The password for authentication. + * + * @var string + */ + private $_password; + + /** + * The auth mode for authentication. + * + * @var string + */ + private $_auth_mode; + + /** + * The ESMTP AUTH parameters available. + * + * @var string[] + */ + private $_esmtpParams = array(); + + /** + * Create a new AuthHandler with $authenticators for support. + * + * @param Swift_Transport_Esmtp_Authenticator[] $authenticators + */ + public function __construct(array $authenticators) + { + $this->setAuthenticators($authenticators); + } + + /** + * Set the Authenticators which can process a login request. + * + * @param Swift_Transport_Esmtp_Authenticator[] $authenticators + */ + public function setAuthenticators(array $authenticators) + { + $this->_authenticators = $authenticators; + } + + /** + * Get the Authenticators which can process a login request. + * + * @return Swift_Transport_Esmtp_Authenticator[] + */ + public function getAuthenticators() + { + return $this->_authenticators; + } + + /** + * Set the username to authenticate with. + * + * @param string $username + */ + public function setUsername($username) + { + $this->_username = $username; + } + + /** + * Get the username to authenticate with. + * + * @return string + */ + public function getUsername() + { + return $this->_username; + } + + /** + * Set the password to authenticate with. + * + * @param string $password + */ + public function setPassword($password) + { + $this->_password = $password; + } + + /** + * Get the password to authenticate with. + * + * @return string + */ + public function getPassword() + { + return $this->_password; + } + + /** + * Set the auth mode to use to authenticate. + * + * @param string $mode + */ + public function setAuthMode($mode) + { + $this->_auth_mode = $mode; + } + + /** + * Get the auth mode to use to authenticate. + * + * @return string + */ + public function getAuthMode() + { + return $this->_auth_mode; + } + + /** + * Get the name of the ESMTP extension this handles. + * + * @return bool + */ + public function getHandledKeyword() + { + return 'AUTH'; + } + + /** + * Set the parameters which the EHLO greeting indicated. + * + * @param string[] $parameters + */ + public function setKeywordParams(array $parameters) + { + $this->_esmtpParams = $parameters; + } + + /** + * Runs immediately after a EHLO has been issued. + * + * @param Swift_Transport_SmtpAgent $agent to read/write + */ + public function afterEhlo(Swift_Transport_SmtpAgent $agent) + { + if ($this->_username) { + $count = 0; + foreach ($this->_getAuthenticatorsForAgent() as $authenticator) { + if (in_array(strtolower($authenticator->getAuthKeyword()), + array_map('strtolower', $this->_esmtpParams))) { + ++$count; + if ($authenticator->authenticate($agent, $this->_username, $this->_password)) { + return; + } + } + } + throw new Swift_TransportException( + 'Failed to authenticate on SMTP server with username "'. + $this->_username.'" using '.$count.' possible authenticators' + ); + } + } + + /** + * Not used. + */ + public function getMailParams() + { + return array(); + } + + /** + * Not used. + */ + public function getRcptParams() + { + return array(); + } + + /** + * Not used. + */ + public function onCommand(Swift_Transport_SmtpAgent $agent, $command, $codes = array(), &$failedRecipients = null, &$stop = false) + { + } + + /** + * Returns +1, -1 or 0 according to the rules for usort(). + * + * This method is called to ensure extensions can be execute in an appropriate order. + * + * @param string $esmtpKeyword to compare with + * + * @return int + */ + public function getPriorityOver($esmtpKeyword) + { + return 0; + } + + /** + * Returns an array of method names which are exposed to the Esmtp class. + * + * @return string[] + */ + public function exposeMixinMethods() + { + return array('setUsername', 'getUsername', 'setPassword', 'getPassword', 'setAuthMode', 'getAuthMode'); + } + + /** + * Not used. + */ + public function resetState() + { + } + + /** + * Returns the authenticator list for the given agent. + * + * @param Swift_Transport_SmtpAgent $agent + * + * @return array + */ + protected function _getAuthenticatorsForAgent() + { + if (!$mode = strtolower($this->_auth_mode)) { + return $this->_authenticators; + } + + foreach ($this->_authenticators as $authenticator) { + if (strtolower($authenticator->getAuthKeyword()) == $mode) { + return array($authenticator); + } + } + + throw new Swift_TransportException('Auth mode '.$mode.' is invalid'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Authenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Authenticator.php new file mode 100644 index 00000000..12a9abf8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Authenticator.php @@ -0,0 +1,35 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An Authentication mechanism. + * + * @author Chris Corbyn + */ +interface Swift_Transport_Esmtp_Authenticator +{ + /** + * Get the name of the AUTH mechanism this Authenticator handles. + * + * @return string + */ + public function getAuthKeyword(); + + /** + * Try to authenticate the user with $username and $password. + * + * @param Swift_Transport_SmtpAgent $agent + * @param string $username + * @param string $password + * + * @return bool + */ + public function authenticate(Swift_Transport_SmtpAgent $agent, $username, $password); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpHandler.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpHandler.php new file mode 100644 index 00000000..c17ef8fe --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpHandler.php @@ -0,0 +1,86 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An ESMTP handler. + * + * @author Chris Corbyn + */ +interface Swift_Transport_EsmtpHandler +{ + /** + * Get the name of the ESMTP extension this handles. + * + * @return bool + */ + public function getHandledKeyword(); + + /** + * Set the parameters which the EHLO greeting indicated. + * + * @param string[] $parameters + */ + public function setKeywordParams(array $parameters); + + /** + * Runs immediately after a EHLO has been issued. + * + * @param Swift_Transport_SmtpAgent $agent to read/write + */ + public function afterEhlo(Swift_Transport_SmtpAgent $agent); + + /** + * Get params which are appended to MAIL FROM:<>. + * + * @return string[] + */ + public function getMailParams(); + + /** + * Get params which are appended to RCPT TO:<>. + * + * @return string[] + */ + public function getRcptParams(); + + /** + * Runs when a command is due to be sent. + * + * @param Swift_Transport_SmtpAgent $agent to read/write + * @param string $command to send + * @param int[] $codes expected in response + * @param string[] $failedRecipients to collect failures + * @param bool $stop to be set true by-reference if the command is now sent + */ + public function onCommand(Swift_Transport_SmtpAgent $agent, $command, $codes = array(), &$failedRecipients = null, &$stop = false); + + /** + * Returns +1, -1 or 0 according to the rules for usort(). + * + * This method is called to ensure extensions can be execute in an appropriate order. + * + * @param string $esmtpKeyword to compare with + * + * @return int + */ + public function getPriorityOver($esmtpKeyword); + + /** + * Returns an array of method names which are exposed to the Esmtp class. + * + * @return string[] + */ + public function exposeMixinMethods(); + + /** + * Tells this handler to clear any buffers and reset its state. + */ + public function resetState(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php new file mode 100644 index 00000000..4106df41 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php @@ -0,0 +1,413 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Sends Messages over SMTP with ESMTP support. + * + * @author Chris Corbyn + */ +class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTransport implements Swift_Transport_SmtpAgent +{ + /** + * ESMTP extension handlers. + * + * @var Swift_Transport_EsmtpHandler[] + */ + private $_handlers = array(); + + /** + * ESMTP capabilities. + * + * @var string[] + */ + private $_capabilities = array(); + + /** + * Connection buffer parameters. + * + * @var array + */ + private $_params = array( + 'protocol' => 'tcp', + 'host' => 'localhost', + 'port' => 25, + 'timeout' => 30, + 'blocking' => 1, + 'tls' => false, + 'type' => Swift_Transport_IoBuffer::TYPE_SOCKET, + 'stream_context_options' => array(), + ); + + /** + * Creates a new EsmtpTransport using the given I/O buffer. + * + * @param Swift_Transport_IoBuffer $buf + * @param Swift_Transport_EsmtpHandler[] $extensionHandlers + * @param Swift_Events_EventDispatcher $dispatcher + */ + public function __construct(Swift_Transport_IoBuffer $buf, array $extensionHandlers, Swift_Events_EventDispatcher $dispatcher) + { + parent::__construct($buf, $dispatcher); + $this->setExtensionHandlers($extensionHandlers); + } + + /** + * Set the host to connect to. + * + * @param string $host + * + * @return Swift_Transport_EsmtpTransport + */ + public function setHost($host) + { + $this->_params['host'] = $host; + + return $this; + } + + /** + * Get the host to connect to. + * + * @return string + */ + public function getHost() + { + return $this->_params['host']; + } + + /** + * Set the port to connect to. + * + * @param int $port + * + * @return Swift_Transport_EsmtpTransport + */ + public function setPort($port) + { + $this->_params['port'] = (int) $port; + + return $this; + } + + /** + * Get the port to connect to. + * + * @return int + */ + public function getPort() + { + return $this->_params['port']; + } + + /** + * Set the connection timeout. + * + * @param int $timeout seconds + * + * @return Swift_Transport_EsmtpTransport + */ + public function setTimeout($timeout) + { + $this->_params['timeout'] = (int) $timeout; + $this->_buffer->setParam('timeout', (int) $timeout); + + return $this; + } + + /** + * Get the connection timeout. + * + * @return int + */ + public function getTimeout() + { + return $this->_params['timeout']; + } + + /** + * Set the encryption type (tls or ssl). + * + * @param string $encryption + * + * @return Swift_Transport_EsmtpTransport + */ + public function setEncryption($encryption) + { + $encryption = strtolower($encryption); + if ('tls' == $encryption) { + $this->_params['protocol'] = 'tcp'; + $this->_params['tls'] = true; + } else { + $this->_params['protocol'] = $encryption; + $this->_params['tls'] = false; + } + + return $this; + } + + /** + * Get the encryption type. + * + * @return string + */ + public function getEncryption() + { + return $this->_params['tls'] ? 'tls' : $this->_params['protocol']; + } + + /** + * Sets the stream context options. + * + * @param array $options + * + * @return Swift_Transport_EsmtpTransport + */ + public function setStreamOptions($options) + { + $this->_params['stream_context_options'] = $options; + + return $this; + } + + /** + * Returns the stream context options. + * + * @return array + */ + public function getStreamOptions() + { + return $this->_params['stream_context_options']; + } + + /** + * Sets the source IP. + * + * @param string $source + * + * @return Swift_Transport_EsmtpTransport + */ + public function setSourceIp($source) + { + $this->_params['sourceIp'] = $source; + + return $this; + } + + /** + * Returns the IP used to connect to the destination. + * + * @return string + */ + public function getSourceIp() + { + return isset($this->_params['sourceIp']) ? $this->_params['sourceIp'] : null; + } + + /** + * Set ESMTP extension handlers. + * + * @param Swift_Transport_EsmtpHandler[] $handlers + * + * @return Swift_Transport_EsmtpTransport + */ + public function setExtensionHandlers(array $handlers) + { + $assoc = array(); + foreach ($handlers as $handler) { + $assoc[$handler->getHandledKeyword()] = $handler; + } + + @uasort($assoc, array($this, '_sortHandlers')); + $this->_handlers = $assoc; + $this->_setHandlerParams(); + + return $this; + } + + /** + * Get ESMTP extension handlers. + * + * @return Swift_Transport_EsmtpHandler[] + */ + public function getExtensionHandlers() + { + return array_values($this->_handlers); + } + + /** + * Run a command against the buffer, expecting the given response codes. + * + * If no response codes are given, the response will not be validated. + * If codes are given, an exception will be thrown on an invalid response. + * + * @param string $command + * @param int[] $codes + * @param string[] $failures An array of failures by-reference + * + * @return string + */ + public function executeCommand($command, $codes = array(), &$failures = null) + { + $failures = (array) $failures; + $stopSignal = false; + $response = null; + foreach ($this->_getActiveHandlers() as $handler) { + $response = $handler->onCommand( + $this, $command, $codes, $failures, $stopSignal + ); + if ($stopSignal) { + return $response; + } + } + + return parent::executeCommand($command, $codes, $failures); + } + + // -- Mixin invocation code + + /** Mixin handling method for ESMTP handlers */ + public function __call($method, $args) + { + foreach ($this->_handlers as $handler) { + if (in_array(strtolower($method), + array_map('strtolower', (array) $handler->exposeMixinMethods()) + )) { + $return = call_user_func_array(array($handler, $method), $args); + // Allow fluid method calls + if (is_null($return) && substr($method, 0, 3) == 'set') { + return $this; + } else { + return $return; + } + } + } + trigger_error('Call to undefined method '.$method, E_USER_ERROR); + } + + /** Get the params to initialize the buffer */ + protected function _getBufferParams() + { + return $this->_params; + } + + /** Overridden to perform EHLO instead */ + protected function _doHeloCommand() + { + try { + $response = $this->executeCommand( + sprintf("EHLO %s\r\n", $this->_domain), array(250) + ); + } catch (Swift_TransportException $e) { + return parent::_doHeloCommand(); + } + + if ($this->_params['tls']) { + try { + $this->executeCommand("STARTTLS\r\n", array(220)); + + if (!$this->_buffer->startTLS()) { + throw new Swift_TransportException('Unable to connect with TLS encryption'); + } + + try { + $response = $this->executeCommand( + sprintf("EHLO %s\r\n", $this->_domain), array(250) + ); + } catch (Swift_TransportException $e) { + return parent::_doHeloCommand(); + } + } catch (Swift_TransportException $e) { + $this->_throwException($e); + } + } + + $this->_capabilities = $this->_getCapabilities($response); + $this->_setHandlerParams(); + foreach ($this->_getActiveHandlers() as $handler) { + $handler->afterEhlo($this); + } + } + + /** Overridden to add Extension support */ + protected function _doMailFromCommand($address) + { + $handlers = $this->_getActiveHandlers(); + $params = array(); + foreach ($handlers as $handler) { + $params = array_merge($params, (array) $handler->getMailParams()); + } + $paramStr = !empty($params) ? ' '.implode(' ', $params) : ''; + $this->executeCommand( + sprintf("MAIL FROM:<%s>%s\r\n", $address, $paramStr), array(250) + ); + } + + /** Overridden to add Extension support */ + protected function _doRcptToCommand($address) + { + $handlers = $this->_getActiveHandlers(); + $params = array(); + foreach ($handlers as $handler) { + $params = array_merge($params, (array) $handler->getRcptParams()); + } + $paramStr = !empty($params) ? ' '.implode(' ', $params) : ''; + $this->executeCommand( + sprintf("RCPT TO:<%s>%s\r\n", $address, $paramStr), array(250, 251, 252) + ); + } + + /** Determine ESMTP capabilities by function group */ + private function _getCapabilities($ehloResponse) + { + $capabilities = array(); + $ehloResponse = trim($ehloResponse); + $lines = explode("\r\n", $ehloResponse); + array_shift($lines); + foreach ($lines as $line) { + if (preg_match('/^[0-9]{3}[ -]([A-Z0-9-]+)((?:[ =].*)?)$/Di', $line, $matches)) { + $keyword = strtoupper($matches[1]); + $paramStr = strtoupper(ltrim($matches[2], ' =')); + $params = !empty($paramStr) ? explode(' ', $paramStr) : array(); + $capabilities[$keyword] = $params; + } + } + + return $capabilities; + } + + /** Set parameters which are used by each extension handler */ + private function _setHandlerParams() + { + foreach ($this->_handlers as $keyword => $handler) { + if (array_key_exists($keyword, $this->_capabilities)) { + $handler->setKeywordParams($this->_capabilities[$keyword]); + } + } + } + + /** Get ESMTP handlers which are currently ok to use */ + private function _getActiveHandlers() + { + $handlers = array(); + foreach ($this->_handlers as $keyword => $handler) { + if (array_key_exists($keyword, $this->_capabilities)) { + $handlers[] = $handler; + } + } + + return $handlers; + } + + /** Custom sort for extension handler ordering */ + private function _sortHandlers($a, $b) + { + return $a->getPriorityOver($b->getHandledKeyword()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php new file mode 100644 index 00000000..311a0f2a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php @@ -0,0 +1,88 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Contains a list of redundant Transports so when one fails, the next is used. + * + * @author Chris Corbyn + */ +class Swift_Transport_FailoverTransport extends Swift_Transport_LoadBalancedTransport +{ + /** + * Registered transport currently used. + * + * @var Swift_Transport + */ + private $_currentTransport; + + // needed as __construct is called from elsewhere explicitly + public function __construct() + { + parent::__construct(); + } + + /** + * Send the given Message. + * + * Recipient/sender data will be retrieved from the Message API. + * The return value is the number of recipients who were accepted for delivery. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $maxTransports = count($this->_transports); + $sent = 0; + $this->_lastUsedTransport = null; + + for ($i = 0; $i < $maxTransports + && $transport = $this->_getNextTransport(); ++$i) { + try { + if (!$transport->isStarted()) { + $transport->start(); + } + + if ($sent = $transport->send($message, $failedRecipients)) { + $this->_lastUsedTransport = $transport; + + return $sent; + } + } catch (Swift_TransportException $e) { + $this->_killCurrentTransport(); + } + } + + if (count($this->_transports) == 0) { + throw new Swift_TransportException( + 'All Transports in FailoverTransport failed, or no Transports available' + ); + } + + return $sent; + } + + protected function _getNextTransport() + { + if (!isset($this->_currentTransport)) { + $this->_currentTransport = parent::_getNextTransport(); + } + + return $this->_currentTransport; + } + + protected function _killCurrentTransport() + { + $this->_currentTransport = null; + parent::_killCurrentTransport(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/IoBuffer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/IoBuffer.php new file mode 100644 index 00000000..af97adf1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/IoBuffer.php @@ -0,0 +1,67 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Buffers input and output to a resource. + * + * @author Chris Corbyn + */ +interface Swift_Transport_IoBuffer extends Swift_InputByteStream, Swift_OutputByteStream +{ + /** A socket buffer over TCP */ + const TYPE_SOCKET = 0x0001; + + /** A process buffer with I/O support */ + const TYPE_PROCESS = 0x0010; + + /** + * Perform any initialization needed, using the given $params. + * + * Parameters will vary depending upon the type of IoBuffer used. + * + * @param array $params + */ + public function initialize(array $params); + + /** + * Set an individual param on the buffer (e.g. switching to SSL). + * + * @param string $param + * @param mixed $value + */ + public function setParam($param, $value); + + /** + * Perform any shutdown logic needed. + */ + public function terminate(); + + /** + * Set an array of string replacements which should be made on data written + * to the buffer. + * + * This could replace LF with CRLF for example. + * + * @param string[] $replacements + */ + public function setWriteTranslations(array $replacements); + + /** + * Get a line of output (including any CRLF). + * + * The $sequence number comes from any writes and may or may not be used + * depending upon the implementation. + * + * @param int $sequence of last write to scan from + * + * @return string + */ + public function readLine($sequence); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/LoadBalancedTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/LoadBalancedTransport.php new file mode 100644 index 00000000..e2adc567 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/LoadBalancedTransport.php @@ -0,0 +1,183 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Redundantly and rotationally uses several Transports when sending. + * + * @author Chris Corbyn + */ +class Swift_Transport_LoadBalancedTransport implements Swift_Transport +{ + /** + * Transports which are deemed useless. + * + * @var Swift_Transport[] + */ + private $_deadTransports = array(); + + /** + * The Transports which are used in rotation. + * + * @var Swift_Transport[] + */ + protected $_transports = array(); + + /** + * The Transport used in the last successful send operation. + * + * @var Swift_Transport + */ + protected $_lastUsedTransport = null; + + // needed as __construct is called from elsewhere explicitly + public function __construct() + { + } + + /** + * Set $transports to delegate to. + * + * @param Swift_Transport[] $transports + */ + public function setTransports(array $transports) + { + $this->_transports = $transports; + $this->_deadTransports = array(); + } + + /** + * Get $transports to delegate to. + * + * @return Swift_Transport[] + */ + public function getTransports() + { + return array_merge($this->_transports, $this->_deadTransports); + } + + /** + * Get the Transport used in the last successful send operation. + * + * @return Swift_Transport + */ + public function getLastUsedTransport() + { + return $this->_lastUsedTransport; + } + + /** + * Test if this Transport mechanism has started. + * + * @return bool + */ + public function isStarted() + { + return count($this->_transports) > 0; + } + + /** + * Start this Transport mechanism. + */ + public function start() + { + $this->_transports = array_merge($this->_transports, $this->_deadTransports); + } + + /** + * Stop this Transport mechanism. + */ + public function stop() + { + foreach ($this->_transports as $transport) { + $transport->stop(); + } + } + + /** + * Send the given Message. + * + * Recipient/sender data will be retrieved from the Message API. + * The return value is the number of recipients who were accepted for delivery. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $maxTransports = count($this->_transports); + $sent = 0; + $this->_lastUsedTransport = null; + + for ($i = 0; $i < $maxTransports + && $transport = $this->_getNextTransport(); ++$i) { + try { + if (!$transport->isStarted()) { + $transport->start(); + } + if ($sent = $transport->send($message, $failedRecipients)) { + $this->_lastUsedTransport = $transport; + break; + } + } catch (Swift_TransportException $e) { + $this->_killCurrentTransport(); + } + } + + if (count($this->_transports) == 0) { + throw new Swift_TransportException( + 'All Transports in LoadBalancedTransport failed, or no Transports available' + ); + } + + return $sent; + } + + /** + * Register a plugin. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + foreach ($this->_transports as $transport) { + $transport->registerPlugin($plugin); + } + } + + /** + * Rotates the transport list around and returns the first instance. + * + * @return Swift_Transport + */ + protected function _getNextTransport() + { + if ($next = array_shift($this->_transports)) { + $this->_transports[] = $next; + } + + return $next; + } + + /** + * Tag the currently used (top of stack) transport as dead/useless. + */ + protected function _killCurrentTransport() + { + if ($transport = array_pop($this->_transports)) { + try { + $transport->stop(); + } catch (Exception $e) { + } + $this->_deadTransports[] = $transport; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailInvoker.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailInvoker.php new file mode 100644 index 00000000..77489ced --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailInvoker.php @@ -0,0 +1,32 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * This interface intercepts calls to the mail() function. + * + * @author Chris Corbyn + */ +interface Swift_Transport_MailInvoker +{ + /** + * Send mail via the mail() function. + * + * This method takes the same arguments as PHP mail(). + * + * @param string $to + * @param string $subject + * @param string $body + * @param string $headers + * @param string $extraParams + * + * @return bool + */ + public function mail($to, $subject, $body, $headers = null, $extraParams = null); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailTransport.php new file mode 100644 index 00000000..32a11efa --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailTransport.php @@ -0,0 +1,295 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Sends Messages using the mail() function. + * + * It is advised that users do not use this transport if at all possible + * since a number of plugin features cannot be used in conjunction with this + * transport due to the internal interface in PHP itself. + * + * The level of error reporting with this transport is incredibly weak, again + * due to limitations of PHP's internal mail() function. You'll get an + * all-or-nothing result from sending. + * + * @author Chris Corbyn + * + * @deprecated since 5.4.5 (to be removed in 6.0) + */ +class Swift_Transport_MailTransport implements Swift_Transport +{ + /** Additional parameters to pass to mail() */ + private $_extraParams = '-f%s'; + + /** The event dispatcher from the plugin API */ + private $_eventDispatcher; + + /** An invoker that calls the mail() function */ + private $_invoker; + + /** + * Create a new MailTransport with the $log. + * + * @param Swift_Transport_MailInvoker $invoker + * @param Swift_Events_EventDispatcher $eventDispatcher + */ + public function __construct(Swift_Transport_MailInvoker $invoker, Swift_Events_EventDispatcher $eventDispatcher) + { + @trigger_error(sprintf('The %s class is deprecated since version 5.4.5 and will be removed in 6.0. Use the Sendmail or SMTP transport instead.', __CLASS__), E_USER_DEPRECATED); + + $this->_invoker = $invoker; + $this->_eventDispatcher = $eventDispatcher; + } + + /** + * Not used. + */ + public function isStarted() + { + return false; + } + + /** + * Not used. + */ + public function start() + { + } + + /** + * Not used. + */ + public function stop() + { + } + + /** + * Set the additional parameters used on the mail() function. + * + * This string is formatted for sprintf() where %s is the sender address. + * + * @param string $params + * + * @return Swift_Transport_MailTransport + */ + public function setExtraParams($params) + { + $this->_extraParams = $params; + + return $this; + } + + /** + * Get the additional parameters used on the mail() function. + * + * This string is formatted for sprintf() where %s is the sender address. + * + * @return string + */ + public function getExtraParams() + { + return $this->_extraParams; + } + + /** + * Send the given Message. + * + * Recipient/sender data will be retrieved from the Message API. + * The return value is the number of recipients who were accepted for delivery. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $failedRecipients = (array) $failedRecipients; + + if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt->bubbleCancelled()) { + return 0; + } + } + + $count = ( + count((array) $message->getTo()) + + count((array) $message->getCc()) + + count((array) $message->getBcc()) + ); + + $toHeader = $message->getHeaders()->get('To'); + $subjectHeader = $message->getHeaders()->get('Subject'); + + if (0 === $count) { + $this->_throwException(new Swift_TransportException('Cannot send message without a recipient')); + } + $to = $toHeader ? $toHeader->getFieldBody() : ''; + $subject = $subjectHeader ? $subjectHeader->getFieldBody() : ''; + + $reversePath = $this->_getReversePath($message); + + // Remove headers that would otherwise be duplicated + $message->getHeaders()->remove('To'); + $message->getHeaders()->remove('Subject'); + + $messageStr = $message->toString(); + + if ($toHeader) { + $message->getHeaders()->set($toHeader); + } + $message->getHeaders()->set($subjectHeader); + + // Separate headers from body + if (false !== $endHeaders = strpos($messageStr, "\r\n\r\n")) { + $headers = substr($messageStr, 0, $endHeaders)."\r\n"; //Keep last EOL + $body = substr($messageStr, $endHeaders + 4); + } else { + $headers = $messageStr."\r\n"; + $body = ''; + } + + unset($messageStr); + + if ("\r\n" != PHP_EOL) { + // Non-windows (not using SMTP) + $headers = str_replace("\r\n", PHP_EOL, $headers); + $subject = str_replace("\r\n", PHP_EOL, $subject); + $body = str_replace("\r\n", PHP_EOL, $body); + } else { + // Windows, using SMTP + $headers = str_replace("\r\n.", "\r\n..", $headers); + $subject = str_replace("\r\n.", "\r\n..", $subject); + $body = str_replace("\r\n.", "\r\n..", $body); + } + + if ($this->_invoker->mail($to, $subject, $body, $headers, $this->_formatExtraParams($this->_extraParams, $reversePath))) { + if ($evt) { + $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS); + $evt->setFailedRecipients($failedRecipients); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + } else { + $failedRecipients = array_merge( + $failedRecipients, + array_keys((array) $message->getTo()), + array_keys((array) $message->getCc()), + array_keys((array) $message->getBcc()) + ); + + if ($evt) { + $evt->setResult(Swift_Events_SendEvent::RESULT_FAILED); + $evt->setFailedRecipients($failedRecipients); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + $message->generateId(); + + $count = 0; + } + + return $count; + } + + /** + * Register a plugin. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_eventDispatcher->bindEventListener($plugin); + } + + /** Throw a TransportException, first sending it to any listeners */ + protected function _throwException(Swift_TransportException $e) + { + if ($evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e)) { + $this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown'); + if (!$evt->bubbleCancelled()) { + throw $e; + } + } else { + throw $e; + } + } + + /** Determine the best-use reverse path for this message */ + private function _getReversePath(Swift_Mime_Message $message) + { + $return = $message->getReturnPath(); + $sender = $message->getSender(); + $from = $message->getFrom(); + $path = null; + if (!empty($return)) { + $path = $return; + } elseif (!empty($sender)) { + $keys = array_keys($sender); + $path = array_shift($keys); + } elseif (!empty($from)) { + $keys = array_keys($from); + $path = array_shift($keys); + } + + return $path; + } + + /** + * Fix CVE-2016-10074 by disallowing potentially unsafe shell characters. + * + * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows. + * + * @param string $string The string to be validated + * + * @return bool + */ + private function _isShellSafe($string) + { + // Future-proof + if (escapeshellcmd($string) !== $string || !in_array(escapeshellarg($string), array("'$string'", "\"$string\""))) { + return false; + } + + $length = strlen($string); + for ($i = 0; $i < $length; ++$i) { + $c = $string[$i]; + // All other characters have a special meaning in at least one common shell, including = and +. + // Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here. + // Note that this does permit non-Latin alphanumeric characters based on the current locale. + if (!ctype_alnum($c) && strpos('@_-.', $c) === false) { + return false; + } + } + + return true; + } + + /** + * Return php mail extra params to use for invoker->mail. + * + * @param $extraParams + * @param $reversePath + * + * @return string|null + */ + private function _formatExtraParams($extraParams, $reversePath) + { + if (false !== strpos($extraParams, '-f%s')) { + if (empty($reversePath) || false === $this->_isShellSafe($reversePath)) { + $extraParams = str_replace('-f%s', '', $extraParams); + } else { + $extraParams = sprintf($extraParams, $reversePath); + } + } + + return !empty($extraParams) ? $extraParams : null; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/NullTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/NullTransport.php new file mode 100644 index 00000000..ad20e0e5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/NullTransport.php @@ -0,0 +1,93 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2009 Fabien Potencier <fabien.potencier@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Pretends messages have been sent, but just ignores them. + * + * @author Fabien Potencier + */ +class Swift_Transport_NullTransport implements Swift_Transport +{ + /** The event dispatcher from the plugin API */ + private $_eventDispatcher; + + /** + * Constructor. + */ + public function __construct(Swift_Events_EventDispatcher $eventDispatcher) + { + $this->_eventDispatcher = $eventDispatcher; + } + + /** + * Tests if this Transport mechanism has started. + * + * @return bool + */ + public function isStarted() + { + return true; + } + + /** + * Starts this Transport mechanism. + */ + public function start() + { + } + + /** + * Stops this Transport mechanism. + */ + public function stop() + { + } + + /** + * Sends the given message. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int The number of sent emails + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt->bubbleCancelled()) { + return 0; + } + } + + if ($evt) { + $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + $count = ( + count((array) $message->getTo()) + + count((array) $message->getCc()) + + count((array) $message->getBcc()) + ); + + return $count; + } + + /** + * Register a plugin. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_eventDispatcher->bindEventListener($plugin); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php new file mode 100644 index 00000000..34ac4ce3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php @@ -0,0 +1,160 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * SendmailTransport for sending mail through a Sendmail/Postfix (etc..) binary. + * + * Supported modes are -bs and -t, with any additional flags desired. + * It is advised to use -bs mode since error reporting with -t mode is not + * possible. + * + * @author Chris Corbyn + */ +class Swift_Transport_SendmailTransport extends Swift_Transport_AbstractSmtpTransport +{ + /** + * Connection buffer parameters. + * + * @var array + */ + private $_params = array( + 'timeout' => 30, + 'blocking' => 1, + 'command' => '/usr/sbin/sendmail -bs', + 'type' => Swift_Transport_IoBuffer::TYPE_PROCESS, + ); + + /** + * Create a new SendmailTransport with $buf for I/O. + * + * @param Swift_Transport_IoBuffer $buf + * @param Swift_Events_EventDispatcher $dispatcher + */ + public function __construct(Swift_Transport_IoBuffer $buf, Swift_Events_EventDispatcher $dispatcher) + { + parent::__construct($buf, $dispatcher); + } + + /** + * Start the standalone SMTP session if running in -bs mode. + */ + public function start() + { + if (false !== strpos($this->getCommand(), ' -bs')) { + parent::start(); + } + } + + /** + * Set the command to invoke. + * + * If using -t mode you are strongly advised to include -oi or -i in the flags. + * For example: /usr/sbin/sendmail -oi -t + * Swift will append a -f<sender> flag if one is not present. + * + * The recommended mode is "-bs" since it is interactive and failure notifications + * are hence possible. + * + * @param string $command + * + * @return Swift_Transport_SendmailTransport + */ + public function setCommand($command) + { + $this->_params['command'] = $command; + + return $this; + } + + /** + * Get the sendmail command which will be invoked. + * + * @return string + */ + public function getCommand() + { + return $this->_params['command']; + } + + /** + * Send the given Message. + * + * Recipient/sender data will be retrieved from the Message API. + * + * The return value is the number of recipients who were accepted for delivery. + * NOTE: If using 'sendmail -t' you will not be aware of any failures until + * they bounce (i.e. send() will always return 100% success). + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $failedRecipients = (array) $failedRecipients; + $command = $this->getCommand(); + $buffer = $this->getBuffer(); + $count = 0; + + if (false !== strpos($command, ' -t')) { + if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt->bubbleCancelled()) { + return 0; + } + } + + if (false === strpos($command, ' -f')) { + $command .= ' -f'.escapeshellarg($this->_getReversePath($message)); + } + + $buffer->initialize(array_merge($this->_params, array('command' => $command))); + + if (false === strpos($command, ' -i') && false === strpos($command, ' -oi')) { + $buffer->setWriteTranslations(array("\r\n" => "\n", "\n." => "\n..")); + } else { + $buffer->setWriteTranslations(array("\r\n" => "\n")); + } + + $count = count((array) $message->getTo()) + + count((array) $message->getCc()) + + count((array) $message->getBcc()) + ; + $message->toByteStream($buffer); + $buffer->flushBuffers(); + $buffer->setWriteTranslations(array()); + $buffer->terminate(); + + if ($evt) { + $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS); + $evt->setFailedRecipients($failedRecipients); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + $message->generateId(); + } elseif (false !== strpos($command, ' -bs')) { + $count = parent::send($message, $failedRecipients); + } else { + $this->_throwException(new Swift_TransportException( + 'Unsupported sendmail command flags ['.$command.']. '. + 'Must be one of "-bs" or "-t" but can include additional flags.' + )); + } + + return $count; + } + + /** Get the params to initialize the buffer */ + protected function _getBufferParams() + { + return $this->_params; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SimpleMailInvoker.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SimpleMailInvoker.php new file mode 100644 index 00000000..4cab66bd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SimpleMailInvoker.php @@ -0,0 +1,39 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * This is the implementation class for {@link Swift_Transport_MailInvoker}. + * + * @author Chris Corbyn + */ +class Swift_Transport_SimpleMailInvoker implements Swift_Transport_MailInvoker +{ + /** + * Send mail via the mail() function. + * + * This method takes the same arguments as PHP mail(). + * + * @param string $to + * @param string $subject + * @param string $body + * @param string $headers + * @param string $extraParams + * + * @return bool + */ + public function mail($to, $subject, $body, $headers = null, $extraParams = null) + { + if (!ini_get('safe_mode')) { + return @mail($to, $subject, $body, $headers, $extraParams); + } + + return @mail($to, $subject, $body, $headers); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SmtpAgent.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SmtpAgent.php new file mode 100644 index 00000000..90e913f8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SmtpAgent.php @@ -0,0 +1,36 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Wraps an IoBuffer to send/receive SMTP commands/responses. + * + * @author Chris Corbyn + */ +interface Swift_Transport_SmtpAgent +{ + /** + * Get the IoBuffer where read/writes are occurring. + * + * @return Swift_Transport_IoBuffer + */ + public function getBuffer(); + + /** + * Run a command against the buffer, expecting the given response codes. + * + * If no response codes are given, the response will not be validated. + * If codes are given, an exception will be thrown on an invalid response. + * + * @param string $command + * @param int[] $codes + * @param string[] $failures An array of failures by-reference + */ + public function executeCommand($command, $codes = array(), &$failures = null); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SpoolTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SpoolTransport.php new file mode 100644 index 00000000..55a79924 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SpoolTransport.php @@ -0,0 +1,117 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2009 Fabien Potencier <fabien.potencier@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages in a queue. + * + * @author Fabien Potencier + */ +class Swift_Transport_SpoolTransport implements Swift_Transport +{ + /** The spool instance */ + private $_spool; + + /** The event dispatcher from the plugin API */ + private $_eventDispatcher; + + /** + * Constructor. + */ + public function __construct(Swift_Events_EventDispatcher $eventDispatcher, Swift_Spool $spool = null) + { + $this->_eventDispatcher = $eventDispatcher; + $this->_spool = $spool; + } + + /** + * Sets the spool object. + * + * @param Swift_Spool $spool + * + * @return Swift_Transport_SpoolTransport + */ + public function setSpool(Swift_Spool $spool) + { + $this->_spool = $spool; + + return $this; + } + + /** + * Get the spool object. + * + * @return Swift_Spool + */ + public function getSpool() + { + return $this->_spool; + } + + /** + * Tests if this Transport mechanism has started. + * + * @return bool + */ + public function isStarted() + { + return true; + } + + /** + * Starts this Transport mechanism. + */ + public function start() + { + } + + /** + * Stops this Transport mechanism. + */ + public function stop() + { + } + + /** + * Sends the given message. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int The number of sent e-mail's + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt->bubbleCancelled()) { + return 0; + } + } + + $success = $this->_spool->queueMessage($message); + + if ($evt) { + $evt->setResult($success ? Swift_Events_SendEvent::RESULT_SPOOLED : Swift_Events_SendEvent::RESULT_FAILED); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + return 1; + } + + /** + * Register a plugin. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_eventDispatcher->bindEventListener($plugin); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php new file mode 100644 index 00000000..5134ea48 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php @@ -0,0 +1,325 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A generic IoBuffer implementation supporting remote sockets and local processes. + * + * @author Chris Corbyn + */ +class Swift_Transport_StreamBuffer extends Swift_ByteStream_AbstractFilterableInputStream implements Swift_Transport_IoBuffer +{ + /** A primary socket */ + private $_stream; + + /** The input stream */ + private $_in; + + /** The output stream */ + private $_out; + + /** Buffer initialization parameters */ + private $_params = array(); + + /** The ReplacementFilterFactory */ + private $_replacementFactory; + + /** Translations performed on data being streamed into the buffer */ + private $_translations = array(); + + /** + * Create a new StreamBuffer using $replacementFactory for transformations. + * + * @param Swift_ReplacementFilterFactory $replacementFactory + */ + public function __construct(Swift_ReplacementFilterFactory $replacementFactory) + { + $this->_replacementFactory = $replacementFactory; + } + + /** + * Perform any initialization needed, using the given $params. + * + * Parameters will vary depending upon the type of IoBuffer used. + * + * @param array $params + */ + public function initialize(array $params) + { + $this->_params = $params; + switch ($params['type']) { + case self::TYPE_PROCESS: + $this->_establishProcessConnection(); + break; + case self::TYPE_SOCKET: + default: + $this->_establishSocketConnection(); + break; + } + } + + /** + * Set an individual param on the buffer (e.g. switching to SSL). + * + * @param string $param + * @param mixed $value + */ + public function setParam($param, $value) + { + if (isset($this->_stream)) { + switch ($param) { + case 'timeout': + if ($this->_stream) { + stream_set_timeout($this->_stream, $value); + } + break; + + case 'blocking': + if ($this->_stream) { + stream_set_blocking($this->_stream, 1); + } + + } + } + $this->_params[$param] = $value; + } + + public function startTLS() + { + return stream_socket_enable_crypto($this->_stream, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); + } + + /** + * Perform any shutdown logic needed. + */ + public function terminate() + { + if (isset($this->_stream)) { + switch ($this->_params['type']) { + case self::TYPE_PROCESS: + fclose($this->_in); + fclose($this->_out); + proc_close($this->_stream); + break; + case self::TYPE_SOCKET: + default: + fclose($this->_stream); + break; + } + } + $this->_stream = null; + $this->_out = null; + $this->_in = null; + } + + /** + * Set an array of string replacements which should be made on data written + * to the buffer. + * + * This could replace LF with CRLF for example. + * + * @param string[] $replacements + */ + public function setWriteTranslations(array $replacements) + { + foreach ($this->_translations as $search => $replace) { + if (!isset($replacements[$search])) { + $this->removeFilter($search); + unset($this->_translations[$search]); + } + } + + foreach ($replacements as $search => $replace) { + if (!isset($this->_translations[$search])) { + $this->addFilter( + $this->_replacementFactory->createFilter($search, $replace), $search + ); + $this->_translations[$search] = true; + } + } + } + + /** + * Get a line of output (including any CRLF). + * + * The $sequence number comes from any writes and may or may not be used + * depending upon the implementation. + * + * @param int $sequence of last write to scan from + * + * @throws Swift_IoException + * + * @return string + */ + public function readLine($sequence) + { + if (isset($this->_out) && !feof($this->_out)) { + $line = fgets($this->_out); + if (strlen($line) == 0) { + $metas = stream_get_meta_data($this->_out); + if ($metas['timed_out']) { + throw new Swift_IoException( + 'Connection to '. + $this->_getReadConnectionDescription(). + ' Timed Out' + ); + } + } + + return $line; + } + } + + /** + * Reads $length bytes from the stream into a string and moves the pointer + * through the stream by $length. + * + * If less bytes exist than are requested the remaining bytes are given instead. + * If no bytes are remaining at all, boolean false is returned. + * + * @param int $length + * + * @throws Swift_IoException + * + * @return string|bool + */ + public function read($length) + { + if (isset($this->_out) && !feof($this->_out)) { + $ret = fread($this->_out, $length); + if (strlen($ret) == 0) { + $metas = stream_get_meta_data($this->_out); + if ($metas['timed_out']) { + throw new Swift_IoException( + 'Connection to '. + $this->_getReadConnectionDescription(). + ' Timed Out' + ); + } + } + + return $ret; + } + } + + /** Not implemented */ + public function setReadPointer($byteOffset) + { + } + + /** Flush the stream contents */ + protected function _flush() + { + if (isset($this->_in)) { + fflush($this->_in); + } + } + + /** Write this bytes to the stream */ + protected function _commit($bytes) + { + if (isset($this->_in)) { + $bytesToWrite = strlen($bytes); + $totalBytesWritten = 0; + + while ($totalBytesWritten < $bytesToWrite) { + $bytesWritten = fwrite($this->_in, substr($bytes, $totalBytesWritten)); + if (false === $bytesWritten || 0 === $bytesWritten) { + break; + } + + $totalBytesWritten += $bytesWritten; + } + + if ($totalBytesWritten > 0) { + return ++$this->_sequence; + } + } + } + + /** + * Establishes a connection to a remote server. + */ + private function _establishSocketConnection() + { + $host = $this->_params['host']; + if (!empty($this->_params['protocol'])) { + $host = $this->_params['protocol'].'://'.$host; + } + $timeout = 15; + if (!empty($this->_params['timeout'])) { + $timeout = $this->_params['timeout']; + } + $options = array(); + if (!empty($this->_params['sourceIp'])) { + $options['socket']['bindto'] = $this->_params['sourceIp'].':0'; + } + if (isset($this->_params['stream_context_options'])) { + $options = array_merge($options, $this->_params['stream_context_options']); + } + $streamContext = stream_context_create($options); + $this->_stream = @stream_socket_client($host.':'.$this->_params['port'], $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $streamContext); + if (false === $this->_stream) { + throw new Swift_TransportException( + 'Connection could not be established with host '.$this->_params['host']. + ' ['.$errstr.' #'.$errno.']' + ); + } + if (!empty($this->_params['blocking'])) { + stream_set_blocking($this->_stream, 1); + } else { + stream_set_blocking($this->_stream, 0); + } + stream_set_timeout($this->_stream, $timeout); + $this->_in = &$this->_stream; + $this->_out = &$this->_stream; + } + + /** + * Opens a process for input/output. + */ + private function _establishProcessConnection() + { + $command = $this->_params['command']; + $descriptorSpec = array( + 0 => array('pipe', 'r'), + 1 => array('pipe', 'w'), + 2 => array('pipe', 'w'), + ); + $this->_stream = proc_open($command, $descriptorSpec, $pipes); + stream_set_blocking($pipes[2], 0); + if ($err = stream_get_contents($pipes[2])) { + throw new Swift_TransportException( + 'Process could not be started ['.$err.']' + ); + } + $this->_in = &$pipes[0]; + $this->_out = &$pipes[1]; + } + + private function _getReadConnectionDescription() + { + switch ($this->_params['type']) { + case self::TYPE_PROCESS: + return 'Process '.$this->_params['command']; + break; + + case self::TYPE_SOCKET: + default: + $host = $this->_params['host']; + if (!empty($this->_params['protocol'])) { + $host = $this->_params['protocol'].'://'.$host; + } + $host .= ':'.$this->_params['port']; + + return $host; + break; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/TransportException.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/TransportException.php new file mode 100644 index 00000000..4ae2412e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/TransportException.php @@ -0,0 +1,29 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * TransportException thrown when an error occurs in the Transport subsystem. + * + * @author Chris Corbyn + */ +class Swift_TransportException extends Swift_IoException +{ + /** + * Create a new TransportException with $message. + * + * @param string $message + * @param int $code + * @param Exception $previous + */ + public function __construct($message, $code = 0, Exception $previous = null) + { + parent::__construct($message, $code, $previous); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Validate.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Validate.php new file mode 100644 index 00000000..e16c212c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Validate.php @@ -0,0 +1,43 @@ +<?php + +/* + * This file is part of SwiftMailer. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Utility Class allowing users to simply check expressions again Swift Grammar. + * + * @author Xavier De Cock <xdecock@gmail.com> + */ +class Swift_Validate +{ + /** + * Grammar Object. + * + * @var Swift_Mime_Grammar + */ + private static $grammar = null; + + /** + * Checks if an e-mail address matches the current grammars. + * + * @param string $email + * + * @return bool + */ + public static function email($email) + { + if (self::$grammar === null) { + self::$grammar = Swift_DependencyContainer::getInstance() + ->lookup('mime.grammar'); + } + + return (bool) preg_match( + '/^'.self::$grammar->getDefinition('addr-spec').'$/D', + $email + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/dependency_maps/cache_deps.php b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/cache_deps.php new file mode 100644 index 00000000..6023448e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/cache_deps.php @@ -0,0 +1,23 @@ +<?php + +Swift_DependencyContainer::getInstance() + ->register('cache') + ->asAliasOf('cache.array') + + ->register('tempdir') + ->asValue('/tmp') + + ->register('cache.null') + ->asSharedInstanceOf('Swift_KeyCache_NullKeyCache') + + ->register('cache.array') + ->asSharedInstanceOf('Swift_KeyCache_ArrayKeyCache') + ->withDependencies(array('cache.inputstream')) + + ->register('cache.disk') + ->asSharedInstanceOf('Swift_KeyCache_DiskKeyCache') + ->withDependencies(array('cache.inputstream', 'tempdir')) + + ->register('cache.inputstream') + ->asNewInstanceOf('Swift_KeyCache_SimpleKeyCacheInputStream') +; diff --git a/vendor/swiftmailer/swiftmailer/lib/dependency_maps/message_deps.php b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/message_deps.php new file mode 100644 index 00000000..64d69d21 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/message_deps.php @@ -0,0 +1,9 @@ +<?php + +Swift_DependencyContainer::getInstance() + ->register('message.message') + ->asNewInstanceOf('Swift_Message') + + ->register('message.mimepart') + ->asNewInstanceOf('Swift_MimePart') +; diff --git a/vendor/swiftmailer/swiftmailer/lib/dependency_maps/mime_deps.php b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/mime_deps.php new file mode 100644 index 00000000..56169c92 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/mime_deps.php @@ -0,0 +1,123 @@ +<?php + +require __DIR__.'/../mime_types.php'; + +Swift_DependencyContainer::getInstance() + ->register('properties.charset') + ->asValue('utf-8') + + ->register('mime.grammar') + ->asSharedInstanceOf('Swift_Mime_Grammar') + + ->register('mime.message') + ->asNewInstanceOf('Swift_Mime_SimpleMessage') + ->withDependencies(array( + 'mime.headerset', + 'mime.qpcontentencoder', + 'cache', + 'mime.grammar', + 'properties.charset', + )) + + ->register('mime.part') + ->asNewInstanceOf('Swift_Mime_MimePart') + ->withDependencies(array( + 'mime.headerset', + 'mime.qpcontentencoder', + 'cache', + 'mime.grammar', + 'properties.charset', + )) + + ->register('mime.attachment') + ->asNewInstanceOf('Swift_Mime_Attachment') + ->withDependencies(array( + 'mime.headerset', + 'mime.base64contentencoder', + 'cache', + 'mime.grammar', + )) + ->addConstructorValue($swift_mime_types) + + ->register('mime.embeddedfile') + ->asNewInstanceOf('Swift_Mime_EmbeddedFile') + ->withDependencies(array( + 'mime.headerset', + 'mime.base64contentencoder', + 'cache', + 'mime.grammar', + )) + ->addConstructorValue($swift_mime_types) + + ->register('mime.headerfactory') + ->asNewInstanceOf('Swift_Mime_SimpleHeaderFactory') + ->withDependencies(array( + 'mime.qpheaderencoder', + 'mime.rfc2231encoder', + 'mime.grammar', + 'properties.charset', + )) + + ->register('mime.headerset') + ->asNewInstanceOf('Swift_Mime_SimpleHeaderSet') + ->withDependencies(array('mime.headerfactory', 'properties.charset')) + + ->register('mime.qpheaderencoder') + ->asNewInstanceOf('Swift_Mime_HeaderEncoder_QpHeaderEncoder') + ->withDependencies(array('mime.charstream')) + + ->register('mime.base64headerencoder') + ->asNewInstanceOf('Swift_Mime_HeaderEncoder_Base64HeaderEncoder') + ->withDependencies(array('mime.charstream')) + + ->register('mime.charstream') + ->asNewInstanceOf('Swift_CharacterStream_NgCharacterStream') + ->withDependencies(array('mime.characterreaderfactory', 'properties.charset')) + + ->register('mime.bytecanonicalizer') + ->asSharedInstanceOf('Swift_StreamFilters_ByteArrayReplacementFilter') + ->addConstructorValue(array(array(0x0D, 0x0A), array(0x0D), array(0x0A))) + ->addConstructorValue(array(array(0x0A), array(0x0A), array(0x0D, 0x0A))) + + ->register('mime.characterreaderfactory') + ->asSharedInstanceOf('Swift_CharacterReaderFactory_SimpleCharacterReaderFactory') + + ->register('mime.safeqpcontentencoder') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_QpContentEncoder') + ->withDependencies(array('mime.charstream', 'mime.bytecanonicalizer')) + + ->register('mime.rawcontentencoder') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_RawContentEncoder') + + ->register('mime.nativeqpcontentencoder') + ->withDependencies(array('properties.charset')) + ->asNewInstanceOf('Swift_Mime_ContentEncoder_NativeQpContentEncoder') + + ->register('mime.qpcontentencoderproxy') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_QpContentEncoderProxy') + ->withDependencies(array('mime.safeqpcontentencoder', 'mime.nativeqpcontentencoder', 'properties.charset')) + + ->register('mime.7bitcontentencoder') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_PlainContentEncoder') + ->addConstructorValue('7bit') + ->addConstructorValue(true) + + ->register('mime.8bitcontentencoder') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_PlainContentEncoder') + ->addConstructorValue('8bit') + ->addConstructorValue(true) + + ->register('mime.base64contentencoder') + ->asSharedInstanceOf('Swift_Mime_ContentEncoder_Base64ContentEncoder') + + ->register('mime.rfc2231encoder') + ->asNewInstanceOf('Swift_Encoder_Rfc2231Encoder') + ->withDependencies(array('mime.charstream')) + + // As of PHP 5.4.7, the quoted_printable_encode() function behaves correctly. + // see https://github.com/php/php-src/commit/18bb426587d62f93c54c40bf8535eb8416603629 + ->register('mime.qpcontentencoder') + ->asAliasOf(version_compare(phpversion(), '5.4.7', '>=') ? 'mime.qpcontentencoderproxy' : 'mime.safeqpcontentencoder') +; + +unset($swift_mime_types); diff --git a/vendor/swiftmailer/swiftmailer/lib/dependency_maps/transport_deps.php b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/transport_deps.php new file mode 100644 index 00000000..77e432cf --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/transport_deps.php @@ -0,0 +1,76 @@ +<?php + +Swift_DependencyContainer::getInstance() + ->register('transport.smtp') + ->asNewInstanceOf('Swift_Transport_EsmtpTransport') + ->withDependencies(array( + 'transport.buffer', + array('transport.authhandler'), + 'transport.eventdispatcher', + )) + + ->register('transport.sendmail') + ->asNewInstanceOf('Swift_Transport_SendmailTransport') + ->withDependencies(array( + 'transport.buffer', + 'transport.eventdispatcher', + )) + + ->register('transport.mail') + ->asNewInstanceOf('Swift_Transport_MailTransport') + ->withDependencies(array('transport.mailinvoker', 'transport.eventdispatcher')) + + ->register('transport.loadbalanced') + ->asNewInstanceOf('Swift_Transport_LoadBalancedTransport') + + ->register('transport.failover') + ->asNewInstanceOf('Swift_Transport_FailoverTransport') + + ->register('transport.spool') + ->asNewInstanceOf('Swift_Transport_SpoolTransport') + ->withDependencies(array('transport.eventdispatcher')) + + ->register('transport.null') + ->asNewInstanceOf('Swift_Transport_NullTransport') + ->withDependencies(array('transport.eventdispatcher')) + + ->register('transport.mailinvoker') + ->asSharedInstanceOf('Swift_Transport_SimpleMailInvoker') + + ->register('transport.buffer') + ->asNewInstanceOf('Swift_Transport_StreamBuffer') + ->withDependencies(array('transport.replacementfactory')) + + ->register('transport.authhandler') + ->asNewInstanceOf('Swift_Transport_Esmtp_AuthHandler') + ->withDependencies(array( + array( + 'transport.crammd5auth', + 'transport.loginauth', + 'transport.plainauth', + 'transport.ntlmauth', + 'transport.xoauth2auth', + ), + )) + + ->register('transport.crammd5auth') + ->asNewInstanceOf('Swift_Transport_Esmtp_Auth_CramMd5Authenticator') + + ->register('transport.loginauth') + ->asNewInstanceOf('Swift_Transport_Esmtp_Auth_LoginAuthenticator') + + ->register('transport.plainauth') + ->asNewInstanceOf('Swift_Transport_Esmtp_Auth_PlainAuthenticator') + + ->register('transport.xoauth2auth') + ->asNewInstanceOf('Swift_Transport_Esmtp_Auth_XOAuth2Authenticator') + + ->register('transport.ntlmauth') + ->asNewInstanceOf('Swift_Transport_Esmtp_Auth_NTLMAuthenticator') + + ->register('transport.eventdispatcher') + ->asNewInstanceOf('Swift_Events_SimpleEventDispatcher') + + ->register('transport.replacementfactory') + ->asSharedInstanceOf('Swift_StreamFilters_StringReplacementFilterFactory') +; diff --git a/vendor/swiftmailer/swiftmailer/lib/mime_types.php b/vendor/swiftmailer/swiftmailer/lib/mime_types.php new file mode 100644 index 00000000..2d7b98dc --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/mime_types.php @@ -0,0 +1,1007 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * autogenerated using http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types + * and https://raw.github.com/minad/mimemagic/master/script/freedesktop.org.xml + */ + +/* + * List of MIME type automatically detected in Swift Mailer. + */ + +// You may add or take away what you like (lowercase required) + +$swift_mime_types = array( + '3dml' => 'text/vnd.in3d.3dml', + '3ds' => 'image/x-3ds', + '3g2' => 'video/3gpp2', + '3gp' => 'video/3gpp', + '7z' => 'application/x-7z-compressed', + 'aab' => 'application/x-authorware-bin', + 'aac' => 'audio/x-aac', + 'aam' => 'application/x-authorware-map', + 'aas' => 'application/x-authorware-seg', + 'abw' => 'application/x-abiword', + 'ac' => 'application/pkix-attr-cert', + 'acc' => 'application/vnd.americandynamics.acc', + 'ace' => 'application/x-ace-compressed', + 'acu' => 'application/vnd.acucobol', + 'acutc' => 'application/vnd.acucorp', + 'adp' => 'audio/adpcm', + 'aep' => 'application/vnd.audiograph', + 'afm' => 'application/x-font-type1', + 'afp' => 'application/vnd.ibm.modcap', + 'ahead' => 'application/vnd.ahead.space', + 'ai' => 'application/postscript', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'air' => 'application/vnd.adobe.air-application-installer-package+zip', + 'ait' => 'application/vnd.dvb.ait', + 'ami' => 'application/vnd.amiga.ami', + 'apk' => 'application/vnd.android.package-archive', + 'appcache' => 'text/cache-manifest', + 'apr' => 'application/vnd.lotus-approach', + 'aps' => 'application/postscript', + 'arc' => 'application/x-freearc', + 'asc' => 'application/pgp-signature', + 'asf' => 'video/x-ms-asf', + 'asm' => 'text/x-asm', + 'aso' => 'application/vnd.accpac.simply.aso', + 'asx' => 'video/x-ms-asf', + 'atc' => 'application/vnd.acucorp', + 'atom' => 'application/atom+xml', + 'atomcat' => 'application/atomcat+xml', + 'atomsvc' => 'application/atomsvc+xml', + 'atx' => 'application/vnd.antix.game-component', + 'au' => 'audio/basic', + 'avi' => 'video/x-msvideo', + 'aw' => 'application/applixware', + 'azf' => 'application/vnd.airzip.filesecure.azf', + 'azs' => 'application/vnd.airzip.filesecure.azs', + 'azw' => 'application/vnd.amazon.ebook', + 'bat' => 'application/x-msdownload', + 'bcpio' => 'application/x-bcpio', + 'bdf' => 'application/x-font-bdf', + 'bdm' => 'application/vnd.syncml.dm+wbxml', + 'bed' => 'application/vnd.realvnc.bed', + 'bh2' => 'application/vnd.fujitsu.oasysprs', + 'bin' => 'application/octet-stream', + 'blb' => 'application/x-blorb', + 'blorb' => 'application/x-blorb', + 'bmi' => 'application/vnd.bmi', + 'bmp' => 'image/bmp', + 'book' => 'application/vnd.framemaker', + 'box' => 'application/vnd.previewsystems.box', + 'boz' => 'application/x-bzip2', + 'bpk' => 'application/octet-stream', + 'btif' => 'image/prs.btif', + 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', + 'c' => 'text/x-c', + 'c11amc' => 'application/vnd.cluetrust.cartomobile-config', + 'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg', + 'c4d' => 'application/vnd.clonk.c4group', + 'c4f' => 'application/vnd.clonk.c4group', + 'c4g' => 'application/vnd.clonk.c4group', + 'c4p' => 'application/vnd.clonk.c4group', + 'c4u' => 'application/vnd.clonk.c4group', + 'cab' => 'application/vnd.ms-cab-compressed', + 'caf' => 'audio/x-caf', + 'cap' => 'application/vnd.tcpdump.pcap', + 'car' => 'application/vnd.curl.car', + 'cat' => 'application/vnd.ms-pki.seccat', + 'cb7' => 'application/x-cbr', + 'cba' => 'application/x-cbr', + 'cbr' => 'application/x-cbr', + 'cbt' => 'application/x-cbr', + 'cbz' => 'application/x-cbr', + 'cc' => 'text/x-c', + 'cct' => 'application/x-director', + 'ccxml' => 'application/ccxml+xml', + 'cdbcmsg' => 'application/vnd.contact.cmsg', + 'cdf' => 'application/x-netcdf', + 'cdkey' => 'application/vnd.mediastation.cdkey', + 'cdmia' => 'application/cdmi-capability', + 'cdmic' => 'application/cdmi-container', + 'cdmid' => 'application/cdmi-domain', + 'cdmio' => 'application/cdmi-object', + 'cdmiq' => 'application/cdmi-queue', + 'cdx' => 'chemical/x-cdx', + 'cdxml' => 'application/vnd.chemdraw+xml', + 'cdy' => 'application/vnd.cinderella', + 'cer' => 'application/pkix-cert', + 'cfs' => 'application/x-cfs-compressed', + 'cgm' => 'image/cgm', + 'chat' => 'application/x-chat', + 'chm' => 'application/vnd.ms-htmlhelp', + 'chrt' => 'application/vnd.kde.kchart', + 'cif' => 'chemical/x-cif', + 'cii' => 'application/vnd.anser-web-certificate-issue-initiation', + 'cil' => 'application/vnd.ms-artgalry', + 'cla' => 'application/vnd.claymore', + 'class' => 'application/java-vm', + 'clkk' => 'application/vnd.crick.clicker.keyboard', + 'clkp' => 'application/vnd.crick.clicker.palette', + 'clkt' => 'application/vnd.crick.clicker.template', + 'clkw' => 'application/vnd.crick.clicker.wordbank', + 'clkx' => 'application/vnd.crick.clicker', + 'clp' => 'application/x-msclip', + 'cmc' => 'application/vnd.cosmocaller', + 'cmdf' => 'chemical/x-cmdf', + 'cml' => 'chemical/x-cml', + 'cmp' => 'application/vnd.yellowriver-custom-menu', + 'cmx' => 'image/x-cmx', + 'cod' => 'application/vnd.rim.cod', + 'com' => 'application/x-msdownload', + 'conf' => 'text/plain', + 'cpio' => 'application/x-cpio', + 'cpp' => 'text/x-c', + 'cpt' => 'application/mac-compactpro', + 'crd' => 'application/x-mscardfile', + 'crl' => 'application/pkix-crl', + 'crt' => 'application/x-x509-ca-cert', + 'csh' => 'application/x-csh', + 'csml' => 'chemical/x-csml', + 'csp' => 'application/vnd.commonspace', + 'css' => 'text/css', + 'cst' => 'application/x-director', + 'csv' => 'text/csv', + 'cu' => 'application/cu-seeme', + 'curl' => 'text/vnd.curl', + 'cww' => 'application/prs.cww', + 'cxt' => 'application/x-director', + 'cxx' => 'text/x-c', + 'dae' => 'model/vnd.collada+xml', + 'daf' => 'application/vnd.mobius.daf', + 'dart' => 'application/vnd.dart', + 'dataless' => 'application/vnd.fdsn.seed', + 'davmount' => 'application/davmount+xml', + 'dbk' => 'application/docbook+xml', + 'dcr' => 'application/x-director', + 'dcurl' => 'text/vnd.curl.dcurl', + 'dd2' => 'application/vnd.oma.dd2+xml', + 'ddd' => 'application/vnd.fujixerox.ddd', + 'deb' => 'application/x-debian-package', + 'def' => 'text/plain', + 'deploy' => 'application/octet-stream', + 'der' => 'application/x-x509-ca-cert', + 'dfac' => 'application/vnd.dreamfactory', + 'dgc' => 'application/x-dgc-compressed', + 'dic' => 'text/x-c', + 'dir' => 'application/x-director', + 'dis' => 'application/vnd.mobius.dis', + 'dist' => 'application/octet-stream', + 'distz' => 'application/octet-stream', + 'djv' => 'image/vnd.djvu', + 'djvu' => 'image/vnd.djvu', + 'dll' => 'application/x-msdownload', + 'dmg' => 'application/x-apple-diskimage', + 'dmp' => 'application/vnd.tcpdump.pcap', + 'dms' => 'application/octet-stream', + 'dna' => 'application/vnd.dna', + 'doc' => 'application/msword', + 'docm' => 'application/vnd.ms-word.document.macroenabled.12', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dot' => 'application/msword', + 'dotm' => 'application/vnd.ms-word.template.macroenabled.12', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'dp' => 'application/vnd.osgi.dp', + 'dpg' => 'application/vnd.dpgraph', + 'dra' => 'audio/vnd.dra', + 'dsc' => 'text/prs.lines.tag', + 'dssc' => 'application/dssc+der', + 'dtb' => 'application/x-dtbook+xml', + 'dtd' => 'application/xml-dtd', + 'dts' => 'audio/vnd.dts', + 'dtshd' => 'audio/vnd.dts.hd', + 'dump' => 'application/octet-stream', + 'dvb' => 'video/vnd.dvb.file', + 'dvi' => 'application/x-dvi', + 'dwf' => 'model/vnd.dwf', + 'dwg' => 'image/vnd.dwg', + 'dxf' => 'image/vnd.dxf', + 'dxp' => 'application/vnd.spotfire.dxp', + 'dxr' => 'application/x-director', + 'ecelp4800' => 'audio/vnd.nuera.ecelp4800', + 'ecelp7470' => 'audio/vnd.nuera.ecelp7470', + 'ecelp9600' => 'audio/vnd.nuera.ecelp9600', + 'ecma' => 'application/ecmascript', + 'edm' => 'application/vnd.novadigm.edm', + 'edx' => 'application/vnd.novadigm.edx', + 'efif' => 'application/vnd.picsel', + 'ei6' => 'application/vnd.pg.osasli', + 'elc' => 'application/octet-stream', + 'emf' => 'application/x-msmetafile', + 'eml' => 'message/rfc822', + 'emma' => 'application/emma+xml', + 'emz' => 'application/x-msmetafile', + 'eol' => 'audio/vnd.digital-winds', + 'eot' => 'application/vnd.ms-fontobject', + 'eps' => 'application/postscript', + 'epub' => 'application/epub+zip', + 'es3' => 'application/vnd.eszigno3+xml', + 'esa' => 'application/vnd.osgi.subsystem', + 'esf' => 'application/vnd.epson.esf', + 'et3' => 'application/vnd.eszigno3+xml', + 'etx' => 'text/x-setext', + 'eva' => 'application/x-eva', + 'evy' => 'application/x-envoy', + 'exe' => 'application/x-msdownload', + 'exi' => 'application/exi', + 'ext' => 'application/vnd.novadigm.ext', + 'ez' => 'application/andrew-inset', + 'ez2' => 'application/vnd.ezpix-album', + 'ez3' => 'application/vnd.ezpix-package', + 'f' => 'text/x-fortran', + 'f4v' => 'video/x-f4v', + 'f77' => 'text/x-fortran', + 'f90' => 'text/x-fortran', + 'fbs' => 'image/vnd.fastbidsheet', + 'fcdt' => 'application/vnd.adobe.formscentral.fcdt', + 'fcs' => 'application/vnd.isac.fcs', + 'fdf' => 'application/vnd.fdf', + 'fe_launch' => 'application/vnd.denovo.fcselayout-link', + 'fg5' => 'application/vnd.fujitsu.oasysgp', + 'fgd' => 'application/x-director', + 'fh' => 'image/x-freehand', + 'fh4' => 'image/x-freehand', + 'fh5' => 'image/x-freehand', + 'fh7' => 'image/x-freehand', + 'fhc' => 'image/x-freehand', + 'fig' => 'application/x-xfig', + 'flac' => 'audio/x-flac', + 'fli' => 'video/x-fli', + 'flo' => 'application/vnd.micrografx.flo', + 'flv' => 'video/x-flv', + 'flw' => 'application/vnd.kde.kivio', + 'flx' => 'text/vnd.fmi.flexstor', + 'fly' => 'text/vnd.fly', + 'fm' => 'application/vnd.framemaker', + 'fnc' => 'application/vnd.frogans.fnc', + 'for' => 'text/x-fortran', + 'fpx' => 'image/vnd.fpx', + 'frame' => 'application/vnd.framemaker', + 'fsc' => 'application/vnd.fsc.weblaunch', + 'fst' => 'image/vnd.fst', + 'ftc' => 'application/vnd.fluxtime.clip', + 'fti' => 'application/vnd.anser-web-funds-transfer-initiation', + 'fvt' => 'video/vnd.fvt', + 'fxp' => 'application/vnd.adobe.fxp', + 'fxpl' => 'application/vnd.adobe.fxp', + 'fzs' => 'application/vnd.fuzzysheet', + 'g2w' => 'application/vnd.geoplan', + 'g3' => 'image/g3fax', + 'g3w' => 'application/vnd.geospace', + 'gac' => 'application/vnd.groove-account', + 'gam' => 'application/x-tads', + 'gbr' => 'application/rpki-ghostbusters', + 'gca' => 'application/x-gca-compressed', + 'gdl' => 'model/vnd.gdl', + 'geo' => 'application/vnd.dynageo', + 'gex' => 'application/vnd.geometry-explorer', + 'ggb' => 'application/vnd.geogebra.file', + 'ggt' => 'application/vnd.geogebra.tool', + 'ghf' => 'application/vnd.groove-help', + 'gif' => 'image/gif', + 'gim' => 'application/vnd.groove-identity-message', + 'gml' => 'application/gml+xml', + 'gmx' => 'application/vnd.gmx', + 'gnumeric' => 'application/x-gnumeric', + 'gph' => 'application/vnd.flographit', + 'gpx' => 'application/gpx+xml', + 'gqf' => 'application/vnd.grafeq', + 'gqs' => 'application/vnd.grafeq', + 'gram' => 'application/srgs', + 'gramps' => 'application/x-gramps-xml', + 'gre' => 'application/vnd.geometry-explorer', + 'grv' => 'application/vnd.groove-injector', + 'grxml' => 'application/srgs+xml', + 'gsf' => 'application/x-font-ghostscript', + 'gtar' => 'application/x-gtar', + 'gtm' => 'application/vnd.groove-tool-message', + 'gtw' => 'model/vnd.gtw', + 'gv' => 'text/vnd.graphviz', + 'gxf' => 'application/gxf', + 'gxt' => 'application/vnd.geonext', + 'gz' => 'application/x-gzip', + 'h' => 'text/x-c', + 'h261' => 'video/h261', + 'h263' => 'video/h263', + 'h264' => 'video/h264', + 'hal' => 'application/vnd.hal+xml', + 'hbci' => 'application/vnd.hbci', + 'hdf' => 'application/x-hdf', + 'hh' => 'text/x-c', + 'hlp' => 'application/winhlp', + 'hpgl' => 'application/vnd.hp-hpgl', + 'hpid' => 'application/vnd.hp-hpid', + 'hps' => 'application/vnd.hp-hps', + 'hqx' => 'application/mac-binhex40', + 'htke' => 'application/vnd.kenameaapp', + 'htm' => 'text/html', + 'html' => 'text/html', + 'hvd' => 'application/vnd.yamaha.hv-dic', + 'hvp' => 'application/vnd.yamaha.hv-voice', + 'hvs' => 'application/vnd.yamaha.hv-script', + 'i2g' => 'application/vnd.intergeo', + 'icc' => 'application/vnd.iccprofile', + 'ice' => 'x-conference/x-cooltalk', + 'icm' => 'application/vnd.iccprofile', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'ief' => 'image/ief', + 'ifb' => 'text/calendar', + 'ifm' => 'application/vnd.shana.informed.formdata', + 'iges' => 'model/iges', + 'igl' => 'application/vnd.igloader', + 'igm' => 'application/vnd.insors.igm', + 'igs' => 'model/iges', + 'igx' => 'application/vnd.micrografx.igx', + 'iif' => 'application/vnd.shana.informed.interchange', + 'imp' => 'application/vnd.accpac.simply.imp', + 'ims' => 'application/vnd.ms-ims', + 'in' => 'text/plain', + 'ink' => 'application/inkml+xml', + 'inkml' => 'application/inkml+xml', + 'install' => 'application/x-install-instructions', + 'iota' => 'application/vnd.astraea-software.iota', + 'ipfix' => 'application/ipfix', + 'ipk' => 'application/vnd.shana.informed.package', + 'irm' => 'application/vnd.ibm.rights-management', + 'irp' => 'application/vnd.irepository.package+xml', + 'iso' => 'application/x-iso9660-image', + 'itp' => 'application/vnd.shana.informed.formtemplate', + 'ivp' => 'application/vnd.immervision-ivp', + 'ivu' => 'application/vnd.immervision-ivu', + 'jad' => 'text/vnd.sun.j2me.app-descriptor', + 'jam' => 'application/vnd.jam', + 'jar' => 'application/java-archive', + 'java' => 'text/x-java-source', + 'jisp' => 'application/vnd.jisp', + 'jlt' => 'application/vnd.hp-jlyt', + 'jnlp' => 'application/x-java-jnlp-file', + 'joda' => 'application/vnd.joost.joda-archive', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpgm' => 'video/jpm', + 'jpgv' => 'video/jpeg', + 'jpm' => 'video/jpm', + 'js' => 'application/javascript', + 'json' => 'application/json', + 'jsonml' => 'application/jsonml+json', + 'kar' => 'audio/midi', + 'karbon' => 'application/vnd.kde.karbon', + 'kfo' => 'application/vnd.kde.kformula', + 'kia' => 'application/vnd.kidspiration', + 'kml' => 'application/vnd.google-earth.kml+xml', + 'kmz' => 'application/vnd.google-earth.kmz', + 'kne' => 'application/vnd.kinar', + 'knp' => 'application/vnd.kinar', + 'kon' => 'application/vnd.kde.kontour', + 'kpr' => 'application/vnd.kde.kpresenter', + 'kpt' => 'application/vnd.kde.kpresenter', + 'kpxx' => 'application/vnd.ds-keypoint', + 'ksp' => 'application/vnd.kde.kspread', + 'ktr' => 'application/vnd.kahootz', + 'ktx' => 'image/ktx', + 'ktz' => 'application/vnd.kahootz', + 'kwd' => 'application/vnd.kde.kword', + 'kwt' => 'application/vnd.kde.kword', + 'lasxml' => 'application/vnd.las.las+xml', + 'latex' => 'application/x-latex', + 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop', + 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml', + 'les' => 'application/vnd.hhe.lesson-player', + 'lha' => 'application/x-lzh-compressed', + 'link66' => 'application/vnd.route66.link66+xml', + 'list' => 'text/plain', + 'list3820' => 'application/vnd.ibm.modcap', + 'listafp' => 'application/vnd.ibm.modcap', + 'lnk' => 'application/x-ms-shortcut', + 'log' => 'text/plain', + 'lostxml' => 'application/lost+xml', + 'lrf' => 'application/octet-stream', + 'lrm' => 'application/vnd.ms-lrm', + 'ltf' => 'application/vnd.frogans.ltf', + 'lvp' => 'audio/vnd.lucent.voice', + 'lwp' => 'application/vnd.lotus-wordpro', + 'lzh' => 'application/x-lzh-compressed', + 'm13' => 'application/x-msmediaview', + 'm14' => 'application/x-msmediaview', + 'm1v' => 'video/mpeg', + 'm21' => 'application/mp21', + 'm2a' => 'audio/mpeg', + 'm2v' => 'video/mpeg', + 'm3a' => 'audio/mpeg', + 'm3u' => 'audio/x-mpegurl', + 'm3u8' => 'application/vnd.apple.mpegurl', + 'm4a' => 'audio/mp4', + 'm4u' => 'video/vnd.mpegurl', + 'm4v' => 'video/x-m4v', + 'ma' => 'application/mathematica', + 'mads' => 'application/mads+xml', + 'mag' => 'application/vnd.ecowin.chart', + 'maker' => 'application/vnd.framemaker', + 'man' => 'text/troff', + 'mar' => 'application/octet-stream', + 'mathml' => 'application/mathml+xml', + 'mb' => 'application/mathematica', + 'mbk' => 'application/vnd.mobius.mbk', + 'mbox' => 'application/mbox', + 'mc1' => 'application/vnd.medcalcdata', + 'mcd' => 'application/vnd.mcd', + 'mcurl' => 'text/vnd.curl.mcurl', + 'mdb' => 'application/x-msaccess', + 'mdi' => 'image/vnd.ms-modi', + 'me' => 'text/troff', + 'mesh' => 'model/mesh', + 'meta4' => 'application/metalink4+xml', + 'metalink' => 'application/metalink+xml', + 'mets' => 'application/mets+xml', + 'mfm' => 'application/vnd.mfmp', + 'mft' => 'application/rpki-manifest', + 'mgp' => 'application/vnd.osgeo.mapguide.package', + 'mgz' => 'application/vnd.proteus.magazine', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mie' => 'application/x-mie', + 'mif' => 'application/vnd.mif', + 'mime' => 'message/rfc822', + 'mj2' => 'video/mj2', + 'mjp2' => 'video/mj2', + 'mk3d' => 'video/x-matroska', + 'mka' => 'audio/x-matroska', + 'mks' => 'video/x-matroska', + 'mkv' => 'video/x-matroska', + 'mlp' => 'application/vnd.dolby.mlp', + 'mmd' => 'application/vnd.chipnuts.karaoke-mmd', + 'mmf' => 'application/vnd.smaf', + 'mmr' => 'image/vnd.fujixerox.edmics-mmr', + 'mng' => 'video/x-mng', + 'mny' => 'application/x-msmoney', + 'mobi' => 'application/x-mobipocket-ebook', + 'mods' => 'application/mods+xml', + 'mov' => 'video/quicktime', + 'movie' => 'video/x-sgi-movie', + 'mp2' => 'audio/mpeg', + 'mp21' => 'application/mp21', + 'mp2a' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mp4a' => 'audio/mp4', + 'mp4s' => 'application/mp4', + 'mp4v' => 'video/mp4', + 'mpc' => 'application/vnd.mophun.certificate', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpg4' => 'video/mp4', + 'mpga' => 'audio/mpeg', + 'mpkg' => 'application/vnd.apple.installer+xml', + 'mpm' => 'application/vnd.blueice.multipass', + 'mpn' => 'application/vnd.mophun.application', + 'mpp' => 'application/vnd.ms-project', + 'mpt' => 'application/vnd.ms-project', + 'mpy' => 'application/vnd.ibm.minipay', + 'mqy' => 'application/vnd.mobius.mqy', + 'mrc' => 'application/marc', + 'mrcx' => 'application/marcxml+xml', + 'ms' => 'text/troff', + 'mscml' => 'application/mediaservercontrol+xml', + 'mseed' => 'application/vnd.fdsn.mseed', + 'mseq' => 'application/vnd.mseq', + 'msf' => 'application/vnd.epson.msf', + 'msh' => 'model/mesh', + 'msi' => 'application/x-msdownload', + 'msl' => 'application/vnd.mobius.msl', + 'msty' => 'application/vnd.muvee.style', + 'mts' => 'model/vnd.mts', + 'mus' => 'application/vnd.musician', + 'musicxml' => 'application/vnd.recordare.musicxml+xml', + 'mvb' => 'application/x-msmediaview', + 'mwf' => 'application/vnd.mfer', + 'mxf' => 'application/mxf', + 'mxl' => 'application/vnd.recordare.musicxml', + 'mxml' => 'application/xv+xml', + 'mxs' => 'application/vnd.triscape.mxs', + 'mxu' => 'video/vnd.mpegurl', + 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install', + 'n3' => 'text/n3', + 'nb' => 'application/mathematica', + 'nbp' => 'application/vnd.wolfram.player', + 'nc' => 'application/x-netcdf', + 'ncx' => 'application/x-dtbncx+xml', + 'nfo' => 'text/x-nfo', + 'ngdat' => 'application/vnd.nokia.n-gage.data', + 'nitf' => 'application/vnd.nitf', + 'nlu' => 'application/vnd.neurolanguage.nlu', + 'nml' => 'application/vnd.enliven', + 'nnd' => 'application/vnd.noblenet-directory', + 'nns' => 'application/vnd.noblenet-sealer', + 'nnw' => 'application/vnd.noblenet-web', + 'npx' => 'image/vnd.net-fpx', + 'nsc' => 'application/x-conference', + 'nsf' => 'application/vnd.lotus-notes', + 'ntf' => 'application/vnd.nitf', + 'nzb' => 'application/x-nzb', + 'oa2' => 'application/vnd.fujitsu.oasys2', + 'oa3' => 'application/vnd.fujitsu.oasys3', + 'oas' => 'application/vnd.fujitsu.oasys', + 'obd' => 'application/x-msbinder', + 'obj' => 'application/x-tgif', + 'oda' => 'application/oda', + 'odb' => 'application/vnd.oasis.opendocument.database', + 'odc' => 'application/vnd.oasis.opendocument.chart', + 'odf' => 'application/vnd.oasis.opendocument.formula', + 'odft' => 'application/vnd.oasis.opendocument.formula-template', + 'odg' => 'application/vnd.oasis.opendocument.graphics', + 'odi' => 'application/vnd.oasis.opendocument.image', + 'odm' => 'application/vnd.oasis.opendocument.text-master', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'oga' => 'audio/ogg', + 'ogg' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'ogx' => 'application/ogg', + 'omdoc' => 'application/omdoc+xml', + 'onepkg' => 'application/onenote', + 'onetmp' => 'application/onenote', + 'onetoc' => 'application/onenote', + 'onetoc2' => 'application/onenote', + 'opf' => 'application/oebps-package+xml', + 'opml' => 'text/x-opml', + 'oprc' => 'application/vnd.palm', + 'org' => 'application/vnd.lotus-organizer', + 'osf' => 'application/vnd.yamaha.openscoreformat', + 'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml', + 'otc' => 'application/vnd.oasis.opendocument.chart-template', + 'otf' => 'application/x-font-otf', + 'otg' => 'application/vnd.oasis.opendocument.graphics-template', + 'oth' => 'application/vnd.oasis.opendocument.text-web', + 'oti' => 'application/vnd.oasis.opendocument.image-template', + 'otp' => 'application/vnd.oasis.opendocument.presentation-template', + 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', + 'ott' => 'application/vnd.oasis.opendocument.text-template', + 'oxps' => 'application/oxps', + 'oxt' => 'application/vnd.openofficeorg.extension', + 'p' => 'text/x-pascal', + 'p10' => 'application/pkcs10', + 'p12' => 'application/x-pkcs12', + 'p7b' => 'application/x-pkcs7-certificates', + 'p7c' => 'application/pkcs7-mime', + 'p7m' => 'application/pkcs7-mime', + 'p7r' => 'application/x-pkcs7-certreqresp', + 'p7s' => 'application/pkcs7-signature', + 'p8' => 'application/pkcs8', + 'pas' => 'text/x-pascal', + 'paw' => 'application/vnd.pawaafile', + 'pbd' => 'application/vnd.powerbuilder6', + 'pbm' => 'image/x-portable-bitmap', + 'pcap' => 'application/vnd.tcpdump.pcap', + 'pcf' => 'application/x-font-pcf', + 'pcl' => 'application/vnd.hp-pcl', + 'pclxl' => 'application/vnd.hp-pclxl', + 'pct' => 'image/x-pict', + 'pcurl' => 'application/vnd.curl.pcurl', + 'pcx' => 'image/x-pcx', + 'pdb' => 'application/vnd.palm', + 'pdf' => 'application/pdf', + 'pfa' => 'application/x-font-type1', + 'pfb' => 'application/x-font-type1', + 'pfm' => 'application/x-font-type1', + 'pfr' => 'application/font-tdpfr', + 'pfx' => 'application/x-pkcs12', + 'pgm' => 'image/x-portable-graymap', + 'pgn' => 'application/x-chess-pgn', + 'pgp' => 'application/pgp-encrypted', + 'php' => 'application/x-php', + 'php3' => 'application/x-php', + 'php4' => 'application/x-php', + 'php5' => 'application/x-php', + 'pic' => 'image/x-pict', + 'pkg' => 'application/octet-stream', + 'pki' => 'application/pkixcmp', + 'pkipath' => 'application/pkix-pkipath', + 'plb' => 'application/vnd.3gpp.pic-bw-large', + 'plc' => 'application/vnd.mobius.plc', + 'plf' => 'application/vnd.pocketlearn', + 'pls' => 'application/pls+xml', + 'pml' => 'application/vnd.ctc-posml', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'portpkg' => 'application/vnd.macports.portpkg', + 'pot' => 'application/vnd.ms-powerpoint', + 'potm' => 'application/vnd.ms-powerpoint.template.macroenabled.12', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppam' => 'application/vnd.ms-powerpoint.addin.macroenabled.12', + 'ppd' => 'application/vnd.cups-ppd', + 'ppm' => 'image/x-portable-pixmap', + 'pps' => 'application/vnd.ms-powerpoint', + 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroenabled.12', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroenabled.12', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'pqa' => 'application/vnd.palm', + 'prc' => 'application/x-mobipocket-ebook', + 'pre' => 'application/vnd.lotus-freelance', + 'prf' => 'application/pics-rules', + 'ps' => 'application/postscript', + 'psb' => 'application/vnd.3gpp.pic-bw-small', + 'psd' => 'image/vnd.adobe.photoshop', + 'psf' => 'application/x-font-linux-psf', + 'pskcxml' => 'application/pskc+xml', + 'ptid' => 'application/vnd.pvi.ptid1', + 'pub' => 'application/x-mspublisher', + 'pvb' => 'application/vnd.3gpp.pic-bw-var', + 'pwn' => 'application/vnd.3m.post-it-notes', + 'pya' => 'audio/vnd.ms-playready.media.pya', + 'pyv' => 'video/vnd.ms-playready.media.pyv', + 'qam' => 'application/vnd.epson.quickanime', + 'qbo' => 'application/vnd.intu.qbo', + 'qfx' => 'application/vnd.intu.qfx', + 'qps' => 'application/vnd.publishare-delta-tree', + 'qt' => 'video/quicktime', + 'qwd' => 'application/vnd.quark.quarkxpress', + 'qwt' => 'application/vnd.quark.quarkxpress', + 'qxb' => 'application/vnd.quark.quarkxpress', + 'qxd' => 'application/vnd.quark.quarkxpress', + 'qxl' => 'application/vnd.quark.quarkxpress', + 'qxt' => 'application/vnd.quark.quarkxpress', + 'ra' => 'audio/x-pn-realaudio', + 'ram' => 'audio/x-pn-realaudio', + 'rar' => 'application/x-rar-compressed', + 'ras' => 'image/x-cmu-raster', + 'rcprofile' => 'application/vnd.ipunplugged.rcprofile', + 'rdf' => 'application/rdf+xml', + 'rdz' => 'application/vnd.data-vision.rdz', + 'rep' => 'application/vnd.businessobjects', + 'res' => 'application/x-dtbresource+xml', + 'rgb' => 'image/x-rgb', + 'rif' => 'application/reginfo+xml', + 'rip' => 'audio/vnd.rip', + 'ris' => 'application/x-research-info-systems', + 'rl' => 'application/resource-lists+xml', + 'rlc' => 'image/vnd.fujixerox.edmics-rlc', + 'rld' => 'application/resource-lists-diff+xml', + 'rm' => 'application/vnd.rn-realmedia', + 'rmi' => 'audio/midi', + 'rmp' => 'audio/x-pn-realaudio-plugin', + 'rms' => 'application/vnd.jcp.javame.midlet-rms', + 'rmvb' => 'application/vnd.rn-realmedia-vbr', + 'rnc' => 'application/relax-ng-compact-syntax', + 'roa' => 'application/rpki-roa', + 'roff' => 'text/troff', + 'rp9' => 'application/vnd.cloanto.rp9', + 'rpss' => 'application/vnd.nokia.radio-presets', + 'rpst' => 'application/vnd.nokia.radio-preset', + 'rq' => 'application/sparql-query', + 'rs' => 'application/rls-services+xml', + 'rsd' => 'application/rsd+xml', + 'rss' => 'application/rss+xml', + 'rtf' => 'application/rtf', + 'rtx' => 'text/richtext', + 's' => 'text/x-asm', + 's3m' => 'audio/s3m', + 'saf' => 'application/vnd.yamaha.smaf-audio', + 'sbml' => 'application/sbml+xml', + 'sc' => 'application/vnd.ibm.secure-container', + 'scd' => 'application/x-msschedule', + 'scm' => 'application/vnd.lotus-screencam', + 'scq' => 'application/scvp-cv-request', + 'scs' => 'application/scvp-cv-response', + 'scurl' => 'text/vnd.curl.scurl', + 'sda' => 'application/vnd.stardivision.draw', + 'sdc' => 'application/vnd.stardivision.calc', + 'sdd' => 'application/vnd.stardivision.impress', + 'sdkd' => 'application/vnd.solent.sdkm+xml', + 'sdkm' => 'application/vnd.solent.sdkm+xml', + 'sdp' => 'application/sdp', + 'sdw' => 'application/vnd.stardivision.writer', + 'see' => 'application/vnd.seemail', + 'seed' => 'application/vnd.fdsn.seed', + 'sema' => 'application/vnd.sema', + 'semd' => 'application/vnd.semd', + 'semf' => 'application/vnd.semf', + 'ser' => 'application/java-serialized-object', + 'setpay' => 'application/set-payment-initiation', + 'setreg' => 'application/set-registration-initiation', + 'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data', + 'sfs' => 'application/vnd.spotfire.sfs', + 'sfv' => 'text/x-sfv', + 'sgi' => 'image/sgi', + 'sgl' => 'application/vnd.stardivision.writer-global', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', + 'shf' => 'application/shf+xml', + 'sid' => 'image/x-mrsid-image', + 'sig' => 'application/pgp-signature', + 'sil' => 'audio/silk', + 'silo' => 'model/mesh', + 'sis' => 'application/vnd.symbian.install', + 'sisx' => 'application/vnd.symbian.install', + 'sit' => 'application/x-stuffit', + 'sitx' => 'application/x-stuffitx', + 'skd' => 'application/vnd.koan', + 'skm' => 'application/vnd.koan', + 'skp' => 'application/vnd.koan', + 'skt' => 'application/vnd.koan', + 'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12', + 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', + 'slt' => 'application/vnd.epson.salt', + 'sm' => 'application/vnd.stepmania.stepchart', + 'smf' => 'application/vnd.stardivision.math', + 'smi' => 'application/smil+xml', + 'smil' => 'application/smil+xml', + 'smv' => 'video/x-smv', + 'smzip' => 'application/vnd.stepmania.package', + 'snd' => 'audio/basic', + 'snf' => 'application/x-font-snf', + 'so' => 'application/octet-stream', + 'spc' => 'application/x-pkcs7-certificates', + 'spf' => 'application/vnd.yamaha.smaf-phrase', + 'spl' => 'application/x-futuresplash', + 'spot' => 'text/vnd.in3d.spot', + 'spp' => 'application/scvp-vp-response', + 'spq' => 'application/scvp-vp-request', + 'spx' => 'audio/ogg', + 'sql' => 'application/x-sql', + 'src' => 'application/x-wais-source', + 'srt' => 'application/x-subrip', + 'sru' => 'application/sru+xml', + 'srx' => 'application/sparql-results+xml', + 'ssdl' => 'application/ssdl+xml', + 'sse' => 'application/vnd.kodak-descriptor', + 'ssf' => 'application/vnd.epson.ssf', + 'ssml' => 'application/ssml+xml', + 'st' => 'application/vnd.sailingtracker.track', + 'stc' => 'application/vnd.sun.xml.calc.template', + 'std' => 'application/vnd.sun.xml.draw.template', + 'stf' => 'application/vnd.wt.stf', + 'sti' => 'application/vnd.sun.xml.impress.template', + 'stk' => 'application/hyperstudio', + 'stl' => 'application/vnd.ms-pki.stl', + 'str' => 'application/vnd.pg.format', + 'stw' => 'application/vnd.sun.xml.writer.template', + 'sub' => 'text/vnd.dvb.subtitle', + 'sus' => 'application/vnd.sus-calendar', + 'susp' => 'application/vnd.sus-calendar', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'svc' => 'application/vnd.dvb.service', + 'svd' => 'application/vnd.svd', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'swa' => 'application/x-director', + 'swf' => 'application/x-shockwave-flash', + 'swi' => 'application/vnd.aristanetworks.swi', + 'sxc' => 'application/vnd.sun.xml.calc', + 'sxd' => 'application/vnd.sun.xml.draw', + 'sxg' => 'application/vnd.sun.xml.writer.global', + 'sxi' => 'application/vnd.sun.xml.impress', + 'sxm' => 'application/vnd.sun.xml.math', + 'sxw' => 'application/vnd.sun.xml.writer', + 't' => 'text/troff', + 't3' => 'application/x-t3vm-image', + 'taglet' => 'application/vnd.mynfc', + 'tao' => 'application/vnd.tao.intent-module-archive', + 'tar' => 'application/x-tar', + 'tcap' => 'application/vnd.3gpp2.tcap', + 'tcl' => 'application/x-tcl', + 'teacher' => 'application/vnd.smart.teacher', + 'tei' => 'application/tei+xml', + 'teicorpus' => 'application/tei+xml', + 'tex' => 'application/x-tex', + 'texi' => 'application/x-texinfo', + 'texinfo' => 'application/x-texinfo', + 'text' => 'text/plain', + 'tfi' => 'application/thraud+xml', + 'tfm' => 'application/x-tex-tfm', + 'tga' => 'image/x-tga', + 'thmx' => 'application/vnd.ms-officetheme', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'tmo' => 'application/vnd.tmobile-livetv', + 'torrent' => 'application/x-bittorrent', + 'tpl' => 'application/vnd.groove-tool-template', + 'tpt' => 'application/vnd.trid.tpt', + 'tr' => 'text/troff', + 'tra' => 'application/vnd.trueapp', + 'trm' => 'application/x-msterminal', + 'tsd' => 'application/timestamped-data', + 'tsv' => 'text/tab-separated-values', + 'ttc' => 'application/x-font-ttf', + 'ttf' => 'application/x-font-ttf', + 'ttl' => 'text/turtle', + 'twd' => 'application/vnd.simtech-mindmapper', + 'twds' => 'application/vnd.simtech-mindmapper', + 'txd' => 'application/vnd.genomatix.tuxedo', + 'txf' => 'application/vnd.mobius.txf', + 'txt' => 'text/plain', + 'u32' => 'application/x-authorware-bin', + 'udeb' => 'application/x-debian-package', + 'ufd' => 'application/vnd.ufdl', + 'ufdl' => 'application/vnd.ufdl', + 'ulx' => 'application/x-glulx', + 'umj' => 'application/vnd.umajin', + 'unityweb' => 'application/vnd.unity', + 'uoml' => 'application/vnd.uoml+xml', + 'uri' => 'text/uri-list', + 'uris' => 'text/uri-list', + 'urls' => 'text/uri-list', + 'ustar' => 'application/x-ustar', + 'utz' => 'application/vnd.uiq.theme', + 'uu' => 'text/x-uuencode', + 'uva' => 'audio/vnd.dece.audio', + 'uvd' => 'application/vnd.dece.data', + 'uvf' => 'application/vnd.dece.data', + 'uvg' => 'image/vnd.dece.graphic', + 'uvh' => 'video/vnd.dece.hd', + 'uvi' => 'image/vnd.dece.graphic', + 'uvm' => 'video/vnd.dece.mobile', + 'uvp' => 'video/vnd.dece.pd', + 'uvs' => 'video/vnd.dece.sd', + 'uvt' => 'application/vnd.dece.ttml+xml', + 'uvu' => 'video/vnd.uvvu.mp4', + 'uvv' => 'video/vnd.dece.video', + 'uvva' => 'audio/vnd.dece.audio', + 'uvvd' => 'application/vnd.dece.data', + 'uvvf' => 'application/vnd.dece.data', + 'uvvg' => 'image/vnd.dece.graphic', + 'uvvh' => 'video/vnd.dece.hd', + 'uvvi' => 'image/vnd.dece.graphic', + 'uvvm' => 'video/vnd.dece.mobile', + 'uvvp' => 'video/vnd.dece.pd', + 'uvvs' => 'video/vnd.dece.sd', + 'uvvt' => 'application/vnd.dece.ttml+xml', + 'uvvu' => 'video/vnd.uvvu.mp4', + 'uvvv' => 'video/vnd.dece.video', + 'uvvx' => 'application/vnd.dece.unspecified', + 'uvvz' => 'application/vnd.dece.zip', + 'uvx' => 'application/vnd.dece.unspecified', + 'uvz' => 'application/vnd.dece.zip', + 'vcard' => 'text/vcard', + 'vcd' => 'application/x-cdlink', + 'vcf' => 'text/x-vcard', + 'vcg' => 'application/vnd.groove-vcard', + 'vcs' => 'text/x-vcalendar', + 'vcx' => 'application/vnd.vcx', + 'vis' => 'application/vnd.visionary', + 'viv' => 'video/vnd.vivo', + 'vob' => 'video/x-ms-vob', + 'vor' => 'application/vnd.stardivision.writer', + 'vox' => 'application/x-authorware-bin', + 'vrml' => 'model/vrml', + 'vsd' => 'application/vnd.visio', + 'vsf' => 'application/vnd.vsf', + 'vss' => 'application/vnd.visio', + 'vst' => 'application/vnd.visio', + 'vsw' => 'application/vnd.visio', + 'vtu' => 'model/vnd.vtu', + 'vxml' => 'application/voicexml+xml', + 'w3d' => 'application/x-director', + 'wad' => 'application/x-doom', + 'wav' => 'audio/x-wav', + 'wax' => 'audio/x-ms-wax', + 'wbmp' => 'image/vnd.wap.wbmp', + 'wbs' => 'application/vnd.criticaltools.wbs+xml', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wcm' => 'application/vnd.ms-works', + 'wdb' => 'application/vnd.ms-works', + 'wdp' => 'image/vnd.ms-photo', + 'weba' => 'audio/webm', + 'webm' => 'video/webm', + 'webp' => 'image/webp', + 'wg' => 'application/vnd.pmi.widget', + 'wgt' => 'application/widget', + 'wks' => 'application/vnd.ms-works', + 'wm' => 'video/x-ms-wm', + 'wma' => 'audio/x-ms-wma', + 'wmd' => 'application/x-ms-wmd', + 'wmf' => 'application/x-msmetafile', + 'wml' => 'text/vnd.wap.wml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmls' => 'text/vnd.wap.wmlscript', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'wmv' => 'video/x-ms-wmv', + 'wmx' => 'video/x-ms-wmx', + 'wmz' => 'application/x-msmetafile', + 'woff' => 'application/font-woff', + 'wpd' => 'application/vnd.wordperfect', + 'wpl' => 'application/vnd.ms-wpl', + 'wps' => 'application/vnd.ms-works', + 'wqd' => 'application/vnd.wqd', + 'wri' => 'application/x-mswrite', + 'wrl' => 'model/vrml', + 'wsdl' => 'application/wsdl+xml', + 'wspolicy' => 'application/wspolicy+xml', + 'wtb' => 'application/vnd.webturbo', + 'wvx' => 'video/x-ms-wvx', + 'x32' => 'application/x-authorware-bin', + 'x3d' => 'model/x3d+xml', + 'x3db' => 'model/x3d+binary', + 'x3dbz' => 'model/x3d+binary', + 'x3dv' => 'model/x3d+vrml', + 'x3dvz' => 'model/x3d+vrml', + 'x3dz' => 'model/x3d+xml', + 'xaml' => 'application/xaml+xml', + 'xap' => 'application/x-silverlight-app', + 'xar' => 'application/vnd.xara', + 'xbap' => 'application/x-ms-xbap', + 'xbd' => 'application/vnd.fujixerox.docuworks.binder', + 'xbm' => 'image/x-xbitmap', + 'xdf' => 'application/xcap-diff+xml', + 'xdm' => 'application/vnd.syncml.dm+xml', + 'xdp' => 'application/vnd.adobe.xdp+xml', + 'xdssc' => 'application/dssc+xml', + 'xdw' => 'application/vnd.fujixerox.docuworks', + 'xenc' => 'application/xenc+xml', + 'xer' => 'application/patch-ops-error+xml', + 'xfdf' => 'application/vnd.adobe.xfdf', + 'xfdl' => 'application/vnd.xfdl', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'xhvml' => 'application/xv+xml', + 'xif' => 'image/vnd.xiff', + 'xla' => 'application/vnd.ms-excel', + 'xlam' => 'application/vnd.ms-excel.addin.macroenabled.12', + 'xlc' => 'application/vnd.ms-excel', + 'xlf' => 'application/x-xliff+xml', + 'xlm' => 'application/vnd.ms-excel', + 'xls' => 'application/vnd.ms-excel', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroenabled.12', + 'xlsm' => 'application/vnd.ms-excel.sheet.macroenabled.12', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xlt' => 'application/vnd.ms-excel', + 'xltm' => 'application/vnd.ms-excel.template.macroenabled.12', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'xlw' => 'application/vnd.ms-excel', + 'xm' => 'audio/xm', + 'xml' => 'application/xml', + 'xo' => 'application/vnd.olpc-sugar', + 'xop' => 'application/xop+xml', + 'xpi' => 'application/x-xpinstall', + 'xpl' => 'application/xproc+xml', + 'xpm' => 'image/x-xpixmap', + 'xpr' => 'application/vnd.is-xpr', + 'xps' => 'application/vnd.ms-xpsdocument', + 'xpw' => 'application/vnd.intercon.formnet', + 'xpx' => 'application/vnd.intercon.formnet', + 'xsl' => 'application/xml', + 'xslt' => 'application/xslt+xml', + 'xsm' => 'application/vnd.syncml+xml', + 'xspf' => 'application/xspf+xml', + 'xul' => 'application/vnd.mozilla.xul+xml', + 'xvm' => 'application/xv+xml', + 'xvml' => 'application/xv+xml', + 'xwd' => 'image/x-xwindowdump', + 'xyz' => 'chemical/x-xyz', + 'xz' => 'application/x-xz', + 'yang' => 'application/yang', + 'yin' => 'application/yin+xml', + 'z1' => 'application/x-zmachine', + 'z2' => 'application/x-zmachine', + 'z3' => 'application/x-zmachine', + 'z4' => 'application/x-zmachine', + 'z5' => 'application/x-zmachine', + 'z6' => 'application/x-zmachine', + 'z7' => 'application/x-zmachine', + 'z8' => 'application/x-zmachine', + 'zaz' => 'application/vnd.zzazz.deck+xml', + 'zip' => 'application/zip', + 'zir' => 'application/vnd.zul', + 'zirz' => 'application/vnd.zul', + 'zmm' => 'application/vnd.handheld-entertainment+xml', + '123' => 'application/vnd.lotus-1-2-3', +); diff --git a/vendor/swiftmailer/swiftmailer/lib/preferences.php b/vendor/swiftmailer/swiftmailer/lib/preferences.php new file mode 100644 index 00000000..e5195014 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/preferences.php @@ -0,0 +1,25 @@ +<?php + +/****************************************************************************/ +/* */ +/* YOU MAY WISH TO MODIFY OR REMOVE THE FOLLOWING LINES WHICH SET DEFAULTS */ +/* */ +/****************************************************************************/ + +$preferences = Swift_Preferences::getInstance(); + +// Sets the default charset so that setCharset() is not needed elsewhere +$preferences->setCharset('utf-8'); + +// Without these lines the default caching mechanism is "array" but this uses a lot of memory. +// If possible, use a disk cache to enable attaching large attachments etc. +// You can override the default temporary directory by setting the TMPDIR environment variable. +if (@is_writable($tmpDir = sys_get_temp_dir())) { + $preferences->setTempDir($tmpDir)->setCacheType('disk'); +} + +// this should only be done when Swiftmailer won't use the native QP content encoder +// see mime_deps.php +if (version_compare(phpversion(), '5.4.7', '<')) { + $preferences->setQPDotEscape(false); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/swift_init.php b/vendor/swiftmailer/swiftmailer/lib/swift_init.php new file mode 100644 index 00000000..ff719634 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/swift_init.php @@ -0,0 +1,28 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Dependency injection initialization for Swift Mailer. + */ + +if (defined('SWIFT_INIT_LOADED')) { + return; +} + +define('SWIFT_INIT_LOADED', true); + +// Load in dependency maps +require __DIR__.'/dependency_maps/cache_deps.php'; +require __DIR__.'/dependency_maps/mime_deps.php'; +require __DIR__.'/dependency_maps/message_deps.php'; +require __DIR__.'/dependency_maps/transport_deps.php'; + +// Load in global library preferences +require __DIR__.'/preferences.php'; diff --git a/vendor/swiftmailer/swiftmailer/lib/swift_required.php b/vendor/swiftmailer/swiftmailer/lib/swift_required.php new file mode 100644 index 00000000..03a72cea --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/swift_required.php @@ -0,0 +1,30 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Autoloader and dependency injection initialization for Swift Mailer. + */ + +if (class_exists('Swift', false)) { + return; +} + +// Load Swift utility class +require __DIR__.'/classes/Swift.php'; + +if (!function_exists('_swiftmailer_init')) { + function _swiftmailer_init() + { + require __DIR__.'/swift_init.php'; + } +} + +// Start the autoloader and lazy-load the init script to set up dependency injection +Swift::registerAutoload('_swiftmailer_init'); diff --git a/vendor/swiftmailer/swiftmailer/lib/swift_required_pear.php b/vendor/swiftmailer/swiftmailer/lib/swift_required_pear.php new file mode 100644 index 00000000..2b5181a9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/swift_required_pear.php @@ -0,0 +1,30 @@ +<?php + +/* + * This file is part of SwiftMailer. + * (c) 2004-2009 Chris Corbyn + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Autoloader and dependency injection initialization for Swift Mailer. + */ + +if (class_exists('Swift', false)) { + return; +} + +// Load Swift utility class +require __DIR__.'/Swift.php'; + +if (!function_exists('_swiftmailer_init')) { + function _swiftmailer_init() + { + require __DIR__.'/swift_init.php'; + } +} + +// Start the autoloader and lazy-load the init script to set up dependency injection +Swift::registerAutoload('_swiftmailer_init'); diff --git a/vendor/swiftmailer/swiftmailer/lib/swiftmailer_generate_mimes_config.php b/vendor/swiftmailer/swiftmailer/lib/swiftmailer_generate_mimes_config.php new file mode 100755 index 00000000..3bb87807 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/swiftmailer_generate_mimes_config.php @@ -0,0 +1,193 @@ +#!/usr/bin/php + +<?php +define('APACHE_MIME_TYPES_URL', 'http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types'); +define('FREEDESKTOP_XML_URL', 'https://raw2.github.com/minad/mimemagic/master/script/freedesktop.org.xml'); + +function generateUpToDateMimeArray() +{ + $preamble = "<?php\n\n"; + $preamble .= "/*\n"; + $preamble .= " * This file is part of SwiftMailer.\n"; + $preamble .= " * (c) 2004-2009 Chris Corbyn\n *\n"; + $preamble .= " * For the full copyright and license information, please view the LICENSE\n"; + $preamble .= " * file that was distributed with this source code.\n *\n"; + $preamble .= " * autogenerated using http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types\n"; + $preamble .= " * and https://raw.github.com/minad/mimemagic/master/script/freedesktop.org.xml\n"; + $preamble .= " */\n\n"; + $preamble .= "/*\n"; + $preamble .= " * List of MIME type automatically detected in Swift Mailer.\n"; + $preamble .= " */\n\n"; + $preamble .= "// You may add or take away what you like (lowercase required)\n\n"; + + // get current mime types files + $mime_types = @file_get_contents(APACHE_MIME_TYPES_URL); + $mime_xml = @file_get_contents(FREEDESKTOP_XML_URL); + + // prepare valid mime types + $valid_mime_types = array(); + + // split mime type and extensions eg. "video/x-matroska mkv mk3d mks" + if (preg_match_all('/^#?([a-z0-9\-\+\/\.]+)[\t]+(.*)$/miu', $mime_types, $matches) !== false) { + // collection of predefined mimetypes (bugfix for wrong resolved or missing mime types) + $valid_mime_types_preset = array( + 'php' => 'application/x-php', + 'php3' => 'application/x-php', + 'php4' => 'application/x-php', + 'php5' => 'application/x-php', + 'zip' => 'application/zip', + 'gif' => 'image/gif', + 'png' => 'image/png', + 'css' => 'text/css', + 'js' => 'text/javascript', + 'txt' => 'text/plain', + 'aif' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'avi' => 'video/avi', + 'bmp' => 'image/bmp', + 'bz2' => 'application/x-bz2', + 'csv' => 'text/csv', + 'dmg' => 'application/x-apple-diskimage', + 'doc' => 'application/msword', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'eml' => 'message/rfc822', + 'aps' => 'application/postscript', + 'exe' => 'application/x-ms-dos-executable', + 'flv' => 'video/x-flv', + 'gz' => 'application/x-gzip', + 'hqx' => 'application/stuffit', + 'htm' => 'text/html', + 'html' => 'text/html', + 'jar' => 'application/x-java-archive', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'm3u' => 'audio/x-mpegurl', + 'm4a' => 'audio/mp4', + 'mdb' => 'application/x-msaccess', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mov' => 'video/quicktime', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'odg' => 'vnd.oasis.opendocument.graphics', + 'odp' => 'vnd.oasis.opendocument.presentation', + 'odt' => 'vnd.oasis.opendocument.text', + 'ods' => 'vnd.oasis.opendocument.spreadsheet', + 'ogg' => 'audio/ogg', + 'pdf' => 'application/pdf', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'ps' => 'application/postscript', + 'rar' => 'application/x-rar-compressed', + 'rtf' => 'application/rtf', + 'tar' => 'application/x-tar', + 'sit' => 'application/x-stuffit', + 'svg' => 'image/svg+xml', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'ttf' => 'application/x-font-truetype', + 'vcf' => 'text/x-vcard', + 'wav' => 'audio/wav', + 'wma' => 'audio/x-ms-wma', + 'wmv' => 'audio/x-ms-wmv', + 'xls' => 'application/excel', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xml' => 'application/xml', + ); + + // wrap array for generating file + foreach ($valid_mime_types_preset as $extension => $mime_type) { + // generate array for mimetype to extension resolver (only first match) + $valid_mime_types[$extension] = "'{$extension}' => '{$mime_type}'"; + } + + // collect extensions + $valid_extensions = array(); + + // all extensions from second match + foreach ($matches[2] as $i => $extensions) { + // explode multiple extensions from string + $extensions = explode(' ', strtolower($extensions)); + + // force array for foreach + if (!is_array($extensions)) { + $extensions = array($extensions); + } + + foreach ($extensions as $extension) { + // get mime type + $mime_type = $matches[1][$i]; + + // check if string length lower than 10 + if (strlen($extension) < 10) { + // add extension + $valid_extensions[] = $extension; + + if (!isset($valid_mime_types[$mime_type])) { + // generate array for mimetype to extension resolver (only first match) + $valid_mime_types[$extension] = "'{$extension}' => '{$mime_type}'"; + } + } + } + } + } + + $xml = simplexml_load_string($mime_xml); + + foreach ($xml as $node) { + // check if there is no pattern + if (!isset($node->glob['pattern'])) { + continue; + } + + // get all matching extensions from match + foreach ((array) $node->glob['pattern'] as $extension) { + // skip none glob extensions + if (strpos($extension, '.') === false) { + continue; + } + + // remove get only last part + $extension = explode('.', strtolower($extension)); + $extension = end($extension); + + // maximum length in database column + if (strlen($extension) <= 9) { + $valid_extensions[] = $extension; + } + } + + if (isset($node->glob['pattern'][0])) { + // mime type + $mime_type = strtolower((string) $node['type']); + + // get first extension + $extension = strtolower(trim($node->glob['ddpattern'][0], '*.')); + + // skip none glob extensions and check if string length between 1 and 10 + if (strpos($extension, '.') !== false || strlen($extension) < 1 || strlen($extension) > 9) { + continue; + } + + // check if string length lower than 10 + if (!isset($valid_mime_types[$mime_type])) { + // generate array for mimetype to extension resolver (only first match) + $valid_mime_types[$extension] = "'{$extension}' => '{$mime_type}'"; + } + } + } + + // full list of valid extensions only + $valid_mime_types = array_unique($valid_mime_types); + ksort($valid_mime_types); + + // combine mime types and extensions array + $output = "$preamble\$swift_mime_types = array(\n ".implode($valid_mime_types, ",\n ")."\n);"; + + // write mime_types.php config file + @file_put_contents('./mime_types.php', $output); +} + +generateUpToDateMimeArray(); diff --git a/vendor/swiftmailer/swiftmailer/phpunit.xml.dist b/vendor/swiftmailer/swiftmailer/phpunit.xml.dist new file mode 100644 index 00000000..606c5b44 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/phpunit.xml.dist @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<phpunit backupGlobals="false" + backupStaticAttributes="false" + colors="true" + convertErrorsToExceptions="true" + convertNoticesToExceptions="true" + convertWarningsToExceptions="true" + processIsolation="false" + stopOnFailure="false" + syntaxCheck="false" + bootstrap="tests/bootstrap.php" +> + <php> + <ini name="intl.default_locale" value="en"/> + <ini name="intl.error_level" value="0"/> + <ini name="memory_limit" value="-1"/> + <ini name="error_reporting" value="-1" /> + </php> + + <testsuites> + <testsuite name="SwiftMailer unit tests"> + <directory>tests/unit</directory> + </testsuite> + <testsuite name="SwiftMailer acceptance tests"> + <directory>tests/acceptance</directory> + </testsuite> + <testsuite name="SwiftMailer bug"> + <directory>tests/bug</directory> + </testsuite> + <testsuite name="SwiftMailer smoke tests"> + <directory>tests/smoke</directory> + </testsuite> + </testsuites> + + <listeners> + <listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" /> + <listener class="Mockery\Adapter\Phpunit\TestListener" /> + </listeners> +</phpunit> diff --git a/vendor/swiftmailer/swiftmailer/tests/IdenticalBinaryConstraint.php b/vendor/swiftmailer/swiftmailer/tests/IdenticalBinaryConstraint.php new file mode 100644 index 00000000..069d11ab --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/IdenticalBinaryConstraint.php @@ -0,0 +1,62 @@ +<?php + +/** + * A binary safe string comparison. + * + * @author Chris Corbyn + */ +class IdenticalBinaryConstraint extends \PHPUnit_Framework_Constraint +{ + protected $value; + + public function __construct($value) + { + $this->value = $value; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + public function matches($other) + { + $aHex = $this->asHexString($this->value); + $bHex = $this->asHexString($other); + + return $aHex === $bHex; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'indentical binary'; + } + + /** + * Get the given string of bytes as a stirng of Hexadecimal sequences. + * + * @param string $binary + * + * @return string + */ + private function asHexString($binary) + { + $hex = ''; + + $bytes = unpack('H*', $binary); + + foreach ($bytes as &$byte) { + $byte = strtoupper($byte); + } + + return implode('', $bytes); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/StreamCollector.php b/vendor/swiftmailer/swiftmailer/tests/StreamCollector.php new file mode 100644 index 00000000..7f079d98 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/StreamCollector.php @@ -0,0 +1,11 @@ +<?php + +class Swift_StreamCollector +{ + public $content = ''; + + public function __invoke($arg) + { + $this->content .= $arg; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/SwiftMailerSmokeTestCase.php b/vendor/swiftmailer/swiftmailer/tests/SwiftMailerSmokeTestCase.php new file mode 100644 index 00000000..21d89e83 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/SwiftMailerSmokeTestCase.php @@ -0,0 +1,46 @@ +<?php + +/** + * Base test for smoke tests. + * + * @author Rouven Weßling + */ +class SwiftMailerSmokeTestCase extends SwiftMailerTestCase +{ + public function setUp() + { + if (!defined('SWIFT_SMOKE_TRANSPORT_TYPE')) { + $this->markTestSkipped( + 'Smoke tests are skipped if tests/smoke.conf.php is not edited' + ); + } + } + + protected function _getMailer() + { + switch (SWIFT_SMOKE_TRANSPORT_TYPE) { + case 'smtp': + $transport = Swift_DependencyContainer::getInstance()->lookup('transport.smtp') + ->setHost(SWIFT_SMOKE_SMTP_HOST) + ->setPort(SWIFT_SMOKE_SMTP_PORT) + ->setUsername(SWIFT_SMOKE_SMTP_USER) + ->setPassword(SWIFT_SMOKE_SMTP_PASS) + ->setEncryption(SWIFT_SMOKE_SMTP_ENCRYPTION) + ; + break; + case 'sendmail': + $transport = Swift_DependencyContainer::getInstance()->lookup('transport.sendmail') + ->setCommand(SWIFT_SMOKE_SENDMAIL_COMMAND) + ; + break; + case 'mail': + case 'nativemail': + $transport = Swift_DependencyContainer::getInstance()->lookup('transport.mail'); + break; + default: + throw new Exception('Undefined transport ['.SWIFT_SMOKE_TRANSPORT_TYPE.']'); + } + + return new Swift_Mailer($transport); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/SwiftMailerTestCase.php b/vendor/swiftmailer/swiftmailer/tests/SwiftMailerTestCase.php new file mode 100644 index 00000000..f0e27361 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/SwiftMailerTestCase.php @@ -0,0 +1,34 @@ +<?php + +/** + * A base test case with some custom expectations. + * + * @author Rouven Weßling + */ +class SwiftMailerTestCase extends \PHPUnit_Framework_TestCase +{ + public static function regExp($pattern) + { + if (!is_string($pattern)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + return new PHPUnit_Framework_Constraint_PCREMatch($pattern); + } + + public function assertIdenticalBinary($expected, $actual, $message = '') + { + $constraint = new IdenticalBinaryConstraint($expected); + self::assertThat($actual, $constraint, $message); + } + + protected function tearDown() + { + \Mockery::close(); + } + + protected function getMockery($class) + { + return \Mockery::mock($class); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/charsets/iso-2022-jp/one.txt b/vendor/swiftmailer/swiftmailer/tests/_samples/charsets/iso-2022-jp/one.txt new file mode 100644 index 00000000..c2923deb --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/charsets/iso-2022-jp/one.txt @@ -0,0 +1,11 @@ +ISO-2022-JPã¯ã€ã‚¤ãƒ³ã‚¿ãƒ¼ãƒãƒƒãƒˆä¸Š(特ã«é›»åメール)ãªã©ã§ä½¿ã‚ã‚Œã‚‹æ—¥æœ¬ã®æ–‡å—ç”¨ã®æ–‡å—符å·åŒ–æ–¹å¼ã€‚ISO/IEC 2022ã®ã‚¨ã‚¹ã‚±ãƒ¼ãƒ—シーケンスを利用ã—ã¦æ–‡å—集åˆã‚’切り替ãˆã‚‹7ビットã®ã‚³ãƒ¼ãƒ‰ã§ã‚ã‚‹ã“ã¨ã‚’特徴ã¨ã™ã‚‹ (アナウンス機能ã®ã‚¨ã‚¹ã‚±ãƒ¼ãƒ—シーケンスã¯çœç•¥ã•れる)。俗ã«ã€ŒJISコードã€ã¨å‘¼ã°ã‚Œã‚‹ã“ã¨ã‚‚ã‚る。
+
+概è¦
+日本語表記ã¸ã®åˆ©ç”¨ãŒæƒ³å®šã•れã¦ã„ã‚‹æ–‡å—コードã§ã‚ã‚Šã€æ—¥æœ¬èªžã®åˆ©ç”¨ã•れるãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã«ãŠã„ã¦ã€æ—¥æœ¬ã®è¦æ ¼ã‚’応用ã—ãŸã‚‚ã®ã§ã‚る。ã¾ãŸæ–‡å—集åˆã¨ã—ã¦ã¯ã€æ—¥æœ¬èªžã§ç”¨ã„られる漢å—ã€ã²ã‚‰ãŒãªã€ã‚«ã‚¿ã‚«ãƒŠã¯ã‚‚ã¡ã‚ã‚“ã€ãƒ©ãƒ†ãƒ³æ–‡å—ã€ã‚®ãƒªã‚·ã‚¢æ–‡å—ã€ã‚リル文å—ãªã©ã‚‚å«ã‚“ã§ãŠã‚Šã€å¦è¡“や産æ¥ã®åˆ†é‡Žã§ã®åˆ©ç”¨ã‚‚考慮ãŸã‚‚ã®ã¨ãªã£ã¦ã„ã‚‹ã€‚è¦æ ¼åã«ã€ISOã®æ—¥æœ¬èªžã®è¨€èªžã‚³ãƒ¼ãƒ‰ã§ã‚ã‚‹jaã§ã¯ãªãã€å›½ãƒ»åœ°åŸŸåコードã®JPãŒç¤ºã•れã¦ã„るゆãˆã‚“ã§ã‚る。
+æ–‡å—集åˆã¨ã—ã¦JIS X 0201ã®C0集åˆï¼ˆåˆ¶å¾¡æ–‡å—)ã€JIS X 0201ã®ãƒ©ãƒ†ãƒ³æ–‡å—集åˆã€ISO 646ã®å›½éš›åŸºæº–版図形文å—ã€JIS X 0208ã®1978年版(JIS C 6226-1978)ã¨1983å¹´ãŠã‚ˆã³1990年版ãŒåˆ©ç”¨ã§ãる。JIS X 0201ã®ç‰‡ä»®åæ–‡å—集åˆã¯åˆ©ç”¨ã§ããªã„。1986年以é™ã€æ—¥æœ¬ã®é›»åメールã§ç”¨ã„られã¦ããŸJUNETã‚³ãƒ¼ãƒ‰ã‚’ã€æ‘井純・Mark Crispin・Erik van der PoelãŒ1993å¹´ã«RFC化ã—ãŸã‚‚ã®(RFC 1468)。後ã«JIS X 0208:1997ã®é™„属書2ã¨ã—ã¦JISã«è¦å®šã•れãŸã€‚MIMEã«ãŠã‘ã‚‹æ–‡å—符å·åŒ–æ–¹å¼ã®è˜åˆ¥ç”¨ã®åå‰ã¨ã—㦠IANA ã«ç™»éŒ²ã•れã¦ã„る。
+ãªãŠã€ç¬¦å·åŒ–ã®ä»•様ã«ã¤ã„ã¦ã¯ISO/IEC 2022#ISO-2022-JPã‚‚å‚照。
+
+ISO-2022-JPã¨éžæ¨™æº–的拡張使用
+「JISコードã€ï¼ˆã¾ãŸã¯ã€ŒISO-2022-JPã€ï¼‰ã¨ã„ã†ã‚³ãƒ¼ãƒ‰åã®è¦å®šä¸‹ã§ã¯ã€ãã®ä»•様通りã®ä½¿ç”¨ãŒæ±‚ã‚られる。ã—ã‹ã—ã€Windows OS上ã§ã¯ã€å®Ÿéš›ã«ã¯CP932コード (Microsoftã«ã‚ˆã‚‹Shift JISã‚’æ‹¡å¼µã—ãŸäºœç¨®ã€‚ISO-2022-JPè¦å®šå¤–æ–‡å—ãŒè¿½åŠ ã•れã¦ã„る。)ã«ã‚ˆã‚‹ç‹¬è‡ªæ‹¡å¼µï¼ˆã®æ–‡å—)をæ–りãªã使ã†ã‚¢ãƒ—リケーションãŒå¤šã„。ã“ã®ä¾‹ã¨ã—ã¦Internet Explorerã‚„Outlook ExpressãŒã‚る。ã¾ãŸã€EmEditorã€ç§€ä¸¸ã‚¨ãƒ‡ã‚£ã‚¿ã‚„Thunderbirdã®ã‚ˆã†ãªMicrosoft社以外ã®Windowsアプリケーションã§ã‚‚åŒæ§˜ã®å ´åˆãŒã‚る。ã“ã®å ´åˆã€ISO-2022-JPã®ç¯„å›²å¤–ã®æ–‡å—を使ã£ã¦ã—ã¾ã†ã¨ã€ç•°ãªã‚‹è£½å“é–“ã§ã¯æœªå®šç¾©ä¸æ˜Žæ–‡å—ã¨ã—ã¦èªè˜ã•れるã‹ã€ã‚‚ã—ãã¯æ–‡å—化ã‘ã‚’èµ·ã“ã™åŽŸå› ã¨ãªã‚‹ã€‚ãã®ãŸã‚ã€Windows用ã®é›»åメールクライアントã§ã‚ã£ã¦ã‚‚ç‹¬è‡ªæ‹¡å¼µã®æ–‡å—を使用ã™ã‚‹ã¨è¦å‘Šã‚’出ã—ãŸã‚Šã€ã‚ãˆã¦ä½¿ãˆãªã„よã†ã«åˆ¶é™ã—ã¦ã„ã‚‹ã‚‚ã®ã‚‚å˜åœ¨ã™ã‚‹ã€‚ã•らã«ã¯ISO-2022-JPã®ç¯„囲内ã§ã‚ã£ã¦ã‚‚CP932ã¯éžæ¨™æº–æ–‡å—(FULLWIDTH TILDEç‰ï¼‰ã‚’æŒã¤ã®ã§æ–‡å—化ã‘ã®åŽŸå› ã«ãªã‚Šå¾—る。
+ã¾ãŸã€ç¬¦å·åŒ–æ–¹å¼åã‚’ISO-2022-JPã¨ã—ã¦ã„ã‚‹ã®ã«ã€æ–‡å—集åˆã¨ã—ã¦ã¯JIS X 0212 (ã„ã‚ゆる補助漢å—) ã‚„JIS X 0201ã®ç‰‡ä»®åæ–‡å—é›†åˆ (ã„ã‚ゆるåŠè§’カナ) をも符å·åŒ–ã—ã¦ã„る例ãŒã‚ã‚‹ãŒã€ISO-2022-JPã§ã¯ã“ã‚Œã‚‰ã®æ–‡å—を許容ã—ã¦ã„ãªã„。ã“れらã®ç¬¦å·åŒ–ã¯ç‹¬è‡ªæ‹¡å¼µã®å®Ÿè£…ã§ã‚りã€ä¸ã«ã¯ISO/IEC 2022ã®ä»•æ§˜ã«æº–æ‹ ã™ã‚‰ã—ã¦ã„ãªã„ã‚‚ã®ã‚‚ã‚ã‚‹[2]。従ã£ã¦å—ä¿¡å´ã®é›»åメールクライアントãŒã“れらã®ç‹¬è‡ªæ‹¡å¼µã«å¯¾å¿œã—ã¦ã„ãªã„å ´åˆã€ãã®æ–‡å—ã‚ã‚‹ã„ã¯ãã®æ–‡å—ã‚’å«ã‚€è¡Œã€æ™‚ã«ã¯ãƒ†ã‚ã‚¹ãƒˆå…¨ä½“ãŒæ–‡å—化ã‘ã™ã‚‹ã“ã¨ãŒã‚る。
+
diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/charsets/iso-8859-1/one.txt b/vendor/swiftmailer/swiftmailer/tests/_samples/charsets/iso-8859-1/one.txt new file mode 100644 index 00000000..3101178a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/charsets/iso-8859-1/one.txt @@ -0,0 +1,19 @@ +Op mat eraus hinnen beschte, rou zënne schaddreg ké. Ké sin Eisen Kaffi prächteg, den haut esou Fielse wa, Well zielen d'Welt am dir. Aus grousse rëschten d'Stroos do, as dat Kléder gewëss d'Kà chen. Schied gehéiert d'Vioule net hu, rou ke zënter Säiten d'Hierz. Ze eise Fletschen mat, gei as gréng d'Lëtzebuerger. Wäit räich no mat. + +Säiten d'Liewen aus en. Un gëtt bléit lossen wee, da wéi alle weisen Kolrettchen. Et deser d'Pan d'Kirmes vun, en wuel Benn rëschten méi. En get drem ménger beschte, da wär Stad welle. Nun Dach d'Pied do, mä gét ruffen gehéiert. Ze onser ugedon fir, d'Liewen Plett'len ech no, si Räis wielen bereet wat. Iwer spilt fir jo. + +An hin däischter Margréitchen, eng ke Frot brommt, vu den Räis néierens. Da hir Hunn Frot nozegon, rout Fläiß Himmel zum si, net gutt Kaffi Gesträich fu. Vill lait Gaart sou wa, Land Mamm Schuebersonndeg rei do. Gei geet Minutt en, gei d'Leit beschte Kolrettchen et, Mamm fergiess un hun. + +Et gutt Heck kommen oft, Lann rëscht rei um, Hunn rëscht schéinste ke der. En lait zielen schnéiwäiss hir, fu rou botze éiweg Minutt, rem fest gudden schaddreg en. Noper bereet Margréitchen mat op, dem denkt d'Leit d'Vioule no, oft ké Himmel Hämmel. En denkt blénken Fréijor net, Gart Schiet d'Natur no wou. No hin Ierd Frot d'Kirmes. Hire aremt un rou, ké den éiweg wielen Milliounen. + +Mir si Hunn Blénkeg. Ké get ston derfir d'Kà chen. Haut d'Pan fu ons, dé frou löschteg d'Meereische rei. Sou op wuel Léift. Stret schlon grousse gin hu. Mä denkt d'Leit hinnen net, ké gét haut fort rëscht. + +Koum d'Pan hannendrun ass ké, ké den brét Kaffi geplot. Schéi Hären d'Pied fu gét, do d'Mier néierens bei. Rëm päift Hämmel am, wee Engel beschéngt mä. Brommt klinzecht der ke, wa rout jeitzt dén. Get Zalot d'Vioule däischter da, jo fir Bänk päift duerch, bei d'Beem schéinen Plett'len jo. Den haut Faarwen ze, eng en Biereg Kirmesdag, um sin alles Faarwen d'Vioule. + +Eng Hunn Schied et, wat wa Frot fest gebotzt. Bei jo bleiwe ruffen Klarinett. Un Feld klinzecht gét, rifft Margréitchen rem ke. Mir dé Noper duurch gewëss, ston sech kille sin en. Gei Stret d'Wise um, Haus Gart wee as. Monn ménger an blo, wat da Gart gefällt Hämmelsbrot. + +Brommt geplot och ze, dat wa Räis Well Kaffi. Do get spilt prächteg, as wär kille bleiwe gewalteg. Onser frësch Margréitchen rem ke, blo en huet ugedon. Onser Hemecht wär de, hu eraus d'Sonn dat, eise deser hannendrun da och. + +As durch Himmel hun, no fest iw'rem schéinste mir, Hunn séngt Hierz ke zum. Séngt iw'rem d'Natur zum an. Ke wär gutt Grénge. Kënnt gudden prächteg mä rei. Dé dir Blénkeg Klarinett Kolrettchen, da fort muerges d'Kanner wou, main Feld ruffen vu wéi. Da gin esou Zalot gewalteg, gét vill Hemecht blénken dé. + +Haut gréng nun et, nei vu Bass gréng d'Gaassen. Fest d'Beem uechter si gin. Oft vu sinn wellen kréien. Et ass lait Zalot schéinen.
\ No newline at end of file diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/charsets/utf-8/one.txt b/vendor/swiftmailer/swiftmailer/tests/_samples/charsets/utf-8/one.txt new file mode 100644 index 00000000..26c94d5d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/charsets/utf-8/one.txt @@ -0,0 +1,22 @@ +Код одно гринÑпана руководишь на. Его вы Ð·Ð½Ð°Ð½Ð¸Ñ Ð´Ð²Ð¸Ð¶ÐµÐ½Ð¸Ðµ. Ты две начать +одиночку, Ñказать оÑнователь удовольÑтвием но миф. Бы какие ÑиÑтема тем. +ПолноÑтью иÑпользует три мы, человек клоунов те наÑ, бы давать творчеÑкую +ÑзотеричеÑÐºÐ°Ñ ÑˆÐµÑ„. + +Мог не помнить никакого ÑÑкономленного, две либо какие пишите бы. Должен +компанию кто те, Ñтот заключалаÑÑŒ проектировщик не ты. Глупые периоды ты +длÑ. Вам который хороший он. Те любых ÐºÑ€ÐµÐ¼Ð½Ð¸Ñ ÐºÐ¾Ð½Ñ†ÐµÐ½Ñ‚Ñ€Ð¸Ñ€ÑƒÑŽÑ‚ÑÑ Ð¼Ð¾Ð³, +Ñобирать принадлежите без вы. + +ДжоÑла меньше хорошего вы миф, за тем году разработки. Даже управлÑющим +руководители был не. Три коде выпуÑкать заботитьÑÑ Ð½Ñƒ. То его ÑиÑтема +удовольÑтвием безоÑтановочно, или ты главной процеÑÑорах. Мы без джоÑл +Ð·Ð½Ð°Ð½Ð¸Ñ Ð¿Ð¾Ð»ÑƒÑ‡Ð°Ñ‚, Ñтатьи оÑтальные мы ещё. + +Ðих руÑÑком каÑаетÑÑ Ð¿Ð¾Ñкольку по, образование должником +ÑиÑтематизированный ну мои. Прийти кандидата универÑитет но наÑ, Ð´Ð»Ñ Ð±Ñ‹ +должны никакого, биг многие причин Ð¸Ð½Ñ‚ÐµÑ€Ð²ÑŒÑŽÐ¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð·Ð°. + +Тем до плиту почему. Вот учёт такие одного бы, об биг разным внешних +промежуток. Ð’Ð°Ñ Ð´Ð¾ какому возможноÑтей безответÑтвенный, были погодите бы +его, по них глупые долгий количеÑтва. diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/charsets/utf-8/three.txt b/vendor/swiftmailer/swiftmailer/tests/_samples/charsets/utf-8/three.txt new file mode 100644 index 00000000..c81ccd59 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/charsets/utf-8/three.txt @@ -0,0 +1,45 @@ +Αν ήδη διάβασε γλιτώσει μεταγλωτίσει, αυτήν θυμάμαι μου μα. Την κατάσταση χÏησιμοποίησΠνα! Τα διαφοÏά φαινόμενο διολισθήσεις πες, υψηλότεÏη Ï€Ïοκαλείς πεÏισσότεÏες όχι κι. Με ελÎγχου γίνεται σας, μικÏής δημιουÏγοÏν τη του. Τις τα γÏάψει εικόνες απαÏάδεκτη? + +Îα ότι Ï€Ïώτοι απαÏαίτητο. Άμεση πετάνε κακόκεφος τον ÏŽÏ‚, να χώÏου πιθανότητες του. Το μÎχÏι οÏίστε λιγότεÏους σας. Πω ναί φυσικά εικόνες. + +Μου οι κώδικα αποκλειστικοÏÏ‚, λες το μάλλον συνεχώς. ÎÎου σημεία απίστευτα σας μα. ΧÏόνου μεταγλωτιστής σε νÎα, τη τις πιάνει μποÏοÏσες Ï€ÏογÏαμματιστÎÏ‚. Των κάνε βγαίνει εντυπωσιακό τα? ΚÏατάει τεσσαÏών δυστυχώς της κι, ήδη υψηλότεÏη εξακολουθεί τα? + +ÎÏα πετάνε μποÏοÏσε λιγότεÏους αν, τα απαÏάδεκτη συγχωνευτεί Ïοή. Τη ÎγÏαψες συνηθίζουν σαν. Όλα με υλικό στήλες χειÏότεÏα. Ανώδυνη δουλÎψει επί ως, αν διαδίκτυο εσωτεÏικών παÏάγοντες από. ΚεντÏικό επιτυχία πες το. + +Πω ναι λÎει τελειώσει, Îξι ως ÎÏγων τελειώσει. Με αÏχεία βουτήξουν ανταγωνιστής ÏŽÏα, Ï€Î¿Î»Ï Î³Ïαφικά σελίδων τα στη. ÎŒÏο οÎλεγχος δημιουÏγοÏν δε, ας θÎλεις ελÎγχου συντακτικό ÏŒÏο! Της θυμάμαι επιδιόÏθωση τα. Για μποÏοÏσε πεÏισσότεÏο αν, μÎγιστη σημαίνει αποφάσισε τα του, άτομο αποτελÎσει τι στα. + +Τι στην αφήσεις διοίκηση στη. Τα εσφαλμÎνη δημιουÏγια επιχείÏιση Îξι! Βήμα μαγικά εκτελÎσει ανά τη. Όλη αφήσεις συνεχώς εμποÏικά αν, το λες κόλπα επιτυχία. Ότι οι ζώνη κειμÎνων. ÎŒÏο κι Ïωτάει γÏαμμής πελάτες, τελειώσει διολισθήσεις καθυστεÏοÏσε αν εγώ? Τι πετοÏν διοίκηση Ï€Ïοβλήματα ήδη. + +Τη γλιτώσει Î±Ï€Î¿Î¸Î·ÎºÎµÏ…Ï„Î¹ÎºÎ¿Ï Î¼Î¹Î±. Πω Îξι δημιουÏγια πιθανότητες, ως Ï€Îντε ελÎγχους εκτελείται λες. Πως εÏωτήσεις διοικητικό συγκεντÏωμÎνοι οι, ας συνεχώς διοικητικό αποστηθίσει σαν. Δε Ï€Ïώτες συνεχώς διολισθήσεις Îχω, από τι κανÎνας βουτήξουν, γειτονιάς Ï€Ïοσεκτικά ανταγωνιστής κι σαν. + +ΔημιουÏγια συνηθίζουν κλπ τι? Όχι ποσοστό διακοπής κι. Κλπ φακÎλους δεδομÎνη εξοÏγιστικά θα? Υποψήφιο καθοÏίζουν με όλη, στα πήÏε Ï€Ïοσοχή εταιÏείες πω, ÏŽÏ‚ τον συνάδελφος διοικητικό δημιουÏγήσεις! ΔοÏλευε επιτίθενται σας θα, με Îνας παÏαγωγικής Îνα, να ναι σημεία μÎγιστη απαÏάδεκτη? + +Σας τεσσαÏών συνεντεÏξης τη, αÏπάζεις σίγουÏος μη για', επί τοπικÎÏ‚ εντολÎÏ‚ ακοÏσει θα? Ως δυστυχής μεταγλωτιστής όλη, να την είχαν σφάλμα απαÏαίτητο! Μην ÏŽÏ‚ άτομο διοÏθώσει χÏησιμοποιοÏνταν. Δεν τα κόλπα πετάξαμε, μη που άγχος Ï…ÏŒÏκη άμεση, Î±Ï†Î¿Ï Î´Ï…ÏƒÏ„Ï…Ï‡ÏŽÏ‚ διακόψουμε ÏŒÏο αν! Όλη μαγικά πετάνε επιδιοÏθώσεις δε, Ïοή φυσικά αποτελÎσει πω. + +ΆπειÏα παÏαπάνω φαινόμενο πω ÏŽÏα, σαν πόÏτες κÏατήσουν συνηθίζουν ως. Κι ÏŽÏα Ï„ÏÎξει είχαμε εφαÏμογή. Απλό σχεδιαστής μεταγλωτιστής ας επί, τις τα όταν ÎγÏαψες γÏαμμής? Όλα κάνεις συνάδελφος εÏγαζόμενοι θα, χαÏÏ„Î¹Î¿Ï Ï‡Î±Î¼Î·Î»ÏŒÏ‚ τα Ïοή. Ως ναι ÏŒÏοφο ÎÏθει, μην πελάτες αποφάσισε μεταφÏαστής με, να βιαστικά εκδόσεις αναζήτησης λες. Των φταίει εκθÎσεις Ï€Ïοσπαθήσεις οι, σπίτι αποστηθίσει ας λες? + +ÎÏ‚ που υπηÏεσία απαÏαίτητο δημιουÏγείς. Μη άÏα χαÏά καθώς νÏχτας, πω ματ μπουν είχαν. Άμεση δημιουÏγείς ÏŽÏ‚ Ïοή, γÏάψει γÏαμμής σίγουÏος στα τι! Αν Î±Ï†Î¿Ï Ï€Ïώτοι εÏγαζόμενων ναί. + +Άμεση διοÏθώσεις με δÏο? Έχουν παÏάδειγμα των θα, μου ÎÏθει θυμάμαι πεÏισσότεÏο το. Ότι θα Î±Ï†Î¿Ï Ï‡Ïειάζονται πεÏισσότεÏες. Σαν συνεχώς πεÏίπου οι. + +ÎÏ‚ Ï€Ïώτης πετάξαμε λες, ÏŒÏο κι Ï€Ïώτες ζητήσεις δυστυχής. Ανά χÏόνου διακοπή επιχειÏηματίες ας, ÏŽÏ‚ μόλις άτομο χειÏότεÏα ÏŒÏο, κÏατάει σχεδιαστής Ï€Ïοσπαθήσεις νÎο το. Πουλάς Ï€ÏοσθÎσει όλη πω, Ï„Ïπου χαÏακτηÏιστικό εγώ σε, πω πιο δοÏλευε αναζήτησης? ΑναφοÏά δίνοντας σαν μη, μάθε δεδομÎνη εσωτεÏικών με ναι, αναφÎÏονται πεÏιβάλλοντος ÏŽÏα αν. Και λÎει απόλαυσε τα, που το ÏŒÏοφο Ï€ÏοσπαθοÏν? + +Πάντα χÏόνου χÏήματα ναι το, σαν σωστά θυμάμαι σκεφτείς τα. Μα αποτελÎσει ανεπιθÏμητη την, πιο το Ï„Îτοιο ατόμου, τη των Ï„Ïόπο εÏγαλείων επιδιόÏθωσης. ΠεÏιβάλλον παÏαγωγικής σου κι, κλπ οι Ï„Ïπου κακόκεφους αποστηθίσει, δε των πλÎον Ï„Ïόποι. Πιθανότητες χαÏακτηÏιστικών σας κι, γÏαφικά δημιουÏγήσεις μια οι, πω πολλοί εξαÏτάται Ï€Ïοσεκτικά εδώ. Σταματάς παÏάγοντες για' ÏŽÏ‚, στις Ïωτάει το ναι! ΚαÏÎκλα ζητήσεις συνδυασμοÏÏ‚ τη ήδη! + +Για μαγικά συνεχώς ακοÏσει το. Σταματάς Ï€Ïοϊόντα βουτήξουν ÏŽÏ‚ Ïοή. Είχαν Ï€Ïώτες οι ναι, μα λες αποστηθίσει ανακαλÏπτεις. ÎŒÏοφο άλγεβÏα παÏαπάνω εδώ τη, Ï€Ïόσληψη λαμβάνουν καταλάθος ήδη ας? Ως και εισαγωγή κÏατήσουν, Îνας κακόκεφους κι μας, όχι κώδικάς παίξουν πω. Πω νÎα κÏατάει εκφÏάσουν, τότε τελικών τη όχι, ας της Ï„ÏÎξει αλλάζοντας αποκλειστικοÏÏ‚. + +Ένας βιβλίο σε άÏα, ναι ως γÏάψει ταξινομεί διοÏθώσεις! Εδώ να γεγονός συγγÏαφείς, ÏŽÏ‚ ήδη διακόψουμε επιχειÏηματίες? Ότι πακÎτων εσφαλμÎνη κι, θα ÏŒÏο κόλπα παÏαγωγικής? Αν Îχω κεντÏικό υψηλότεÏη, κι δεν ίδιο πετάνε παÏατηÏοÏμενη! Που λοιπόν σημαντικό μα, Ï€Ïοκαλείς χειÏοκÏοτήματα ως όλα, μα επί κόλπα άγχος γÏαμμÎÏ‚! Δε σου κάνεις βουτήξουν, μη ÎÏγων επενδυτής χÏησιμοποίησΠστα, ως του Ï€Ïώτες διάσημα σημαντικό. + +Βιβλίο τεÏάστιο Ï€ÏοκÏπτουν σαν το, σαν Ï„Ïόπο επιδιόÏθωση ας. Είχαν Ï€Ïοσοχή Ï€Ïοσπάθεια κι ματ, εδώ ως Îτσι σελίδων συζήτηση. Και στην βγαίνει εσφαλμÎνη με, δυστυχής παÏάδειγμα δε μας, από σε Ï…ÏŒÏκη επιδιόÏθωσης. ÎÎα πω νÎου πιθανό, στήλες συγγÏαφείς μπαίνοντας μα για', το Ïωτήσει κακόκεφους της? Μου σε αÏÎσει συγγÏαφής συγχωνευτεί, μη μου Ï…ÏŒÏκη ξÎχασε διακοπής! ÎÏ‚ επί αποφάσισε αποκλειστικοÏÏ‚ χÏησιμοποιώντας, χÏήματα σελίδων ταξινομεί ναι με. + +Μη ανά γÏαμμή απόλαυσε, πω ναι μάτσο διασφαλίζεται. Τη Îξι μόλις εÏγάστηκε δημιουÏγοÏν, Îκδοση αναφοÏά δυσκολότεÏο οι νÎο. Σας ως μποÏοÏσε παÏάδειγμα, αν ότι δοÏλευε μποÏοÏσε αποκλειστικοÏÏ‚, πιο λÎει βουτήξουν διοÏθώσει ως. Έχω τελευταία κακόκεφους ας, όσο εÏγαζόμενων δημιουÏγήσεις τα. + +Του αν δουλÎψει μποÏοÏσε, πετοÏν χαμηλός εδώ ας? ΚÏκλο Ï„Ïπους με που, δεν σε Îχουν συνεχώς χειÏότεÏα, τις τι απαÏάδεκτη συνηθίζουν? Θα μην τους αυτήν, τη Îνα πήÏε πακÎτων, κι Ï€ÏοκÏπτουν πεÏιβάλλον πως. Μα για δουλÎψει απόλαυσε εφαμοÏγής, ÏŽÏ‚ εδώ σημαίνει μποÏοÏσες, άμεση ακοÏσει Ï€Ïοσοχή τη εδώ? + +Στα δώσε αθόÏυβες λιγότεÏους οι, δε αναγκάζονται αποκλειστικοÏÏ‚ όλα! Ας μπουν διοικητικό μια, πάντα ελÎγχου διοÏθώσεις ÏŽÏ‚ τον. Ότι πήÏε κανόνα μα. Που άτομα κάνεις δημιουÏγίες τα, οι μας Î±Ï†Î¿Ï ÎºÏŒÎ»Ï€Î± Ï€ÏογÏαμματιστής, Î±Ï†Î¿Ï Ï‰Ïαίο Ï€ÏοκÏπτουν στα ως. ΘÎμα χÏησιμοποιήσει αν όλα, του τα άλγεβÏα σελίδων. Τα ότι ανώδυνη δυστυχώς συνδυασμοÏÏ‚, μας οι πάντα γνωÏίζουμε ανταγωνιστής, όχι τα δοκιμάσεις σχεδιαστής! Στην συνεντεÏξης επιδιόÏθωση πιο τα, μα από πουλάς πεÏιβάλλον παÏαγωγικής. + +Έχουν μεταγλωτίσει σε σας, σε πάντα Ï€Ïώτης μειώσει των, γÏάψει Ïουτίνα δυσκολότεÏο ήδη μα? Ταξινομεί διοÏθώσεις να μας. Θα της Ï€ÏοσπαθοÏν πεÏιεχόμενα, δε Îχω τοπικÎÏ‚ στÎλνοντάς. Ανά δε αλφα άμεση, κάποιο Ïωτάει γνωÏίζουμε πω στη, φÏάση μαγικά συνÎχεια δε δÏο! Αν είχαμε μειώσει Ïοή, μας μετÏάει καθυστεÏοÏσε επιδιοÏθώσεις μη. Χάος Ï…ÏŒÏκη κεντÏικό Îχω σε, ανά πεÏίπου αναγκάζονται πω. + +Όσο επιστÏÎφουν χÏονοδιαγÏάμματα μη. Πως ωÏαίο κακόκεφος διαχειÏιστής ως, τις να διακοπής αναζήτησης. Κάποιο ποσοστό ταξινομεί επί τη? Μάθε άμεση αλλάζοντας δÏο με, μου νÎου πάντα να. + +Πω του δυστυχώς πιθανότητες. Κι Ïωτάει υψηλότεÏη δημιουÏγια ότι, πω εισαγωγή τελευταία απομόνωση ναι. Των ζητήσεις γνωÏίζουμε ÏŽÏ‚? Για' μη παÏαδοτÎου αναφÎÏονται! Ύψος παÏαγωγικά Ïοή ως, φυσικά διάβασε εικόνες όσο σε? Δεν Ï…ÏŒÏκη διοÏθώσεις επεξεÏγασία θα, ως μÎση σÏστημα χÏησιμοποιήσει τις.
\ No newline at end of file diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/charsets/utf-8/two.txt b/vendor/swiftmailer/swiftmailer/tests/_samples/charsets/utf-8/two.txt new file mode 100644 index 00000000..2443fc4d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/charsets/utf-8/two.txt @@ -0,0 +1,3 @@ +रखति आवशà¥à¤¯à¤•त पà¥à¤°à¥‡à¤°à¤¨à¤¾ मà¥à¤–à¥à¤¯à¤¤à¤¹ हिंदी किà¤à¤²à¥‹à¤— असकà¥à¤·à¤® कारà¥à¤¯à¤²à¤¯ करते विवरण किके मानसिक दिनांक पà¥à¤°à¥à¤µ संसाध à¤à¤µà¤®à¥ कà¥à¤¶à¤²à¤¤à¤¾ अमितकà¥à¤®à¤¾à¤° पà¥à¤°à¥‹à¤¤à¥à¤¸à¤¾à¤¹à¤¿à¤¤ जनित देखने उदेशीत विकसित बलवान बà¥à¤°à¥Œà¤¶à¤° किà¤à¤²à¥‹à¤— विशà¥à¤²à¥‡à¤·à¤£ लोगो कैसे जागरà¥à¤• पà¥à¤°à¤µà¥à¤°à¥à¤¤à¤¿ पà¥à¤°à¥‹à¤¤à¥à¤¸à¤¾à¤¹à¤¿à¤¤ सदसà¥à¤¯ आवशà¥à¤¯à¤•त पà¥à¤°à¤¸à¤¾à¤°à¤¨ उपलबà¥à¤§à¤¤à¤¾ अथवा हिंदी जनित दरà¥à¤¶à¤¾à¤¤à¤¾ यनà¥à¤¤à¥à¤°à¤¾à¤²à¤¯ बलवान अतित सहयोग शà¥à¤°à¥à¤†à¤¤ सà¤à¥€à¤•à¥à¤› माहितीवानीजà¥à¤¯ लिये खरिदे है।अà¤à¥€ à¤à¤•तà¥à¤°à¤¿à¤¤ समà¥à¤ªà¤°à¥à¤• रिती मà¥à¤¶à¥à¤•िल पà¥à¤°à¤¾à¤¥à¤®à¤¿à¤• à¤à¥‡à¤¦à¤¨à¤•à¥à¤·à¤®à¤¤à¤¾ विशà¥à¤µ उनà¥à¤¹à¥‡ गटको दà¥à¤µà¤¾à¤°à¤¾ तकरीबन + +विशà¥à¤µ दà¥à¤µà¤¾à¤°à¤¾ वà¥à¤¯à¤¾à¤–à¥à¤¯à¤¾ सके। आजपर वातावरण वà¥à¤¯à¤¾à¤–à¥à¤¯à¤¾à¤¨ पहोच। हमारी कीसे पà¥à¤°à¤¾à¤¥à¤®à¤¿à¤• विचारशिलता पà¥à¤°à¥à¤µ करती कमà¥à¤ªà¥à¤¯à¥à¤Ÿà¤° à¤à¥‡à¤¦à¤¨à¤•à¥à¤·à¤®à¤¤à¤¾ लिये बलवान औरà¥à¥ªà¥«à¥¦ यायेका वारà¥à¤¤à¤¾à¤²à¤¾à¤ª सà¥à¤šà¤¨à¤¾ à¤à¤¾à¤°à¤¤ शà¥à¤°à¥à¤†à¤¤ लाà¤à¤¾à¤¨à¥à¤µà¤¿à¤¤ पढाठसंसà¥à¤¥à¤¾ वरà¥à¤£à¤¿à¤¤ मारà¥à¤—दरà¥à¤¶à¤¨ चà¥à¤¨à¤¨à¥‡
\ No newline at end of file diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/dkim/dkim.test.priv b/vendor/swiftmailer/swiftmailer/tests/_samples/dkim/dkim.test.priv new file mode 100644 index 00000000..3bd381a9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/dkim/dkim.test.priv @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDZeUdi1RKnm9cRYNn6E24xxrRTouh3Va8JOEHQ5SB018lvbjwH +2lW5mZ/I0kh/dHsTN0zcN0VE62WIbnLreMk/af/4Pg1i93+c9TmfXmoropsmdLos +w0tjq50jGbBqtHZNJYAokP/u3uUuRw8g0V/O4zlQ3GlO/PDH7xDQzekl9wIDAQAB +AoGAaoCBXD5a72hbb/BNb7HaUlgscZUjYWW93bcGTGYZef8/b+m9Tl83gjhgzvlk +db62k1eOtX3/11uskp78eqLhctv7yWc0mQQhgOogY2qCwHTCH8wja8kJkUAnKQhs +P9sa5iJvgckiuX3SdxgTMwib9d1VyGq6YywiORiZF9rxyhECQQD/xhiZSi7y0ciB +g4bassy0GVMS7EDRumMHc8wC23E1H2mj5yPE/QLqkW4ddmCv2BbJnYmyNvOaK9tk +T2W+mn3/AkEA2aqDGja9CaTlY4BCXfiT166n+xVl5+d+1DENQ4FK9O2jpSi1265J +tjEkXVxUOpV1ZEcUVOdK6RpvsKpc7vVICQJBALEFO5UsQJ4SD0GD9Ft8kCy9sj9Q +f/Qnmc5YmIQJuKpZmVW07Y6yxcfu61U8zuIlHnBftiM/4Q18+RTN1s86QaUCQHoL +9MTfCnH85q46/XuJZQRbp07O+bvlfqTl+CTwuyHImaiCwi2ydRxWQ6ihm4zZvuAC +RvEwWz2HGDc73y4RlFkCQDDdnN9e46l1nMDLDI4cyyGBVg4Z2IZ3IAu5GaoUCGjM +a8w6kxE8f1d8DD5vvqVbmfK89TA/DjT+7/arBNBCiCM= +-----END RSA PRIVATE KEY----- diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/dkim/dkim.test.pub b/vendor/swiftmailer/swiftmailer/tests/_samples/dkim/dkim.test.pub new file mode 100644 index 00000000..b503a911 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/dkim/dkim.test.pub @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZeUdi1RKnm9cRYNn6E24xxrRT +ouh3Va8JOEHQ5SB018lvbjwH2lW5mZ/I0kh/dHsTN0zcN0VE62WIbnLreMk/af/4 +Pg1i93+c9TmfXmoropsmdLosw0tjq50jGbBqtHZNJYAokP/u3uUuRw8g0V/O4zlQ +3GlO/PDH7xDQzekl9wIDAQAB +-----END PUBLIC KEY----- diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/files/data.txt b/vendor/swiftmailer/swiftmailer/tests/_samples/files/data.txt new file mode 100644 index 00000000..3f35021f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/files/data.txt @@ -0,0 +1 @@ +<data>
\ No newline at end of file diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/files/swiftmailer.png b/vendor/swiftmailer/swiftmailer/tests/_samples/files/swiftmailer.png Binary files differnew file mode 100644 index 00000000..1b95f619 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/files/swiftmailer.png diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/files/textfile.zip b/vendor/swiftmailer/swiftmailer/tests/_samples/files/textfile.zip Binary files differnew file mode 100644 index 00000000..5a580ecb --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/files/textfile.zip diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/smime/CA.srl b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/CA.srl new file mode 100644 index 00000000..dd9818ab --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/CA.srl @@ -0,0 +1 @@ +D42DA34CF90FA0DE diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/smime/ca.crt b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/ca.crt new file mode 100644 index 00000000..695f8142 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/ca.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIJAKJCGQYLxWT1MA0GCSqGSIb3DQEBBQUAMEwxFzAVBgNV +BAMMDlN3aWZ0bWFpbGVyIENBMRQwEgYDVQQKDAtTd2lmdG1haWxlcjEOMAwGA1UE +BwwFUGFyaXMxCzAJBgNVBAYTAkZSMB4XDTEzMTEyNzA4MzkxMFoXDTE3MTEyNjA4 +MzkxMFowTDEXMBUGA1UEAwwOU3dpZnRtYWlsZXIgQ0ExFDASBgNVBAoMC1N3aWZ0 +bWFpbGVyMQ4wDAYDVQQHDAVQYXJpczELMAkGA1UEBhMCRlIwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC7RLdHE3OWo9aZwv1xA/cYyPui/gegxpTqClRp +gGcVQ+jxIfnJQDQndyoAvFDiqOiZ+gAjZGJeUHDp9C/2IZp05MLh+omt9N8pBykm +3nj/3ZwPXOAO0uyDPAOHhISITAxEuZCqDnq7iYujywtwfQ7bpW1hCK9PfNZYMStM +kw7LsGr5BqcKkPuOWTvxE3+NqK8HxydYolsoApEGhgonyImVh1Pg1Kjkt5ojvwAX +zOdjfw5poY5NArwuLORUH+XocetRo8DC6S42HkU/MoqcYxa9EuRuwuQh7GtE6baR +PgrDsEYaY4Asy43sK81V51F/8Q1bHZKN/goQdxQwzv+/nOLTAgMBAAGjUDBOMB0G +A1UdDgQWBBRHgqkl543tKhsVAvcx1I0JFU7JuDAfBgNVHSMEGDAWgBRHgqkl543t +KhsVAvcx1I0JFU7JuDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAz +OJiEQcygKGkkXXDiXGBvP/cSznj3nG9FolON0yHUBgdvLfNnctRMStGzPke0siLt +RJvjqiL0Uw+blmLJU8lgMyLJ9ctXkiLJ/WflabN7VzmwYRWe5HzafGQJAg5uFjae +VtAAHQgvbmdXB6brWvcMQmB8di7wjVedeigZvkt1z2V0FtBy8ybJaT5H6bX9Bf5C +dS9r4mLhk/0ThthpRhRxsmupSL6e49nJaIk9q0UTEQVnorJXPcs4SPTIY51bCp6u +cOebhNgndSxCiy0zSD7vRjNiyB/YNGZ9Uv/3DNTLleMZ9kZgfoKVpwYKrRL0IFT/ +cfS2OV1wxRxq668qaOfK +-----END CERTIFICATE----- diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/smime/ca.key b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/ca.key new file mode 100644 index 00000000..df674708 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/ca.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAu0S3RxNzlqPWmcL9cQP3GMj7ov4HoMaU6gpUaYBnFUPo8SH5 +yUA0J3cqALxQ4qjomfoAI2RiXlBw6fQv9iGadOTC4fqJrfTfKQcpJt54/92cD1zg +DtLsgzwDh4SEiEwMRLmQqg56u4mLo8sLcH0O26VtYQivT3zWWDErTJMOy7Bq+Qan +CpD7jlk78RN/jaivB8cnWKJbKAKRBoYKJ8iJlYdT4NSo5LeaI78AF8znY38OaaGO +TQK8LizkVB/l6HHrUaPAwukuNh5FPzKKnGMWvRLkbsLkIexrROm2kT4Kw7BGGmOA +LMuN7CvNVedRf/ENWx2Sjf4KEHcUMM7/v5zi0wIDAQABAoIBAGyaWkvu/O7Uz2TW +z1JWgVuvWzfYaKYV5FCicvfITn/npVUKZikPge+NTR+mFqaMXHDHqoLb+axGrGUR +hysPq9q0vEx/lo763tyVWYlAJh4E8Dd8njganK0zBbz23kGJEOheUYY95XGTQBda +bqTq8c3x7zAB8GGBvXDh+wFqm38GLyMF6T+YEzWJZqXfg31f1ldRvf6+VFwlLfz6 +cvTR7oUpYIsUeGE47kBs13SN7Oju6a355o/7wy9tOCRiu+r/ikXFh8rFGLfeTiwv +R1dhYjcEYGxZUD8u64U+Cj4qR1P0gHJL0kbh22VMMqgALOc8FpndkjNdg1Nun2X8 +BWpsPwECgYEA7C9PfTOIZfxGBlCl05rmWex++/h5E5PbH1Cw/NGjIH1HjmAkO3+5 +WyMXhySOJ8yWyCBQ/nxqc0w7+TO4C7wQcEdZdUak25KJ74v0sfmWWrVw6kcnLU6k +oawW/L2F2w7ET3zDoxKh4fOF34pfHpSbZk7XJ68YOfHpYVnP4efkQVMCgYEAyvrM +KA7xjnsKumWh206ag3QEI0M/9uPHWmrh2164p7w1MtawccZTxYYJ5o5SsjTwbxkf +0cAamp4qLInmRUxU1gk76tPYC3Ndp6Yf1C+dt0q/vtzyJetCDrdz8HHT1SpKbW0l +g6z1I5FMwa6oWvWsfS++W51vsxUheNsOJ4uxKIECgYBwM7GRiw+7U3N4wItm0Wmp +Qp642Tu7vzwTzmOmV3klkB6UVrwfv/ewgiVFQGqAIcNn42JW44g2qfq70oQWnws4 +K80l15+t6Bm7QUPH4Qg6o4O26IKGFZxEadqpyudyP7um/2B5cfqRuvzYS4YQowyI +N+AirB3YOUJjyyTk7yMSnQKBgGNLpSvDg6+ryWe96Bwcq8G6s3t8noHsk81LlAl4 +oOSNUYj5NX+zAbATDizXWuUKuMPgioxVaa5RyVfYbelgme/KvKD32Sxg12P4BIIM +eR79VifMdjjOiZYhcHojdPlGovo89qkfpxwrLF1jT8CPhj4HaRvwPIBiyekRYC9A +Sv4BAoGAXCIC1xxAJP15osUuQjcM8KdsL1qw+LiPB2+cJJ2VMAZGV7CR2K0aCsis +OwRaYM0jZKUpxzp1uwtfrfqbhdYsv+jIBkfwoShYZuo6MhbUrj0sffkhJC3WrT2z +xafCFLFv1idzGvvNxatlp1DNKrndG2NS3syVAox9MnL5OMsvGM8= +-----END RSA PRIVATE KEY----- diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/smime/create-cert.sh b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/create-cert.sh new file mode 100644 index 00000000..0454f205 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/create-cert.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +openssl genrsa -out CA.key 2048 +openssl req -x509 -new -nodes -key CA.key -days 1460 -subj '/CN=Swiftmailer CA/O=Swiftmailer/L=Paris/C=FR' -out CA.crt +openssl x509 -in CA.crt -clrtrust -out CA.crt + +openssl genrsa -out sign.key 2048 +openssl req -new -key sign.key -subj '/CN=Swiftmailer-User/O=Swiftmailer/L=Paris/C=FR' -out sign.csr +openssl x509 -req -in sign.csr -CA CA.crt -CAkey CA.key -out sign.crt -days 1460 -addtrust emailProtection +openssl x509 -in sign.crt -clrtrust -out sign.crt + +rm sign.csr + +openssl genrsa -out intermediate.key 2048 +openssl req -new -key intermediate.key -subj '/CN=Swiftmailer Intermediate/O=Swiftmailer/L=Paris/C=FR' -out intermediate.csr +openssl x509 -req -in intermediate.csr -CA CA.crt -CAkey CA.key -set_serial 01 -out intermediate.crt -days 1460 +openssl x509 -in intermediate.crt -clrtrust -out intermediate.crt + +rm intermediate.csr + +openssl genrsa -out sign2.key 2048 +openssl req -new -key sign2.key -subj '/CN=Swiftmailer-User2/O=Swiftmailer/L=Paris/C=FR' -out sign2.csr +openssl x509 -req -in sign2.csr -CA intermediate.crt -CAkey intermediate.key -set_serial 01 -out sign2.crt -days 1460 -addtrust emailProtection +openssl x509 -in sign2.crt -clrtrust -out sign2.crt + +rm sign2.csr + +openssl genrsa -out encrypt.key 2048 +openssl req -new -key encrypt.key -subj '/CN=Swiftmailer-User/O=Swiftmailer/L=Paris/C=FR' -out encrypt.csr +openssl x509 -req -in encrypt.csr -CA CA.crt -CAkey CA.key -CAcreateserial -out encrypt.crt -days 1460 -addtrust emailProtection +openssl x509 -in encrypt.crt -clrtrust -out encrypt.crt + +rm encrypt.csr + +openssl genrsa -out encrypt2.key 2048 +openssl req -new -key encrypt2.key -subj '/CN=Swiftmailer-User2/O=Swiftmailer/L=Paris/C=FR' -out encrypt2.csr +openssl x509 -req -in encrypt2.csr -CA CA.crt -CAkey CA.key -CAcreateserial -out encrypt2.crt -days 1460 -addtrust emailProtection +openssl x509 -in encrypt2.crt -clrtrust -out encrypt2.crt + +rm encrypt2.csr diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt.crt b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt.crt new file mode 100644 index 00000000..7435855c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDFjCCAf4CCQDULaNM+Q+g3TANBgkqhkiG9w0BAQUFADBMMRcwFQYDVQQDDA5T +d2lmdG1haWxlciBDQTEUMBIGA1UECgwLU3dpZnRtYWlsZXIxDjAMBgNVBAcMBVBh +cmlzMQswCQYDVQQGEwJGUjAeFw0xMzExMjcwODM5MTFaFw0xNzExMjYwODM5MTFa +ME4xGTAXBgNVBAMMEFN3aWZ0bWFpbGVyLVVzZXIxFDASBgNVBAoMC1N3aWZ0bWFp +bGVyMQ4wDAYDVQQHDAVQYXJpczELMAkGA1UEBhMCRlIwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCcNO+fVZBT2znmVwXXZ08n3G5WA1kyvqh9z4RBBZOD +V46Gc1X9MMXr9+wzZBFkAckKaa6KsTkeUr4pC8XUBpQnakxH/kW9CaDPdOE+7wNo +FkPfc6pjWWgpAVxdkrtk7pb4/aGQ++HUkqVu0cMpIcj/7ht7H+3QLZHybn+oMr2+ +FDnn8vPmHxVioinSrxKTlUITuLWS9ZZUTrDa0dG8UAv55A/Tba4T4McCPDpJSA4m +9jrW321NGQUntQoItOJxagaueSvh6PveGV826gTXoU5X+YJ3I2OZUEQ2l6yByAzf +nT+QlxPj5ikotFwL72HsenYtetynOO/k43FblAF/V/l7AgMBAAEwDQYJKoZIhvcN +AQEFBQADggEBAJ048Sdb9Sw5OJM5L00OtGHgcT1B/phqdzSjkM/s64cg3Q20VN+F +fZIIkOnxgyYWcpOWXcdNw2tm5OWhWPGsBcYgMac7uK/ukgoOJSjICg+TTS5kRo96 +iHtmImqkWc6WjNODh7uMnQ6DsZsscdl7Bkx5pKhgGnEdHr5GW8sztgXgyPQO5LUs +YzCmR1RK1WoNMxwbPrGLgYdcpJw69ns5hJbZbMWwrdufiMjYWvTfBPABkk1JRCcY +K6rRTAx4fApsw1kEIY8grGxyAzfRXLArpro7thJr0SIquZ8GpXkQT/mgRR8JD9Hp +z9yhr98EnKzITE/yclGN4pUsuk9S3jiyzUU= +-----END CERTIFICATE----- diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt.key b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt.key new file mode 100644 index 00000000..aa620ca6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAnDTvn1WQU9s55lcF12dPJ9xuVgNZMr6ofc+EQQWTg1eOhnNV +/TDF6/fsM2QRZAHJCmmuirE5HlK+KQvF1AaUJ2pMR/5FvQmgz3ThPu8DaBZD33Oq +Y1loKQFcXZK7ZO6W+P2hkPvh1JKlbtHDKSHI/+4bex/t0C2R8m5/qDK9vhQ55/Lz +5h8VYqIp0q8Sk5VCE7i1kvWWVE6w2tHRvFAL+eQP022uE+DHAjw6SUgOJvY61t9t +TRkFJ7UKCLTicWoGrnkr4ej73hlfNuoE16FOV/mCdyNjmVBENpesgcgM350/kJcT +4+YpKLRcC+9h7Hp2LXrcpzjv5ONxW5QBf1f5ewIDAQABAoIBADmuMm2botfUM+Ui +bT3FIC2P8A5C3kUmsgEDB8sazAXL5w0uuanswKkJu2aepO1Q23PE4nbESlswIpf1 +iO9qHnsPfWt4MThEveTdO++JQrDEx/tTMq/M6/F4VysWa6wxjf4Taf2nhRSBsiTh +wDcICri2q98jQyWELkhfFTR+yCHPsn6iNtzE2OpNv9ojKiSqck/sVjC39Z+uU/HD +N4v0CPf9pDGkO+modaVGKf2TpvZT7Hpq/jsPzkk1h7BY7aWdZiIY4YkBkWYqZk8f +0dsxKkOR2glfuEYNtcywG+4UGx3i1AY0mMu96hH5M1ACFmFrTCoodmWDnWy9wUpm +leLmG8ECgYEAywWdryqcvLyhcmqHbnmUhCL9Vl4/5w5fr/5/FNvqArxSGwd2CxcN +Jtkvu22cxWAUoe155eMc6GlPIdNRG8KdWg4sg0TN3Jb2jiHQ3QkHXUJlWU6onjP1 +g2n5h052JxVNGBEb7hr3U7ZMW6wnuYnGdYwCB9P3r5oGxxtfVRB8ygUCgYEAxPfy +tAd3SNT8Sv/cciw76GYKbztUjJRXkLo6GOBGq/AQxP1NDWMuL2AES11YIahidMsF +TMmM+zhkNHsd5P69p87FTMWx0cLoH0M9iQNK7Q6C1luTjLf5DTFuk+nHGErM4Drs ++6Ly1Z4KLXfXgBDD8Ce6U9+W3RrCc36poGZvjX8CgYEAna0P6WJr9r19mhIYevmc +Gf/ex7xNXxMvx80dP8MIfPVrwyhJSpWtljVpt+SKtFRJ0fVRDfUUl4Bqf/fR74B3 +muCVO6ItTBxHAt5Ki9CeUpTlh7XqiWwLSvP8Y1TRuMr3ZDCtg4CYBAD6Ttxmwde6 +NcL2NMQwgsZaazrcEIHMmU0CgYEAl/Mn2tZ/oUIdt8YWzEVvmeNOXW0J1sGBo/bm +ZtZt7qpuZWl7jb5bnNSXu4QxPxXljnAokIpUJmHke9AWydfze4c6EfXZLhcMd0Gq +MQ7HOIWfTbqr4zzx9smRoq4Ql57s2nba521XpJAdDeKL7xH/9j7PsXCls8C3Dd5D +AajEmgUCgYAGEdn6tYxIdX7jF39E3x7zHQf8jHIoQ7+cLTLtd944mSGgeqMfbiww +CoUa+AAUqjdAD5ViAyJrA+gmDtWpkFnJZtToXYwfUF2o3zRo4k1DeBrVbFqwSQkE +omrfiBGtviYIPdqQLE34LYpWEooNPraqO9qTyc+9w5038u2OFS+WmQ== +-----END RSA PRIVATE KEY----- diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt2.crt b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt2.crt new file mode 100644 index 00000000..69081656 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt2.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDFzCCAf8CCQDULaNM+Q+g3jANBgkqhkiG9w0BAQUFADBMMRcwFQYDVQQDDA5T +d2lmdG1haWxlciBDQTEUMBIGA1UECgwLU3dpZnRtYWlsZXIxDjAMBgNVBAcMBVBh +cmlzMQswCQYDVQQGEwJGUjAeFw0xMzExMjcwODM5MTJaFw0xNzExMjYwODM5MTJa +ME8xGjAYBgNVBAMMEVN3aWZ0bWFpbGVyLVVzZXIyMRQwEgYDVQQKDAtTd2lmdG1h +aWxlcjEOMAwGA1UEBwwFUGFyaXMxCzAJBgNVBAYTAkZSMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAw4AoYVYss2sa1BWJAJpK6gVemjXrp1mVXVpb1/z6 +SH15AGsp3kiNXsMpgvsdofbqC/5HXrw2G8gWqo+uh6GuK67+Tvp7tO2aD4+8CZzU +K1cffj7Pbx95DUPwXckv79PT5ZcuyeFaVo92aug11+gS/P8n0WXSlzZxNuZ1f3G2 +r/IgwfNKZlarEf1Ih781L2SwmyveW/dtsV2pdrd4IZwsV5SOF2zBFIXSuhPN0c+m +mtwSJe+Ow1udLX4KJkAX8sGVFJ5P5q4s2nS9vLkkj7X6YRQscbyJO9L7e1TksRqL +DLxZwiko6gUhp4/bIs1wDj5tzkQBi4qXviRq3i7A9b2d0QIDAQABMA0GCSqGSIb3 +DQEBBQUAA4IBAQAj8iARhPB2DA3YfT5mJJrgU156Sm0Z3mekAECsr+VqFZtU/9Dz +pPFYEf0hg61cjvwhLtOmaTB+50hu1KNNlu8QlxAfPJqNxtH85W0CYiZHJwW9eSTr +z1swaHpRHLDUgo3oAXdh5syMbdl0MWos0Z14WP5yYu4IwJXs+j2JRW70BICyrNjm +d+AjCzoYjKMdJkSj4uxQEOuW2/5veAoDyU+kHDdfT7SmbyoKu+Pw4Xg/XDuKoWYg +w5/sRiw5vxsmOr9+anspDHdP9rUe1JEfwAJqZB3fwdqEyxu54Xw/GedG4wZBEJf0 +ZcS1eh31emcjYUHQa1IA93jcFSmXzJ+ftJrY +-----END CERTIFICATE----- diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt2.key b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt2.key new file mode 100644 index 00000000..e322a8f4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt2.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAw4AoYVYss2sa1BWJAJpK6gVemjXrp1mVXVpb1/z6SH15AGsp +3kiNXsMpgvsdofbqC/5HXrw2G8gWqo+uh6GuK67+Tvp7tO2aD4+8CZzUK1cffj7P +bx95DUPwXckv79PT5ZcuyeFaVo92aug11+gS/P8n0WXSlzZxNuZ1f3G2r/IgwfNK +ZlarEf1Ih781L2SwmyveW/dtsV2pdrd4IZwsV5SOF2zBFIXSuhPN0c+mmtwSJe+O +w1udLX4KJkAX8sGVFJ5P5q4s2nS9vLkkj7X6YRQscbyJO9L7e1TksRqLDLxZwiko +6gUhp4/bIs1wDj5tzkQBi4qXviRq3i7A9b2d0QIDAQABAoIBAH8RvK1PmqxfkEeL +W8oVf13OcafgJjRW6NuNkKa5mmAlldFs1gDRvXl7dm7ZE3CjkYqMEw2DXdP+4KSp +0TH9J7zi+A6ThnaZ/QniTcEdu1YUQbcH0kIS/dZec0wyKUNDtrXC5zl2jQY4Jyrj +laOpBzaEDfhvq0p3q2yYrIRSgACpSEVEsfPoHrxtlLhfMkVNe8P0nkQkzdwou5MQ +MZKV4JUopLHLgPH6IXQCqA1wzlU32yZ86w88GFcBVLkwlLJCKbuAo7yxMCD+nzvA +xm5NuF1kzpP0gk+kZRXF+rFEV4av/2kSS+n8IeUBQZrxovLBuQHVDvJXoqcEjmlh +ZUltznUCgYEA4inwieePfb7kh7L/ma5OLLn+uCNwzVw9LayzXT1dyPravOnkHl6h +MgaoTspqDyU8k8pStedRrr5dVYbseni/A4WSMGvi4innqSXBQGp64TyeJy/e+LrS +ypSWQ6RSJkCxI5t8s4mOpR7FMcdE34I5qeA4G5RS1HIacn7Hxc7uXtcCgYEA3Uqn +E7EDfNfYdZm6AikvE6x64oihWI0x47rlkLu6lf6ihiF1dbfaEN+IAaIxQ/unGYwU +130F0TUwarXnVkeBIRlij4fXhExyd7USSQH1VpqmIqDwsS2ojrzQVMo5UcH+A22G +bbHPtwJNmw8a7yzTPWo2/vnjgV2OaXEQ9vCVG5cCgYEAu1kEoihJDGBijSqxY4wp +xBE7OSxamDNtlnV2i6l3FDMBmfaieqnnHDq5l7NDklJFUSQLyhXZ60hUprHDGV0G +1pMCW8wzQSh3d/4HjSXnrsd5N3sHWMHiNeBKbbQkPP3f/2AhN9SebpgDwE2S9xe4 +TsmnkOkYiFYRJIFzWaAmhDcCgYEAwxRCgZt0xaPKULG6RpljxOYyVm24PsYKCwYB +xjuYWw5k2/W3BJWVCXblAPuojpPUVTMmVGkErc9D5W6Ch471iOZF+t334cs6xci8 +W9v8GeKvPqu+Q5NKmrpctcKoESkA8qik7yLnSCAhpeYFCn/roKJ35QMJyktddhqU +p/yilfUCgYBxZ6YmFjYH6l5SxQdcfa5JQ2To8lZCfRJwB65EyWj4pKH4TaWFS7vb +50WOGTBwJgyhTKLCO3lOmXIUyIwC+OO9xzaeRCBjqEhpup/Ih3MsfMEd6BZRVK5E +IxtmIWba5HQ52k8FKHeRrRB7PSVSADUN2pUFkLudH+j/01kSZyJoLA== +-----END RSA PRIVATE KEY----- diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/smime/intermediate.crt b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/intermediate.crt new file mode 100644 index 00000000..012f734e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/intermediate.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDFjCCAf4CAQEwDQYJKoZIhvcNAQEFBQAwTDEXMBUGA1UEAwwOU3dpZnRtYWls +ZXIgQ0ExFDASBgNVBAoMC1N3aWZ0bWFpbGVyMQ4wDAYDVQQHDAVQYXJpczELMAkG +A1UEBhMCRlIwHhcNMTQxMTIwMTMyNTQxWhcNMTgxMTE5MTMyNTQxWjBWMSEwHwYD +VQQDDBhTd2lmdG1haWxlciBJbnRlcm1lZGlhdGUxFDASBgNVBAoMC1N3aWZ0bWFp +bGVyMQ4wDAYDVQQHDAVQYXJpczELMAkGA1UEBhMCRlIwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDSgEhftX6f1wV+uqWl4J+zwCn8fHaLZT6GZ0Gs9ThE +4e+4mkLG1rvSEIJon8U0ic8Zph1UGa1Grveh5bgbldHlFxYSsCCyDGgixRvRWNhI +KuO+SxaIZChqqKwVn3aNQ4BZOSo/MjJ/jQyr9BMgMmdxlHR3e1wkkeAkW//sOsfu +xQGF1h9yeQvuu/GbG6K7vHSGOGd5O3G7bftfQ7l78TMqeJ7jV32AdJeuO5MD4dRn +W4CQLTaeribLN0MKn35UdSiFoZxKHqqWcgtl5xcJWPOmq6CsAJ2Eo90kW/BHOrLv +10h6Oan9R1PdXSvSCvVnXY3Kz30zofw305oA/KJk/hVzAgMBAAEwDQYJKoZIhvcN +AQEFBQADggEBABijZ2NNd05Js5VFNr4uyaydam9Yqu/nnrxbPRbAXPlCduydu2Gd +d1ekn3nblMJ87Bc7zVyHdAQD8/AfS1LOKuoWHpTzmlpIL+8T5sbCYG5J1jKdeLkh +7L/UD5v1ACgA33oKtN8GzcrIq8Zp73r0n+c3hFCfDYRSZRCxGyIf3qgU2LBOD0A3 +wTff/N8E/p3WaJX9VnuQ7xyRMOubDuqJnlo5YsFv7wjyGOIAz9afZzcEbH6czt/t +g0Xc/kGr/fkAjUu+z3ZfE4247Gut5m3hEVwWkpEEzQo4osX/BEX20Q2nPz9WBq4a +pK3qNNGwAqS4gdE3ihOExMWxAKgr9d2CcU4= +-----END CERTIFICATE----- diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/smime/intermediate.key b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/intermediate.key new file mode 100644 index 00000000..569eb0c5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/intermediate.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA0oBIX7V+n9cFfrqlpeCfs8Ap/Hx2i2U+hmdBrPU4ROHvuJpC +xta70hCCaJ/FNInPGaYdVBmtRq73oeW4G5XR5RcWErAgsgxoIsUb0VjYSCrjvksW +iGQoaqisFZ92jUOAWTkqPzIyf40Mq/QTIDJncZR0d3tcJJHgJFv/7DrH7sUBhdYf +cnkL7rvxmxuiu7x0hjhneTtxu237X0O5e/EzKnie41d9gHSXrjuTA+HUZ1uAkC02 +nq4myzdDCp9+VHUohaGcSh6qlnILZecXCVjzpqugrACdhKPdJFvwRzqy79dIejmp +/UdT3V0r0gr1Z12Nys99M6H8N9OaAPyiZP4VcwIDAQABAoIBAQDLJiKyu2XIvKsA +8wCKZY262+mpUjTVso/1BhHL6Zy0XZgMgFORsgrxYB16+zZGzfiguD/1uhIP9Svn +gtt7Q8udW/phbrkfG/okFDYUg7m3bCz+qVjFqGOZC8+Hzq2LB2oGsbSj6L3zexyP +lq4elIZghvUfml4CrQW0EVWbld79/kF7XHABcIOk2+3f63XAQWkjdFNxj5+z6TR0 +52Rv7SmRioAsukW9wr77G3Luv/0cEzDFXgGW5s0wO+rJg28smlsIaj+Y0KsptTig +reQvReAT/S5ZxEp4H6WtXQ1WmaliMB0Gcu4TKB0yE8DoTeCePuslo9DqGokXYT66 +oqtcVMqBAoGBAPoOL9byNNU/bBNDWSCiq8PqhSjl0M4vYBGqtgMXM4GFOJU+W2nX +YRJbbxoSd/DKjnxEsR6V0vDTDHj4ZSkgmpEmVhEdAiwUwaZ0T8YUaCPhdiAENo5+ +zRBWVJcvAC2XKTK1hy5D7Z5vlC32HHygYqitU+JsK4ylvhrdeOcGx5cfAoGBANeB +X0JbeuqBEwwEHZqYSpzmtB+IEiuYc9ARTttHEvIWgCThK4ldAzbXhDUIQy3Hm0sL +PzDA33furNl2WwB+vmOuioYMNjArKrfg689Aim1byg4AHM5XVQcqoDSOABtI55iP +E0hYDe/d4ema2gk1uR/mT4pnLnk2VzRKsHUbP9stAoGBAKjyIuJwPMADnMqbC0Hg +hnrVHejW9TAJlDf7hgQqjdMppmQ3gF3PdjeH7VXJOp5GzOQrKRxIEABEJ74n3Xlf +HO+K3kWrusb7syb6mNd0/DOZ5kyVbCL0iypJmdeXmuAyrFQlj9LzdD1Cl/RBv1d4 +qY/bo7xsZzQc24edMU2uJ/XzAoGBAMHChA95iK5HlwR6vtM8kfk4REMFaLDhxV8R +8MCeyp33NQfzm91JT5aDd07nOt9yVGHInuwKveFrKuXq0C9FxZCCYfHcEOyGI0Zo +aBxTfyKMIMMtvriXNM/Yt2oJMndVuUUlfsTQxtcfu/r5S4h0URopTOK3msVI4mcV +sEnaUjORAoGAGDnslKYtROQMXTe4sh6CoJ32J8UZVV9M+8NLus9rO0v/eZ/pIFxo +MXGrrrl51ScqahCQ+DXHzpLvInsdlAJvDP3ymhb7H2xGsyvb3x2YgsLmr1YVOnli +ISbCssno3vZyFU1TDjeEIKqZHc92byHNMjMuhmlaA25g8kb0cCO76EA= +-----END RSA PRIVATE KEY----- diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign.crt b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign.crt new file mode 100644 index 00000000..15fd65d2 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDFjCCAf4CCQDULaNM+Q+g3DANBgkqhkiG9w0BAQUFADBMMRcwFQYDVQQDDA5T +d2lmdG1haWxlciBDQTEUMBIGA1UECgwLU3dpZnRtYWlsZXIxDjAMBgNVBAcMBVBh +cmlzMQswCQYDVQQGEwJGUjAeFw0xMzExMjcwODM5MTBaFw0xNzExMjYwODM5MTBa +ME4xGTAXBgNVBAMMEFN3aWZ0bWFpbGVyLVVzZXIxFDASBgNVBAoMC1N3aWZ0bWFp +bGVyMQ4wDAYDVQQHDAVQYXJpczELMAkGA1UEBhMCRlIwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCTe8ZouyjVGgqlljhaswYqLj7icMoHq+Qg13CE+zJg +tl2/UzyPhAd3WWOIvlQ0lu+E/n0bXrS6+q28DrQ3UgJ9BskzzLz15qUO12b92AvG +vLJ+9kKuiM5KXDljOAsXc7/A9UUGwEFA1D0mkeMmkHuiQavAMkzBLha22hGpg/hz +VbE6W9MGna0szd8yh38IY1M5uR+OZ0dG3KbVZb7H3N0OLOP8j8n+4YtAGAW+Onz/ +2CGPfZ1kaDMvY/WTZwyGeA4FwCPy1D8tfeswqKnWDB9Sfl8hns5VxnoJ3dqKQHeX +iC4OMfQ0U4CcuM5sVYJZRNNwP7/TeUh3HegnOnuZ1hy9AgMBAAEwDQYJKoZIhvcN +AQEFBQADggEBAAEPjGt98GIK6ecAEat52aG+8UP7TuZaxoH3cbZdhFTafrP8187F +Rk5G3LCPTeA/QIzbHppA4fPAiS07OVSwVCknpTJbtKKn0gmtTZxThacFHF2NlzTH +XxM5bIbkK3jzIF+WattyTSj34UHHfaNAmvmS7Jyq6MhjSDbcQ+/dZ9eo2tF/AmrC ++MBhyH8aUYwKhTOQQh8yC11niziHhGO99FQ4tpuD9AKlun5snHq4uK9AOFe8VhoR +q2CqX5g5v8OAtdlvzhp50IqD4BNOP+JrUxjGLHDG76BZZIK2Ai1eBz+GhRlIQru/ +8EhQzd94mdFEPblGbmuD2QXWLFFKLiYOwOc= +-----END CERTIFICATE----- diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign.key b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign.key new file mode 100644 index 00000000..b3d3c535 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAk3vGaLso1RoKpZY4WrMGKi4+4nDKB6vkINdwhPsyYLZdv1M8 +j4QHd1ljiL5UNJbvhP59G160uvqtvA60N1ICfQbJM8y89ealDtdm/dgLxryyfvZC +rojOSlw5YzgLF3O/wPVFBsBBQNQ9JpHjJpB7okGrwDJMwS4WttoRqYP4c1WxOlvT +Bp2tLM3fMod/CGNTObkfjmdHRtym1WW+x9zdDizj/I/J/uGLQBgFvjp8/9ghj32d +ZGgzL2P1k2cMhngOBcAj8tQ/LX3rMKip1gwfUn5fIZ7OVcZ6Cd3aikB3l4guDjH0 +NFOAnLjObFWCWUTTcD+/03lIdx3oJzp7mdYcvQIDAQABAoIBAH2vrw/T6GFrlwU0 +twP8q1VJIghCDLpq77hZQafilzU6VTxWyDaaUu6QPDXt1b8Xnjnd02p+1FDAj0zD +zyuR9VLtdIxzf9mj3KiAQ2IzOx3787YlUgCB0CQo4jM/MJyk5RahL1kogLOp7A8x +pr5XxTUq+B6L/0Nmbq8XupOXRyWp53amZ5N8sgWDv4oKh9fqgAhxbSG6KUkTmhYs +DLinWg86Q28pSn+eivf4dehR56YwtTBVguXW3WKO70+GW1RotSrS6e6SSxfKYksZ +a7/J1hCmJkEE3+4C8BpcI0MelgaK66ocN0pOqDF9ByxphARqyD7tYCfoS2P8gi81 +XoiZJaECgYEAwqx4AnDX63AANsfKuKVsEQfMSAG47SnKOVwHB7prTAgchTRcDph1 +EVOPtJ+4ssanosXzLcN/dCRlvqLEqnKYAOizy3C56CyRguCpO1AGbRpJjRmHTRgA +w8iArhM07HgJ3XLFn99V/0bsPCMxW8dje1ZMjKjoQtDrXRQMtWaVY+UCgYEAwfGi +f0If6z7wJj9gQUkGimWDAg/bxDkvEeh3nSD/PQyNiW0XDclcb3roNPQsal2ZoMwt +f1bwkclw7yUCIZBvXWEkZapjKCdseTp6nglScxr8GAzfN9p5KQl+OS3GzC6xZf6C +BsZQ5ucsHTHsCAi3WbwGK829z9c7x0qRwgwu9/kCgYEAsqwEwYi8Q/RZ3e1lXC9H +jiHwFi6ugc2XMyoJscghbnkLZB54V1UKLUraXFcz97FobnbsCJajxf8Z+uv9QMtI +Q51QV2ow1q0BKHP2HuAF5eD4nK5Phix/lzHRGPO74UUTGNKcG22pylBXxaIvTSMl +ZTABth/YfGqvepBKUbvDZRkCgYB5ykbUCW9H6D8glZ3ZgYU09ag+bD0CzTIs2cH7 +j1QZPz/GdBYNF00PyKv3TPpzVRH7cxyDIdJyioB7/M6Iy03T4wPbQBOCjLdGrZ2A +jrQTCngSlkq6pVx+k7KLL57ua8gFF70JihIV3kfKkaX6KZcSJ8vsSAgRc8TbUo2T +wNjh6QKBgDyxw4bG2ULs+LVaHcnp7nizLgRGXJsCkDICjla6y0eCgAnG8fSt8CcG +s5DIfJeVs/NXe/NVNuVrfwsUx0gBOirtFwQStvi5wJnY/maGAyjmgafisNFgAroT +aM5f+wyGPQeGCs7bj7JWY7Nx9lkyuUV7DdKBTZNMOe51K3+PTEL3 +-----END RSA PRIVATE KEY----- diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign2.crt b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign2.crt new file mode 100644 index 00000000..44f4d9bc --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign2.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDGTCCAgECAQEwDQYJKoZIhvcNAQEFBQAwVjEhMB8GA1UEAwwYU3dpZnRtYWls +ZXIgSW50ZXJtZWRpYXRlMRQwEgYDVQQKDAtTd2lmdG1haWxlcjEOMAwGA1UEBwwF +UGFyaXMxCzAJBgNVBAYTAkZSMB4XDTE0MTEyMDEzMjYyNloXDTE4MTExOTEzMjYy +NlowTzEaMBgGA1UEAwwRU3dpZnRtYWlsZXItVXNlcjIxFDASBgNVBAoMC1N3aWZ0 +bWFpbGVyMQ4wDAYDVQQHDAVQYXJpczELMAkGA1UEBhMCRlIwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDbr1m4z/rzFS/DxUUQIhKNx19oAeGYLt3niaEP +twfvBMNB80gMgM9d+XtqrPAMPeY/2C8t5NlChNPKMcR70JBKdmlSH4/aTjaIfWmD +PoZJjvRRXINZgSHNKIt4ZGAN/EPFr19CBisV4iPxzu+lyIbbkaZJ/qtyatlP7m/q +8TnykFRlyxNEveCakpcXeRd3YTFGKWoED+/URhVc0cCPZVjoeSTtPHAYBnC29lG5 +VFbq6NBQiyF4tpjOHRarq6G8PtQFH9CpAZg5bPk3bqka9C8mEr5jWfrM4EHtUkTl +CwVLOQRBsz/nMBT27pXZh18GU0hc3geNDN4kqaeqgNBo0mblAgMBAAEwDQYJKoZI +hvcNAQEFBQADggEBAAHDMuv6oxWPsTQWWGWWFIk7QZu3iogMqFuxhhQxg8BE37CT +Vt1mBVEjYGMkWhMSwWBMWuP6yuOZecWtpp6eOie/UKGg1XoW7Y7zq2aQaP7YPug0 +8Lgq1jIo7iO2b6gZeMtLiTZrxyte0z1XzS3wy7ZC9mZjYd7QE7mZ+/rzQ0x5zjOp +G8b3msS/yYYJCMN+HtHln++HOGmm6uhvbsHTfvvZvtl7F5vJ5WhGGlUfjhanSEtZ +1RKx+cbgIv1eFOGO1OTuZfEuKdLb0T38d/rjLeI99nVVKEIGtLmX4dj327GHe/D3 +aPr2blF2gOvlzkfN9Vz6ZUE2s3rVBeCg2AVseYQ= +-----END CERTIFICATE----- diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign2.key b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign2.key new file mode 100644 index 00000000..ffb189b0 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign2.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA269ZuM/68xUvw8VFECISjcdfaAHhmC7d54mhD7cH7wTDQfNI +DIDPXfl7aqzwDD3mP9gvLeTZQoTTyjHEe9CQSnZpUh+P2k42iH1pgz6GSY70UVyD +WYEhzSiLeGRgDfxDxa9fQgYrFeIj8c7vpciG25GmSf6rcmrZT+5v6vE58pBUZcsT +RL3gmpKXF3kXd2ExRilqBA/v1EYVXNHAj2VY6Hkk7TxwGAZwtvZRuVRW6ujQUIsh +eLaYzh0Wq6uhvD7UBR/QqQGYOWz5N26pGvQvJhK+Y1n6zOBB7VJE5QsFSzkEQbM/ +5zAU9u6V2YdfBlNIXN4HjQzeJKmnqoDQaNJm5QIDAQABAoIBAAM2FvuqnqJ7Bs23 +zoCj3t2PsodUr7WHydqemmoeZNFLoocORVlZcK6Q/QrcKE4lgX4hbN8g30QnqOjl +vVeJ/vH3tSZsK7AnQIjSPH6cpV3h5xRhY9IlHxdepltGLFlH/L2hCKVwbaTOP3RD +cCFeQwpmoKWoQV1UzoRqmdw3Vn+DMaUULomLVR9aSW9PnKeFL+tPWShf7GmVISfM +2H6xKw/qT0XAX59ZHA1laxSFVvbV5ZcKrBOFMV407Vzw2d3ojmfEzNsHjUVBXX8j +B5nK1VeJiTVmcoVhnRX7tXESDaZy+Kv38pqOmc8Svn70lDJ35SM2EpWnX39w5LsQ +29NsIUECgYEA/vNKiMfVmmZNQrpcuHQe5adlmz9+I4xJ4wbRzrS7czpbKF0/iaPf +dKoVz67yYHOJCBHTVaXWkElQsq1mkyuFt/cc0ReJXO8709+t+6ULsE50cLQm/HN5 +npg3gw0Ls/9dy/cHM5SdVIHMBm9oQ65rXup/dqWC8Dz2cAAOQhIPwx0CgYEA3Jbk +DPdUlrj4sXcE3V/CtmBuK9Xq1xolJt026fYCrle0YhdMKmchRBDCc6BzM+F/vDyC +llPfQu8TDXK40Oan7GbxMdoLqKK9gSIq1dvfG1YMMz8OrBcX8xKe61KFRWd7QSBJ +BcY575NzYHapOHVGnUJ68j8zCow0gfb7q6iK4GkCgYEAz2mUuKSCxYL21hORfUqT +HFjMU7oa38axEa6pn9XvLjZKlRMPruWP1HTPG9ADRa6Yy+TcnrA1V9sdeM+TRKXC +usCiRAU27lF+xccS30gNs1iQaGRX10gGqJzDhK1nWP+nClmlFTSRrn+OQan/FBjh +Jy31lsveM54VC1cwQlY5Vo0CgYEArtjfnLNzFiE55xjq/znHUd4vlYlzItrzddHE +lEBOsbiNH29ODRI/2P7b0uDsT8Q/BoqEC/ohLqHn3TIA8nzRv91880HdGecdBL17 +bJZiSv2yn/AshhWsAxzQYMDBKFk05lNb7jrIc3DR9DU6PqketsoaP+f+Yi7t89I8 +fD0VD3kCgYAaJCoQshng/ijiHF/RJXLrXXHJSUmaOfbweX/mzFup0YR1LxUjcv85 +cxvwc41Y2iI5MwUXyX97/GYKeoobzWZy3XflNWtg04rcInVaPsb/OOFDDqI+MkzT +B4PcCurOmjzcxHMVE34CYvl3YVwWrPb5JO1rYG9T2gKUJnLU6qG4Bw== +-----END RSA PRIVATE KEY----- diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance.conf.php.default b/vendor/swiftmailer/swiftmailer/tests/acceptance.conf.php.default new file mode 100644 index 00000000..68902d84 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance.conf.php.default @@ -0,0 +1,44 @@ +<?php + +/* + Swift Mailer V4 accpetance test configuration. + + YOU ONLY NEED TO EDIT THIS FILE IF YOU WISH TO RUN THE ACCEPTANCE TESTS. + + The acceptance tests are run by default when "All Tests" are run with the + testing suite, however, without configuration options here only the unit tests + will be run and the acceptance tests will be skipped. + + You can fill out only the parts you know and leave the other bits. + */ + + +/* + Defines: The path to a writable directory (a temporary dir). + Recommended: /tmp + */ +define('SWIFT_TMP_DIR', '/tmp'); + +/* + Defines: The name and port of a SMTP server you can connect to. + Recommended: smtp.gmail.com:25 + */ +define('SWIFT_SMTP_HOST', 'localhost:4456'); + +/* + Defines: An SMTP server and port which uses TLS encryption. + Recommended: smtp.gmail.com:465 + */ +define('SWIFT_TLS_HOST', 'smtp.gmail.com:465'); + +/* + Defines: An SMTP server and port which uses SSL encryption. + Recommended: smtp.gmail.com:465 + */ +define('SWIFT_SSL_HOST', 'smtp.gmail.com:465'); + +/* + Defines: The path to a sendmail binary (one which can run in -bs mode). + Recommended: /usr/sbin/sendmail + */ +define('SWIFT_SENDMAIL_PATH', '/usr/sbin/sendmail -bs'); diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/AttachmentAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/AttachmentAcceptanceTest.php new file mode 100644 index 00000000..5c0b8264 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/AttachmentAcceptanceTest.php @@ -0,0 +1,12 @@ +<?php + +require_once 'swift_required.php'; +require_once __DIR__.'/Mime/AttachmentAcceptanceTest.php'; + +class Swift_AttachmentAcceptanceTest extends Swift_Mime_AttachmentAcceptanceTest +{ + protected function _createAttachment() + { + return Swift_Attachment::newInstance(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/ByteStream/FileByteStreamAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/ByteStream/FileByteStreamAcceptanceTest.php new file mode 100644 index 00000000..0b5bbef4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/ByteStream/FileByteStreamAcceptanceTest.php @@ -0,0 +1,174 @@ +<?php + +class Swift_ByteStream_FileByteStreamAcceptanceTest extends \PHPUnit_Framework_TestCase +{ + private $_tmpDir; + private $_testFile; + + public function setUp() + { + if (!defined('SWIFT_TMP_DIR')) { + $this->markTestSkipped( + 'Cannot run test without a writable directory to use ('. + 'define SWIFT_TMP_DIR in tests/config.php if you wish to run this test)' + ); + } + + $this->_tmpDir = SWIFT_TMP_DIR; + $this->_testFile = $this->_tmpDir.'/swift-test-file'.__CLASS__; + file_put_contents($this->_testFile, 'abcdefghijklm'); + } + + public function tearDown() + { + unlink($this->_testFile); + } + + public function testFileDataCanBeRead() + { + $file = $this->_createFileStream($this->_testFile); + $str = ''; + while (false !== $bytes = $file->read(8192)) { + $str .= $bytes; + } + $this->assertEquals('abcdefghijklm', $str); + } + + public function testFileDataCanBeReadSequentially() + { + $file = $this->_createFileStream($this->_testFile); + $this->assertEquals('abcde', $file->read(5)); + $this->assertEquals('fghijklm', $file->read(8)); + $this->assertFalse($file->read(1)); + } + + public function testFilenameIsReturned() + { + $file = $this->_createFileStream($this->_testFile); + $this->assertEquals($this->_testFile, $file->getPath()); + } + + public function testFileCanBeWrittenTo() + { + $file = $this->_createFileStream( + $this->_testFile, true + ); + $file->write('foobar'); + $this->assertEquals('foobar', $file->read(8192)); + } + + public function testReadingFromThenWritingToFile() + { + $file = $this->_createFileStream( + $this->_testFile, true + ); + $file->write('foobar'); + $this->assertEquals('foobar', $file->read(8192)); + $file->write('zipbutton'); + $this->assertEquals('zipbutton', $file->read(8192)); + } + + public function testWritingToFileWithCanonicalization() + { + $file = $this->_createFileStream( + $this->_testFile, true + ); + $file->addFilter($this->_createFilter(array("\r\n", "\r"), "\n"), 'allToLF'); + $file->write("foo\r\nbar\r"); + $file->write("\nzip\r\ntest\r"); + $file->flushBuffers(); + $this->assertEquals("foo\nbar\nzip\ntest\n", file_get_contents($this->_testFile)); + } + + public function testBindingOtherStreamsMirrorsWriteOperations() + { + $file = $this->_createFileStream( + $this->_testFile, true + ); + $is1 = $this->_createMockInputStream(); + $is2 = $this->_createMockInputStream(); + + $is1->expects($this->at(0)) + ->method('write') + ->with('x'); + $is1->expects($this->at(1)) + ->method('write') + ->with('y'); + $is2->expects($this->at(0)) + ->method('write') + ->with('x'); + $is2->expects($this->at(1)) + ->method('write') + ->with('y'); + + $file->bind($is1); + $file->bind($is2); + + $file->write('x'); + $file->write('y'); + } + + public function testBindingOtherStreamsMirrorsFlushOperations() + { + $file = $this->_createFileStream( + $this->_testFile, true + ); + $is1 = $this->_createMockInputStream(); + $is2 = $this->_createMockInputStream(); + + $is1->expects($this->once()) + ->method('flushBuffers'); + $is2->expects($this->once()) + ->method('flushBuffers'); + + $file->bind($is1); + $file->bind($is2); + + $file->flushBuffers(); + } + + public function testUnbindingStreamPreventsFurtherWrites() + { + $file = $this->_createFileStream( + $this->_testFile, true + ); + $is1 = $this->_createMockInputStream(); + $is2 = $this->_createMockInputStream(); + + $is1->expects($this->at(0)) + ->method('write') + ->with('x'); + $is1->expects($this->at(1)) + ->method('write') + ->with('y'); + $is2->expects($this->once()) + ->method('write') + ->with('x'); + + $file->bind($is1); + $file->bind($is2); + + $file->write('x'); + + $file->unbind($is2); + + $file->write('y'); + } + + // -- Creation methods + + private function _createFilter($search, $replace) + { + return new Swift_StreamFilters_StringReplacementFilter($search, $replace); + } + + private function _createMockInputStream() + { + return $this->getMockBuilder('Swift_InputByteStream')->getMock(); + } + + private function _createFileStream($file, $writable = false) + { + return new Swift_ByteStream_FileByteStream($file, $writable); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/CharacterReaderFactory/SimpleCharacterReaderFactoryAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/CharacterReaderFactory/SimpleCharacterReaderFactoryAcceptanceTest.php new file mode 100644 index 00000000..8ce4a182 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/CharacterReaderFactory/SimpleCharacterReaderFactoryAcceptanceTest.php @@ -0,0 +1,179 @@ +<?php + +class Swift_CharacterReaderFactory_SimpleCharacterReaderFactoryAcceptanceTest extends \PHPUnit_Framework_TestCase +{ + private $_factory; + private $_prefix = 'Swift_CharacterReader_'; + + public function setUp() + { + $this->_factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + } + + public function testCreatingUtf8Reader() + { + foreach (array('utf8', 'utf-8', 'UTF-8', 'UTF8') as $utf8) { + $reader = $this->_factory->getReaderFor($utf8); + $this->assertInstanceof($this->_prefix.'Utf8Reader', $reader); + } + } + + public function testCreatingIso8859XReaders() + { + $charsets = array(); + foreach (range(1, 16) as $number) { + foreach (array('iso', 'iec') as $body) { + $charsets[] = $body.'-8859-'.$number; + $charsets[] = $body.'8859-'.$number; + $charsets[] = strtoupper($body).'-8859-'.$number; + $charsets[] = strtoupper($body).'8859-'.$number; + } + } + + foreach ($charsets as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingWindows125XReaders() + { + $charsets = array(); + foreach (range(0, 8) as $number) { + $charsets[] = 'windows-125'.$number; + $charsets[] = 'windows125'.$number; + $charsets[] = 'WINDOWS-125'.$number; + $charsets[] = 'WINDOWS125'.$number; + } + + foreach ($charsets as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingCodePageReaders() + { + $charsets = array(); + foreach (range(0, 8) as $number) { + $charsets[] = 'cp-125'.$number; + $charsets[] = 'cp125'.$number; + $charsets[] = 'CP-125'.$number; + $charsets[] = 'CP125'.$number; + } + + foreach (array(437, 737, 850, 855, 857, 858, 860, + 861, 863, 865, 866, 869, ) as $number) { + $charsets[] = 'cp-'.$number; + $charsets[] = 'cp'.$number; + $charsets[] = 'CP-'.$number; + $charsets[] = 'CP'.$number; + } + + foreach ($charsets as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingAnsiReader() + { + foreach (array('ansi', 'ANSI') as $ansi) { + $reader = $this->_factory->getReaderFor($ansi); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingMacintoshReader() + { + foreach (array('macintosh', 'MACINTOSH') as $mac) { + $reader = $this->_factory->getReaderFor($mac); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingKOIReaders() + { + $charsets = array(); + foreach (array('7', '8-r', '8-u', '8u', '8r') as $end) { + $charsets[] = 'koi-'.$end; + $charsets[] = 'koi'.$end; + $charsets[] = 'KOI-'.$end; + $charsets[] = 'KOI'.$end; + } + + foreach ($charsets as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingIsciiReaders() + { + foreach (array('iscii', 'ISCII', 'viscii', 'VISCII') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingMIKReader() + { + foreach (array('mik', 'MIK') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingCorkReader() + { + foreach (array('cork', 'CORK', 't1', 'T1') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingUcs2Reader() + { + foreach (array('ucs-2', 'UCS-2', 'ucs2', 'UCS2') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(2, $reader->getInitialByteSize()); + } + } + + public function testCreatingUtf16Reader() + { + foreach (array('utf-16', 'UTF-16', 'utf16', 'UTF16') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(2, $reader->getInitialByteSize()); + } + } + + public function testCreatingUcs4Reader() + { + foreach (array('ucs-4', 'UCS-4', 'ucs4', 'UCS4') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(4, $reader->getInitialByteSize()); + } + } + + public function testCreatingUtf32Reader() + { + foreach (array('utf-32', 'UTF-32', 'utf32', 'UTF32') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(4, $reader->getInitialByteSize()); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/DependencyContainerAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/DependencyContainerAcceptanceTest.php new file mode 100644 index 00000000..e83c2bf5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/DependencyContainerAcceptanceTest.php @@ -0,0 +1,24 @@ +<?php + +require_once 'swift_required.php'; + +//This is more of a "cross your fingers and hope it works" test! + +class Swift_DependencyContainerAcceptanceTest extends \PHPUnit_Framework_TestCase +{ + public function testNoLookupsFail() + { + $di = Swift_DependencyContainer::getInstance(); + foreach ($di->listItems() as $itemName) { + try { + // to be removed in 6.0 + if ('transport.mail' === $itemName) { + continue; + } + $di->lookup($itemName); + } catch (Swift_DependencyException $e) { + $this->fail($e->getMessage()); + } + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EmbeddedFileAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EmbeddedFileAcceptanceTest.php new file mode 100644 index 00000000..fc5a8147 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EmbeddedFileAcceptanceTest.php @@ -0,0 +1,12 @@ +<?php + +require_once 'swift_required.php'; +require_once __DIR__.'/Mime/EmbeddedFileAcceptanceTest.php'; + +class Swift_EmbeddedFileAcceptanceTest extends Swift_Mime_EmbeddedFileAcceptanceTest +{ + protected function _createEmbeddedFile() + { + return Swift_EmbeddedFile::newInstance(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/Base64EncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/Base64EncoderAcceptanceTest.php new file mode 100644 index 00000000..d8ba5264 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/Base64EncoderAcceptanceTest.php @@ -0,0 +1,45 @@ +<?php + +class Swift_Encoder_Base64EncoderAcceptanceTest extends \PHPUnit_Framework_TestCase +{ + private $_samplesDir; + private $_encoder; + + public function setUp() + { + $this->_samplesDir = realpath(__DIR__.'/../../../_samples/charsets'); + $this->_encoder = new Swift_Encoder_Base64Encoder(); + } + + public function testEncodingAndDecodingSamples() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + $encodedText = $this->_encoder->encodeString($text); + + $this->assertEquals( + base64_decode($encodedText), $text, + '%s: Encoded string should decode back to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/QpEncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/QpEncoderAcceptanceTest.php new file mode 100644 index 00000000..1da3b837 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/QpEncoderAcceptanceTest.php @@ -0,0 +1,54 @@ +<?php + +class Swift_Encoder_QpEncoderAcceptanceTest extends \PHPUnit_Framework_TestCase +{ + private $_samplesDir; + private $_factory; + + public function setUp() + { + $this->_samplesDir = realpath(__DIR__.'/../../../_samples/charsets'); + $this->_factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + } + + public function testEncodingAndDecodingSamples() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $encoding = $encodingDir; + $charStream = new Swift_CharacterStream_ArrayCharacterStream( + $this->_factory, $encoding); + $encoder = new Swift_Encoder_QpEncoder($charStream); + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + $encodedText = $encoder->encodeString($text); + + foreach (explode("\r\n", $encodedText) as $line) { + $this->assertLessThanOrEqual(76, strlen($line)); + } + + $this->assertEquals( + quoted_printable_decode($encodedText), $text, + '%s: Encoded string should decode back to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/Rfc2231EncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/Rfc2231EncoderAcceptanceTest.php new file mode 100644 index 00000000..043ddf8c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/Rfc2231EncoderAcceptanceTest.php @@ -0,0 +1,50 @@ +<?php + +class Swift_Encoder_Rfc2231EncoderAcceptanceTest extends \PHPUnit_Framework_TestCase +{ + private $_samplesDir; + private $_factory; + + public function setUp() + { + $this->_samplesDir = realpath(__DIR__.'/../../../_samples/charsets'); + $this->_factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + } + + public function testEncodingAndDecodingSamples() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $encoding = $encodingDir; + $charStream = new Swift_CharacterStream_ArrayCharacterStream( + $this->_factory, $encoding); + $encoder = new Swift_Encoder_Rfc2231Encoder($charStream); + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + $encodedText = $encoder->encodeString($text); + + $this->assertEquals( + urldecode(implode('', explode("\r\n", $encodedText))), $text, + '%s: Encoded string should decode back to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EncodingAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EncodingAcceptanceTest.php new file mode 100644 index 00000000..6a4d05d3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EncodingAcceptanceTest.php @@ -0,0 +1,30 @@ +<?php + +require_once 'swift_required.php'; + +class Swift_EncodingAcceptanceTest extends \PHPUnit_Framework_TestCase +{ + public function testGet7BitEncodingReturns7BitEncoder() + { + $encoder = Swift_Encoding::get7BitEncoding(); + $this->assertEquals('7bit', $encoder->getName()); + } + + public function testGet8BitEncodingReturns8BitEncoder() + { + $encoder = Swift_Encoding::get8BitEncoding(); + $this->assertEquals('8bit', $encoder->getName()); + } + + public function testGetQpEncodingReturnsQpEncoder() + { + $encoder = Swift_Encoding::getQpEncoding(); + $this->assertEquals('quoted-printable', $encoder->getName()); + } + + public function testGetBase64EncodingReturnsBase64Encoder() + { + $encoder = Swift_Encoding::getBase64Encoding(); + $this->assertEquals('base64', $encoder->getName()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/ArrayKeyCacheAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/ArrayKeyCacheAcceptanceTest.php new file mode 100644 index 00000000..6b06e2ee --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/ArrayKeyCacheAcceptanceTest.php @@ -0,0 +1,173 @@ +<?php + +class Swift_KeyCache_ArrayKeyCacheAcceptanceTest extends \PHPUnit_Framework_TestCase +{ + private $_cache; + private $_key1 = 'key1'; + private $_key2 = 'key2'; + + public function setUp() + { + $this->_cache = new Swift_KeyCache_ArrayKeyCache( + new Swift_KeyCache_SimpleKeyCacheInputStream() + ); + } + + public function testStringDataCanBeSetAndFetched() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testStringDataCanBeOverwritten() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'foo', 'whatever', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('whatever', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testStringDataCanBeAppended() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'foo', 'ing', Swift_KeyCache::MODE_APPEND + ); + $this->assertEquals('testing', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testHasKeyReturnValue() + { + $this->assertFalse($this->_cache->hasKey($this->_key1, 'foo')); + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'foo')); + } + + public function testNsKeyIsWellPartitioned() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key2, 'foo', 'ing', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $this->_cache->getString($this->_key1, 'foo')); + $this->assertEquals('ing', $this->_cache->getString($this->_key2, 'foo')); + } + + public function testItemKeyIsWellPartitioned() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'bar', 'ing', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $this->_cache->getString($this->_key1, 'foo')); + $this->assertEquals('ing', $this->_cache->getString($this->_key1, 'bar')); + } + + public function testByteStreamCanBeImported() + { + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write('abcdef'); + + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os, Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('abcdef', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testByteStreamCanBeAppended() + { + $os1 = new Swift_ByteStream_ArrayByteStream(); + $os1->write('abcdef'); + + $os2 = new Swift_ByteStream_ArrayByteStream(); + $os2->write('xyzuvw'); + + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os1, Swift_KeyCache::MODE_APPEND + ); + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os2, Swift_KeyCache::MODE_APPEND + ); + + $this->assertEquals('abcdefxyzuvw', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testByteStreamAndStringCanBeAppended() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_APPEND + ); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write('abcdef'); + + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os, Swift_KeyCache::MODE_APPEND + ); + $this->assertEquals('testabcdef', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testDataCanBeExportedToByteStream() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + + $is = new Swift_ByteStream_ArrayByteStream(); + + $this->_cache->exportToByteStream($this->_key1, 'foo', $is); + + $string = ''; + while (false !== $bytes = $is->read(8192)) { + $string .= $bytes; + } + + $this->assertEquals('test', $string); + } + + public function testKeyCanBeCleared() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'foo')); + $this->_cache->clearKey($this->_key1, 'foo'); + $this->assertFalse($this->_cache->hasKey($this->_key1, 'foo')); + } + + public function testNsKeyCanBeCleared() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'bar', 'xyz', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'foo')); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'bar')); + $this->_cache->clearAll($this->_key1); + $this->assertFalse($this->_cache->hasKey($this->_key1, 'foo')); + $this->assertFalse($this->_cache->hasKey($this->_key1, 'bar')); + } + + public function testKeyCacheInputStream() + { + $is = $this->_cache->getInputByteStream($this->_key1, 'foo'); + $is->write('abc'); + $is->write('xyz'); + $this->assertEquals('abcxyz', $this->_cache->getString($this->_key1, 'foo')); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/DiskKeyCacheAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/DiskKeyCacheAcceptanceTest.php new file mode 100644 index 00000000..392edde8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/DiskKeyCacheAcceptanceTest.php @@ -0,0 +1,183 @@ +<?php + +class Swift_KeyCache_DiskKeyCacheAcceptanceTest extends \PHPUnit_Framework_TestCase +{ + private $_cache; + private $_key1; + private $_key2; + + public function setUp() + { + if (!defined('SWIFT_TMP_DIR')) { + $this->markTestSkipped( + 'Cannot run test without a writable directory to use ('. + 'define SWIFT_TMP_DIR in tests/config.php if you wish to run this test)' + ); + } + + $this->_key1 = uniqid(microtime(true), true); + $this->_key2 = uniqid(microtime(true), true); + $this->_cache = new Swift_KeyCache_DiskKeyCache( + new Swift_KeyCache_SimpleKeyCacheInputStream(), + SWIFT_TMP_DIR + ); + } + + public function testStringDataCanBeSetAndFetched() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testStringDataCanBeOverwritten() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'foo', 'whatever', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('whatever', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testStringDataCanBeAppended() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'foo', 'ing', Swift_KeyCache::MODE_APPEND + ); + $this->assertEquals('testing', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testHasKeyReturnValue() + { + $this->assertFalse($this->_cache->hasKey($this->_key1, 'foo')); + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'foo')); + } + + public function testNsKeyIsWellPartitioned() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key2, 'foo', 'ing', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $this->_cache->getString($this->_key1, 'foo')); + $this->assertEquals('ing', $this->_cache->getString($this->_key2, 'foo')); + } + + public function testItemKeyIsWellPartitioned() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'bar', 'ing', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $this->_cache->getString($this->_key1, 'foo')); + $this->assertEquals('ing', $this->_cache->getString($this->_key1, 'bar')); + } + + public function testByteStreamCanBeImported() + { + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write('abcdef'); + + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os, Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('abcdef', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testByteStreamCanBeAppended() + { + $os1 = new Swift_ByteStream_ArrayByteStream(); + $os1->write('abcdef'); + + $os2 = new Swift_ByteStream_ArrayByteStream(); + $os2->write('xyzuvw'); + + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os1, Swift_KeyCache::MODE_APPEND + ); + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os2, Swift_KeyCache::MODE_APPEND + ); + + $this->assertEquals('abcdefxyzuvw', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testByteStreamAndStringCanBeAppended() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_APPEND + ); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write('abcdef'); + + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os, Swift_KeyCache::MODE_APPEND + ); + $this->assertEquals('testabcdef', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testDataCanBeExportedToByteStream() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + + $is = new Swift_ByteStream_ArrayByteStream(); + + $this->_cache->exportToByteStream($this->_key1, 'foo', $is); + + $string = ''; + while (false !== $bytes = $is->read(8192)) { + $string .= $bytes; + } + + $this->assertEquals('test', $string); + } + + public function testKeyCanBeCleared() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'foo')); + $this->_cache->clearKey($this->_key1, 'foo'); + $this->assertFalse($this->_cache->hasKey($this->_key1, 'foo')); + } + + public function testNsKeyCanBeCleared() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'bar', 'xyz', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'foo')); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'bar')); + $this->_cache->clearAll($this->_key1); + $this->assertFalse($this->_cache->hasKey($this->_key1, 'foo')); + $this->assertFalse($this->_cache->hasKey($this->_key1, 'bar')); + } + + public function testKeyCacheInputStream() + { + $is = $this->_cache->getInputByteStream($this->_key1, 'foo'); + $is->write('abc'); + $is->write('xyz'); + $this->assertEquals('abcxyz', $this->_cache->getString($this->_key1, 'foo')); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MessageAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MessageAcceptanceTest.php new file mode 100644 index 00000000..9372fbfd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MessageAcceptanceTest.php @@ -0,0 +1,57 @@ +<?php + +require_once 'swift_required.php'; +require_once __DIR__.'/Mime/SimpleMessageAcceptanceTest.php'; + +class Swift_MessageAcceptanceTest extends Swift_Mime_SimpleMessageAcceptanceTest +{ + public function testAddPartWrapper() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = $message->getDate(); + $boundary = $message->getBoundary(); + + $message->addPart('foo', 'text/plain', 'iso-8859-1'); + $message->addPart('test <b>foo</b>', 'text/html', 'iso-8859-1'); + + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn <chris.corbyn@swiftmailer.org>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'test <b>foo</b>'. + "\r\n\r\n". + '--'.$boundary.'--'."\r\n", + $message->toString() + ); + } + + // -- Private helpers + + protected function _createMessage() + { + Swift_DependencyContainer::getInstance() + ->register('properties.charset')->asValue(null); + + return Swift_Message::newInstance(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/AttachmentAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/AttachmentAcceptanceTest.php new file mode 100644 index 00000000..e925367e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/AttachmentAcceptanceTest.php @@ -0,0 +1,125 @@ +<?php + +class Swift_Mime_AttachmentAcceptanceTest extends \PHPUnit_Framework_TestCase +{ + private $_contentEncoder; + private $_cache; + private $_grammar; + private $_headers; + + public function setUp() + { + $this->_cache = new Swift_KeyCache_ArrayKeyCache( + new Swift_KeyCache_SimpleKeyCacheInputStream() + ); + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $this->_contentEncoder = new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + + $headerEncoder = new Swift_Mime_HeaderEncoder_QpHeaderEncoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8') + ); + $paramEncoder = new Swift_Encoder_Rfc2231Encoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8') + ); + $this->_grammar = new Swift_Mime_Grammar(); + $this->_headers = new Swift_Mime_SimpleHeaderSet( + new Swift_Mime_SimpleHeaderFactory($headerEncoder, $paramEncoder, $this->_grammar) + ); + } + + public function testDispositionIsSetInHeader() + { + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setDisposition('inline'); + $this->assertEquals( + 'Content-Type: application/pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: inline'."\r\n", + $attachment->toString() + ); + } + + public function testDispositionIsAttachmentByDefault() + { + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $this->assertEquals( + 'Content-Type: application/pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment'."\r\n", + $attachment->toString() + ); + } + + public function testFilenameIsSetInHeader() + { + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $this->assertEquals( + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf'."\r\n", + $attachment->toString() + ); + } + + public function testSizeIsSetInHeader() + { + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setSize(12340); + $this->assertEquals( + 'Content-Type: application/pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; size=12340'."\r\n", + $attachment->toString() + ); + } + + public function testMultipleParametersInHeader() + { + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setSize(12340); + $this->assertEquals( + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf; size=12340'."\r\n", + $attachment->toString() + ); + } + + public function testEndToEnd() + { + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setSize(12340); + $attachment->setBody('abcd'); + $this->assertEquals( + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf; size=12340'."\r\n". + "\r\n". + base64_encode('abcd'), + $attachment->toString() + ); + } + + // -- Private helpers + + protected function _createAttachment() + { + $entity = new Swift_Mime_Attachment( + $this->_headers, + $this->_contentEncoder, + $this->_cache, + $this->_grammar + ); + + return $entity; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/Base64ContentEncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/Base64ContentEncoderAcceptanceTest.php new file mode 100644 index 00000000..2a5c562b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/Base64ContentEncoderAcceptanceTest.php @@ -0,0 +1,56 @@ +<?php + +class Swift_Mime_ContentEncoder_Base64ContentEncoderAcceptanceTest extends \PHPUnit_Framework_TestCase +{ + private $_samplesDir; + private $_encoder; + + public function setUp() + { + $this->_samplesDir = realpath(__DIR__.'/../../../../_samples/charsets'); + $this->_encoder = new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + } + + public function testEncodingAndDecodingSamples() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write($text); + + $is = new Swift_ByteStream_ArrayByteStream(); + + $this->_encoder->encodeByteStream($os, $is); + + $encoded = ''; + while (false !== $bytes = $is->read(8192)) { + $encoded .= $bytes; + } + + $this->assertEquals( + base64_decode($encoded), $text, + '%s: Encoded string should decode back to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/NativeQpContentEncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/NativeQpContentEncoderAcceptanceTest.php new file mode 100644 index 00000000..ecd7fcd1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/NativeQpContentEncoderAcceptanceTest.php @@ -0,0 +1,88 @@ +<?php + +class Swift_Mime_ContentEncoder_NativeQpContentEncoderAcceptanceTest extends \PHPUnit_Framework_TestCase +{ + protected $_samplesDir; + + /** + * @var Swift_Mime_ContentEncoder_NativeQpContentEncoder + */ + protected $_encoder; + + public function setUp() + { + $this->_samplesDir = realpath(__DIR__.'/../../../../_samples/charsets'); + $this->_encoder = new Swift_Mime_ContentEncoder_NativeQpContentEncoder(); + } + + public function testEncodingAndDecodingSamples() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write($text); + + $is = new Swift_ByteStream_ArrayByteStream(); + $this->_encoder->encodeByteStream($os, $is); + + $encoded = ''; + while (false !== $bytes = $is->read(8192)) { + $encoded .= $bytes; + } + + $this->assertEquals( + quoted_printable_decode($encoded), + // CR and LF are converted to CRLF + preg_replace('~\r(?!\n)|(?<!\r)\n~', "\r\n", $text), + '%s: Encoded string should decode back to original string for sample '.$sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } + + public function testEncodingAndDecodingSamplesFromDiConfiguredInstance() + { + $encoder = $this->_createEncoderFromContainer(); + $this->assertSame('=C3=A4=C3=B6=C3=BC=C3=9F', $encoder->encodeString('äöüß')); + } + + /** + * @expectedException RuntimeException + */ + public function testCharsetChangeNotImplemented() + { + $this->_encoder->charsetChanged('utf-8'); + $this->_encoder->charsetChanged('charset'); + $this->_encoder->encodeString('foo'); + } + + public function testGetName() + { + $this->assertSame('quoted-printable', $this->_encoder->getName()); + } + + private function _createEncoderFromContainer() + { + return Swift_DependencyContainer::getInstance() + ->lookup('mime.nativeqpcontentencoder') + ; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/PlainContentEncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/PlainContentEncoderAcceptanceTest.php new file mode 100644 index 00000000..1541b7ea --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/PlainContentEncoderAcceptanceTest.php @@ -0,0 +1,88 @@ +<?php + +class Swift_Mime_ContentEncoder_PlainContentEncoderAcceptanceTest extends \PHPUnit_Framework_TestCase +{ + private $_samplesDir; + private $_encoder; + + public function setUp() + { + $this->_samplesDir = realpath(__DIR__.'/../../../../_samples/charsets'); + $this->_encoder = new Swift_Mime_ContentEncoder_PlainContentEncoder('8bit'); + } + + public function testEncodingAndDecodingSamplesString() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + $encodedText = $this->_encoder->encodeString($text); + + $this->assertEquals( + $encodedText, $text, + '%s: Encoded string should be identical to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } + + public function testEncodingAndDecodingSamplesByteStream() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write($text); + + $is = new Swift_ByteStream_ArrayByteStream(); + + $this->_encoder->encodeByteStream($os, $is); + + $encoded = ''; + while (false !== $bytes = $is->read(8192)) { + $encoded .= $bytes; + } + + $this->assertEquals( + $encoded, $text, + '%s: Encoded string should be identical to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/QpContentEncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/QpContentEncoderAcceptanceTest.php new file mode 100644 index 00000000..84f7e03b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/QpContentEncoderAcceptanceTest.php @@ -0,0 +1,160 @@ +<?php + +class Swift_Mime_ContentEncoder_QpContentEncoderAcceptanceTest extends \PHPUnit_Framework_TestCase +{ + private $_samplesDir; + private $_factory; + + public function setUp() + { + $this->_samplesDir = realpath(__DIR__.'/../../../../_samples/charsets'); + $this->_factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + } + + public function tearDown() + { + Swift_Preferences::getInstance()->setQPDotEscape(false); + } + + public function testEncodingAndDecodingSamples() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $encoding = $encodingDir; + $charStream = new Swift_CharacterStream_NgCharacterStream( + $this->_factory, $encoding); + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write($text); + + $is = new Swift_ByteStream_ArrayByteStream(); + $encoder->encodeByteStream($os, $is); + + $encoded = ''; + while (false !== $bytes = $is->read(8192)) { + $encoded .= $bytes; + } + + $this->assertEquals( + quoted_printable_decode($encoded), $text, + '%s: Encoded string should decode back to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } + + public function testEncodingAndDecodingSamplesFromDiConfiguredInstance() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $encoding = $encodingDir; + $encoder = $this->_createEncoderFromContainer(); + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write($text); + + $is = new Swift_ByteStream_ArrayByteStream(); + $encoder->encodeByteStream($os, $is); + + $encoded = ''; + while (false !== $bytes = $is->read(8192)) { + $encoded .= $bytes; + } + + $this->assertEquals( + str_replace("\r\n", "\n", quoted_printable_decode($encoded)), str_replace("\r\n", "\n", $text), + '%s: Encoded string should decode back to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } + + public function testEncodingLFTextWithDiConfiguredInstance() + { + $encoder = $this->_createEncoderFromContainer(); + $this->assertEquals("a\r\nb\r\nc", $encoder->encodeString("a\nb\nc")); + } + + public function testEncodingCRTextWithDiConfiguredInstance() + { + $encoder = $this->_createEncoderFromContainer(); + $this->assertEquals("a\r\nb\r\nc", $encoder->encodeString("a\rb\rc")); + } + + public function testEncodingLFCRTextWithDiConfiguredInstance() + { + $encoder = $this->_createEncoderFromContainer(); + $this->assertEquals("a\r\n\r\nb\r\n\r\nc", $encoder->encodeString("a\n\rb\n\rc")); + } + + public function testEncodingCRLFTextWithDiConfiguredInstance() + { + $encoder = $this->_createEncoderFromContainer(); + $this->assertEquals("a\r\nb\r\nc", $encoder->encodeString("a\r\nb\r\nc")); + } + + public function testEncodingDotStuffingWithDiConfiguredInstance() + { + // Enable DotEscaping + Swift_Preferences::getInstance()->setQPDotEscape(true); + $encoder = $this->_createEncoderFromContainer(); + $this->assertEquals("a=2E\r\n=2E\r\n=2Eb\r\nc", $encoder->encodeString("a.\r\n.\r\n.b\r\nc")); + // Return to default + Swift_Preferences::getInstance()->setQPDotEscape(false); + $encoder = $this->_createEncoderFromContainer(); + $this->assertEquals("a.\r\n.\r\n.b\r\nc", $encoder->encodeString("a.\r\n.\r\n.b\r\nc")); + } + + public function testDotStuffingEncodingAndDecodingSamplesFromDiConfiguredInstance() + { + // Enable DotEscaping + Swift_Preferences::getInstance()->setQPDotEscape(true); + $this->testEncodingAndDecodingSamplesFromDiConfiguredInstance(); + } + + private function _createEncoderFromContainer() + { + return Swift_DependencyContainer::getInstance() + ->lookup('mime.qpcontentencoder') + ; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/EmbeddedFileAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/EmbeddedFileAcceptanceTest.php new file mode 100644 index 00000000..8a04df6b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/EmbeddedFileAcceptanceTest.php @@ -0,0 +1,138 @@ +<?php + +class Swift_Mime_EmbeddedFileAcceptanceTest extends \PHPUnit_Framework_TestCase +{ + private $_contentEncoder; + private $_cache; + private $_grammar; + private $_headers; + + public function setUp() + { + $this->_cache = new Swift_KeyCache_ArrayKeyCache( + new Swift_KeyCache_SimpleKeyCacheInputStream() + ); + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $this->_contentEncoder = new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + + $headerEncoder = new Swift_Mime_HeaderEncoder_QpHeaderEncoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8') + ); + $paramEncoder = new Swift_Encoder_Rfc2231Encoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8') + ); + $this->_grammar = new Swift_Mime_Grammar(); + $this->_headers = new Swift_Mime_SimpleHeaderSet( + new Swift_Mime_SimpleHeaderFactory($headerEncoder, $paramEncoder, $this->_grammar) + ); + } + + public function testContentIdIsSetInHeader() + { + $file = $this->_createEmbeddedFile(); + $file->setContentType('application/pdf'); + $file->setId('foo@bar'); + $this->assertEquals( + 'Content-Type: application/pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <foo@bar>'."\r\n". + 'Content-Disposition: inline'."\r\n", + $file->toString() + ); + } + + public function testDispositionIsSetInHeader() + { + $file = $this->_createEmbeddedFile(); + $id = $file->getId(); + $file->setContentType('application/pdf'); + $file->setDisposition('attachment'); + $this->assertEquals( + 'Content-Type: application/pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.$id.'>'."\r\n". + 'Content-Disposition: attachment'."\r\n", + $file->toString() + ); + } + + public function testFilenameIsSetInHeader() + { + $file = $this->_createEmbeddedFile(); + $id = $file->getId(); + $file->setContentType('application/pdf'); + $file->setFilename('foo.pdf'); + $this->assertEquals( + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.$id.'>'."\r\n". + 'Content-Disposition: inline; filename=foo.pdf'."\r\n", + $file->toString() + ); + } + + public function testSizeIsSetInHeader() + { + $file = $this->_createEmbeddedFile(); + $id = $file->getId(); + $file->setContentType('application/pdf'); + $file->setSize(12340); + $this->assertEquals( + 'Content-Type: application/pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.$id.'>'."\r\n". + 'Content-Disposition: inline; size=12340'."\r\n", + $file->toString() + ); + } + + public function testMultipleParametersInHeader() + { + $file = $this->_createEmbeddedFile(); + $id = $file->getId(); + $file->setContentType('application/pdf'); + $file->setFilename('foo.pdf'); + $file->setSize(12340); + + $this->assertEquals( + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.$id.'>'."\r\n". + 'Content-Disposition: inline; filename=foo.pdf; size=12340'."\r\n", + $file->toString() + ); + } + + public function testEndToEnd() + { + $file = $this->_createEmbeddedFile(); + $id = $file->getId(); + $file->setContentType('application/pdf'); + $file->setFilename('foo.pdf'); + $file->setSize(12340); + $file->setBody('abcd'); + $this->assertEquals( + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.$id.'>'."\r\n". + 'Content-Disposition: inline; filename=foo.pdf; size=12340'."\r\n". + "\r\n". + base64_encode('abcd'), + $file->toString() + ); + } + + // -- Private helpers + + protected function _createEmbeddedFile() + { + $entity = new Swift_Mime_EmbeddedFile( + $this->_headers, + $this->_contentEncoder, + $this->_cache, + $this->_grammar + ); + + return $entity; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/HeaderEncoder/Base64HeaderEncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/HeaderEncoder/Base64HeaderEncoderAcceptanceTest.php new file mode 100644 index 00000000..304867a4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/HeaderEncoder/Base64HeaderEncoderAcceptanceTest.php @@ -0,0 +1,32 @@ +<?php + +class Swift_Mime_HeaderEncoder_Base64HeaderEncoderAcceptanceTest extends \PHPUnit_Framework_TestCase +{ + private $_encoder; + + public function setUp() + { + $this->_encoder = new Swift_Mime_HeaderEncoder_Base64HeaderEncoder(); + } + + public function testEncodingJIS() + { + if (function_exists('mb_convert_encoding')) { + // base64_encode and split cannot handle long JIS text to fold + $subject = 'é•·ã„é•·ã„é•·ã„é•·ã„é•·ã„é•·ã„é•·ã„é•·ã„é•·ã„é•·ã„é•·ã„é•·ã„é•·ã„é•·ã„é•·ã„é•·ã„é•·ã„é•·ã„é•·ã„é•·ã„ä»¶å'; + + $encodedWrapperLength = strlen('=?iso-2022-jp?'.$this->_encoder->getName().'??='); + + $old = mb_internal_encoding(); + mb_internal_encoding('utf-8'); + $newstring = mb_encode_mimeheader($subject, 'iso-2022-jp', 'B', "\r\n"); + mb_internal_encoding($old); + + $encoded = $this->_encoder->encodeString($subject, 0, 75 - $encodedWrapperLength, 'iso-2022-jp'); + $this->assertEquals( + $encoded, $newstring, + 'Encoded string should decode back to original string for sample ' + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/MimePartAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/MimePartAcceptanceTest.php new file mode 100644 index 00000000..8232fe63 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/MimePartAcceptanceTest.php @@ -0,0 +1,129 @@ +<?php + +class Swift_Mime_MimePartAcceptanceTest extends \PHPUnit_Framework_TestCase +{ + private $_contentEncoder; + private $_cache; + private $_grammar; + private $_headers; + + public function setUp() + { + $this->_cache = new Swift_KeyCache_ArrayKeyCache( + new Swift_KeyCache_SimpleKeyCacheInputStream() + ); + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $this->_contentEncoder = new Swift_Mime_ContentEncoder_QpContentEncoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'), + new Swift_StreamFilters_ByteArrayReplacementFilter( + array(array(0x0D, 0x0A), array(0x0D), array(0x0A)), + array(array(0x0A), array(0x0A), array(0x0D, 0x0A)) + ) + ); + + $headerEncoder = new Swift_Mime_HeaderEncoder_QpHeaderEncoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8') + ); + $paramEncoder = new Swift_Encoder_Rfc2231Encoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8') + ); + $this->_grammar = new Swift_Mime_Grammar(); + $this->_headers = new Swift_Mime_SimpleHeaderSet( + new Swift_Mime_SimpleHeaderFactory($headerEncoder, $paramEncoder, $this->_grammar) + ); + } + + public function testCharsetIsSetInHeader() + { + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setCharset('utf-8'); + $part->setBody('foobar'); + $this->assertEquals( + 'Content-Type: text/plain; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foobar', + $part->toString() + ); + } + + public function testFormatIsSetInHeaders() + { + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setFormat('flowed'); + $part->setBody('> foobar'); + $this->assertEquals( + 'Content-Type: text/plain; format=flowed'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + '> foobar', + $part->toString() + ); + } + + public function testDelSpIsSetInHeaders() + { + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setDelSp(true); + $part->setBody('foobar'); + $this->assertEquals( + 'Content-Type: text/plain; delsp=yes'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foobar', + $part->toString() + ); + } + + public function testAll3ParamsInHeaders() + { + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setCharset('utf-8'); + $part->setFormat('fixed'); + $part->setDelSp(true); + $part->setBody('foobar'); + $this->assertEquals( + 'Content-Type: text/plain; charset=utf-8; format=fixed; delsp=yes'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foobar', + $part->toString() + ); + } + + public function testBodyIsCanonicalized() + { + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setCharset('utf-8'); + $part->setBody("foobar\r\rtest\ning\r"); + $this->assertEquals( + 'Content-Type: text/plain; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + "foobar\r\n". + "\r\n". + "test\r\n". + "ing\r\n", + $part->toString() + ); + } + + // -- Private helpers + + protected function _createMimePart() + { + $entity = new Swift_Mime_MimePart( + $this->_headers, + $this->_contentEncoder, + $this->_cache, + $this->_grammar + ); + + return $entity; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/SimpleMessageAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/SimpleMessageAcceptanceTest.php new file mode 100644 index 00000000..cd6d910d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/SimpleMessageAcceptanceTest.php @@ -0,0 +1,1251 @@ +<?php + +class Swift_Mime_SimpleMessageAcceptanceTest extends \PHPUnit_Framework_TestCase +{ + public function setUp() + { + Swift_Preferences::getInstance()->setCharset(null); //TODO: Test with the charset defined + } + + public function testBasicHeaders() + { + /* -- RFC 2822, 3.6. + */ + + $message = $this->_createMessage(); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString(), + '%s: Only required headers, and non-empty headers should be displayed' + ); + } + + public function testSubjectIsDisplayedIfSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testDateCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $id = $message->getId(); + $message->setDate(1234); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', 1234)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testMessageIdCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setId('foo@bar'); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <foo@bar>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testContentTypeCanBeChanged() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setContentType('text/html'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/html'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testCharsetCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setContentType('text/html'); + $message->setCharset('iso-8859-1'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/html; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testFormatCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFormat('flowed'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain; format=flowed'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testEncoderCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setContentType('text/html'); + $message->setEncoder( + new Swift_Mime_ContentEncoder_PlainContentEncoder('7bit') + ); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/html'."\r\n". + 'Content-Transfer-Encoding: 7bit'."\r\n", + $message->toString() + ); + } + + public function testFromAddressCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom('chris.corbyn@swiftmailer.org'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: chris.corbyn@swiftmailer.org'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testFromAddressCanBeSetWithName() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris Corbyn')); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn <chris.corbyn@swiftmailer.org>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testMultipleFromAddressesCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org', + )); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn <chris.corbyn@swiftmailer.org>, mark@swiftmailer.org'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testReturnPathAddressCanBeSet() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Return-Path: <chris@w3style.co.uk>'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn <chris.corbyn@swiftmailer.org>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testEmptyReturnPathHeaderCanBeUsed() + { + $message = $this->_createMessage(); + $message->setReturnPath(''); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Return-Path: <>'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn <chris.corbyn@swiftmailer.org>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testSenderCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setSender('chris.corbyn@swiftmailer.org'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Sender: chris.corbyn@swiftmailer.org'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testSenderCanBeSetWithName() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setSender(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Sender: Chris <chris.corbyn@swiftmailer.org>'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testReplyToCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array('chris@w3style.co.uk' => 'Myself')); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris <chris.corbyn@swiftmailer.org>'."\r\n". + 'Reply-To: Myself <chris@w3style.co.uk>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testMultipleReplyAddressCanBeUsed() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris <chris.corbyn@swiftmailer.org>'."\r\n". + 'Reply-To: Myself <chris@w3style.co.uk>, Me <my.other@address.com>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testToAddressCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $message->setTo('mark@swiftmailer.org'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris <chris.corbyn@swiftmailer.org>'."\r\n". + 'Reply-To: Myself <chris@w3style.co.uk>, Me <my.other@address.com>'."\r\n". + 'To: mark@swiftmailer.org'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testMultipleToAddressesCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $message->setTo(array( + 'mark@swiftmailer.org', 'chris@swiftmailer.org' => 'Chris Corbyn', + )); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris <chris.corbyn@swiftmailer.org>'."\r\n". + 'Reply-To: Myself <chris@w3style.co.uk>, Me <my.other@address.com>'."\r\n". + 'To: mark@swiftmailer.org, Chris Corbyn <chris@swiftmailer.org>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testCcAddressCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $message->setTo(array( + 'mark@swiftmailer.org', 'chris@swiftmailer.org' => 'Chris Corbyn', + )); + $message->setCc('john@some-site.com'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris <chris.corbyn@swiftmailer.org>'."\r\n". + 'Reply-To: Myself <chris@w3style.co.uk>, Me <my.other@address.com>'."\r\n". + 'To: mark@swiftmailer.org, Chris Corbyn <chris@swiftmailer.org>'."\r\n". + 'Cc: john@some-site.com'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testMultipleCcAddressesCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $message->setTo(array( + 'mark@swiftmailer.org', 'chris@swiftmailer.org' => 'Chris Corbyn', + )); + $message->setCc(array( + 'john@some-site.com' => 'John West', + 'fred@another-site.co.uk' => 'Big Fred', + )); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris <chris.corbyn@swiftmailer.org>'."\r\n". + 'Reply-To: Myself <chris@w3style.co.uk>, Me <my.other@address.com>'."\r\n". + 'To: mark@swiftmailer.org, Chris Corbyn <chris@swiftmailer.org>'."\r\n". + 'Cc: John West <john@some-site.com>, Big Fred <fred@another-site.co.uk>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testBccAddressCanBeSet() + { + //Obviously Transports need to setBcc(array()) and send to each Bcc recipient + // separately in accordance with RFC 2822/2821 + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $message->setTo(array( + 'mark@swiftmailer.org', 'chris@swiftmailer.org' => 'Chris Corbyn', + )); + $message->setCc(array( + 'john@some-site.com' => 'John West', + 'fred@another-site.co.uk' => 'Big Fred', + )); + $message->setBcc('x@alphabet.tld'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris <chris.corbyn@swiftmailer.org>'."\r\n". + 'Reply-To: Myself <chris@w3style.co.uk>, Me <my.other@address.com>'."\r\n". + 'To: mark@swiftmailer.org, Chris Corbyn <chris@swiftmailer.org>'."\r\n". + 'Cc: John West <john@some-site.com>, Big Fred <fred@another-site.co.uk>'."\r\n". + 'Bcc: x@alphabet.tld'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testMultipleBccAddressesCanBeSet() + { + //Obviously Transports need to setBcc(array()) and send to each Bcc recipient + // separately in accordance with RFC 2822/2821 + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $message->setTo(array( + 'mark@swiftmailer.org', 'chris@swiftmailer.org' => 'Chris Corbyn', + )); + $message->setCc(array( + 'john@some-site.com' => 'John West', + 'fred@another-site.co.uk' => 'Big Fred', + )); + $message->setBcc(array('x@alphabet.tld', 'a@alphabet.tld' => 'A')); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris <chris.corbyn@swiftmailer.org>'."\r\n". + 'Reply-To: Myself <chris@w3style.co.uk>, Me <my.other@address.com>'."\r\n". + 'To: mark@swiftmailer.org, Chris Corbyn <chris@swiftmailer.org>'."\r\n". + 'Cc: John West <john@some-site.com>, Big Fred <fred@another-site.co.uk>'."\r\n". + 'Bcc: x@alphabet.tld, A <a@alphabet.tld>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testStringBodyIsAppended() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $message->setBody( + 'just a test body'."\r\n". + 'with a new line' + ); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Return-Path: <chris@w3style.co.uk>'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn <chris.corbyn@swiftmailer.org>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'just a test body'."\r\n". + 'with a new line', + $message->toString() + ); + } + + public function testStringBodyIsEncoded() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $message->setBody( + 'Just s'.pack('C*', 0xC2, 0x01, 0x01).'me multi-'."\r\n". + 'line message!' + ); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Return-Path: <chris@w3style.co.uk>'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn <chris.corbyn@swiftmailer.org>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'Just s=C2=01=01me multi-'."\r\n". + 'line message!', + $message->toString() + ); + } + + public function testChildrenCanBeAttached() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = $message->getDate(); + $boundary = $message->getBoundary(); + + $part1 = $this->_createMimePart(); + $part1->setContentType('text/plain'); + $part1->setCharset('iso-8859-1'); + $part1->setBody('foo'); + + $message->attach($part1); + + $part2 = $this->_createMimePart(); + $part2->setContentType('text/html'); + $part2->setCharset('iso-8859-1'); + $part2->setBody('test <b>foo</b>'); + + $message->attach($part2); + + $this->assertEquals( + 'Return-Path: <chris@w3style.co.uk>'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn <chris.corbyn@swiftmailer.org>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'test <b>foo</b>'. + "\r\n\r\n". + '--'.$boundary.'--'."\r\n", + $message->toString() + ); + } + + public function testAttachmentsBeingAttached() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setCharset('iso-8859-1'); + $part->setBody('foo'); + + $message->attach($part); + + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setBody('<pdf data>'); + + $message->attach($attachment); + + $this->assertRegExp( + '~^'. + 'Return-Path: <chris@w3style.co.uk>'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn <chris.corbyn@swiftmailer.org>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/mixed;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: text/plain; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--\\1--'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf'."\r\n". + "\r\n". + preg_quote(base64_encode('<pdf data>'), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $message->toString() + ); + } + + public function testAttachmentsAndEmbeddedFilesBeingAttached() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setCharset('iso-8859-1'); + $part->setBody('foo'); + + $message->attach($part); + + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setBody('<pdf data>'); + + $message->attach($attachment); + + $file = $this->_createEmbeddedFile(); + $file->setContentType('image/jpeg'); + $file->setFilename('myimage.jpg'); + $file->setBody('<image data>'); + + $message->attach($file); + + $cid = $file->getId(); + + $this->assertRegExp( + '~^'. + 'Return-Path: <chris@w3style.co.uk>'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn <chris.corbyn@swiftmailer.org>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/mixed;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: text/plain; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: multipart/related;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\2'."\r\n". + 'Content-Type: image/jpeg; name=myimage.jpg'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.$cid.'>'."\r\n". + 'Content-Disposition: inline; filename=myimage.jpg'."\r\n". + "\r\n". + preg_quote(base64_encode('<image data>'), '~'). + "\r\n\r\n". + '--\\2--'."\r\n". + "\r\n\r\n". + '--\\1--'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf'."\r\n". + "\r\n". + preg_quote(base64_encode('<pdf data>'), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $message->toString() + ); + } + + public function testComplexEmbeddingOfContent() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setBody('<pdf data>'); + + $message->attach($attachment); + + $file = $this->_createEmbeddedFile(); + $file->setContentType('image/jpeg'); + $file->setFilename('myimage.jpg'); + $file->setBody('<image data>'); + + $part = $this->_createMimePart(); + $part->setContentType('text/html'); + $part->setCharset('iso-8859-1'); + $part->setBody('foo <img src="'.$message->embed($file).'" />'); + + $message->attach($part); + + $cid = $file->getId(); + + $this->assertRegExp( + '~^'. + 'Return-Path: <chris@w3style.co.uk>'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn <chris.corbyn@swiftmailer.org>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/mixed;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: multipart/related;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: text/html; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo <img src=3D"cid:'.$cid.'" />'.//=3D is just = in QP + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: image/jpeg; name=myimage.jpg'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.$cid.'>'."\r\n". + 'Content-Disposition: inline; filename=myimage.jpg'."\r\n". + "\r\n". + preg_quote(base64_encode('<image data>'), '~'). + "\r\n\r\n". + '--\\1--'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf'."\r\n". + "\r\n". + preg_quote(base64_encode('<pdf data>'), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $message->toString() + ); + } + + public function testAttachingAndDetachingContent() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setCharset('iso-8859-1'); + $part->setBody('foo'); + + $message->attach($part); + + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setBody('<pdf data>'); + + $message->attach($attachment); + + $file = $this->_createEmbeddedFile(); + $file->setContentType('image/jpeg'); + $file->setFilename('myimage.jpg'); + $file->setBody('<image data>'); + + $message->attach($file); + + $cid = $file->getId(); + + $message->detach($attachment); + + $this->assertRegExp( + '~^'. + 'Return-Path: <chris@w3style.co.uk>'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn <chris.corbyn@swiftmailer.org>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: multipart/related;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: image/jpeg; name=myimage.jpg'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.$cid.'>'."\r\n". + 'Content-Disposition: inline; filename=myimage.jpg'."\r\n". + "\r\n". + preg_quote(base64_encode('<image data>'), '~'). + "\r\n\r\n". + '--\\1--'."\r\n". + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $message->toString(), + '%s: Attachment should have been detached' + ); + } + + public function testBoundaryDoesNotAppearAfterAllPartsAreDetached() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = $message->getDate(); + $boundary = $message->getBoundary(); + + $part1 = $this->_createMimePart(); + $part1->setContentType('text/plain'); + $part1->setCharset('iso-8859-1'); + $part1->setBody('foo'); + + $message->attach($part1); + + $part2 = $this->_createMimePart(); + $part2->setContentType('text/html'); + $part2->setCharset('iso-8859-1'); + $part2->setBody('test <b>foo</b>'); + + $message->attach($part2); + + $message->detach($part1); + $message->detach($part2); + + $this->assertEquals( + 'Return-Path: <chris@w3style.co.uk>'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn <chris.corbyn@swiftmailer.org>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString(), + '%s: Message should be restored to orignal state after parts are detached' + ); + } + + public function testCharsetFormatOrDelSpAreNotShownWhenBoundaryIsSet() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $message->setCharset('utf-8'); + $message->setFormat('flowed'); + $message->setDelSp(true); + + $id = $message->getId(); + $date = $message->getDate(); + $boundary = $message->getBoundary(); + + $part1 = $this->_createMimePart(); + $part1->setContentType('text/plain'); + $part1->setCharset('iso-8859-1'); + $part1->setBody('foo'); + + $message->attach($part1); + + $part2 = $this->_createMimePart(); + $part2->setContentType('text/html'); + $part2->setCharset('iso-8859-1'); + $part2->setBody('test <b>foo</b>'); + + $message->attach($part2); + + $this->assertEquals( + 'Return-Path: <chris@w3style.co.uk>'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn <chris.corbyn@swiftmailer.org>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'test <b>foo</b>'. + "\r\n\r\n". + '--'.$boundary.'--'."\r\n", + $message->toString() + ); + } + + public function testBodyCanBeSetWithAttachments() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $message->setContentType('text/html'); + $message->setCharset('iso-8859-1'); + $message->setBody('foo'); + + $id = $message->getId(); + $date = date('r', $message->getDate()); + $boundary = $message->getBoundary(); + + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setBody('<pdf data>'); + + $message->attach($attachment); + + $this->assertEquals( + 'Return-Path: <chris@w3style.co.uk>'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn <chris.corbyn@swiftmailer.org>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/mixed;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf'."\r\n". + "\r\n". + base64_encode('<pdf data>'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n", + $message->toString() + ); + } + + public function testHtmlPartAlwaysAppearsLast() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = date('r', $message->getDate()); + $boundary = $message->getBoundary(); + + $part1 = $this->_createMimePart(); + $part1->setContentType('text/html'); + $part1->setBody('foo'); + + $part2 = $this->_createMimePart(); + $part2->setContentType('text/plain'); + $part2->setBody('bar'); + + $message->attach($part1); + $message->attach($part2); + + $this->assertEquals( + 'Return-Path: <chris@w3style.co.uk>'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn <chris.corbyn@swiftmailer.org>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'bar'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary.'--'."\r\n", + $message->toString() + ); + } + + public function testBodyBecomesPartIfOtherPartsAttached() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $message->setContentType('text/html'); + $message->setBody('foo'); + + $id = $message->getId(); + $date = date('r', $message->getDate()); + $boundary = $message->getBoundary(); + + $part2 = $this->_createMimePart(); + $part2->setContentType('text/plain'); + $part2->setBody('bar'); + + $message->attach($part2); + + $this->assertEquals( + 'Return-Path: <chris@w3style.co.uk>'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn <chris.corbyn@swiftmailer.org>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'bar'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary.'--'."\r\n", + $message->toString() + ); + } + + public function testBodyIsCanonicalized() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $message->setBody( + 'just a test body'."\n". + 'with a new line' + ); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Return-Path: <chris@w3style.co.uk>'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn <chris.corbyn@swiftmailer.org>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'just a test body'."\r\n". + 'with a new line', + $message->toString() + ); + } + + // -- Private helpers + + protected function _createMessage() + { + return new Swift_Message(); + } + + protected function _createMimePart() + { + return new Swift_MimePart(); + } + + protected function _createAttachment() + { + return new Swift_Attachment(); + } + + protected function _createEmbeddedFile() + { + return new Swift_EmbeddedFile(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MimePartAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MimePartAcceptanceTest.php new file mode 100644 index 00000000..f42405df --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MimePartAcceptanceTest.php @@ -0,0 +1,15 @@ +<?php + +require_once 'swift_required.php'; +require_once __DIR__.'/Mime/MimePartAcceptanceTest.php'; + +class Swift_MimePartAcceptanceTest extends Swift_Mime_MimePartAcceptanceTest +{ + protected function _createMimePart() + { + Swift_DependencyContainer::getInstance() + ->register('properties.charset')->asValue(null); + + return Swift_MimePart::newInstance(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/AbstractStreamBufferAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/AbstractStreamBufferAcceptanceTest.php new file mode 100644 index 00000000..dc0d0a1d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/AbstractStreamBufferAcceptanceTest.php @@ -0,0 +1,133 @@ +<?php + +abstract class Swift_Transport_StreamBuffer_AbstractStreamBufferAcceptanceTest extends \PHPUnit_Framework_TestCase +{ + protected $_buffer; + + abstract protected function _initializeBuffer(); + + public function setUp() + { + if (true == getenv('TRAVIS')) { + $this->markTestSkipped( + 'Will fail on travis-ci if not skipped due to travis blocking '. + 'socket mailing tcp connections.' + ); + } + + $this->_buffer = new Swift_Transport_StreamBuffer( + $this->getMockBuilder('Swift_ReplacementFilterFactory')->getMock() + ); + } + + public function testReadLine() + { + $this->_initializeBuffer(); + + $line = $this->_buffer->readLine(0); + $this->assertRegExp('/^[0-9]{3}.*?\r\n$/D', $line); + $seq = $this->_buffer->write("QUIT\r\n"); + $this->assertTrue((bool) $seq); + $line = $this->_buffer->readLine($seq); + $this->assertRegExp('/^[0-9]{3}.*?\r\n$/D', $line); + $this->_buffer->terminate(); + } + + public function testWrite() + { + $this->_initializeBuffer(); + + $line = $this->_buffer->readLine(0); + $this->assertRegExp('/^[0-9]{3}.*?\r\n$/D', $line); + + $seq = $this->_buffer->write("HELO foo\r\n"); + $this->assertTrue((bool) $seq); + $line = $this->_buffer->readLine($seq); + $this->assertRegExp('/^[0-9]{3}.*?\r\n$/D', $line); + + $seq = $this->_buffer->write("QUIT\r\n"); + $this->assertTrue((bool) $seq); + $line = $this->_buffer->readLine($seq); + $this->assertRegExp('/^[0-9]{3}.*?\r\n$/D', $line); + $this->_buffer->terminate(); + } + + public function testBindingOtherStreamsMirrorsWriteOperations() + { + $this->_initializeBuffer(); + + $is1 = $this->_createMockInputStream(); + $is2 = $this->_createMockInputStream(); + + $is1->expects($this->at(0)) + ->method('write') + ->with('x'); + $is1->expects($this->at(1)) + ->method('write') + ->with('y'); + $is2->expects($this->at(0)) + ->method('write') + ->with('x'); + $is2->expects($this->at(1)) + ->method('write') + ->with('y'); + + $this->_buffer->bind($is1); + $this->_buffer->bind($is2); + + $this->_buffer->write('x'); + $this->_buffer->write('y'); + } + + public function testBindingOtherStreamsMirrorsFlushOperations() + { + $this->_initializeBuffer(); + + $is1 = $this->_createMockInputStream(); + $is2 = $this->_createMockInputStream(); + + $is1->expects($this->once()) + ->method('flushBuffers'); + $is2->expects($this->once()) + ->method('flushBuffers'); + + $this->_buffer->bind($is1); + $this->_buffer->bind($is2); + + $this->_buffer->flushBuffers(); + } + + public function testUnbindingStreamPreventsFurtherWrites() + { + $this->_initializeBuffer(); + + $is1 = $this->_createMockInputStream(); + $is2 = $this->_createMockInputStream(); + + $is1->expects($this->at(0)) + ->method('write') + ->with('x'); + $is1->expects($this->at(1)) + ->method('write') + ->with('y'); + $is2->expects($this->once()) + ->method('write') + ->with('x'); + + $this->_buffer->bind($is1); + $this->_buffer->bind($is2); + + $this->_buffer->write('x'); + + $this->_buffer->unbind($is2); + + $this->_buffer->write('y'); + } + + // -- Creation Methods + + private function _createMockInputStream() + { + return $this->getMockBuilder('Swift_InputByteStream')->getMock(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/BasicSocketAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/BasicSocketAcceptanceTest.php new file mode 100644 index 00000000..51c998c6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/BasicSocketAcceptanceTest.php @@ -0,0 +1,33 @@ +<?php + +require_once __DIR__.'/AbstractStreamBufferAcceptanceTest.php'; + +class Swift_Transport_StreamBuffer_BasicSocketAcceptanceTest extends Swift_Transport_StreamBuffer_AbstractStreamBufferAcceptanceTest +{ + public function setUp() + { + if (!defined('SWIFT_SMTP_HOST')) { + $this->markTestSkipped( + 'Cannot run test without an SMTP host to connect to (define '. + 'SWIFT_SMTP_HOST in tests/acceptance.conf.php if you wish to run this test)' + ); + } + parent::setUp(); + } + + protected function _initializeBuffer() + { + $parts = explode(':', SWIFT_SMTP_HOST); + $host = $parts[0]; + $port = isset($parts[1]) ? $parts[1] : 25; + + $this->_buffer->initialize(array( + 'type' => Swift_Transport_IoBuffer::TYPE_SOCKET, + 'host' => $host, + 'port' => $port, + 'protocol' => 'tcp', + 'blocking' => 1, + 'timeout' => 15, + )); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/ProcessAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/ProcessAcceptanceTest.php new file mode 100644 index 00000000..0e2924eb --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/ProcessAcceptanceTest.php @@ -0,0 +1,26 @@ +<?php + +require_once __DIR__.'/AbstractStreamBufferAcceptanceTest.php'; + +class Swift_Transport_StreamBuffer_ProcessAcceptanceTest extends Swift_Transport_StreamBuffer_AbstractStreamBufferAcceptanceTest +{ + public function setUp() + { + if (!defined('SWIFT_SENDMAIL_PATH')) { + $this->markTestSkipped( + 'Cannot run test without a path to sendmail (define '. + 'SWIFT_SENDMAIL_PATH in tests/acceptance.conf.php if you wish to run this test)' + ); + } + + parent::setUp(); + } + + protected function _initializeBuffer() + { + $this->_buffer->initialize(array( + 'type' => Swift_Transport_IoBuffer::TYPE_PROCESS, + 'command' => SWIFT_SENDMAIL_PATH.' -bs', + )); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SocketTimeoutTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SocketTimeoutTest.php new file mode 100644 index 00000000..d550f818 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SocketTimeoutTest.php @@ -0,0 +1,67 @@ +<?php + +class Swift_Transport_StreamBuffer_SocketTimeoutTest extends \PHPUnit_Framework_TestCase +{ + protected $_buffer; + + protected $_randomHighPort; + + protected $_server; + + public function setUp() + { + if (!defined('SWIFT_SMTP_HOST')) { + $this->markTestSkipped( + 'Cannot run test without an SMTP host to connect to (define '. + 'SWIFT_SMTP_HOST in tests/acceptance.conf.php if you wish to run this test)' + ); + } + + $serverStarted = false; + for ($i = 0; $i < 5; ++$i) { + $this->_randomHighPort = rand(50000, 65000); + $this->_server = stream_socket_server('tcp://127.0.0.1:'.$this->_randomHighPort); + if ($this->_server) { + $serverStarted = true; + } + } + + $this->_buffer = new Swift_Transport_StreamBuffer( + $this->getMockBuilder('Swift_ReplacementFilterFactory')->getMock() + ); + } + + protected function _initializeBuffer() + { + $host = '127.0.0.1'; + $port = $this->_randomHighPort; + + $this->_buffer->initialize(array( + 'type' => Swift_Transport_IoBuffer::TYPE_SOCKET, + 'host' => $host, + 'port' => $port, + 'protocol' => 'tcp', + 'blocking' => 1, + 'timeout' => 1, + )); + } + + public function testTimeoutException() + { + $this->_initializeBuffer(); + $e = null; + try { + $line = $this->_buffer->readLine(0); + } catch (Exception $e) { + } + $this->assertInstanceof('Swift_IoException', $e, 'IO Exception Not Thrown On Connection Timeout'); + $this->assertRegExp('/Connection to .* Timed Out/', $e->getMessage()); + } + + public function tearDown() + { + if ($this->_server) { + stream_socket_shutdown($this->_server, STREAM_SHUT_RDWR); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SslSocketAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SslSocketAcceptanceTest.php new file mode 100644 index 00000000..2863f965 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SslSocketAcceptanceTest.php @@ -0,0 +1,40 @@ +<?php + +require_once __DIR__.'/AbstractStreamBufferAcceptanceTest.php'; + +class Swift_Transport_StreamBuffer_SslSocketAcceptanceTest extends Swift_Transport_StreamBuffer_AbstractStreamBufferAcceptanceTest +{ + public function setUp() + { + $streams = stream_get_transports(); + if (!in_array('ssl', $streams)) { + $this->markTestSkipped( + 'SSL is not configured for your system. It is not possible to run this test' + ); + } + if (!defined('SWIFT_SSL_HOST')) { + $this->markTestSkipped( + 'Cannot run test without an SSL enabled SMTP host to connect to (define '. + 'SWIFT_SSL_HOST in tests/acceptance.conf.php if you wish to run this test)' + ); + } + + parent::setUp(); + } + + protected function _initializeBuffer() + { + $parts = explode(':', SWIFT_SSL_HOST); + $host = $parts[0]; + $port = isset($parts[1]) ? $parts[1] : 25; + + $this->_buffer->initialize(array( + 'type' => Swift_Transport_IoBuffer::TYPE_SOCKET, + 'host' => $host, + 'port' => $port, + 'protocol' => 'ssl', + 'blocking' => 1, + 'timeout' => 15, + )); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/TlsSocketAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/TlsSocketAcceptanceTest.php new file mode 100644 index 00000000..6fc85057 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/TlsSocketAcceptanceTest.php @@ -0,0 +1,39 @@ +<?php + +require_once __DIR__.'/AbstractStreamBufferAcceptanceTest.php'; + +class Swift_Transport_StreamBuffer_TlsSocketAcceptanceTest extends Swift_Transport_StreamBuffer_AbstractStreamBufferAcceptanceTest +{ + public function setUp() + { + $streams = stream_get_transports(); + if (!in_array('tls', $streams)) { + $this->markTestSkipped( + 'TLS is not configured for your system. It is not possible to run this test' + ); + } + if (!defined('SWIFT_TLS_HOST')) { + $this->markTestSkipped( + 'Cannot run test without a TLS enabled SMTP host to connect to (define '. + 'SWIFT_TLS_HOST in tests/acceptance.conf.php if you wish to run this test)' + ); + } + parent::setUp(); + } + + protected function _initializeBuffer() + { + $parts = explode(':', SWIFT_TLS_HOST); + $host = $parts[0]; + $port = isset($parts[1]) ? $parts[1] : 25; + + $this->_buffer->initialize(array( + 'type' => Swift_Transport_IoBuffer::TYPE_SOCKET, + 'host' => $host, + 'port' => $port, + 'protocol' => 'tls', + 'blocking' => 1, + 'timeout' => 15, + )); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bootstrap.php b/vendor/swiftmailer/swiftmailer/tests/bootstrap.php new file mode 100644 index 00000000..27091a28 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bootstrap.php @@ -0,0 +1,21 @@ +<?php + +require_once dirname(__DIR__).'/vendor/autoload.php'; + +// Disable garbage collector to prevent segfaults +gc_disable(); + +set_include_path(get_include_path().PATH_SEPARATOR.dirname(__DIR__).'/lib'); + +Mockery::getConfiguration()->allowMockingNonExistentMethods(true); + +if (is_file(__DIR__.'/acceptance.conf.php')) { + require_once __DIR__.'/acceptance.conf.php'; +} +if (is_file(__DIR__.'/smoke.conf.php')) { + require_once __DIR__.'/smoke.conf.php'; +} +require_once __DIR__.'/StreamCollector.php'; +require_once __DIR__.'/IdenticalBinaryConstraint.php'; +require_once __DIR__.'/SwiftMailerTestCase.php'; +require_once __DIR__.'/SwiftMailerSmokeTestCase.php'; diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug111Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug111Test.php new file mode 100644 index 00000000..ba29ba87 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug111Test.php @@ -0,0 +1,42 @@ +<?php + +class Swift_Bug111Test extends \PHPUnit_Framework_TestCase +{ + public function testUnstructuredHeaderSlashesShouldNotBeEscaped() + { + $complicated_header = array( + 'to' => array( + 'email1@example.com', + 'email2@example.com', + 'email3@example.com', + 'email4@example.com', + 'email5@example.com', + ), + 'sub' => array( + '-name-' => array( + 'email1', + '"email2"', + 'email3\\', + 'email4', + 'email5', + ), + '-url-' => array( + 'http://google.com', + 'http://yahoo.com', + 'http://hotmail.com', + 'http://aol.com', + 'http://facebook.com', + ), + ), + ); + $json = json_encode($complicated_header); + + $message = new Swift_Message(); + $headers = $message->getHeaders(); + $headers->addTextHeader('X-SMTPAPI', $json); + $header = $headers->get('X-SMTPAPI'); + + $this->assertEquals('Swift_Mime_Headers_UnstructuredHeader', get_class($header)); + $this->assertEquals($json, $header->getFieldBody()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug118Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug118Test.php new file mode 100644 index 00000000..bd10c716 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug118Test.php @@ -0,0 +1,20 @@ +<?php + +class Swift_Bug118Test extends \PHPUnit_Framework_TestCase +{ + private $_message; + + public function setUp() + { + $this->_message = new Swift_Message(); + } + + public function testCallingGenerateIdChangesTheMessageId() + { + $currentId = $this->_message->getId(); + $this->_message->generateId(); + $newId = $this->_message->getId(); + + $this->assertNotEquals($currentId, $newId); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug206Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug206Test.php new file mode 100644 index 00000000..fdfa5302 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug206Test.php @@ -0,0 +1,38 @@ +<?php + +class Swift_Bug206Test extends \PHPUnit_Framework_TestCase +{ + private $_factory; + + public function setUp() + { + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $headerEncoder = new Swift_Mime_HeaderEncoder_QpHeaderEncoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8') + ); + $paramEncoder = new Swift_Encoder_Rfc2231Encoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8') + ); + $grammar = new Swift_Mime_Grammar(); + $this->_factory = new Swift_Mime_SimpleHeaderFactory($headerEncoder, $paramEncoder, $grammar); + } + + public function testMailboxHeaderEncoding() + { + $this->_testHeaderIsFullyEncoded('email@example.org', 'Family Name, Name', ' "Family Name, Name" <email@example.org>'); + $this->_testHeaderIsFullyEncoded('email@example.org', 'Family Namé, Name', ' Family =?utf-8?Q?Nam=C3=A9=2C?= Name'); + $this->_testHeaderIsFullyEncoded('email@example.org', 'Family Namé , Name', ' Family =?utf-8?Q?Nam=C3=A9_=2C?= Name'); + $this->_testHeaderIsFullyEncoded('email@example.org', 'Family Namé ;Name', ' Family =?utf-8?Q?Nam=C3=A9_=3BName?= '); + } + + private function _testHeaderIsFullyEncoded($email, $name, $expected) + { + $mailboxHeader = $this->_factory->createMailboxHeader('To', array( + $email => $name, + )); + + $headerBody = substr($mailboxHeader->toString(), 3, strlen($expected)); + + $this->assertEquals($expected, $headerBody); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug274Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug274Test.php new file mode 100644 index 00000000..f5f057ae --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug274Test.php @@ -0,0 +1,21 @@ +<?php + +class Swift_Bug274Test extends \PHPUnit_Framework_TestCase +{ + public function testEmptyFileNameAsAttachment() + { + $message = new Swift_Message(); + $this->setExpectedException('Swift_IoException', 'The path cannot be empty'); + $message->attach(Swift_Attachment::fromPath('')); + } + + public function testNonEmptyFileNameAsAttachment() + { + $message = new Swift_Message(); + try { + $message->attach(Swift_Attachment::fromPath(__FILE__)); + } catch (Exception $e) { + $this->fail('Path should not be empty'); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug34Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug34Test.php new file mode 100644 index 00000000..9c76ceb3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug34Test.php @@ -0,0 +1,75 @@ +<?php + +class Swift_Bug34Test extends \PHPUnit_Framework_TestCase +{ + public function setUp() + { + Swift_Preferences::getInstance()->setCharset('utf-8'); + } + + public function testEmbeddedFilesWithMultipartDataCreateMultipartRelatedContentAsAnAlternative() + { + $message = Swift_Message::newInstance(); + $message->setCharset('utf-8'); + $message->setSubject('test subject'); + $message->addPart('plain part', 'text/plain'); + + $image = Swift_Image::newInstance('<image data>', 'image.gif', 'image/gif'); + $cid = $message->embed($image); + + $message->setBody('<img src="'.$cid.'" />', 'text/html'); + + $message->setTo(array('user@domain.tld' => 'User')); + + $message->setFrom(array('other@domain.tld' => 'Other')); + $message->setSender(array('other@domain.tld' => 'Other')); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + $cidVal = $image->getId(); + + $this->assertRegExp( + '~^'. + 'Sender: Other <other@domain.tld>'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: test subject'."\r\n". + 'From: Other <other@domain.tld>'."\r\n". + 'To: User <user@domain.tld>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'plain part'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: multipart/related;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: text/html; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + '<img.*?/>'. + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: image/gif; name=image.gif'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.$cidVal.'>'."\r\n". + 'Content-Disposition: inline; filename=image.gif'."\r\n". + "\r\n". + preg_quote(base64_encode('<image data>'), '~'). + "\r\n\r\n". + '--\\1--'."\r\n". + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $message->toString() + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug35Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug35Test.php new file mode 100644 index 00000000..e07ee8f0 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug35Test.php @@ -0,0 +1,73 @@ +<?php + +class Swift_Bug35Test extends \PHPUnit_Framework_TestCase +{ + public function setUp() + { + Swift_Preferences::getInstance()->setCharset('utf-8'); + } + + public function testHTMLPartAppearsLastEvenWhenAttachmentsAdded() + { + $message = Swift_Message::newInstance(); + $message->setCharset('utf-8'); + $message->setSubject('test subject'); + $message->addPart('plain part', 'text/plain'); + + $attachment = Swift_Attachment::newInstance('<data>', 'image.gif', 'image/gif'); + $message->attach($attachment); + + $message->setBody('HTML part', 'text/html'); + + $message->setTo(array('user@domain.tld' => 'User')); + + $message->setFrom(array('other@domain.tld' => 'Other')); + $message->setSender(array('other@domain.tld' => 'Other')); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + + $this->assertRegExp( + '~^'. + 'Sender: Other <other@domain.tld>'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: test subject'."\r\n". + 'From: Other <other@domain.tld>'."\r\n". + 'To: User <user@domain.tld>'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/mixed;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: text/plain; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'plain part'. + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: text/html; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'HTML part'. + "\r\n\r\n". + '--\\1--'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: image/gif; name=image.gif'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=image.gif'."\r\n". + "\r\n". + preg_quote(base64_encode('<data>'), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $message->toString() + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug38Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug38Test.php new file mode 100644 index 00000000..a8a9212c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug38Test.php @@ -0,0 +1,194 @@ +<?php + +class Swift_Bug38Test extends \PHPUnit_Framework_TestCase +{ + private $_attFile; + private $_attFileName; + private $_attFileType; + + public function setUp() + { + $this->_attFileName = 'data.txt'; + $this->_attFileType = 'text/plain'; + $this->_attFile = __DIR__.'/../../_samples/files/data.txt'; + Swift_Preferences::getInstance()->setCharset('utf-8'); + } + + public function testWritingMessageToByteStreamProducesCorrectStructure() + { + $message = new Swift_Message(); + $message->setSubject('test subject'); + $message->setTo('user@domain.tld'); + $message->setCc('other@domain.tld'); + $message->setFrom('user@domain.tld'); + + $image = new Swift_Image('<data>', 'image.gif', 'image/gif'); + + $cid = $message->embed($image); + $message->setBody('HTML part', 'text/html'); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + $imgId = $image->getId(); + + $stream = new Swift_ByteStream_ArrayByteStream(); + + $message->toByteStream($stream); + + $this->assertPatternInStream( + '~^'. + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: test subject'."\r\n". + 'From: user@domain.tld'."\r\n". + 'To: user@domain.tld'."\r\n". + 'Cc: other@domain.tld'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/related;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'HTML part'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: image/gif; name=image.gif'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.preg_quote($imgId, '~').'>'."\r\n". + 'Content-Disposition: inline; filename=image.gif'."\r\n". + "\r\n". + preg_quote(base64_encode('<data>'), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $stream + ); + } + + public function testWritingMessageToByteStreamTwiceProducesCorrectStructure() + { + $message = new Swift_Message(); + $message->setSubject('test subject'); + $message->setTo('user@domain.tld'); + $message->setCc('other@domain.tld'); + $message->setFrom('user@domain.tld'); + + $image = new Swift_Image('<data>', 'image.gif', 'image/gif'); + + $cid = $message->embed($image); + $message->setBody('HTML part', 'text/html'); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + $imgId = $image->getId(); + + $pattern = '~^'. + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: test subject'."\r\n". + 'From: user@domain.tld'."\r\n". + 'To: user@domain.tld'."\r\n". + 'Cc: other@domain.tld'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/related;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'HTML part'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: image/gif; name=image.gif'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-ID: <'.preg_quote($imgId, '~').'>'."\r\n". + 'Content-Disposition: inline; filename=image.gif'."\r\n". + "\r\n". + preg_quote(base64_encode('<data>'), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D' + ; + + $streamA = new Swift_ByteStream_ArrayByteStream(); + $streamB = new Swift_ByteStream_ArrayByteStream(); + + $message->toByteStream($streamA); + $message->toByteStream($streamB); + + $this->assertPatternInStream($pattern, $streamA); + $this->assertPatternInStream($pattern, $streamB); + } + + public function testWritingMessageToByteStreamTwiceUsingAFileAttachment() + { + $message = new Swift_Message(); + $message->setSubject('test subject'); + $message->setTo('user@domain.tld'); + $message->setCc('other@domain.tld'); + $message->setFrom('user@domain.tld'); + + $attachment = Swift_Attachment::fromPath($this->_attFile); + + $message->attach($attachment); + + $message->setBody('HTML part', 'text/html'); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + + $streamA = new Swift_ByteStream_ArrayByteStream(); + $streamB = new Swift_ByteStream_ArrayByteStream(); + + $pattern = '~^'. + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: test subject'."\r\n". + 'From: user@domain.tld'."\r\n". + 'To: user@domain.tld'."\r\n". + 'Cc: other@domain.tld'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/mixed;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'HTML part'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: '.$this->_attFileType.'; name='.$this->_attFileName."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename='.$this->_attFileName."\r\n". + "\r\n". + preg_quote(base64_encode(file_get_contents($this->_attFile)), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D' + ; + + $message->toByteStream($streamA); + $message->toByteStream($streamB); + + $this->assertPatternInStream($pattern, $streamA); + $this->assertPatternInStream($pattern, $streamB); + } + + // -- Helpers + + public function assertPatternInStream($pattern, $stream, $message = '%s') + { + $string = ''; + while (false !== $bytes = $stream->read(8192)) { + $string .= $bytes; + } + $this->assertRegExp($pattern, $string, $message); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug518Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug518Test.php new file mode 100644 index 00000000..b83984fe --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug518Test.php @@ -0,0 +1,38 @@ +<?php + +use Mockery as m; + +class Swift_Bug518Test extends \PHPUnit_Framework_TestCase +{ + public function testIfEmailChangesAfterQueued() + { + $failedRecipients = 'value'; + $message = new Swift_Message(); + $message->setTo('foo@bar.com'); + + $that = $this; + $messageValidation = function ($m) use ($that) { + //the getTo should return the same value as we put in + $that->assertEquals('foo@bar.com', key($m->getTo()), 'The message has changed after it was put to the memory queue'); + + return true; + }; + + $transport = m::mock('Swift_Transport'); + $transport->shouldReceive('isStarted')->andReturn(true); + $transport->shouldReceive('send') + ->with(m::on($messageValidation), $failedRecipients) + ->andReturn(1); + + $memorySpool = new Swift_MemorySpool(); + $memorySpool->queueMessage($message); + + /* + * The message is queued in memory. + * Lets change the message + */ + $message->setTo('other@value.com'); + + $memorySpool->flushQueue($transport, $failedRecipients); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug51Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug51Test.php new file mode 100644 index 00000000..b9c33b09 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug51Test.php @@ -0,0 +1,121 @@ +<?php + +class Swift_Bug51Test extends \SwiftMailerTestCase +{ + private $_attachmentFile; + private $_outputFile; + + public function setUp() + { + if (!defined('SWIFT_TMP_DIR') || !is_writable(SWIFT_TMP_DIR)) { + $this->markTestSkipped( + 'Cannot run test without a writable directory to use ('. + 'define SWIFT_TMP_DIR in tests/config.php if you wish to run this test)' + ); + } + + $this->_attachmentFile = SWIFT_TMP_DIR.'/attach.rand.bin'; + file_put_contents($this->_attachmentFile, ''); + + $this->_outputFile = SWIFT_TMP_DIR.'/attach.out.bin'; + file_put_contents($this->_outputFile, ''); + } + + public function tearDown() + { + unlink($this->_attachmentFile); + unlink($this->_outputFile); + } + + public function testAttachmentsDoNotGetTruncatedUsingToByteStream() + { + //Run 100 times with 10KB attachments + for ($i = 0; $i < 10; ++$i) { + $message = $this->_createMessageWithRandomAttachment( + 10000, $this->_attachmentFile + ); + + file_put_contents($this->_outputFile, ''); + $message->toByteStream( + new Swift_ByteStream_FileByteStream($this->_outputFile, true) + ); + + $emailSource = file_get_contents($this->_outputFile); + + $this->assertAttachmentFromSourceMatches( + file_get_contents($this->_attachmentFile), + $emailSource + ); + } + } + + public function testAttachmentsDoNotGetTruncatedUsingToString() + { + //Run 100 times with 10KB attachments + for ($i = 0; $i < 10; ++$i) { + $message = $this->_createMessageWithRandomAttachment( + 10000, $this->_attachmentFile + ); + + $emailSource = $message->toString(); + + $this->assertAttachmentFromSourceMatches( + file_get_contents($this->_attachmentFile), + $emailSource + ); + } + } + + // -- Custom Assertions + + public function assertAttachmentFromSourceMatches($attachmentData, $source) + { + $encHeader = 'Content-Transfer-Encoding: base64'; + $base64declaration = strpos($source, $encHeader); + + $attachmentDataStart = strpos($source, "\r\n\r\n", $base64declaration); + $attachmentDataEnd = strpos($source, "\r\n--", $attachmentDataStart); + + if (false === $attachmentDataEnd) { + $attachmentBase64 = trim(substr($source, $attachmentDataStart)); + } else { + $attachmentBase64 = trim(substr( + $source, $attachmentDataStart, + $attachmentDataEnd - $attachmentDataStart + )); + } + + $this->assertIdenticalBinary($attachmentData, base64_decode($attachmentBase64)); + } + + // -- Creation Methods + + private function _fillFileWithRandomBytes($byteCount, $file) + { + // I was going to use dd with if=/dev/random but this way seems more + // cross platform even if a hella expensive!! + + file_put_contents($file, ''); + $fp = fopen($file, 'wb'); + for ($i = 0; $i < $byteCount; ++$i) { + $byteVal = rand(0, 255); + fwrite($fp, pack('i', $byteVal)); + } + fclose($fp); + } + + private function _createMessageWithRandomAttachment($size, $attachmentPath) + { + $this->_fillFileWithRandomBytes($size, $attachmentPath); + + $message = Swift_Message::newInstance() + ->setSubject('test') + ->setBody('test') + ->setFrom('a@b.c') + ->setTo('d@e.f') + ->attach(Swift_Attachment::fromPath($attachmentPath)) + ; + + return $message; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug534Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug534Test.php new file mode 100644 index 00000000..263cae51 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug534Test.php @@ -0,0 +1,38 @@ +<?php + +use Mockery as m; + +class Swift_Bug534Test extends \PHPUnit_Framework_TestCase +{ + public function testEmbeddedImagesAreEmbedded() + { + $message = Swift_Message::newInstance() + ->setFrom('from@example.com') + ->setTo('to@example.com') + ->setSubject('test') + ; + $cid = $message->embed(Swift_Image::fromPath(__DIR__.'/../../_samples/files/swiftmailer.png')); + $message->setBody('<img src="'.$cid.'" />', 'text/html'); + + $that = $this; + $messageValidation = function (Swift_Mime_Message $message) use ($that) { + preg_match('/cid:(.*)"/', $message->toString(), $matches); + $cid = $matches[1]; + preg_match('/Content-ID: <(.*)>/', $message->toString(), $matches); + $contentId = $matches[1]; + $that->assertEquals($cid, $contentId, 'cid in body and mime part Content-ID differ'); + + return true; + }; + + $failedRecipients = array(); + + $transport = m::mock('Swift_Transport'); + $transport->shouldReceive('isStarted')->andReturn(true); + $transport->shouldReceive('send')->with(m::on($messageValidation), $failedRecipients)->andReturn(1); + + $memorySpool = new Swift_MemorySpool(); + $memorySpool->queueMessage($message); + $memorySpool->flushQueue($transport, $failedRecipients); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug650Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug650Test.php new file mode 100644 index 00000000..3393fb80 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug650Test.php @@ -0,0 +1,36 @@ +<?php + +class Swift_Bug650Test extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider encodingDataProvider + * + * @param string $name + * @param string $expectedEncodedName + */ + public function testMailboxHeaderEncoding($name, $expectedEncodedName) + { + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $charStream = new Swift_CharacterStream_NgCharacterStream($factory, 'utf-8'); + $encoder = new Swift_Mime_HeaderEncoder_QpHeaderEncoder($charStream); + $header = new Swift_Mime_Headers_MailboxHeader('To', $encoder, new Swift_Mime_Grammar()); + $header->setCharset('utf-8'); + + $header->setNameAddresses(array( + 'test@example.com' => $name, + )); + + $this->assertSame('To: '.$expectedEncodedName." <test@example.com>\r\n", $header->toString()); + } + + public function encodingDataProvider() + { + return array( + array('this is " a test ö', 'this is =?utf-8?Q?=22?= a test =?utf-8?Q?=C3=B6?='), + array(': this is a test ö', '=?utf-8?Q?=3A?= this is a test =?utf-8?Q?=C3=B6?='), + array('( test ö', '=?utf-8?Q?=28?= test =?utf-8?Q?=C3=B6?='), + array('[ test ö', '=?utf-8?Q?=5B?= test =?utf-8?Q?=C3=B6?='), + array('@ test ö)', '=?utf-8?Q?=40?= test =?utf-8?Q?=C3=B6=29?='), + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug71Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug71Test.php new file mode 100644 index 00000000..4b804537 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug71Test.php @@ -0,0 +1,20 @@ +<?php + +class Swift_Bug71Test extends \PHPUnit_Framework_TestCase +{ + private $_message; + + public function setUp() + { + $this->_message = new Swift_Message('test'); + } + + public function testCallingToStringAfterSettingNewBodyReflectsChanges() + { + $this->_message->setBody('BODY1'); + $this->assertRegExp('/BODY1/', $this->_message->toString()); + + $this->_message->setBody('BODY2'); + $this->assertRegExp('/BODY2/', $this->_message->toString()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug76Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug76Test.php new file mode 100644 index 00000000..c2b12cb4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug76Test.php @@ -0,0 +1,82 @@ +<?php + +class Swift_Bug76Test extends \PHPUnit_Framework_TestCase +{ + private $_inputFile; + private $_outputFile; + private $_encoder; + + public function setUp() + { + if (!defined('SWIFT_TMP_DIR') || !is_writable(SWIFT_TMP_DIR)) { + $this->markTestSkipped( + 'Cannot run test without a writable directory to use ('. + 'define SWIFT_TMP_DIR in tests/config.php if you wish to run this test)' + ); + } + + $this->_inputFile = SWIFT_TMP_DIR.'/in.bin'; + file_put_contents($this->_inputFile, ''); + + $this->_outputFile = SWIFT_TMP_DIR.'/out.bin'; + file_put_contents($this->_outputFile, ''); + + $this->_encoder = $this->_createEncoder(); + } + + public function tearDown() + { + unlink($this->_inputFile); + unlink($this->_outputFile); + } + + public function testBase64EncodedLineLengthNeverExceeds76CharactersEvenIfArgsDo() + { + $this->_fillFileWithRandomBytes(1000, $this->_inputFile); + + $os = $this->_createStream($this->_inputFile); + $is = $this->_createStream($this->_outputFile); + + $this->_encoder->encodeByteStream($os, $is, 0, 80); //Exceeds 76 + + $this->assertMaxLineLength(76, $this->_outputFile, + '%s: Line length should not exceed 76 characters' + ); + } + + // -- Custom Assertions + + public function assertMaxLineLength($length, $filePath, $message = '%s') + { + $lines = file($filePath); + foreach ($lines as $line) { + $this->assertTrue((strlen(trim($line)) <= 76), $message); + } + } + + // -- Creation Methods + + private function _fillFileWithRandomBytes($byteCount, $file) + { + // I was going to use dd with if=/dev/random but this way seems more + // cross platform even if a hella expensive!! + + file_put_contents($file, ''); + $fp = fopen($file, 'wb'); + for ($i = 0; $i < $byteCount; ++$i) { + $byteVal = rand(0, 255); + fwrite($fp, pack('i', $byteVal)); + } + fclose($fp); + } + + private function _createEncoder() + { + return new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + } + + private function _createStream($file) + { + return new Swift_ByteStream_FileByteStream($file, true); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/BugFileByteStreamConsecutiveReadCallsTest.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/BugFileByteStreamConsecutiveReadCallsTest.php new file mode 100644 index 00000000..35733ec5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/BugFileByteStreamConsecutiveReadCallsTest.php @@ -0,0 +1,19 @@ +<?php + + +class Swift_FileByteStreamConsecutiveReadCalls extends \PHPUnit_Framework_TestCase +{ + /** + * @test + * @expectedException \Swift_IoException + */ + public function shouldThrowExceptionOnConsecutiveRead() + { + $fbs = new \Swift_ByteStream_FileByteStream('does not exist'); + try { + $fbs->read(100); + } catch (\Swift_IoException $exc) { + $fbs->read(100); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/fixtures/MimeEntityFixture.php b/vendor/swiftmailer/swiftmailer/tests/fixtures/MimeEntityFixture.php new file mode 100644 index 00000000..e2cf86a5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/fixtures/MimeEntityFixture.php @@ -0,0 +1,59 @@ +<?php + +class MimeEntityFixture implements Swift_Mime_MimeEntity +{ + private $level; + private $string; + private $contentType; + + public function __construct($level = null, $string = '', $contentType = null) + { + $this->level = $level; + $this->string = $string; + $this->contentType = $contentType; + } + + public function getNestingLevel() + { + return $this->level; + } + + public function toString() + { + return $this->string; + } + + public function getContentType() + { + return $this->contentType; + } + + // These methods are here to account for the implemented interfaces + public function getId() + { + } + public function getHeaders() + { + } + public function getBody() + { + } + public function setBody($body, $contentType = null) + { + } + public function toByteStream(Swift_InputByteStream $is) + { + } + public function charsetChanged($charset) + { + } + public function encoderChanged(Swift_Mime_ContentEncoder $encoder) + { + } + public function getChildren() + { + } + public function setChildren(array $children) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/smoke.conf.php.default b/vendor/swiftmailer/swiftmailer/tests/smoke.conf.php.default new file mode 100644 index 00000000..0de2763f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/smoke.conf.php.default @@ -0,0 +1,63 @@ +<?php + +/* + Swift Mailer V4 smoke test configuration. + + YOU ONLY NEED TO EDIT THIS FILE IF YOU WISH TO RUN THE SMOKE TESTS. + + The smoke tests are run by default when "All Tests" are run with the + testing suite, however, without configuration options here only the unit tests + will be run and the smoke tests will be skipped. + */ + +/* + Defines: The an address which Swift can send to (it will also send "from" this address). + Recommended: (your own email address?) + */ +define('SWIFT_SMOKE_EMAIL_ADDRESS', 'test@swiftmailer.org'); + +/* + Defines: The specific transport you want to mail with. + Recommended: Any of 'smtp', 'sendmail' or 'mail' + */ +define('SWIFT_SMOKE_TRANSPORT_TYPE', 'smtp'); + +// SMTP-specific settings + +/* + Defines: An SMTP server to connect to + Recommended: smtp.your-isp.com (varies wildly!) + */ +define('SWIFT_SMOKE_SMTP_HOST', 'localhost'); + +/* + Defines: The SMTP port to connect to + Recommended: 25 + */ +define('SWIFT_SMOKE_SMTP_PORT', '4456'); + +/* + Defines: A username to authenticate with SMTP (if needed). + Recommended: (none) + */ +define('SWIFT_SMOKE_SMTP_USER', ''); + +/* + Defines: A password to authenticate with SMTP (if needed). + Recommended: (none) + */ +define('SWIFT_SMOKE_SMTP_PASS', ''); + +/* + Defines: The encryption needed on your SMTP server. + Recommended: (none), or 'tls' or 'ssl' + */ +define('SWIFT_SMOKE_SMTP_ENCRYPTION', ''); + +// Sendmail specific settings + +/* + Defines: The command to use when sending via sendmail + Recommended: /usr/sbin/sendmail -bs (or "-oi -t") + */ +define('SWIFT_SMOKE_SENDMAIL_COMMAND', '/usr/sbin/sendmail -bs'); diff --git a/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/AttachmentSmokeTest.php b/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/AttachmentSmokeTest.php new file mode 100644 index 00000000..c3b61c39 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/AttachmentSmokeTest.php @@ -0,0 +1,33 @@ +<?php + +/** + * @group smoke + */ +class Swift_Smoke_AttachmentSmokeTest extends SwiftMailerSmokeTestCase +{ + private $_attFile; + + public function setUp() + { + parent::setup(); // For skip + $this->_attFile = __DIR__.'/../../../_samples/files/textfile.zip'; + } + + public function testAttachmentSending() + { + $mailer = $this->_getMailer(); + $message = Swift_Message::newInstance() + ->setSubject('[Swift Mailer] AttachmentSmokeTest') + ->setFrom(array(SWIFT_SMOKE_EMAIL_ADDRESS => 'Swift Mailer')) + ->setTo(SWIFT_SMOKE_EMAIL_ADDRESS) + ->setBody('This message should contain an attached ZIP file (named "textfile.zip").'.PHP_EOL. + 'When unzipped, the archive should produce a text file which reads:'.PHP_EOL. + '"This is part of a Swift Mailer v4 smoke test."' + ) + ->attach(Swift_Attachment::fromPath($this->_attFile)) + ; + $this->assertEquals(1, $mailer->send($message), + '%s: The smoke test should send a single message' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/BasicSmokeTest.php b/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/BasicSmokeTest.php new file mode 100644 index 00000000..c7501d46 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/BasicSmokeTest.php @@ -0,0 +1,23 @@ +<?php + +/** + * @group smoke + */ +class Swift_Smoke_BasicSmokeTest extends SwiftMailerSmokeTestCase +{ + public function testBasicSending() + { + $mailer = $this->_getMailer(); + $message = Swift_Message::newInstance() + ->setSubject('[Swift Mailer] BasicSmokeTest') + ->setFrom(array(SWIFT_SMOKE_EMAIL_ADDRESS => 'Swift Mailer')) + ->setTo(SWIFT_SMOKE_EMAIL_ADDRESS) + ->setBody('One, two, three, four, five...'.PHP_EOL. + 'six, seven, eight...' + ) + ; + $this->assertEquals(1, $mailer->send($message), + '%s: The smoke test should send a single message' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/HtmlWithAttachmentSmokeTest.php b/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/HtmlWithAttachmentSmokeTest.php new file mode 100644 index 00000000..dca14341 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/HtmlWithAttachmentSmokeTest.php @@ -0,0 +1,31 @@ +<?php + +/** + * @group smoke + */ +class Swift_Smoke_HtmlWithAttachmentSmokeTest extends SwiftMailerSmokeTestCase +{ + private $_attFile; + + public function setUp() + { + $this->_attFile = __DIR__.'/../../../_samples/files/textfile.zip'; + } + + public function testAttachmentSending() + { + $mailer = $this->_getMailer(); + $message = Swift_Message::newInstance('[Swift Mailer] HtmlWithAttachmentSmokeTest') + ->setFrom(array(SWIFT_SMOKE_EMAIL_ADDRESS => 'Swift Mailer')) + ->setTo(SWIFT_SMOKE_EMAIL_ADDRESS) + ->attach(Swift_Attachment::fromPath($this->_attFile)) + ->setBody('<p>This HTML-formatted message should contain an attached ZIP file (named "textfile.zip").'.PHP_EOL. + 'When unzipped, the archive should produce a text file which reads:</p>'.PHP_EOL. + '<p><q>This is part of a Swift Mailer v4 smoke test.</q></p>', 'text/html' + ) + ; + $this->assertEquals(1, $mailer->send($message), + '%s: The smoke test should send a single message' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/InternationalSmokeTest.php b/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/InternationalSmokeTest.php new file mode 100644 index 00000000..fafd7271 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/InternationalSmokeTest.php @@ -0,0 +1,40 @@ +<?php + +/** + * @group smoke + */ +class Swift_Smoke_InternationalSmokeTest extends SwiftMailerSmokeTestCase +{ + private $_attFile; + + public function setUp() + { + parent::setup(); // For skip + $this->_attFile = __DIR__.'/../../../_samples/files/textfile.zip'; + } + + public function testAttachmentSending() + { + $mailer = $this->_getMailer(); + $message = Swift_Message::newInstance() + ->setCharset('utf-8') + ->setSubject('[Swift Mailer] InternationalSmokeTest (διεθνής)') + ->setFrom(array(SWIFT_SMOKE_EMAIL_ADDRESS => 'ΧÏιστοφοÏου (Swift Mailer)')) + ->setTo(SWIFT_SMOKE_EMAIL_ADDRESS) + ->setBody('This message should contain an attached ZIP file (named "κείμενο, εδάφιο, θÎμα.zip").'.PHP_EOL. + 'When unzipped, the archive should produce a text file which reads:'.PHP_EOL. + '"This is part of a Swift Mailer v4 smoke test."'.PHP_EOL. + PHP_EOL. + 'Following is some arbitrary Greek text:'.PHP_EOL. + 'Δεν βÏÎθηκαν λÎξεις.' + ) + ->attach(Swift_Attachment::fromPath($this->_attFile) + ->setContentType('application/zip') + ->setFilename('κείμενο, εδάφιο, θÎμα.zip') + ) + ; + $this->assertEquals(1, $mailer->send($message), + '%s: The smoke test should send a single message' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/ByteStream/ArrayByteStreamTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/ByteStream/ArrayByteStreamTest.php new file mode 100644 index 00000000..2a50b190 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/ByteStream/ArrayByteStreamTest.php @@ -0,0 +1,203 @@ +<?php + +class Swift_ByteStream_ArrayByteStreamTest extends \PHPUnit_Framework_TestCase +{ + public function testReadingSingleBytesFromBaseInput() + { + $input = array('a', 'b', 'c'); + $bs = $this->_createArrayStream($input); + $output = array(); + while (false !== $bytes = $bs->read(1)) { + $output[] = $bytes; + } + $this->assertEquals($input, $output, + '%s: Bytes read from stream should be the same as bytes in constructor' + ); + } + + public function testReadingMultipleBytesFromBaseInput() + { + $input = array('a', 'b', 'c', 'd'); + $bs = $this->_createArrayStream($input); + $output = array(); + while (false !== $bytes = $bs->read(2)) { + $output[] = $bytes; + } + $this->assertEquals(array('ab', 'cd'), $output, + '%s: Bytes read from stream should be in pairs' + ); + } + + public function testReadingOddOffsetOnLastByte() + { + $input = array('a', 'b', 'c', 'd', 'e'); + $bs = $this->_createArrayStream($input); + $output = array(); + while (false !== $bytes = $bs->read(2)) { + $output[] = $bytes; + } + $this->assertEquals(array('ab', 'cd', 'e'), $output, + '%s: Bytes read from stream should be in pairs except final read' + ); + } + + public function testSettingPointerPartway() + { + $input = array('a', 'b', 'c'); + $bs = $this->_createArrayStream($input); + $bs->setReadPointer(1); + $this->assertEquals('b', $bs->read(1), + '%s: Byte should be second byte since pointer as at offset 1' + ); + } + + public function testResettingPointerAfterExhaustion() + { + $input = array('a', 'b', 'c'); + $bs = $this->_createArrayStream($input); + while (false !== $bs->read(1)); + + $bs->setReadPointer(0); + $this->assertEquals('a', $bs->read(1), + '%s: Byte should be first byte since pointer as at offset 0' + ); + } + + public function testPointerNeverSetsBelowZero() + { + $input = array('a', 'b', 'c'); + $bs = $this->_createArrayStream($input); + + $bs->setReadPointer(-1); + $this->assertEquals('a', $bs->read(1), + '%s: Byte should be first byte since pointer should be at offset 0' + ); + } + + public function testPointerNeverSetsAboveStackSize() + { + $input = array('a', 'b', 'c'); + $bs = $this->_createArrayStream($input); + + $bs->setReadPointer(3); + $this->assertFalse($bs->read(1), + '%s: Stream should be at end and thus return false' + ); + } + + public function testBytesCanBeWrittenToStream() + { + $input = array('a', 'b', 'c'); + $bs = $this->_createArrayStream($input); + + $bs->write('de'); + + $output = array(); + while (false !== $bytes = $bs->read(1)) { + $output[] = $bytes; + } + $this->assertEquals(array('a', 'b', 'c', 'd', 'e'), $output, + '%s: Bytes read from stream should be from initial stack + written' + ); + } + + public function testContentsCanBeFlushed() + { + $input = array('a', 'b', 'c'); + $bs = $this->_createArrayStream($input); + + $bs->flushBuffers(); + + $this->assertFalse($bs->read(1), + '%s: Contents have been flushed so read() should return false' + ); + } + + public function testConstructorCanTakeStringArgument() + { + $bs = $this->_createArrayStream('abc'); + $output = array(); + while (false !== $bytes = $bs->read(1)) { + $output[] = $bytes; + } + $this->assertEquals(array('a', 'b', 'c'), $output, + '%s: Bytes read from stream should be the same as bytes in constructor' + ); + } + + public function testBindingOtherStreamsMirrorsWriteOperations() + { + $bs = $this->_createArrayStream(''); + $is1 = $this->getMockBuilder('Swift_InputByteStream')->getMock(); + $is2 = $this->getMockBuilder('Swift_InputByteStream')->getMock(); + + $is1->expects($this->at(0)) + ->method('write') + ->with('x'); + $is1->expects($this->at(1)) + ->method('write') + ->with('y'); + $is2->expects($this->at(0)) + ->method('write') + ->with('x'); + $is2->expects($this->at(1)) + ->method('write') + ->with('y'); + + $bs->bind($is1); + $bs->bind($is2); + + $bs->write('x'); + $bs->write('y'); + } + + public function testBindingOtherStreamsMirrorsFlushOperations() + { + $bs = $this->_createArrayStream(''); + $is1 = $this->getMockBuilder('Swift_InputByteStream')->getMock(); + $is2 = $this->getMockBuilder('Swift_InputByteStream')->getMock(); + + $is1->expects($this->once()) + ->method('flushBuffers'); + $is2->expects($this->once()) + ->method('flushBuffers'); + + $bs->bind($is1); + $bs->bind($is2); + + $bs->flushBuffers(); + } + + public function testUnbindingStreamPreventsFurtherWrites() + { + $bs = $this->_createArrayStream(''); + $is1 = $this->getMockBuilder('Swift_InputByteStream')->getMock(); + $is2 = $this->getMockBuilder('Swift_InputByteStream')->getMock(); + + $is1->expects($this->at(0)) + ->method('write') + ->with('x'); + $is1->expects($this->at(1)) + ->method('write') + ->with('y'); + $is2->expects($this->once()) + ->method('write') + ->with('x'); + + $bs->bind($is1); + $bs->bind($is2); + + $bs->write('x'); + + $bs->unbind($is2); + + $bs->write('y'); + } + + // -- Creation Methods + + private function _createArrayStream($input) + { + return new Swift_ByteStream_ArrayByteStream($input); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/GenericFixedWidthReaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/GenericFixedWidthReaderTest.php new file mode 100644 index 00000000..3f7a46cf --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/GenericFixedWidthReaderTest.php @@ -0,0 +1,43 @@ +<?php + +class Swift_CharacterReader_GenericFixedWidthReaderTest extends \PHPUnit_Framework_TestCase +{ + public function testInitialByteSizeMatchesWidth() + { + $reader = new Swift_CharacterReader_GenericFixedWidthReader(1); + $this->assertSame(1, $reader->getInitialByteSize()); + + $reader = new Swift_CharacterReader_GenericFixedWidthReader(4); + $this->assertSame(4, $reader->getInitialByteSize()); + } + + public function testValidationValueIsBasedOnOctetCount() + { + $reader = new Swift_CharacterReader_GenericFixedWidthReader(4); + + $this->assertSame( + 1, $reader->validateByteSequence(array(0x01, 0x02, 0x03), 3) + ); //3 octets + + $this->assertSame( + 2, $reader->validateByteSequence(array(0x01, 0x0A), 2) + ); //2 octets + + $this->assertSame( + 3, $reader->validateByteSequence(array(0xFE), 1) + ); //1 octet + + $this->assertSame( + 0, $reader->validateByteSequence(array(0xFE, 0x03, 0x67, 0x9A), 4) + ); //All 4 octets + } + + public function testValidationFailsIfTooManyOctets() + { + $reader = new Swift_CharacterReader_GenericFixedWidthReader(6); + + $this->assertSame(-1, $reader->validateByteSequence( + array(0xFE, 0x03, 0x67, 0x9A, 0x10, 0x09, 0x85), 7 + )); //7 octets + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/UsAsciiReaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/UsAsciiReaderTest.php new file mode 100644 index 00000000..41f8f703 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/UsAsciiReaderTest.php @@ -0,0 +1,52 @@ +<?php + +class Swift_CharacterReader_UsAsciiReaderTest extends \PHPUnit_Framework_TestCase +{ + /* + + for ($c = '', $size = 1; false !== $bytes = $os->read($size); ) { + $c .= $bytes; + $size = $v->validateCharacter($c); + if (-1 == $size) { + throw new Exception( ... invalid char .. ); + } elseif (0 == $size) { + return $c; //next character in $os + } + } + + */ + + private $_reader; + + public function setUp() + { + $this->_reader = new Swift_CharacterReader_UsAsciiReader(); + } + + public function testAllValidAsciiCharactersReturnZero() + { + for ($ordinal = 0x00; $ordinal <= 0x7F; ++$ordinal) { + $this->assertSame( + 0, $this->_reader->validateByteSequence(array($ordinal), 1) + ); + } + } + + public function testMultipleBytesAreInvalid() + { + for ($ordinal = 0x00; $ordinal <= 0x7F; $ordinal += 2) { + $this->assertSame( + -1, $this->_reader->validateByteSequence(array($ordinal, $ordinal + 1), 2) + ); + } + } + + public function testBytesAboveAsciiRangeAreInvalid() + { + for ($ordinal = 0x80; $ordinal <= 0xFF; ++$ordinal) { + $this->assertSame( + -1, $this->_reader->validateByteSequence(array($ordinal), 1) + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/Utf8ReaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/Utf8ReaderTest.php new file mode 100644 index 00000000..34e9c91d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/Utf8ReaderTest.php @@ -0,0 +1,65 @@ +<?php + +class Swift_CharacterReader_Utf8ReaderTest extends \PHPUnit_Framework_TestCase +{ + private $_reader; + + public function setUp() + { + $this->_reader = new Swift_CharacterReader_Utf8Reader(); + } + + public function testLeading7BitOctetCausesReturnZero() + { + for ($ordinal = 0x00; $ordinal <= 0x7F; ++$ordinal) { + $this->assertSame( + 0, $this->_reader->validateByteSequence(array($ordinal), 1) + ); + } + } + + public function testLeadingByteOf2OctetCharCausesReturn1() + { + for ($octet = 0xC0; $octet <= 0xDF; ++$octet) { + $this->assertSame( + 1, $this->_reader->validateByteSequence(array($octet), 1) + ); + } + } + + public function testLeadingByteOf3OctetCharCausesReturn2() + { + for ($octet = 0xE0; $octet <= 0xEF; ++$octet) { + $this->assertSame( + 2, $this->_reader->validateByteSequence(array($octet), 1) + ); + } + } + + public function testLeadingByteOf4OctetCharCausesReturn3() + { + for ($octet = 0xF0; $octet <= 0xF7; ++$octet) { + $this->assertSame( + 3, $this->_reader->validateByteSequence(array($octet), 1) + ); + } + } + + public function testLeadingByteOf5OctetCharCausesReturn4() + { + for ($octet = 0xF8; $octet <= 0xFB; ++$octet) { + $this->assertSame( + 4, $this->_reader->validateByteSequence(array($octet), 1) + ); + } + } + + public function testLeadingByteOf6OctetCharCausesReturn5() + { + for ($octet = 0xFC; $octet <= 0xFD; ++$octet) { + $this->assertSame( + 5, $this->_reader->validateByteSequence(array($octet), 1) + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterStream/ArrayCharacterStreamTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterStream/ArrayCharacterStreamTest.php new file mode 100644 index 00000000..cbd412bd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterStream/ArrayCharacterStreamTest.php @@ -0,0 +1,360 @@ +<?php + +class Swift_CharacterStream_ArrayCharacterStreamTest extends \SwiftMailerTestCase +{ + public function testValidatorAlgorithmOnImportString() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importString(pack('C*', + 0xD0, 0x94, + 0xD0, 0xB6, + 0xD0, 0xBE, + 0xD1, 0x8D, + 0xD0, 0xBB, + 0xD0, 0xB0 + ) + ); + } + + public function testCharactersWrittenUseValidator() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $stream->write(pack('C*', + 0xD0, 0xBB, + 0xD1, 0x8E, + 0xD0, 0xB1, + 0xD1, 0x8B, + 0xD1, 0x85 + ) + ); + } + + public function testReadCharactersAreInTact() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + //String + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + //Stream + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $stream->write(pack('C*', + 0xD0, 0xBB, + 0xD1, 0x8E, + 0xD0, 0xB1, + 0xD1, 0x8B, + 0xD1, 0x85 + ) + ); + + $this->assertIdenticalBinary(pack('C*', 0xD0, 0x94), $stream->read(1)); + $this->assertIdenticalBinary( + pack('C*', 0xD0, 0xB6, 0xD0, 0xBE), $stream->read(2) + ); + $this->assertIdenticalBinary(pack('C*', 0xD0, 0xBB), $stream->read(1)); + $this->assertIdenticalBinary( + pack('C*', 0xD1, 0x8E, 0xD0, 0xB1, 0xD1, 0x8B), $stream->read(3) + ); + $this->assertIdenticalBinary(pack('C*', 0xD1, 0x85), $stream->read(1)); + + $this->assertFalse($stream->read(1)); + } + + public function testCharactersCanBeReadAsByteArrays() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + //String + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + //Stream + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $stream->write(pack('C*', + 0xD0, 0xBB, + 0xD1, 0x8E, + 0xD0, 0xB1, + 0xD1, 0x8B, + 0xD1, 0x85 + ) + ); + + $this->assertEquals(array(0xD0, 0x94), $stream->readBytes(1)); + $this->assertEquals(array(0xD0, 0xB6, 0xD0, 0xBE), $stream->readBytes(2)); + $this->assertEquals(array(0xD0, 0xBB), $stream->readBytes(1)); + $this->assertEquals( + array(0xD1, 0x8E, 0xD0, 0xB1, 0xD1, 0x8B), $stream->readBytes(3) + ); + $this->assertEquals(array(0xD1, 0x85), $stream->readBytes(1)); + + $this->assertFalse($stream->readBytes(1)); + } + + public function testRequestingLargeCharCountPastEndOfStream() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $this->assertIdenticalBinary(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE), + $stream->read(100) + ); + + $this->assertFalse($stream->read(1)); + } + + public function testRequestingByteArrayCountPastEndOfStream() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $this->assertEquals(array(0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE), + $stream->readBytes(100) + ); + + $this->assertFalse($stream->readBytes(1)); + } + + public function testPointerOffsetCanBeSet() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $this->assertIdenticalBinary(pack('C*', 0xD0, 0x94), $stream->read(1)); + + $stream->setPointer(0); + + $this->assertIdenticalBinary(pack('C*', 0xD0, 0x94), $stream->read(1)); + + $stream->setPointer(2); + + $this->assertIdenticalBinary(pack('C*', 0xD0, 0xBE), $stream->read(1)); + } + + public function testContentsCanBeFlushed() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $stream->flushContents(); + + $this->assertFalse($stream->read(1)); + } + + public function testByteStreamCanBeImportingUsesValidator() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + $os = $this->_getByteStream(); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $os->shouldReceive('setReadPointer') + ->between(0, 1) + ->with(0); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xD0)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0x94)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xD0)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xB6)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xD0)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xBE)); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importByteStream($os); + } + + public function testImportingStreamProducesCorrectCharArray() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + $os = $this->_getByteStream(); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $os->shouldReceive('setReadPointer') + ->between(0, 1) + ->with(0); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xD0)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0x94)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xD0)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xB6)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xD0)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xBE)); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importByteStream($os); + + $this->assertIdenticalBinary(pack('C*', 0xD0, 0x94), $stream->read(1)); + $this->assertIdenticalBinary(pack('C*', 0xD0, 0xB6), $stream->read(1)); + $this->assertIdenticalBinary(pack('C*', 0xD0, 0xBE), $stream->read(1)); + + $this->assertFalse($stream->read(1)); + } + + public function testAlgorithmWithFixedWidthCharsets() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(2); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1, 0x8D), 2); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0, 0xBB), 2); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0, 0xB0), 2); + + $stream = new Swift_CharacterStream_ArrayCharacterStream( + $factory, 'utf-8' + ); + $stream->importString(pack('C*', 0xD1, 0x8D, 0xD0, 0xBB, 0xD0, 0xB0)); + + $this->assertIdenticalBinary(pack('C*', 0xD1, 0x8D), $stream->read(1)); + $this->assertIdenticalBinary(pack('C*', 0xD0, 0xBB), $stream->read(1)); + $this->assertIdenticalBinary(pack('C*', 0xD0, 0xB0), $stream->read(1)); + + $this->assertFalse($stream->read(1)); + } + + // -- Creation methods + + private function _getReader() + { + return $this->getMockery('Swift_CharacterReader'); + } + + private function _getFactory($reader) + { + $factory = $this->getMockery('Swift_CharacterReaderFactory'); + $factory->shouldReceive('getReaderFor') + ->zeroOrMoreTimes() + ->with('utf-8') + ->andReturn($reader); + + return $factory; + } + + private function _getByteStream() + { + return $this->getMockery('Swift_OutputByteStream'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/DependencyContainerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/DependencyContainerTest.php new file mode 100644 index 00000000..70690e2d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/DependencyContainerTest.php @@ -0,0 +1,175 @@ +<?php + +class One +{ + public $arg1; + public $arg2; + public function __construct($arg1 = null, $arg2 = null) + { + $this->arg1 = $arg1; + $this->arg2 = $arg2; + } +} + +class Swift_DependencyContainerTest extends \PHPUnit_Framework_TestCase +{ + private $_container; + + public function setUp() + { + $this->_container = new Swift_DependencyContainer(); + } + + public function testRegisterAndLookupValue() + { + $this->_container->register('foo')->asValue('bar'); + $this->assertEquals('bar', $this->_container->lookup('foo')); + } + + public function testHasReturnsTrueForRegisteredValue() + { + $this->_container->register('foo')->asValue('bar'); + $this->assertTrue($this->_container->has('foo')); + } + + public function testHasReturnsFalseForUnregisteredValue() + { + $this->assertFalse($this->_container->has('foo')); + } + + public function testRegisterAndLookupNewInstance() + { + $this->_container->register('one')->asNewInstanceOf('One'); + $this->assertInstanceof('One', $this->_container->lookup('one')); + } + + public function testHasReturnsTrueForRegisteredInstance() + { + $this->_container->register('one')->asNewInstanceOf('One'); + $this->assertTrue($this->_container->has('one')); + } + + public function testNewInstanceIsAlwaysNew() + { + $this->_container->register('one')->asNewInstanceOf('One'); + $a = $this->_container->lookup('one'); + $b = $this->_container->lookup('one'); + $this->assertEquals($a, $b); + } + + public function testRegisterAndLookupSharedInstance() + { + $this->_container->register('one')->asSharedInstanceOf('One'); + $this->assertInstanceof('One', $this->_container->lookup('one')); + } + + public function testHasReturnsTrueForSharedInstance() + { + $this->_container->register('one')->asSharedInstanceOf('One'); + $this->assertTrue($this->_container->has('one')); + } + + public function testMultipleSharedInstancesAreSameInstance() + { + $this->_container->register('one')->asSharedInstanceOf('One'); + $a = $this->_container->lookup('one'); + $b = $this->_container->lookup('one'); + $this->assertEquals($a, $b); + } + + public function testNewInstanceWithDependencies() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('one')->asNewInstanceOf('One') + ->withDependencies(array('foo')); + $obj = $this->_container->lookup('one'); + $this->assertSame('FOO', $obj->arg1); + } + + public function testNewInstanceWithMultipleDependencies() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('bar')->asValue(42); + $this->_container->register('one')->asNewInstanceOf('One') + ->withDependencies(array('foo', 'bar')); + $obj = $this->_container->lookup('one'); + $this->assertSame('FOO', $obj->arg1); + $this->assertSame(42, $obj->arg2); + } + + public function testNewInstanceWithInjectedObjects() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('one')->asNewInstanceOf('One'); + $this->_container->register('two')->asNewInstanceOf('One') + ->withDependencies(array('one', 'foo')); + $obj = $this->_container->lookup('two'); + $this->assertEquals($this->_container->lookup('one'), $obj->arg1); + $this->assertSame('FOO', $obj->arg2); + } + + public function testNewInstanceWithAddConstructorValue() + { + $this->_container->register('one')->asNewInstanceOf('One') + ->addConstructorValue('x') + ->addConstructorValue(99); + $obj = $this->_container->lookup('one'); + $this->assertSame('x', $obj->arg1); + $this->assertSame(99, $obj->arg2); + } + + public function testNewInstanceWithAddConstructorLookup() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('bar')->asValue(42); + $this->_container->register('one')->asNewInstanceOf('One') + ->addConstructorLookup('foo') + ->addConstructorLookup('bar'); + + $obj = $this->_container->lookup('one'); + $this->assertSame('FOO', $obj->arg1); + $this->assertSame(42, $obj->arg2); + } + + public function testResolvedDependenciesCanBeLookedUp() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('one')->asNewInstanceOf('One'); + $this->_container->register('two')->asNewInstanceOf('One') + ->withDependencies(array('one', 'foo')); + $deps = $this->_container->createDependenciesFor('two'); + $this->assertEquals( + array($this->_container->lookup('one'), 'FOO'), $deps + ); + } + + public function testArrayOfDependenciesCanBeSpecified() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('one')->asNewInstanceOf('One'); + $this->_container->register('two')->asNewInstanceOf('One') + ->withDependencies(array(array('one', 'foo'), 'foo')); + + $obj = $this->_container->lookup('two'); + $this->assertEquals(array($this->_container->lookup('one'), 'FOO'), $obj->arg1); + $this->assertSame('FOO', $obj->arg2); + } + + public function testAliasCanBeSet() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('bar')->asAliasOf('foo'); + + $this->assertSame('FOO', $this->_container->lookup('bar')); + } + + public function testAliasOfAliasCanBeSet() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('bar')->asAliasOf('foo'); + $this->_container->register('zip')->asAliasOf('bar'); + $this->_container->register('button')->asAliasOf('zip'); + + $this->assertSame('FOO', $this->_container->lookup('button')); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Base64EncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Base64EncoderTest.php new file mode 100644 index 00000000..1e712fe5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Base64EncoderTest.php @@ -0,0 +1,173 @@ +<?php + +class Swift_Encoder_Base64EncoderTest extends \PHPUnit_Framework_TestCase +{ + private $_encoder; + + public function setUp() + { + $this->_encoder = new Swift_Encoder_Base64Encoder(); + } + + /* + There's really no point in testing the entire base64 encoding to the + level QP encoding has been tested. base64_encode() has been in PHP for + years. + */ + + public function testInputOutputRatioIs3to4Bytes() + { + /* + RFC 2045, 6.8 + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + */ + + $this->assertEquals( + 'MTIz', $this->_encoder->encodeString('123'), + '%s: 3 bytes of input should yield 4 bytes of output' + ); + $this->assertEquals( + 'MTIzNDU2', $this->_encoder->encodeString('123456'), + '%s: 6 bytes in input should yield 8 bytes of output' + ); + $this->assertEquals( + 'MTIzNDU2Nzg5', $this->_encoder->encodeString('123456789'), + '%s: 9 bytes in input should yield 12 bytes of output' + ); + } + + public function testPadLength() + { + /* + RFC 2045, 6.8 + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a body. When fewer than 24 input bits + are available in an input group, zero bits are added (on the right) + to form an integral number of 6-bit groups. Padding at the end of + the data is performed using the "=" character. Since all base64 + input is an integral number of octets, only the following cases can + arise: (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded output will be + an integral multiple of 4 characters with no "=" padding, (2) the + final quantum of encoding input is exactly 8 bits; here, the final + unit of encoded output will be two characters followed by two "=" + padding characters, or (3) the final quantum of encoding input is + exactly 16 bits; here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + + for ($i = 0; $i < 30; ++$i) { + $input = pack('C', rand(0, 255)); + $this->assertRegExp( + '~^[a-zA-Z0-9/\+]{2}==$~', $this->_encoder->encodeString($input), + '%s: A single byte should have 2 bytes of padding' + ); + } + + for ($i = 0; $i < 30; ++$i) { + $input = pack('C*', rand(0, 255), rand(0, 255)); + $this->assertRegExp( + '~^[a-zA-Z0-9/\+]{3}=$~', $this->_encoder->encodeString($input), + '%s: Two bytes should have 1 byte of padding' + ); + } + + for ($i = 0; $i < 30; ++$i) { + $input = pack('C*', rand(0, 255), rand(0, 255), rand(0, 255)); + $this->assertRegExp( + '~^[a-zA-Z0-9/\+]{4}$~', $this->_encoder->encodeString($input), + '%s: Three bytes should have no padding' + ); + } + } + + public function testMaximumLineLengthIs76Characters() + { + /* + The encoded output stream must be represented in lines of no more + than 76 characters each. All line breaks or other characters not + found in Table 1 must be ignored by decoding software. + */ + + $input = + 'abcdefghijklmnopqrstuvwxyz'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + '1234567890'. + 'abcdefghijklmnopqrstuvwxyz'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + '1234567890'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + $output = + 'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQk'.//38 + 'NERUZHSElKS0xNTk9QUVJTVFVWV1hZWjEyMzQ1'."\r\n".//76 * + 'Njc4OTBhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3'.//38 + 'h5ekFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFla'."\r\n".//76 * + 'MTIzNDU2Nzg5MEFCQ0RFRkdISUpLTE1OT1BRUl'.//38 + 'NUVVZXWFla'; //48 + + $this->assertEquals( + $output, $this->_encoder->encodeString($input), + '%s: Lines should be no more than 76 characters' + ); + } + + public function testMaximumLineLengthCanBeSpecified() + { + $input = + 'abcdefghijklmnopqrstuvwxyz'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + '1234567890'. + 'abcdefghijklmnopqrstuvwxyz'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + '1234567890'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + $output = + 'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQk'.//38 + 'NERUZHSElKS0'."\r\n".//50 * + 'xNTk9QUVJTVFVWV1hZWjEyMzQ1Njc4OTBhYmNk'.//38 + 'ZWZnaGlqa2xt'."\r\n".//50 * + 'bm9wcXJzdHV2d3h5ekFCQ0RFRkdISUpLTE1OT1'.//38 + 'BRUlNUVVZXWF'."\r\n".//50 * + 'laMTIzNDU2Nzg5MEFCQ0RFRkdISUpLTE1OT1BR'.//38 + 'UlNUVVZXWFla'; //50 * + + $this->assertEquals( + $output, $this->_encoder->encodeString($input, 0, 50), + '%s: Lines should be no more than 100 characters' + ); + } + + public function testFirstLineLengthCanBeDifferent() + { + $input = + 'abcdefghijklmnopqrstuvwxyz'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + '1234567890'. + 'abcdefghijklmnopqrstuvwxyz'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + '1234567890'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + $output = + 'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQk'.//38 + 'NERUZHSElKS0xNTk9QU'."\r\n".//57 * + 'VJTVFVWV1hZWjEyMzQ1Njc4OTBhYmNkZWZnaGl'.//38 + 'qa2xtbm9wcXJzdHV2d3h5ekFCQ0RFRkdISUpLT'."\r\n".//76 * + 'E1OT1BRUlNUVVZXWFlaMTIzNDU2Nzg5MEFCQ0R'.//38 + 'FRkdISUpLTE1OT1BRUlNUVVZXWFla'; //67 + + $this->assertEquals( + $output, $this->_encoder->encodeString($input, 19), + '%s: First line offset is 19 so first line should be 57 chars long' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/QpEncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/QpEncoderTest.php new file mode 100644 index 00000000..0bf4d181 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/QpEncoderTest.php @@ -0,0 +1,402 @@ +<?php + +class Swift_Encoder_QpEncoderTest extends \SwiftMailerTestCase +{ + /* -- RFC 2045, 6.7 -- + (1) (General 8bit representation) Any octet, except a CR or + LF that is part of a CRLF line break of the canonical + (standard) form of the data being encoded, may be + represented by an "=" followed by a two digit + hexadecimal representation of the octet's value. The + digits of the hexadecimal alphabet, for this purpose, + are "0123456789ABCDEF". Uppercase letters must be + used; lowercase letters are not allowed. Thus, for + example, the decimal value 12 (US-ASCII form feed) can + be represented by "=0C", and the decimal value 61 (US- + ASCII EQUAL SIGN) can be represented by "=3D". This + rule must be followed except when the following rules + allow an alternative encoding. + */ + + public function testPermittedCharactersAreNotEncoded() + { + /* -- RFC 2045, 6.7 -- + (2) (Literal representation) Octets with decimal values of + 33 through 60 inclusive, and 62 through 126, inclusive, + MAY be represented as the US-ASCII characters which + correspond to those octets (EXCLAMATION POINT through + LESS THAN, and GREATER THAN through TILDE, + respectively). + */ + + foreach (array_merge(range(33, 60), range(62, 126)) as $ordinal) { + $char = chr($ordinal); + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($char); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($ordinal)); + $charStream->shouldReceive('readBytes') + ->atLeast()->times(1) + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + + $this->assertIdenticalBinary($char, $encoder->encodeString($char)); + } + } + + public function testWhiteSpaceAtLineEndingIsEncoded() + { + /* -- RFC 2045, 6.7 -- + (3) (White Space) Octets with values of 9 and 32 MAY be + represented as US-ASCII TAB (HT) and SPACE characters, + respectively, but MUST NOT be so represented at the end + of an encoded line. Any TAB (HT) or SPACE characters + on an encoded line MUST thus be followed on that line + by a printable character. In particular, an "=" at the + end of an encoded line, indicating a soft line break + (see rule #5) may follow one or more TAB (HT) or SPACE + characters. It follows that an octet with decimal + value 9 or 32 appearing at the end of an encoded line + must be represented according to Rule #1. This rule is + necessary because some MTAs (Message Transport Agents, + programs which transport messages from one user to + another, or perform a portion of such transfers) are + known to pad lines of text with SPACEs, and others are + known to remove "white space" characters from the end + of a line. Therefore, when decoding a Quoted-Printable + body, any trailing white space on a line must be + deleted, as it will necessarily have been added by + intermediate transport agents. + */ + + $HT = chr(0x09); //9 + $SPACE = chr(0x20); //32 + + //HT + $string = 'a'.$HT.$HT."\r\n".'b'; + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x09)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x09)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + $this->assertEquals( + 'a'.$HT.'=09'."\r\n".'b', + $encoder->encodeString($string) + ); + + //SPACE + $string = 'a'.$SPACE.$SPACE."\r\n".'b'; + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x20)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x20)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + $this->assertEquals( + 'a'.$SPACE.'=20'."\r\n".'b', + $encoder->encodeString($string) + ); + } + + public function testCRLFIsLeftAlone() + { + /* + (4) (Line Breaks) A line break in a text body, represented + as a CRLF sequence in the text canonical form, must be + represented by a (RFC 822) line break, which is also a + CRLF sequence, in the Quoted-Printable encoding. Since + the canonical representation of media types other than + text do not generally include the representation of + line breaks as CRLF sequences, no hard line breaks + (i.e. line breaks that are intended to be meaningful + and to be displayed to the user) can occur in the + quoted-printable encoding of such types. Sequences + like "=0D", "=0A", "=0A=0D" and "=0D=0A" will routinely + appear in non-text data represented in quoted- + printable, of course. + + Note that many implementations may elect to encode the + local representation of various content types directly + rather than converting to canonical form first, + encoding, and then converting back to local + representation. In particular, this may apply to plain + text material on systems that use newline conventions + other than a CRLF terminator sequence. Such an + implementation optimization is permissible, but only + when the combined canonicalization-encoding step is + equivalent to performing the three steps separately. + */ + + $string = 'a'."\r\n".'b'."\r\n".'c'."\r\n"; + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('c'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes')->once()->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + $this->assertEquals($string, $encoder->encodeString($string)); + } + + public function testLinesLongerThan76CharactersAreSoftBroken() + { + /* + (5) (Soft Line Breaks) The Quoted-Printable encoding + REQUIRES that encoded lines be no more than 76 + characters long. If longer lines are to be encoded + with the Quoted-Printable encoding, "soft" line breaks + must be used. An equal sign as the last character on a + encoded line indicates such a non-significant ("soft") + line break in the encoded text. + */ + + $input = str_repeat('a', 140); + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($input); + + $output = ''; + for ($i = 0; $i < 140; ++$i) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + + if (75 == $i) { + $output .= "=\r\n"; + } + $output .= 'a'; + } + + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + $this->assertEquals($output, $encoder->encodeString($input)); + } + + public function testMaxLineLengthCanBeSpecified() + { + $input = str_repeat('a', 100); + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($input); + + $output = ''; + for ($i = 0; $i < 100; ++$i) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + + if (53 == $i) { + $output .= "=\r\n"; + } + $output .= 'a'; + } + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + $this->assertEquals($output, $encoder->encodeString($input, 0, 54)); + } + + public function testBytesBelowPermittedRangeAreEncoded() + { + /* + According to Rule (1 & 2) + */ + + foreach (range(0, 32) as $ordinal) { + $char = chr($ordinal); + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($char); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($ordinal)); + $charStream->shouldReceive('readBytes') + ->atLeast()->times(1) + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + + $this->assertEquals( + sprintf('=%02X', $ordinal), $encoder->encodeString($char) + ); + } + } + + public function testDecimalByte61IsEncoded() + { + /* + According to Rule (1 & 2) + */ + + $char = '='; + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($char); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(61)); + $charStream->shouldReceive('readBytes') + ->atLeast()->times(1) + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + + $this->assertEquals('=3D', $encoder->encodeString('=')); + } + + public function testBytesAbovePermittedRangeAreEncoded() + { + /* + According to Rule (1 & 2) + */ + + foreach (range(127, 255) as $ordinal) { + $char = chr($ordinal); + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($char); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($ordinal)); + $charStream->shouldReceive('readBytes') + ->atLeast()->times(1) + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + + $this->assertEquals( + sprintf('=%02X', $ordinal), $encoder->encodeString($char) + ); + } + } + + public function testFirstLineLengthCanBeDifferent() + { + $input = str_repeat('a', 140); + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($input); + + $output = ''; + for ($i = 0; $i < 140; ++$i) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + + if (53 == $i || 53 + 75 == $i) { + $output .= "=\r\n"; + } + $output .= 'a'; + } + + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + $this->assertEquals( + $output, $encoder->encodeString($input, 22), + '%s: First line should start at offset 22 so can only have max length 54' + ); + } + + public function testTextIsPreWrapped() + { + $encoder = $this->createEncoder(); + + $input = str_repeat('a', 70)."\r\n". + str_repeat('a', 70)."\r\n". + str_repeat('a', 70); + + $this->assertEquals( + $input, $encoder->encodeString($input) + ); + } + + // -- Creation methods + + private function _createCharStream() + { + return $this->getMockery('Swift_CharacterStream')->shouldIgnoreMissing(); + } + + private function createEncoder() + { + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $charStream = new Swift_CharacterStream_NgCharacterStream($factory, 'utf-8'); + + return new Swift_Encoder_QpEncoder($charStream); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Rfc2231EncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Rfc2231EncoderTest.php new file mode 100644 index 00000000..28eae6f8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Rfc2231EncoderTest.php @@ -0,0 +1,141 @@ +<?php + +class Swift_Encoder_Rfc2231EncoderTest extends \SwiftMailerTestCase +{ + private $_rfc2045Token = '/^[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+$/D'; + + /* -- + This algorithm is described in RFC 2231, but is barely touched upon except + for mentioning bytes can be represented as their octet values (e.g. %20 for + the SPACE character). + + The tests here focus on how to use that representation to always generate text + which matches RFC 2045's definition of "token". + */ + + public function testEncodingAsciiCharactersProducesValidToken() + { + $charStream = $this->getMockery('Swift_CharacterStream'); + + $string = ''; + foreach (range(0x00, 0x7F) as $octet) { + $char = pack('C', $octet); + $string .= $char; + $charStream->shouldReceive('read') + ->once() + ->andReturn($char); + } + + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + $charStream->shouldReceive('read') + ->atLeast()->times(1) + ->andReturn(false); + + $encoder = new Swift_Encoder_Rfc2231Encoder($charStream); + $encoded = $encoder->encodeString($string); + + foreach (explode("\r\n", $encoded) as $line) { + $this->assertRegExp($this->_rfc2045Token, $line, + '%s: Encoder should always return a valid RFC 2045 token.'); + } + } + + public function testEncodingNonAsciiCharactersProducesValidToken() + { + $charStream = $this->getMockery('Swift_CharacterStream'); + + $string = ''; + foreach (range(0x80, 0xFF) as $octet) { + $char = pack('C', $octet); + $string .= $char; + $charStream->shouldReceive('read') + ->once() + ->andReturn($char); + } + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + $charStream->shouldReceive('read') + ->atLeast()->times(1) + ->andReturn(false); + $encoder = new Swift_Encoder_Rfc2231Encoder($charStream); + + $encoded = $encoder->encodeString($string); + + foreach (explode("\r\n", $encoded) as $line) { + $this->assertRegExp($this->_rfc2045Token, $line, + '%s: Encoder should always return a valid RFC 2045 token.'); + } + } + + public function testMaximumLineLengthCanBeSet() + { + $charStream = $this->getMockery('Swift_CharacterStream'); + + $string = ''; + for ($x = 0; $x < 200; ++$x) { + $char = 'a'; + $string .= $char; + $charStream->shouldReceive('read') + ->once() + ->andReturn($char); + } + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + $charStream->shouldReceive('read') + ->atLeast()->times(1) + ->andReturn(false); + $encoder = new Swift_Encoder_Rfc2231Encoder($charStream); + + $encoded = $encoder->encodeString($string, 0, 75); + + $this->assertEquals( + str_repeat('a', 75)."\r\n". + str_repeat('a', 75)."\r\n". + str_repeat('a', 50), + $encoded, + '%s: Lines should be wrapped at each 75 characters' + ); + } + + public function testFirstLineCanHaveShorterLength() + { + $charStream = $this->getMockery('Swift_CharacterStream'); + + $string = ''; + for ($x = 0; $x < 200; ++$x) { + $char = 'a'; + $string .= $char; + $charStream->shouldReceive('read') + ->once() + ->andReturn($char); + } + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + $charStream->shouldReceive('read') + ->atLeast()->times(1) + ->andReturn(false); + $encoder = new Swift_Encoder_Rfc2231Encoder($charStream); + $encoded = $encoder->encodeString($string, 25, 75); + + $this->assertEquals( + str_repeat('a', 50)."\r\n". + str_repeat('a', 75)."\r\n". + str_repeat('a', 75), + $encoded, + '%s: First line should be 25 bytes shorter than the others.' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/CommandEventTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/CommandEventTest.php new file mode 100644 index 00000000..3849e09e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/CommandEventTest.php @@ -0,0 +1,36 @@ +<?php + +class Swift_Events_CommandEventTest extends \PHPUnit_Framework_TestCase +{ + public function testCommandCanBeFetchedByGetter() + { + $evt = $this->_createEvent($this->_createTransport(), "FOO\r\n"); + $this->assertEquals("FOO\r\n", $evt->getCommand()); + } + + public function testSuccessCodesCanBeFetchedViaGetter() + { + $evt = $this->_createEvent($this->_createTransport(), "FOO\r\n", array(250)); + $this->assertEquals(array(250), $evt->getSuccessCodes()); + } + + public function testSourceIsBuffer() + { + $transport = $this->_createTransport(); + $evt = $this->_createEvent($transport, "FOO\r\n"); + $ref = $evt->getSource(); + $this->assertEquals($transport, $ref); + } + + // -- Creation Methods + + private function _createEvent(Swift_Transport $source, $command, $successCodes = array()) + { + return new Swift_Events_CommandEvent($source, $command, $successCodes); + } + + private function _createTransport() + { + return $this->getMockBuilder('Swift_Transport')->getMock(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/EventObjectTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/EventObjectTest.php new file mode 100644 index 00000000..62a91be5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/EventObjectTest.php @@ -0,0 +1,34 @@ +<?php + +class Swift_Events_EventObjectTest extends \PHPUnit_Framework_TestCase +{ + public function testEventSourceCanBeReturnedViaGetter() + { + $source = new stdClass(); + $evt = $this->_createEvent($source); + $ref = $evt->getSource(); + $this->assertEquals($source, $ref); + } + + public function testEventDoesNotHaveCancelledBubbleWhenNew() + { + $source = new stdClass(); + $evt = $this->_createEvent($source); + $this->assertFalse($evt->bubbleCancelled()); + } + + public function testBubbleCanBeCancelledInEvent() + { + $source = new stdClass(); + $evt = $this->_createEvent($source); + $evt->cancelBubble(); + $this->assertTrue($evt->bubbleCancelled()); + } + + // -- Creation Methods + + private function _createEvent($source) + { + return new Swift_Events_EventObject($source); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/ResponseEventTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/ResponseEventTest.php new file mode 100644 index 00000000..1028e8e9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/ResponseEventTest.php @@ -0,0 +1,40 @@ +<?php + +class Swift_Events_ResponseEventTest extends \PHPUnit_Framework_TestCase +{ + public function testResponseCanBeFetchViaGetter() + { + $evt = $this->_createEvent($this->_createTransport(), "250 Ok\r\n", true); + $this->assertEquals("250 Ok\r\n", $evt->getResponse(), + '%s: Response should be available via getResponse()' + ); + } + + public function testResultCanBeFetchedViaGetter() + { + $evt = $this->_createEvent($this->_createTransport(), "250 Ok\r\n", false); + $this->assertFalse($evt->isValid(), + '%s: Result should be checkable via isValid()' + ); + } + + public function testSourceIsBuffer() + { + $transport = $this->_createTransport(); + $evt = $this->_createEvent($transport, "250 Ok\r\n", true); + $ref = $evt->getSource(); + $this->assertEquals($transport, $ref); + } + + // -- Creation Methods + + private function _createEvent(Swift_Transport $source, $response, $result) + { + return new Swift_Events_ResponseEvent($source, $response, $result); + } + + private function _createTransport() + { + return $this->getMockBuilder('Swift_Transport')->getMock(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SendEventTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SendEventTest.php new file mode 100644 index 00000000..8a3fe92f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SendEventTest.php @@ -0,0 +1,99 @@ +<?php + +class Swift_Events_SendEventTest extends \PHPUnit_Framework_TestCase +{ + public function testMessageCanBeFetchedViaGetter() + { + $message = $this->_createMessage(); + $transport = $this->_createTransport(); + + $evt = $this->_createEvent($transport, $message); + + $ref = $evt->getMessage(); + $this->assertEquals($message, $ref, + '%s: Message should be returned from getMessage()' + ); + } + + public function testTransportCanBeFetchViaGetter() + { + $message = $this->_createMessage(); + $transport = $this->_createTransport(); + + $evt = $this->_createEvent($transport, $message); + + $ref = $evt->getTransport(); + $this->assertEquals($transport, $ref, + '%s: Transport should be returned from getTransport()' + ); + } + + public function testTransportCanBeFetchViaGetSource() + { + $message = $this->_createMessage(); + $transport = $this->_createTransport(); + + $evt = $this->_createEvent($transport, $message); + + $ref = $evt->getSource(); + $this->assertEquals($transport, $ref, + '%s: Transport should be returned from getSource()' + ); + } + + public function testResultCanBeSetAndGet() + { + $message = $this->_createMessage(); + $transport = $this->_createTransport(); + + $evt = $this->_createEvent($transport, $message); + + $evt->setResult( + Swift_Events_SendEvent::RESULT_SUCCESS | Swift_Events_SendEvent::RESULT_TENTATIVE + ); + + $this->assertTrue((bool) ($evt->getResult() & Swift_Events_SendEvent::RESULT_SUCCESS)); + $this->assertTrue((bool) ($evt->getResult() & Swift_Events_SendEvent::RESULT_TENTATIVE)); + } + + public function testFailedRecipientsCanBeSetAndGet() + { + $message = $this->_createMessage(); + $transport = $this->_createTransport(); + + $evt = $this->_createEvent($transport, $message); + + $evt->setFailedRecipients(array('foo@bar', 'zip@button')); + + $this->assertEquals(array('foo@bar', 'zip@button'), $evt->getFailedRecipients(), + '%s: FailedRecipients should be returned from getter' + ); + } + + public function testFailedRecipientsGetsPickedUpCorrectly() + { + $message = $this->_createMessage(); + $transport = $this->_createTransport(); + + $evt = $this->_createEvent($transport, $message); + $this->assertEquals(array(), $evt->getFailedRecipients()); + } + + // -- Creation Methods + + private function _createEvent(Swift_Transport $source, + Swift_Mime_Message $message) + { + return new Swift_Events_SendEvent($source, $message); + } + + private function _createTransport() + { + return $this->getMockBuilder('Swift_Transport')->getMock(); + } + + private function _createMessage() + { + return $this->getMockBuilder('Swift_Mime_Message')->getMock(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SimpleEventDispatcherTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SimpleEventDispatcherTest.php new file mode 100644 index 00000000..fdc9c138 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SimpleEventDispatcherTest.php @@ -0,0 +1,142 @@ +<?php + +class Swift_Events_SimpleEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + private $_dispatcher; + + public function setUp() + { + $this->_dispatcher = new Swift_Events_SimpleEventDispatcher(); + } + + public function testSendEventCanBeCreated() + { + $transport = $this->getMockBuilder('Swift_Transport')->getMock(); + $message = $this->getMockBuilder('Swift_Mime_Message')->getMock(); + $evt = $this->_dispatcher->createSendEvent($transport, $message); + $this->assertInstanceof('Swift_Events_SendEvent', $evt); + $this->assertSame($message, $evt->getMessage()); + $this->assertSame($transport, $evt->getTransport()); + } + + public function testCommandEventCanBeCreated() + { + $buf = $this->getMockBuilder('Swift_Transport')->getMock(); + $evt = $this->_dispatcher->createCommandEvent($buf, "FOO\r\n", array(250)); + $this->assertInstanceof('Swift_Events_CommandEvent', $evt); + $this->assertSame($buf, $evt->getSource()); + $this->assertEquals("FOO\r\n", $evt->getCommand()); + $this->assertEquals(array(250), $evt->getSuccessCodes()); + } + + public function testResponseEventCanBeCreated() + { + $buf = $this->getMockBuilder('Swift_Transport')->getMock(); + $evt = $this->_dispatcher->createResponseEvent($buf, "250 Ok\r\n", true); + $this->assertInstanceof('Swift_Events_ResponseEvent', $evt); + $this->assertSame($buf, $evt->getSource()); + $this->assertEquals("250 Ok\r\n", $evt->getResponse()); + $this->assertTrue($evt->isValid()); + } + + public function testTransportChangeEventCanBeCreated() + { + $transport = $this->getMockBuilder('Swift_Transport')->getMock(); + $evt = $this->_dispatcher->createTransportChangeEvent($transport); + $this->assertInstanceof('Swift_Events_TransportChangeEvent', $evt); + $this->assertSame($transport, $evt->getSource()); + } + + public function testTransportExceptionEventCanBeCreated() + { + $transport = $this->getMockBuilder('Swift_Transport')->getMock(); + $ex = new Swift_TransportException(''); + $evt = $this->_dispatcher->createTransportExceptionEvent($transport, $ex); + $this->assertInstanceof('Swift_Events_TransportExceptionEvent', $evt); + $this->assertSame($transport, $evt->getSource()); + $this->assertSame($ex, $evt->getException()); + } + + public function testListenersAreNotifiedOfDispatchedEvent() + { + $transport = $this->getMockBuilder('Swift_Transport')->getMock(); + + $evt = $this->_dispatcher->createTransportChangeEvent($transport); + + $listenerA = $this->getMockBuilder('Swift_Events_TransportChangeListener')->getMock(); + $listenerB = $this->getMockBuilder('Swift_Events_TransportChangeListener')->getMock(); + + $this->_dispatcher->bindEventListener($listenerA); + $this->_dispatcher->bindEventListener($listenerB); + + $listenerA->expects($this->once()) + ->method('transportStarted') + ->with($evt); + $listenerB->expects($this->once()) + ->method('transportStarted') + ->with($evt); + + $this->_dispatcher->dispatchEvent($evt, 'transportStarted'); + } + + public function testListenersAreOnlyCalledIfImplementingCorrectInterface() + { + $transport = $this->getMockBuilder('Swift_Transport')->getMock(); + $message = $this->getMockBuilder('Swift_Mime_Message')->getMock(); + + $evt = $this->_dispatcher->createSendEvent($transport, $message); + + $targetListener = $this->getMockBuilder('Swift_Events_SendListener')->getMock(); + $otherListener = $this->getMockBuilder('DummyListener')->getMock(); + + $this->_dispatcher->bindEventListener($targetListener); + $this->_dispatcher->bindEventListener($otherListener); + + $targetListener->expects($this->once()) + ->method('sendPerformed') + ->with($evt); + $otherListener->expects($this->never()) + ->method('sendPerformed'); + + $this->_dispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + public function testListenersCanCancelBubblingOfEvent() + { + $transport = $this->getMockBuilder('Swift_Transport')->getMock(); + $message = $this->getMockBuilder('Swift_Mime_Message')->getMock(); + + $evt = $this->_dispatcher->createSendEvent($transport, $message); + + $listenerA = $this->getMockBuilder('Swift_Events_SendListener')->getMock(); + $listenerB = $this->getMockBuilder('Swift_Events_SendListener')->getMock(); + + $this->_dispatcher->bindEventListener($listenerA); + $this->_dispatcher->bindEventListener($listenerB); + + $listenerA->expects($this->once()) + ->method('sendPerformed') + ->with($evt) + ->will($this->returnCallback(function ($object) { + $object->cancelBubble(true); + })); + $listenerB->expects($this->never()) + ->method('sendPerformed'); + + $this->_dispatcher->dispatchEvent($evt, 'sendPerformed'); + + $this->assertTrue($evt->bubbleCancelled()); + } + + private function _createDispatcher(array $map) + { + return new Swift_Events_SimpleEventDispatcher($map); + } +} + +class DummyListener implements Swift_Events_EventListener +{ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportChangeEventTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportChangeEventTest.php new file mode 100644 index 00000000..8d7b2376 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportChangeEventTest.php @@ -0,0 +1,32 @@ +<?php + +class Swift_Events_TransportChangeEventTest extends \PHPUnit_Framework_TestCase +{ + public function testGetTransportReturnsTransport() + { + $transport = $this->_createTransport(); + $evt = $this->_createEvent($transport); + $ref = $evt->getTransport(); + $this->assertEquals($transport, $ref); + } + + public function testSourceIsTransport() + { + $transport = $this->_createTransport(); + $evt = $this->_createEvent($transport); + $ref = $evt->getSource(); + $this->assertEquals($transport, $ref); + } + + // -- Creation Methods + + private function _createEvent(Swift_Transport $source) + { + return new Swift_Events_TransportChangeEvent($source); + } + + private function _createTransport() + { + return $this->getMockBuilder('Swift_Transport')->getMock(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportExceptionEventTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportExceptionEventTest.php new file mode 100644 index 00000000..0324cdb8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportExceptionEventTest.php @@ -0,0 +1,43 @@ +<?php + +class Swift_Events_TransportExceptionEventTest extends \PHPUnit_Framework_TestCase +{ + public function testExceptionCanBeFetchViaGetter() + { + $ex = $this->_createException(); + $transport = $this->_createTransport(); + $evt = $this->_createEvent($transport, $ex); + $ref = $evt->getException(); + $this->assertEquals($ex, $ref, + '%s: Exception should be available via getException()' + ); + } + + public function testSourceIsTransport() + { + $ex = $this->_createException(); + $transport = $this->_createTransport(); + $evt = $this->_createEvent($transport, $ex); + $ref = $evt->getSource(); + $this->assertEquals($transport, $ref, + '%s: Transport should be available via getSource()' + ); + } + + // -- Creation Methods + + private function _createEvent(Swift_Transport $transport, Swift_TransportException $ex) + { + return new Swift_Events_TransportExceptionEvent($transport, $ex); + } + + private function _createTransport() + { + return $this->getMockBuilder('Swift_Transport')->getMock(); + } + + private function _createException() + { + return new Swift_TransportException(''); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/ArrayKeyCacheTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/ArrayKeyCacheTest.php new file mode 100644 index 00000000..634b7ad0 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/ArrayKeyCacheTest.php @@ -0,0 +1,242 @@ +<?php + +class Swift_KeyCache_ArrayKeyCacheTest extends \PHPUnit_Framework_TestCase +{ + private $_key1 = 'key1'; + private $_key2 = 'key2'; + + public function testStringDataCanBeSetAndFetched() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $cache->getString($this->_key1, 'foo')); + } + + public function testStringDataCanBeOverwritten() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $cache->setString( + $this->_key1, 'foo', 'whatever', Swift_KeyCache::MODE_WRITE + ); + + $this->assertEquals('whatever', $cache->getString($this->_key1, 'foo')); + } + + public function testStringDataCanBeAppended() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $cache->setString( + $this->_key1, 'foo', 'ing', Swift_KeyCache::MODE_APPEND + ); + + $this->assertEquals('testing', $cache->getString($this->_key1, 'foo')); + } + + public function testHasKeyReturnValue() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + + $this->assertTrue($cache->hasKey($this->_key1, 'foo')); + } + + public function testNsKeyIsWellPartitioned() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $cache->setString( + $this->_key2, 'foo', 'ing', Swift_KeyCache::MODE_WRITE + ); + + $this->assertEquals('test', $cache->getString($this->_key1, 'foo')); + $this->assertEquals('ing', $cache->getString($this->_key2, 'foo')); + } + + public function testItemKeyIsWellPartitioned() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $cache->setString( + $this->_key1, 'bar', 'ing', Swift_KeyCache::MODE_WRITE + ); + + $this->assertEquals('test', $cache->getString($this->_key1, 'foo')); + $this->assertEquals('ing', $cache->getString($this->_key1, 'bar')); + } + + public function testByteStreamCanBeImported() + { + $os = $this->_createOutputStream(); + $os->expects($this->at(0)) + ->method('read') + ->will($this->returnValue('abc')); + $os->expects($this->at(1)) + ->method('read') + ->will($this->returnValue('def')); + $os->expects($this->at(2)) + ->method('read') + ->will($this->returnValue(false)); + + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->importFromByteStream( + $this->_key1, 'foo', $os, Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('abcdef', $cache->getString($this->_key1, 'foo')); + } + + public function testByteStreamCanBeAppended() + { + $os1 = $this->_createOutputStream(); + $os1->expects($this->at(0)) + ->method('read') + ->will($this->returnValue('abc')); + $os1->expects($this->at(1)) + ->method('read') + ->will($this->returnValue('def')); + $os1->expects($this->at(2)) + ->method('read') + ->will($this->returnValue(false)); + + $os2 = $this->_createOutputStream(); + $os2->expects($this->at(0)) + ->method('read') + ->will($this->returnValue('xyz')); + $os2->expects($this->at(1)) + ->method('read') + ->will($this->returnValue('uvw')); + $os2->expects($this->at(2)) + ->method('read') + ->will($this->returnValue(false)); + + $is = $this->_createKeyCacheInputStream(true); + + $cache = $this->_createCache($is); + + $cache->importFromByteStream( + $this->_key1, 'foo', $os1, Swift_KeyCache::MODE_APPEND + ); + $cache->importFromByteStream( + $this->_key1, 'foo', $os2, Swift_KeyCache::MODE_APPEND + ); + + $this->assertEquals('abcdefxyzuvw', $cache->getString($this->_key1, 'foo')); + } + + public function testByteStreamAndStringCanBeAppended() + { + $os = $this->_createOutputStream(); + $os->expects($this->at(0)) + ->method('read') + ->will($this->returnValue('abc')); + $os->expects($this->at(1)) + ->method('read') + ->will($this->returnValue('def')); + $os->expects($this->at(2)) + ->method('read') + ->will($this->returnValue(false)); + + $is = $this->_createKeyCacheInputStream(true); + + $cache = $this->_createCache($is); + + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_APPEND + ); + $cache->importFromByteStream( + $this->_key1, 'foo', $os, Swift_KeyCache::MODE_APPEND + ); + $this->assertEquals('testabcdef', $cache->getString($this->_key1, 'foo')); + } + + public function testDataCanBeExportedToByteStream() + { + //See acceptance test for more detail + $is = $this->_createInputStream(); + $is->expects($this->atLeastOnce()) + ->method('write'); + + $kcis = $this->_createKeyCacheInputStream(true); + + $cache = $this->_createCache($kcis); + + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + + $cache->exportToByteStream($this->_key1, 'foo', $is); + } + + public function testKeyCanBeCleared() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($cache->hasKey($this->_key1, 'foo')); + $cache->clearKey($this->_key1, 'foo'); + $this->assertFalse($cache->hasKey($this->_key1, 'foo')); + } + + public function testNsKeyCanBeCleared() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $cache->setString( + $this->_key1, 'bar', 'xyz', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($cache->hasKey($this->_key1, 'foo')); + $this->assertTrue($cache->hasKey($this->_key1, 'bar')); + $cache->clearAll($this->_key1); + $this->assertFalse($cache->hasKey($this->_key1, 'foo')); + $this->assertFalse($cache->hasKey($this->_key1, 'bar')); + } + + // -- Creation methods + + private function _createCache($is) + { + return new Swift_KeyCache_ArrayKeyCache($is); + } + + private function _createKeyCacheInputStream() + { + return $this->getMockBuilder('Swift_KeyCache_KeyCacheInputStream')->getMock(); + } + + private function _createOutputStream() + { + return $this->getMockBuilder('Swift_OutputByteStream')->getMock(); + } + + private function _createInputStream() + { + return $this->getMockBuilder('Swift_InputByteStream')->getMock(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/SimpleKeyCacheInputStreamTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/SimpleKeyCacheInputStreamTest.php new file mode 100644 index 00000000..38fbc0df --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/SimpleKeyCacheInputStreamTest.php @@ -0,0 +1,73 @@ +<?php + +class Swift_KeyCache_SimpleKeyCacheInputStreamTest extends \PHPUnit_Framework_TestCase +{ + private $_nsKey = 'ns1'; + + public function testStreamWritesToCacheInAppendMode() + { + $cache = $this->getMockBuilder('Swift_KeyCache')->getMock(); + $cache->expects($this->at(0)) + ->method('setString') + ->with($this->_nsKey, 'foo', 'a', Swift_KeyCache::MODE_APPEND); + $cache->expects($this->at(1)) + ->method('setString') + ->with($this->_nsKey, 'foo', 'b', Swift_KeyCache::MODE_APPEND); + $cache->expects($this->at(2)) + ->method('setString') + ->with($this->_nsKey, 'foo', 'c', Swift_KeyCache::MODE_APPEND); + + $stream = new Swift_KeyCache_SimpleKeyCacheInputStream(); + $stream->setKeyCache($cache); + $stream->setNsKey($this->_nsKey); + $stream->setItemKey('foo'); + + $stream->write('a'); + $stream->write('b'); + $stream->write('c'); + } + + public function testFlushContentClearsKey() + { + $cache = $this->getMockBuilder('Swift_KeyCache')->getMock(); + $cache->expects($this->once()) + ->method('clearKey') + ->with($this->_nsKey, 'foo'); + + $stream = new Swift_KeyCache_SimpleKeyCacheInputStream(); + $stream->setKeyCache($cache); + $stream->setNsKey($this->_nsKey); + $stream->setItemKey('foo'); + + $stream->flushBuffers(); + } + + public function testClonedStreamStillReferencesSameCache() + { + $cache = $this->getMockBuilder('Swift_KeyCache')->getMock(); + $cache->expects($this->at(0)) + ->method('setString') + ->with($this->_nsKey, 'foo', 'a', Swift_KeyCache::MODE_APPEND); + $cache->expects($this->at(1)) + ->method('setString') + ->with($this->_nsKey, 'foo', 'b', Swift_KeyCache::MODE_APPEND); + $cache->expects($this->at(2)) + ->method('setString') + ->with('test', 'bar', 'x', Swift_KeyCache::MODE_APPEND); + + $stream = new Swift_KeyCache_SimpleKeyCacheInputStream(); + $stream->setKeyCache($cache); + $stream->setNsKey($this->_nsKey); + $stream->setItemKey('foo'); + + $stream->write('a'); + $stream->write('b'); + + $newStream = clone $stream; + $newStream->setKeyCache($cache); + $newStream->setNsKey('test'); + $newStream->setItemKey('bar'); + + $newStream->write('x'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mailer/ArrayRecipientIteratorTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mailer/ArrayRecipientIteratorTest.php new file mode 100644 index 00000000..ff0bce4f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mailer/ArrayRecipientIteratorTest.php @@ -0,0 +1,42 @@ +<?php + +class Swift_Mailer_ArrayRecipientIteratorTest extends \PHPUnit_Framework_TestCase +{ + public function testHasNextReturnsFalseForEmptyArray() + { + $it = new Swift_Mailer_ArrayRecipientIterator(array()); + $this->assertFalse($it->hasNext()); + } + + public function testHasNextReturnsTrueIfItemsLeft() + { + $it = new Swift_Mailer_ArrayRecipientIterator(array('foo@bar' => 'Foo')); + $this->assertTrue($it->hasNext()); + } + + public function testReadingToEndOfListCausesHasNextToReturnFalse() + { + $it = new Swift_Mailer_ArrayRecipientIterator(array('foo@bar' => 'Foo')); + $this->assertTrue($it->hasNext()); + $it->nextRecipient(); + $this->assertFalse($it->hasNext()); + } + + public function testReturnedValueHasPreservedKeyValuePair() + { + $it = new Swift_Mailer_ArrayRecipientIterator(array('foo@bar' => 'Foo')); + $this->assertEquals(array('foo@bar' => 'Foo'), $it->nextRecipient()); + } + + public function testIteratorMovesNextAfterEachIteration() + { + $it = new Swift_Mailer_ArrayRecipientIterator(array( + 'foo@bar' => 'Foo', + 'zip@button' => 'Zip thing', + 'test@test' => null, + )); + $this->assertEquals(array('foo@bar' => 'Foo'), $it->nextRecipient()); + $this->assertEquals(array('zip@button' => 'Zip thing'), $it->nextRecipient()); + $this->assertEquals(array('test@test' => null), $it->nextRecipient()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/MailerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/MailerTest.php new file mode 100644 index 00000000..389264fb --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/MailerTest.php @@ -0,0 +1,147 @@ +<?php + +class Swift_MailerTest extends \SwiftMailerTestCase +{ + public function testTransportIsStartedWhenSending() + { + $transport = $this->_createTransport(); + $message = $this->_createMessage(); + + $started = false; + $transport->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$started) { + return $started; + }); + $transport->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$started) { + $started = true; + + return; + }); + + $mailer = $this->_createMailer($transport); + $mailer->send($message); + } + + public function testTransportIsOnlyStartedOnce() + { + $transport = $this->_createTransport(); + $message = $this->_createMessage(); + + $started = false; + $transport->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$started) { + return $started; + }); + $transport->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$started) { + $started = true; + + return; + }); + + $mailer = $this->_createMailer($transport); + for ($i = 0; $i < 10; ++$i) { + $mailer->send($message); + } + } + + public function testMessageIsPassedToTransport() + { + $transport = $this->_createTransport(); + $message = $this->_createMessage(); + $transport->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()); + + $mailer = $this->_createMailer($transport); + $mailer->send($message); + } + + public function testSendReturnsCountFromTransport() + { + $transport = $this->_createTransport(); + $message = $this->_createMessage(); + $transport->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturn(57); + + $mailer = $this->_createMailer($transport); + $this->assertEquals(57, $mailer->send($message)); + } + + public function testFailedRecipientReferenceIsPassedToTransport() + { + $failures = array(); + + $transport = $this->_createTransport(); + $message = $this->_createMessage(); + $transport->shouldReceive('send') + ->once() + ->with($message, $failures) + ->andReturn(57); + + $mailer = $this->_createMailer($transport); + $mailer->send($message, $failures); + } + + public function testSendRecordsRfcComplianceExceptionAsEntireSendFailure() + { + $failures = array(); + + $rfcException = new Swift_RfcComplianceException('test'); + $transport = $this->_createTransport(); + $message = $this->_createMessage(); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo&invalid' => 'Foo', 'bar@valid.tld' => 'Bar')); + $transport->shouldReceive('send') + ->once() + ->with($message, $failures) + ->andThrow($rfcException); + + $mailer = $this->_createMailer($transport); + $this->assertEquals(0, $mailer->send($message, $failures), '%s: Should return 0'); + $this->assertEquals(array('foo&invalid', 'bar@valid.tld'), $failures, '%s: Failures should contain all addresses since the entire message failed to compile'); + } + + public function testRegisterPluginDelegatesToTransport() + { + $plugin = $this->_createPlugin(); + $transport = $this->_createTransport(); + $mailer = $this->_createMailer($transport); + + $transport->shouldReceive('registerPlugin') + ->once() + ->with($plugin); + + $mailer->registerPlugin($plugin); + } + + // -- Creation methods + + private function _createPlugin() + { + return $this->getMockery('Swift_Events_EventListener')->shouldIgnoreMissing(); + } + + private function _createTransport() + { + return $this->getMockery('Swift_Transport')->shouldIgnoreMissing(); + } + + private function _createMessage() + { + return $this->getMockery('Swift_Mime_Message')->shouldIgnoreMissing(); + } + + private function _createMailer(Swift_Transport $transport) + { + return new Swift_Mailer($transport); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/MessageTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/MessageTest.php new file mode 100644 index 00000000..00039f10 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/MessageTest.php @@ -0,0 +1,130 @@ +<?php + +class Swift_MessageTest extends \PHPUnit_Framework_TestCase +{ + public function testCloning() + { + $message1 = new Swift_Message('subj', 'body', 'ctype'); + $message2 = new Swift_Message('subj', 'body', 'ctype'); + $message1_clone = clone $message1; + + $this->_recursiveObjectCloningCheck($message1, $message2, $message1_clone); + } + + public function testCloningWithSigners() + { + $message1 = new Swift_Message('subj', 'body', 'ctype'); + $signer = new Swift_Signers_DKIMSigner(dirname(dirname(__DIR__)).'/_samples/dkim/dkim.test.priv', 'test.example', 'example'); + $message1->attachSigner($signer); + $message2 = new Swift_Message('subj', 'body', 'ctype'); + $signer = new Swift_Signers_DKIMSigner(dirname(dirname(__DIR__)).'/_samples/dkim/dkim.test.priv', 'test.example', 'example'); + $message2->attachSigner($signer); + $message1_clone = clone $message1; + + $this->_recursiveObjectCloningCheck($message1, $message2, $message1_clone); + } + + public function testBodySwap() + { + $message1 = new Swift_Message('Test'); + $html = Swift_MimePart::newInstance('<html></html>', 'text/html'); + $html->getHeaders()->addTextHeader('X-Test-Remove', 'Test-Value'); + $html->getHeaders()->addTextHeader('X-Test-Alter', 'Test-Value'); + $message1->attach($html); + $source = $message1->toString(); + $message2 = clone $message1; + $message2->setSubject('Message2'); + foreach ($message2->getChildren() as $child) { + $child->setBody('Test'); + $child->getHeaders()->removeAll('X-Test-Remove'); + $child->getHeaders()->get('X-Test-Alter')->setValue('Altered'); + } + $final = $message1->toString(); + if ($source != $final) { + $this->fail("Difference although object cloned \n [".$source."]\n[".$final."]\n"); + } + $final = $message2->toString(); + if ($final == $source) { + $this->fail('Two body matches although they should differ'."\n [".$source."]\n[".$final."]\n"); + } + $id_1 = $message1->getId(); + $id_2 = $message2->getId(); + $this->assertEquals($id_1, $id_2, 'Message Ids differ'); + $id_2 = $message2->generateId(); + $this->assertNotEquals($id_1, $id_2, 'Message Ids are the same'); + } + + // -- Private helpers + protected function _recursiveObjectCloningCheck($obj1, $obj2, $obj1_clone) + { + $obj1_properties = (array) $obj1; + $obj2_properties = (array) $obj2; + $obj1_clone_properties = (array) $obj1_clone; + + foreach ($obj1_properties as $property => $value) { + if (is_object($value)) { + $obj1_value = $obj1_properties[$property]; + $obj2_value = $obj2_properties[$property]; + $obj1_clone_value = $obj1_clone_properties[$property]; + + if ($obj1_value !== $obj2_value) { + // two separetely instanciated objects property not referencing same object + $this->assertFalse( + // but object's clone does - not everything copied + $obj1_value === $obj1_clone_value, + "Property `$property` cloning error: source and cloned objects property is referencing same object" + ); + } else { + // two separetely instanciated objects have same reference + $this->assertFalse( + // but object's clone doesn't - overdone making copies + $obj1_value !== $obj1_clone_value, + "Property `$property` not properly cloned: it should reference same object as cloning source (overdone copping)" + ); + } + // recurse + $this->_recursiveObjectCloningCheck($obj1_value, $obj2_value, $obj1_clone_value); + } elseif (is_array($value)) { + $obj1_value = $obj1_properties[$property]; + $obj2_value = $obj2_properties[$property]; + $obj1_clone_value = $obj1_clone_properties[$property]; + + return $this->_recursiveArrayCloningCheck($obj1_value, $obj2_value, $obj1_clone_value); + } + } + } + + protected function _recursiveArrayCloningCheck($array1, $array2, $array1_clone) + { + foreach ($array1 as $key => $value) { + if (is_object($value)) { + $arr1_value = $array1[$key]; + $arr2_value = $array2[$key]; + $arr1_clone_value = $array1_clone[$key]; + if ($arr1_value !== $arr2_value) { + // two separetely instanciated objects property not referencing same object + $this->assertFalse( + // but object's clone does - not everything copied + $arr1_value === $arr1_clone_value, + "Key `$key` cloning error: source and cloned objects property is referencing same object" + ); + } else { + // two separetely instanciated objects have same reference + $this->assertFalse( + // but object's clone doesn't - overdone making copies + $arr1_value !== $arr1_clone_value, + "Key `$key` not properly cloned: it should reference same object as cloning source (overdone copping)" + ); + } + // recurse + $this->_recursiveObjectCloningCheck($arr1_value, $arr2_value, $arr1_clone_value); + } elseif (is_array($value)) { + $arr1_value = $array1[$key]; + $arr2_value = $array2[$key]; + $arr1_clone_value = $array1_clone[$key]; + + return $this->_recursiveArrayCloningCheck($arr1_value, $arr2_value, $arr1_clone_value); + } + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AbstractMimeEntityTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AbstractMimeEntityTest.php new file mode 100644 index 00000000..ffe9cd8d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AbstractMimeEntityTest.php @@ -0,0 +1,1054 @@ +<?php + +require_once dirname(dirname(dirname(__DIR__))).'/fixtures/MimeEntityFixture.php'; + +abstract class Swift_Mime_AbstractMimeEntityTest extends \SwiftMailerTestCase +{ + public function testGetHeadersReturnsHeaderSet() + { + $headers = $this->_createHeaderSet(); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $this->assertSame($headers, $entity->getHeaders()); + } + + public function testContentTypeIsReturnedFromHeader() + { + $ctype = $this->_createHeader('Content-Type', 'image/jpeg-test'); + $headers = $this->_createHeaderSet(array('Content-Type' => $ctype)); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $this->assertEquals('image/jpeg-test', $entity->getContentType()); + } + + public function testContentTypeIsSetInHeader() + { + $ctype = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $headers = $this->_createHeaderSet(array('Content-Type' => $ctype)); + + $ctype->shouldReceive('setFieldBodyModel') + ->once() + ->with('image/jpeg'); + $ctype->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes() + ->with(\Mockery::not('image/jpeg')); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setContentType('image/jpeg'); + } + + public function testContentTypeHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addParameterizedHeader') + ->once() + ->with('Content-Type', 'image/jpeg'); + $headers->shouldReceive('addParameterizedHeader') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setContentType('image/jpeg'); + } + + public function testContentTypeCanBeSetViaSetBody() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addParameterizedHeader') + ->once() + ->with('Content-Type', 'text/html'); + $headers->shouldReceive('addParameterizedHeader') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setBody('<b>foo</b>', 'text/html'); + } + + public function testGetEncoderFromConstructor() + { + $encoder = $this->_createEncoder('base64'); + $entity = $this->_createEntity($this->_createHeaderSet(), $encoder, + $this->_createCache() + ); + $this->assertSame($encoder, $entity->getEncoder()); + } + + public function testSetAndGetEncoder() + { + $encoder = $this->_createEncoder('base64'); + $headers = $this->_createHeaderSet(); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setEncoder($encoder); + $this->assertSame($encoder, $entity->getEncoder()); + } + + public function testSettingEncoderUpdatesTransferEncoding() + { + $encoder = $this->_createEncoder('base64'); + $encoding = $this->_createHeader( + 'Content-Transfer-Encoding', '8bit', array(), false + ); + $headers = $this->_createHeaderSet(array( + 'Content-Transfer-Encoding' => $encoding, + )); + $encoding->shouldReceive('setFieldBodyModel') + ->once() + ->with('base64'); + $encoding->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setEncoder($encoder); + } + + public function testSettingEncoderAddsEncodingHeaderIfNonePresent() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addTextHeader') + ->once() + ->with('Content-Transfer-Encoding', 'something'); + $headers->shouldReceive('addTextHeader') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setEncoder($this->_createEncoder('something')); + } + + public function testIdIsReturnedFromHeader() + { + /* -- RFC 2045, 7. + In constructing a high-level user agent, it may be desirable to allow + one body to make reference to another. Accordingly, bodies may be + labelled using the "Content-ID" header field, which is syntactically + identical to the "Message-ID" header field + */ + + $cid = $this->_createHeader('Content-ID', 'zip@button'); + $headers = $this->_createHeaderSet(array('Content-ID' => $cid)); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $this->assertEquals('zip@button', $entity->getId()); + } + + public function testIdIsSetInHeader() + { + $cid = $this->_createHeader('Content-ID', 'zip@button', array(), false); + $headers = $this->_createHeaderSet(array('Content-ID' => $cid)); + + $cid->shouldReceive('setFieldBodyModel') + ->once() + ->with('foo@bar'); + $cid->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setId('foo@bar'); + } + + public function testIdIsAutoGenerated() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertRegExp('/^.*?@.*?$/D', $entity->getId()); + } + + public function testGenerateIdCreatesNewId() + { + $headers = $this->_createHeaderSet(); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $id1 = $entity->generateId(); + $id2 = $entity->generateId(); + $this->assertNotEquals($id1, $id2); + } + + public function testGenerateIdSetsNewId() + { + $headers = $this->_createHeaderSet(); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $id = $entity->generateId(); + $this->assertEquals($id, $entity->getId()); + } + + public function testDescriptionIsReadFromHeader() + { + /* -- RFC 2045, 8. + The ability to associate some descriptive information with a given + body is often desirable. For example, it may be useful to mark an + "image" body as "a picture of the Space Shuttle Endeavor." Such text + may be placed in the Content-Description header field. This header + field is always optional. + */ + + $desc = $this->_createHeader('Content-Description', 'something'); + $headers = $this->_createHeaderSet(array('Content-Description' => $desc)); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $this->assertEquals('something', $entity->getDescription()); + } + + public function testDescriptionIsSetInHeader() + { + $desc = $this->_createHeader('Content-Description', '', array(), false); + $desc->shouldReceive('setFieldBodyModel')->once()->with('whatever'); + + $headers = $this->_createHeaderSet(array('Content-Description' => $desc)); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setDescription('whatever'); + } + + public function testDescriptionHeaderIsAddedIfNotPresent() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addTextHeader') + ->once() + ->with('Content-Description', 'whatever'); + $headers->shouldReceive('addTextHeader') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setDescription('whatever'); + } + + public function testSetAndGetMaxLineLength() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setMaxLineLength(60); + $this->assertEquals(60, $entity->getMaxLineLength()); + } + + public function testEncoderIsUsedForStringGeneration() + { + $encoder = $this->_createEncoder('base64', false); + $encoder->expects($this->once()) + ->method('encodeString') + ->with('blah'); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $encoder, $this->_createCache() + ); + $entity->setBody('blah'); + $entity->toString(); + } + + public function testMaxLineLengthIsProvidedWhenEncoding() + { + $encoder = $this->_createEncoder('base64', false); + $encoder->expects($this->once()) + ->method('encodeString') + ->with('blah', 0, 65); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $encoder, $this->_createCache() + ); + $entity->setBody('blah'); + $entity->setMaxLineLength(65); + $entity->toString(); + } + + public function testHeadersAppearInString() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->once() + ->andReturn( + "Content-Type: text/plain; charset=utf-8\r\n". + "X-MyHeader: foobar\r\n" + ); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $this->assertEquals( + "Content-Type: text/plain; charset=utf-8\r\n". + "X-MyHeader: foobar\r\n", + $entity->toString() + ); + } + + public function testSetAndGetBody() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setBody("blah\r\nblah!"); + $this->assertEquals("blah\r\nblah!", $entity->getBody()); + } + + public function testBodyIsAppended() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->once() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setBody("blah\r\nblah!"); + $this->assertEquals( + "Content-Type: text/plain; charset=utf-8\r\n". + "\r\n". + "blah\r\nblah!", + $entity->toString() + ); + } + + public function testGetBodyReturnsStringFromByteStream() + { + $os = $this->_createOutputStream('byte stream string'); + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setBody($os); + $this->assertEquals('byte stream string', $entity->getBody()); + } + + public function testByteStreamBodyIsAppended() + { + $headers = $this->_createHeaderSet(array(), false); + $os = $this->_createOutputStream('streamed'); + $headers->shouldReceive('toString') + ->once() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setBody($os); + $this->assertEquals( + "Content-Type: text/plain; charset=utf-8\r\n". + "\r\n". + 'streamed', + $entity->toString() + ); + } + + public function testBoundaryCanBeRetrieved() + { + /* -- RFC 2046, 5.1.1. + boundary := 0*69<bchars> bcharsnospace + + bchars := bcharsnospace / " " + + bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" / + "+" / "_" / "," / "-" / "." / + "/" / ":" / "=" / "?" + */ + + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertRegExp( + '/^[a-zA-Z0-9\'\(\)\+_\-,\.\/:=\?\ ]{0,69}[a-zA-Z0-9\'\(\)\+_\-,\.\/:=\?]$/D', + $entity->getBoundary() + ); + } + + public function testBoundaryNeverChanges() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $firstBoundary = $entity->getBoundary(); + for ($i = 0; $i < 10; ++$i) { + $this->assertEquals($firstBoundary, $entity->getBoundary()); + } + } + + public function testBoundaryCanBeSet() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setBoundary('foobar'); + $this->assertEquals('foobar', $entity->getBoundary()); + } + + public function testAddingChildrenGeneratesBoundaryInHeaders() + { + $child = $this->_createChild(); + $cType = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $cType->shouldReceive('setParameter') + ->once() + ->with('boundary', \Mockery::any()); + $cType->shouldReceive('setParameter') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($this->_createHeaderSet(array( + 'Content-Type' => $cType, + )), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + } + + public function testChildrenOfLevelAttachmentAndLessCauseMultipartMixed() + { + for ($level = Swift_Mime_MimeEntity::LEVEL_MIXED; + $level > Swift_Mime_MimeEntity::LEVEL_TOP; $level /= 2) { + $child = $this->_createChild($level); + $cType = $this->_createHeader( + 'Content-Type', 'text/plain', array(), false + ); + $cType->shouldReceive('setFieldBodyModel') + ->once() + ->with('multipart/mixed'); + $cType->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + } + } + + public function testChildrenOfLevelAlternativeAndLessCauseMultipartAlternative() + { + for ($level = Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE; + $level > Swift_Mime_MimeEntity::LEVEL_MIXED; $level /= 2) { + $child = $this->_createChild($level); + $cType = $this->_createHeader( + 'Content-Type', 'text/plain', array(), false + ); + $cType->shouldReceive('setFieldBodyModel') + ->once() + ->with('multipart/alternative'); + $cType->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + } + } + + public function testChildrenOfLevelRelatedAndLessCauseMultipartRelated() + { + for ($level = Swift_Mime_MimeEntity::LEVEL_RELATED; + $level > Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE; $level /= 2) { + $child = $this->_createChild($level); + $cType = $this->_createHeader( + 'Content-Type', 'text/plain', array(), false + ); + $cType->shouldReceive('setFieldBodyModel') + ->once() + ->with('multipart/related'); + $cType->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + } + } + + public function testHighestLevelChildDeterminesContentType() + { + $combinations = array( + array('levels' => array(Swift_Mime_MimeEntity::LEVEL_MIXED, + Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + Swift_Mime_MimeEntity::LEVEL_RELATED, + ), + 'type' => 'multipart/mixed', + ), + array('levels' => array(Swift_Mime_MimeEntity::LEVEL_MIXED, + Swift_Mime_MimeEntity::LEVEL_RELATED, + ), + 'type' => 'multipart/mixed', + ), + array('levels' => array(Swift_Mime_MimeEntity::LEVEL_MIXED, + Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + ), + 'type' => 'multipart/mixed', + ), + array('levels' => array(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + Swift_Mime_MimeEntity::LEVEL_RELATED, + ), + 'type' => 'multipart/alternative', + ), + ); + + foreach ($combinations as $combination) { + $children = array(); + foreach ($combination['levels'] as $level) { + $children[] = $this->_createChild($level); + } + + $cType = $this->_createHeader( + 'Content-Type', 'text/plain', array(), false + ); + $cType->shouldReceive('setFieldBodyModel') + ->once() + ->with($combination['type']); + + $headerSet = $this->_createHeaderSet(array('Content-Type' => $cType)); + $headerSet->shouldReceive('newInstance') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use ($headerSet) { + return $headerSet; + }); + $entity = $this->_createEntity($headerSet, + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren($children); + } + } + + public function testChildrenAppearNestedInString() + { + /* -- RFC 2046, 5.1.1. + (excerpt too verbose to paste here) + */ + + $headers = $this->_createHeaderSet(array(), false); + + $child1 = new MimeEntityFixture(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + "Content-Type: text/plain\r\n". + "\r\n". + 'foobar', 'text/plain' + ); + + $child2 = new MimeEntityFixture(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + "Content-Type: text/html\r\n". + "\r\n". + '<b>foobar</b>', 'text/html' + ); + + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: multipart/alternative; boundary=\"xxx\"\r\n"); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setBoundary('xxx'); + $entity->setChildren(array($child1, $child2)); + + $this->assertEquals( + "Content-Type: multipart/alternative; boundary=\"xxx\"\r\n". + "\r\n". + "\r\n--xxx\r\n". + "Content-Type: text/plain\r\n". + "\r\n". + "foobar\r\n". + "\r\n--xxx\r\n". + "Content-Type: text/html\r\n". + "\r\n". + "<b>foobar</b>\r\n". + "\r\n--xxx--\r\n", + $entity->toString() + ); + } + + public function testMixingLevelsIsHierarchical() + { + $headers = $this->_createHeaderSet(array(), false); + $newHeaders = $this->_createHeaderSet(array(), false); + + $part = $this->_createChild(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + "Content-Type: text/plain\r\n". + "\r\n". + 'foobar' + ); + + $attachment = $this->_createChild(Swift_Mime_MimeEntity::LEVEL_MIXED, + "Content-Type: application/octet-stream\r\n". + "\r\n". + 'data' + ); + + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: multipart/mixed; boundary=\"xxx\"\r\n"); + $headers->shouldReceive('newInstance') + ->zeroOrMoreTimes() + ->andReturn($newHeaders); + $newHeaders->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: multipart/alternative; boundary=\"yyy\"\r\n"); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setBoundary('xxx'); + $entity->setChildren(array($part, $attachment)); + + $this->assertRegExp( + '~^'. + "Content-Type: multipart/mixed; boundary=\"xxx\"\r\n". + "\r\n\r\n--xxx\r\n". + "Content-Type: multipart/alternative; boundary=\"yyy\"\r\n". + "\r\n\r\n--(.*?)\r\n". + "Content-Type: text/plain\r\n". + "\r\n". + 'foobar'. + "\r\n\r\n--\\1--\r\n". + "\r\n\r\n--xxx\r\n". + "Content-Type: application/octet-stream\r\n". + "\r\n". + 'data'. + "\r\n\r\n--xxx--\r\n". + '$~', + $entity->toString() + ); + } + + public function testSettingEncoderNotifiesChildren() + { + $child = $this->_createChild(0, '', false); + $encoder = $this->_createEncoder('base64'); + + $child->shouldReceive('encoderChanged') + ->once() + ->with($encoder); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + $entity->setEncoder($encoder); + } + + public function testReceiptOfEncoderChangeNotifiesChildren() + { + $child = $this->_createChild(0, '', false); + $encoder = $this->_createEncoder('base64'); + + $child->shouldReceive('encoderChanged') + ->once() + ->with($encoder); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + $entity->encoderChanged($encoder); + } + + public function testReceiptOfCharsetChangeNotifiesChildren() + { + $child = $this->_createChild(0, '', false); + $child->shouldReceive('charsetChanged') + ->once() + ->with('windows-874'); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + $entity->charsetChanged('windows-874'); + } + + public function testEntityIsWrittenToByteStream() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $is = $this->_createInputStream(false); + $is->expects($this->atLeastOnce()) + ->method('write'); + + $entity->toByteStream($is); + } + + public function testEntityHeadersAreComittedToByteStream() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $is = $this->_createInputStream(false); + $is->expects($this->atLeastOnce()) + ->method('write'); + $is->expects($this->atLeastOnce()) + ->method('commit'); + + $entity->toByteStream($is); + } + + public function testOrderingTextBeforeHtml() + { + $htmlChild = new MimeEntityFixture(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + "Content-Type: text/html\r\n". + "\r\n". + 'HTML PART', + 'text/html' + ); + $textChild = new MimeEntityFixture(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + "Content-Type: text/plain\r\n". + "\r\n". + 'TEXT PART', + 'text/plain' + ); + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: multipart/alternative; boundary=\"xxx\"\r\n"); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setBoundary('xxx'); + $entity->setChildren(array($htmlChild, $textChild)); + + $this->assertEquals( + "Content-Type: multipart/alternative; boundary=\"xxx\"\r\n". + "\r\n\r\n--xxx\r\n". + "Content-Type: text/plain\r\n". + "\r\n". + 'TEXT PART'. + "\r\n\r\n--xxx\r\n". + "Content-Type: text/html\r\n". + "\r\n". + 'HTML PART'. + "\r\n\r\n--xxx--\r\n", + $entity->toString() + ); + } + + public function testUnsettingChildrenRestoresContentType() + { + $cType = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $child = $this->_createChild(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE); + + $cType->shouldReceive('setFieldBodyModel') + ->twice() + ->with('image/jpeg'); + $cType->shouldReceive('setFieldBodyModel') + ->once() + ->with('multipart/alternative'); + $cType->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes() + ->with(\Mockery::not('multipart/alternative', 'image/jpeg')); + + $entity = $this->_createEntity($this->_createHeaderSet(array( + 'Content-Type' => $cType, + )), + $this->_createEncoder(), $this->_createCache() + ); + + $entity->setContentType('image/jpeg'); + $entity->setChildren(array($child)); + $entity->setChildren(array()); + } + + public function testBodyIsReadFromCacheWhenUsingToStringIfPresent() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $cache = $this->_createCache(false); + $cache->shouldReceive('hasKey') + ->once() + ->with(\Mockery::any(), 'body') + ->andReturn(true); + $cache->shouldReceive('getString') + ->once() + ->with(\Mockery::any(), 'body') + ->andReturn("\r\ncache\r\ncache!"); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $cache + ); + + $entity->setBody("blah\r\nblah!"); + $this->assertEquals( + "Content-Type: text/plain; charset=utf-8\r\n". + "\r\n". + "cache\r\ncache!", + $entity->toString() + ); + } + + public function testBodyIsAddedToCacheWhenUsingToString() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $cache = $this->_createCache(false); + $cache->shouldReceive('hasKey') + ->once() + ->with(\Mockery::any(), 'body') + ->andReturn(false); + $cache->shouldReceive('setString') + ->once() + ->with(\Mockery::any(), 'body', "\r\nblah\r\nblah!", Swift_KeyCache::MODE_WRITE); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $cache + ); + + $entity->setBody("blah\r\nblah!"); + $entity->toString(); + } + + public function testBodyIsClearedFromCacheIfNewBodySet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $cache = $this->_createCache(false); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $cache + ); + + $entity->setBody("blah\r\nblah!"); + $entity->toString(); + + // We set the expectation at this point because we only care what happens when calling setBody() + $cache->shouldReceive('clearKey') + ->once() + ->with(\Mockery::any(), 'body'); + + $entity->setBody("new\r\nnew!"); + } + + public function testBodyIsNotClearedFromCacheIfSameBodySet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $cache = $this->_createCache(false); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $cache + ); + + $entity->setBody("blah\r\nblah!"); + $entity->toString(); + + // We set the expectation at this point because we only care what happens when calling setBody() + $cache->shouldReceive('clearKey') + ->never(); + + $entity->setBody("blah\r\nblah!"); + } + + public function testBodyIsClearedFromCacheIfNewEncoderSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $cache = $this->_createCache(false); + $otherEncoder = $this->_createEncoder(); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $cache + ); + + $entity->setBody("blah\r\nblah!"); + $entity->toString(); + + // We set the expectation at this point because we only care what happens when calling setEncoder() + $cache->shouldReceive('clearKey') + ->once() + ->with(\Mockery::any(), 'body'); + + $entity->setEncoder($otherEncoder); + } + + public function testBodyIsReadFromCacheWhenUsingToByteStreamIfPresent() + { + $is = $this->_createInputStream(); + $cache = $this->_createCache(false); + $cache->shouldReceive('hasKey') + ->once() + ->with(\Mockery::any(), 'body') + ->andReturn(true); + $cache->shouldReceive('exportToByteStream') + ->once() + ->with(\Mockery::any(), 'body', $is); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $cache + ); + $entity->setBody('foo'); + + $entity->toByteStream($is); + } + + public function testBodyIsAddedToCacheWhenUsingToByteStream() + { + $is = $this->_createInputStream(); + $cache = $this->_createCache(false); + $cache->shouldReceive('hasKey') + ->once() + ->with(\Mockery::any(), 'body') + ->andReturn(false); + $cache->shouldReceive('getInputByteStream') + ->once() + ->with(\Mockery::any(), 'body'); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $cache + ); + $entity->setBody('foo'); + + $entity->toByteStream($is); + } + + public function testFluidInterface() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + + $this->assertSame($entity, + $entity + ->setContentType('text/plain') + ->setEncoder($this->_createEncoder()) + ->setId('foo@bar') + ->setDescription('my description') + ->setMaxLineLength(998) + ->setBody('xx') + ->setBoundary('xyz') + ->setChildren(array()) + ); + } + + // -- Private helpers + + abstract protected function _createEntity($headers, $encoder, $cache); + + protected function _createChild($level = null, $string = '', $stub = true) + { + $child = $this->getMockery('Swift_Mime_MimeEntity')->shouldIgnoreMissing(); + if (isset($level)) { + $child->shouldReceive('getNestingLevel') + ->zeroOrMoreTimes() + ->andReturn($level); + } + $child->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn($string); + + return $child; + } + + protected function _createEncoder($name = 'quoted-printable', $stub = true) + { + $encoder = $this->getMockBuilder('Swift_Mime_ContentEncoder')->getMock(); + $encoder->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)); + $encoder->expects($this->any()) + ->method('encodeString') + ->will($this->returnCallback(function () { + $args = func_get_args(); + + return array_shift($args); + })); + + return $encoder; + } + + protected function _createCache($stub = true) + { + return $this->getMockery('Swift_KeyCache')->shouldIgnoreMissing(); + } + + protected function _createHeaderSet($headers = array(), $stub = true) + { + $set = $this->getMockery('Swift_Mime_HeaderSet')->shouldIgnoreMissing(); + $set->shouldReceive('get') + ->zeroOrMoreTimes() + ->andReturnUsing(function ($key) use ($headers) { + return $headers[$key]; + }); + $set->shouldReceive('has') + ->zeroOrMoreTimes() + ->andReturnUsing(function ($key) use ($headers) { + return array_key_exists($key, $headers); + }); + + return $set; + } + + protected function _createHeader($name, $model = null, $params = array(), $stub = true) + { + $header = $this->getMockery('Swift_Mime_ParameterizedHeader')->shouldIgnoreMissing(); + $header->shouldReceive('getFieldName') + ->zeroOrMoreTimes() + ->andReturn($name); + $header->shouldReceive('getFieldBodyModel') + ->zeroOrMoreTimes() + ->andReturn($model); + $header->shouldReceive('getParameter') + ->zeroOrMoreTimes() + ->andReturnUsing(function ($key) use ($params) { + return $params[$key]; + }); + + return $header; + } + + protected function _createOutputStream($data = null, $stub = true) + { + $os = $this->getMockery('Swift_OutputByteStream'); + if (isset($data)) { + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use ($data) { + static $first = true; + if (!$first) { + return false; + } + + $first = false; + + return $data; + }); + $os->shouldReceive('setReadPointer') + ->zeroOrMoreTimes(); + } + + return $os; + } + + protected function _createInputStream($stub = true) + { + return $this->getMockBuilder('Swift_InputByteStream')->getMock(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AttachmentTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AttachmentTest.php new file mode 100644 index 00000000..bd2499c0 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AttachmentTest.php @@ -0,0 +1,320 @@ +<?php + +class Swift_Mime_AttachmentTest extends Swift_Mime_AbstractMimeEntityTest +{ + public function testNestingLevelIsAttachment() + { + $attachment = $this->_createAttachment($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals( + Swift_Mime_MimeEntity::LEVEL_MIXED, $attachment->getNestingLevel() + ); + } + + public function testDispositionIsReturnedFromHeader() + { + /* -- RFC 2183, 2.1, 2.2. + */ + + $disposition = $this->_createHeader('Content-Disposition', 'attachment'); + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('attachment', $attachment->getDisposition()); + } + + public function testDispositionIsSetInHeader() + { + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array(), false + ); + $disposition->shouldReceive('setFieldBodyModel') + ->once() + ->with('inline'); + $disposition->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $attachment->setDisposition('inline'); + } + + public function testDispositionIsAddedIfNonePresent() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addParameterizedHeader') + ->once() + ->with('Content-Disposition', 'inline'); + $headers->shouldReceive('addParameterizedHeader') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($headers, $this->_createEncoder(), + $this->_createCache() + ); + $attachment->setDisposition('inline'); + } + + public function testDispositionIsAutoDefaultedToAttachment() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addParameterizedHeader') + ->once() + ->with('Content-Disposition', 'attachment'); + $headers->shouldReceive('addParameterizedHeader') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($headers, $this->_createEncoder(), + $this->_createCache() + ); + } + + public function testDefaultContentTypeInitializedToOctetStream() + { + $cType = $this->_createHeader('Content-Type', '', + array(), false + ); + $cType->shouldReceive('setFieldBodyModel') + ->once() + ->with('application/octet-stream'); + $cType->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + } + + public function testFilenameIsReturnedFromHeader() + { + /* -- RFC 2183, 2.3. + */ + + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array('filename' => 'foo.txt') + ); + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('foo.txt', $attachment->getFilename()); + } + + public function testFilenameIsSetInHeader() + { + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array('filename' => 'foo.txt'), false + ); + $disposition->shouldReceive('setParameter') + ->once() + ->with('filename', 'bar.txt'); + $disposition->shouldReceive('setParameter') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $attachment->setFilename('bar.txt'); + } + + public function testSettingFilenameSetsNameInContentType() + { + /* + This is a legacy requirement which isn't covered by up-to-date RFCs. + */ + + $cType = $this->_createHeader('Content-Type', 'text/plain', + array(), false + ); + $cType->shouldReceive('setParameter') + ->once() + ->with('name', 'bar.txt'); + $cType->shouldReceive('setParameter') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $attachment->setFilename('bar.txt'); + } + + public function testSizeIsReturnedFromHeader() + { + /* -- RFC 2183, 2.7. + */ + + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array('size' => 1234) + ); + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(1234, $attachment->getSize()); + } + + public function testSizeIsSetInHeader() + { + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array(), false + ); + $disposition->shouldReceive('setParameter') + ->once() + ->with('size', 12345); + $disposition->shouldReceive('setParameter') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $attachment->setSize(12345); + } + + public function testFilnameCanBeReadFromFileStream() + { + $file = $this->_createFileStream('/bar/file.ext', ''); + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array('filename' => 'foo.txt'), false + ); + $disposition->shouldReceive('setParameter') + ->once() + ->with('filename', 'file.ext'); + + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $attachment->setFile($file); + } + + public function testContentTypeCanBeSetViaSetFile() + { + $file = $this->_createFileStream('/bar/file.ext', ''); + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array('filename' => 'foo.txt'), false + ); + $disposition->shouldReceive('setParameter') + ->once() + ->with('filename', 'file.ext'); + + $ctype = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $ctype->shouldReceive('setFieldBodyModel') + ->once() + ->with('text/html'); + $ctype->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $headers = $this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, + 'Content-Type' => $ctype, + )); + + $attachment = $this->_createAttachment($headers, $this->_createEncoder(), + $this->_createCache() + ); + $attachment->setFile($file, 'text/html'); + } + + public function XtestContentTypeCanBeLookedUpFromCommonListIfNotProvided() + { + $file = $this->_createFileStream('/bar/file.zip', ''); + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array('filename' => 'foo.zip'), false + ); + $disposition->shouldReceive('setParameter') + ->once() + ->with('filename', 'file.zip'); + + $ctype = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $ctype->shouldReceive('setFieldBodyModel') + ->once() + ->with('application/zip'); + $ctype->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $headers = $this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, + 'Content-Type' => $ctype, + )); + + $attachment = $this->_createAttachment($headers, $this->_createEncoder(), + $this->_createCache(), array('zip' => 'application/zip', 'txt' => 'text/plain') + ); + $attachment->setFile($file); + } + + public function testDataCanBeReadFromFile() + { + $file = $this->_createFileStream('/foo/file.ext', '<some data>'); + $attachment = $this->_createAttachment($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $attachment->setFile($file); + $this->assertEquals('<some data>', $attachment->getBody()); + } + + public function testFluidInterface() + { + $attachment = $this->_createAttachment($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertSame($attachment, + $attachment + ->setContentType('application/pdf') + ->setEncoder($this->_createEncoder()) + ->setId('foo@bar') + ->setDescription('my pdf') + ->setMaxLineLength(998) + ->setBody('xx') + ->setBoundary('xyz') + ->setChildren(array()) + ->setDisposition('inline') + ->setFilename('afile.txt') + ->setSize(123) + ->setFile($this->_createFileStream('foo.txt', '')) + ); + } + + // -- Private helpers + + protected function _createEntity($headers, $encoder, $cache) + { + return $this->_createAttachment($headers, $encoder, $cache); + } + + protected function _createAttachment($headers, $encoder, $cache, $mimeTypes = array()) + { + return new Swift_Mime_Attachment($headers, $encoder, $cache, new Swift_Mime_Grammar(), $mimeTypes); + } + + protected function _createFileStream($path, $data, $stub = true) + { + $file = $this->getMockery('Swift_FileStream'); + $file->shouldReceive('getPath') + ->zeroOrMoreTimes() + ->andReturn($path); + $file->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use ($data) { + static $first = true; + if (!$first) { + return false; + } + + $first = false; + + return $data; + }); + $file->shouldReceive('setReadPointer') + ->zeroOrMoreTimes(); + + return $file; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/Base64ContentEncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/Base64ContentEncoderTest.php new file mode 100644 index 00000000..0442af33 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/Base64ContentEncoderTest.php @@ -0,0 +1,323 @@ +<?php + +class Swift_Mime_ContentEncoder_Base64ContentEncoderTest extends \SwiftMailerTestCase +{ + private $_encoder; + + public function setUp() + { + $this->_encoder = new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + } + + public function testNameIsBase64() + { + $this->assertEquals('base64', $this->_encoder->getName()); + } + + /* + There's really no point in testing the entire base64 encoding to the + level QP encoding has been tested. base64_encode() has been in PHP for + years. + */ + + public function testInputOutputRatioIs3to4Bytes() + { + /* + RFC 2045, 6.8 + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + */ + + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn('123'); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is); + $this->assertEquals('MTIz', $collection->content); + } + + public function testPadLength() + { + /* + RFC 2045, 6.8 + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a body. When fewer than 24 input bits + are available in an input group, zero bits are added (on the right) + to form an integral number of 6-bit groups. Padding at the end of + the data is performed using the "=" character. Since all base64 + input is an integral number of octets, only the following cases can + arise: (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded output will be + an integral multiple of 4 characters with no "=" padding, (2) the + final quantum of encoding input is exactly 8 bits; here, the final + unit of encoded output will be two characters followed by two "=" + padding characters, or (3) the final quantum of encoding input is + exactly 16 bits; here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + + for ($i = 0; $i < 30; ++$i) { + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn(pack('C', rand(0, 255))); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is); + $this->assertRegExp('~^[a-zA-Z0-9/\+]{2}==$~', $collection->content, + '%s: A single byte should have 2 bytes of padding' + ); + } + + for ($i = 0; $i < 30; ++$i) { + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn(pack('C*', rand(0, 255), rand(0, 255))); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is); + $this->assertRegExp('~^[a-zA-Z0-9/\+]{3}=$~', $collection->content, + '%s: Two bytes should have 1 byte of padding' + ); + } + + for ($i = 0; $i < 30; ++$i) { + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn(pack('C*', rand(0, 255), rand(0, 255), rand(0, 255))); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is); + $this->assertRegExp('~^[a-zA-Z0-9/\+]{4}$~', $collection->content, + '%s: Three bytes should have no padding' + ); + } + } + + public function testMaximumLineLengthIs76Characters() + { + /* + The encoded output stream must be represented in lines of no more + than 76 characters each. All line breaks or other characters not + found in Table 1 must be ignored by decoding software. + */ + + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //12 + $os->shouldReceive('read') + ->once() + ->andReturn('mnopqrstuvwx'); //24 + $os->shouldReceive('read') + ->once() + ->andReturn('yzabc1234567'); //36 + $os->shouldReceive('read') + ->once() + ->andReturn('890ABCDEFGHI'); //48 + $os->shouldReceive('read') + ->once() + ->andReturn('JKLMNOPQRSTU'); //60 + $os->shouldReceive('read') + ->once() + ->andReturn('VWXYZ1234567'); //72 + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //84 + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is); + $this->assertEquals( + "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmMxMjM0NTY3ODkwQUJDREVGR0hJSktMTU5PUFFS\r\n". + 'U1RVVldYWVoxMjM0NTY3YWJjZGVmZ2hpamts', + $collection->content + ); + } + + public function testMaximumLineLengthCanBeDifferent() + { + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //12 + $os->shouldReceive('read') + ->once() + ->andReturn('mnopqrstuvwx'); //24 + $os->shouldReceive('read') + ->once() + ->andReturn('yzabc1234567'); //36 + $os->shouldReceive('read') + ->once() + ->andReturn('890ABCDEFGHI'); //48 + $os->shouldReceive('read') + ->once() + ->andReturn('JKLMNOPQRSTU'); //60 + $os->shouldReceive('read') + ->once() + ->andReturn('VWXYZ1234567'); //72 + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //84 + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is, 0, 50); + $this->assertEquals( + "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmMxMjM0NTY3OD\r\n". + "kwQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVoxMjM0NTY3YWJj\r\n". + 'ZGVmZ2hpamts', + $collection->content + ); + } + + public function testMaximumLineLengthIsNeverMoreThan76Chars() + { + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //12 + $os->shouldReceive('read') + ->once() + ->andReturn('mnopqrstuvwx'); //24 + $os->shouldReceive('read') + ->once() + ->andReturn('yzabc1234567'); //36 + $os->shouldReceive('read') + ->once() + ->andReturn('890ABCDEFGHI'); //48 + $os->shouldReceive('read') + ->once() + ->andReturn('JKLMNOPQRSTU'); //60 + $os->shouldReceive('read') + ->once() + ->andReturn('VWXYZ1234567'); //72 + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //84 + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is, 0, 100); + $this->assertEquals( + "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmMxMjM0NTY3ODkwQUJDREVGR0hJSktMTU5PUFFS\r\n". + 'U1RVVldYWVoxMjM0NTY3YWJjZGVmZ2hpamts', + $collection->content + ); + } + + public function testFirstLineLengthCanBeDifferent() + { + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //12 + $os->shouldReceive('read') + ->once() + ->andReturn('mnopqrstuvwx'); //24 + $os->shouldReceive('read') + ->once() + ->andReturn('yzabc1234567'); //36 + $os->shouldReceive('read') + ->once() + ->andReturn('890ABCDEFGHI'); //48 + $os->shouldReceive('read') + ->once() + ->andReturn('JKLMNOPQRSTU'); //60 + $os->shouldReceive('read') + ->once() + ->andReturn('VWXYZ1234567'); //72 + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //84 + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is, 19); + $this->assertEquals( + "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmMxMjM0NTY3ODkwQUJDR\r\n". + 'EVGR0hJSktMTU5PUFFSU1RVVldYWVoxMjM0NTY3YWJjZGVmZ2hpamts', + $collection->content + ); + } + + private function _createOutputByteStream($stub = false) + { + return $this->getMockery('Swift_OutputByteStream')->shouldIgnoreMissing(); + } + + private function _createInputByteStream($stub = false) + { + return $this->getMockery('Swift_InputByteStream')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/PlainContentEncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/PlainContentEncoderTest.php new file mode 100644 index 00000000..b523a64c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/PlainContentEncoderTest.php @@ -0,0 +1,173 @@ +<?php + +class Swift_Mime_ContentEncoder_PlainContentEncoderTest extends \SwiftMailerTestCase +{ + public function testNameCanBeSpecifiedInConstructor() + { + $encoder = $this->_getEncoder('7bit'); + $this->assertEquals('7bit', $encoder->getName()); + + $encoder = $this->_getEncoder('8bit'); + $this->assertEquals('8bit', $encoder->getName()); + } + + public function testNoOctetsAreModifiedInString() + { + $encoder = $this->_getEncoder('7bit'); + foreach (range(0x00, 0xFF) as $octet) { + $byte = pack('C', $octet); + $this->assertIdenticalBinary($byte, $encoder->encodeString($byte)); + } + } + + public function testNoOctetsAreModifiedInByteStream() + { + $encoder = $this->_getEncoder('7bit'); + foreach (range(0x00, 0xFF) as $octet) { + $byte = pack('C', $octet); + + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn($byte); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder->encodeByteStream($os, $is); + $this->assertIdenticalBinary($byte, $collection->content); + } + } + + public function testLineLengthCanBeSpecified() + { + $encoder = $this->_getEncoder('7bit'); + + $chars = array(); + for ($i = 0; $i < 50; ++$i) { + $chars[] = 'a'; + } + $input = implode(' ', $chars); //99 chars long + + $this->assertEquals( + 'a a a a a a a a a a a a a a a a a a a a a a a a a '."\r\n".//50 * + 'a a a a a a a a a a a a a a a a a a a a a a a a a', //99 + $encoder->encodeString($input, 0, 50), + '%s: Lines should be wrapped at 50 chars' + ); + } + + public function testLineLengthCanBeSpecifiedInByteStream() + { + $encoder = $this->_getEncoder('7bit'); + + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + + for ($i = 0; $i < 50; ++$i) { + $os->shouldReceive('read') + ->once() + ->andReturn('a '); + } + + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder->encodeByteStream($os, $is, 0, 50); + $this->assertEquals( + str_repeat('a ', 25)."\r\n".str_repeat('a ', 25), + $collection->content + ); + } + + public function testencodeStringGeneratesCorrectCrlf() + { + $encoder = $this->_getEncoder('7bit', true); + $this->assertEquals("a\r\nb", $encoder->encodeString("a\rb"), + '%s: Line endings should be standardized' + ); + $this->assertEquals("a\r\nb", $encoder->encodeString("a\nb"), + '%s: Line endings should be standardized' + ); + $this->assertEquals("a\r\n\r\nb", $encoder->encodeString("a\n\rb"), + '%s: Line endings should be standardized' + ); + $this->assertEquals("a\r\n\r\nb", $encoder->encodeString("a\r\rb"), + '%s: Line endings should be standardized' + ); + $this->assertEquals("a\r\n\r\nb", $encoder->encodeString("a\n\nb"), + '%s: Line endings should be standardized' + ); + } + + public function crlfProvider() + { + return array( + array("\r", "a\r\nb"), + array("\n", "a\r\nb"), + array("\n\r", "a\r\n\r\nb"), + array("\n\n", "a\r\n\r\nb"), + array("\r\r", "a\r\n\r\nb"), + ); + } + + /** + * @dataProvider crlfProvider + */ + public function testCanonicEncodeByteStreamGeneratesCorrectCrlf($test, $expected) + { + $encoder = $this->_getEncoder('7bit', true); + + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn('a'); + $os->shouldReceive('read') + ->once() + ->andReturn($test); + $os->shouldReceive('read') + ->once() + ->andReturn('b'); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder->encodeByteStream($os, $is); + $this->assertEquals($expected, $collection->content); + } + + // -- Private helpers + + private function _getEncoder($name, $canonical = false) + { + return new Swift_Mime_ContentEncoder_PlainContentEncoder($name, $canonical); + } + + private function _createOutputByteStream($stub = false) + { + return $this->getMockery('Swift_OutputByteStream')->shouldIgnoreMissing(); + } + + private function _createInputByteStream($stub = false) + { + return $this->getMockery('Swift_InputByteStream')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/QpContentEncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/QpContentEncoderTest.php new file mode 100644 index 00000000..81992891 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/QpContentEncoderTest.php @@ -0,0 +1,518 @@ +<?php + +class Swift_Mime_ContentEncoder_QpContentEncoderTest extends \SwiftMailerTestCase +{ + public function testNameIsQuotedPrintable() + { + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder( + $this->_createCharacterStream(true) + ); + $this->assertEquals('quoted-printable', $encoder->getName()); + } + + /* -- RFC 2045, 6.7 -- + (1) (General 8bit representation) Any octet, except a CR or + LF that is part of a CRLF line break of the canonical + (standard) form of the data being encoded, may be + represented by an "=" followed by a two digit + hexadecimal representation of the octet's value. The + digits of the hexadecimal alphabet, for this purpose, + are "0123456789ABCDEF". Uppercase letters must be + used; lowercase letters are not allowed. Thus, for + example, the decimal value 12 (US-ASCII form feed) can + be represented by "=0C", and the decimal value 61 (US- + ASCII EQUAL SIGN) can be represented by "=3D". This + rule must be followed except when the following rules + allow an alternative encoding. + */ + + public function testPermittedCharactersAreNotEncoded() + { + /* -- RFC 2045, 6.7 -- + (2) (Literal representation) Octets with decimal values of + 33 through 60 inclusive, and 62 through 126, inclusive, + MAY be represented as the US-ASCII characters which + correspond to those octets (EXCLAMATION POINT through + LESS THAN, and GREATER THAN through TILDE, + respectively). + */ + + foreach (array_merge(range(33, 60), range(62, 126)) as $ordinal) { + $char = chr($ordinal); + + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($ordinal)); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + $this->assertIdenticalBinary($char, $collection->content); + } + } + + public function testLinearWhiteSpaceAtLineEndingIsEncoded() + { + /* -- RFC 2045, 6.7 -- + (3) (White Space) Octets with values of 9 and 32 MAY be + represented as US-ASCII TAB (HT) and SPACE characters, + respectively, but MUST NOT be so represented at the end + of an encoded line. Any TAB (HT) or SPACE characters + on an encoded line MUST thus be followed on that line + by a printable character. In particular, an "=" at the + end of an encoded line, indicating a soft line break + (see rule #5) may follow one or more TAB (HT) or SPACE + characters. It follows that an octet with decimal + value 9 or 32 appearing at the end of an encoded line + must be represented according to Rule #1. This rule is + necessary because some MTAs (Message Transport Agents, + programs which transport messages from one user to + another, or perform a portion of such transfers) are + known to pad lines of text with SPACEs, and others are + known to remove "white space" characters from the end + of a line. Therefore, when decoding a Quoted-Printable + body, any trailing white space on a line must be + deleted, as it will necessarily have been added by + intermediate transport agents. + */ + + $HT = chr(0x09); //9 + $SPACE = chr(0x20); //32 + + //HT + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x09)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x09)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + + $this->assertEquals("a\t=09\r\nb", $collection->content); + + //SPACE + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x20)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x20)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + + $this->assertEquals("a =20\r\nb", $collection->content); + } + + public function testCRLFIsLeftAlone() + { + /* + (4) (Line Breaks) A line break in a text body, represented + as a CRLF sequence in the text canonical form, must be + represented by a (RFC 822) line break, which is also a + CRLF sequence, in the Quoted-Printable encoding. Since + the canonical representation of media types other than + text do not generally include the representation of + line breaks as CRLF sequences, no hard line breaks + (i.e. line breaks that are intended to be meaningful + and to be displayed to the user) can occur in the + quoted-printable encoding of such types. Sequences + like "=0D", "=0A", "=0A=0D" and "=0D=0A" will routinely + appear in non-text data represented in quoted- + printable, of course. + + Note that many implementations may elect to encode the + local representation of various content types directly + rather than converting to canonical form first, + encoding, and then converting back to local + representation. In particular, this may apply to plain + text material on systems that use newline conventions + other than a CRLF terminator sequence. Such an + implementation optimization is permissible, but only + when the combined canonicalization-encoding step is + equivalent to performing the three steps separately. + */ + + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('c'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + $this->assertEquals("a\r\nb\r\nc\r\n", $collection->content); + } + + public function testLinesLongerThan76CharactersAreSoftBroken() + { + /* + (5) (Soft Line Breaks) The Quoted-Printable encoding + REQUIRES that encoded lines be no more than 76 + characters long. If longer lines are to be encoded + with the Quoted-Printable encoding, "soft" line breaks + must be used. An equal sign as the last character on a + encoded line indicates such a non-significant ("soft") + line break in the encoded text. + */ + + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + + for ($seq = 0; $seq <= 140; ++$seq) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + } + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + $this->assertEquals(str_repeat('a', 75)."=\r\n".str_repeat('a', 66), $collection->content); + } + + public function testMaxLineLengthCanBeSpecified() + { + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + + for ($seq = 0; $seq <= 100; ++$seq) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + } + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is, 0, 54); + $this->assertEquals(str_repeat('a', 53)."=\r\n".str_repeat('a', 48), $collection->content); + } + + public function testBytesBelowPermittedRangeAreEncoded() + { + /* + According to Rule (1 & 2) + */ + + foreach (range(0, 32) as $ordinal) { + $char = chr($ordinal); + + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($ordinal)); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + $this->assertEquals(sprintf('=%02X', $ordinal), $collection->content); + } + } + + public function testDecimalByte61IsEncoded() + { + /* + According to Rule (1 & 2) + */ + + $char = chr(61); + + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(61)); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + $this->assertEquals(sprintf('=%02X', 61), $collection->content); + } + + public function testBytesAbovePermittedRangeAreEncoded() + { + /* + According to Rule (1 & 2) + */ + + foreach (range(127, 255) as $ordinal) { + $char = chr($ordinal); + + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($ordinal)); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + $this->assertEquals(sprintf('=%02X', $ordinal), $collection->content); + } + } + + public function testFirstLineLengthCanBeDifferent() + { + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + + for ($seq = 0; $seq <= 140; ++$seq) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + } + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is, 22); + $this->assertEquals( + str_repeat('a', 53)."=\r\n".str_repeat('a', 75)."=\r\n".str_repeat('a', 13), + $collection->content + ); + } + + public function testObserverInterfaceCanChangeCharset() + { + $stream = $this->_createCharacterStream(); + $stream->shouldReceive('setCharacterSet') + ->once() + ->with('windows-1252'); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($stream); + $encoder->charsetChanged('windows-1252'); + } + + public function testTextIsPreWrapped() + { + $encoder = $this->createEncoder(); + + $input = str_repeat('a', 70)."\r\n". + str_repeat('a', 70)."\r\n". + str_repeat('a', 70); + + $os = new Swift_ByteStream_ArrayByteStream(); + $is = new Swift_ByteStream_ArrayByteStream(); + $is->write($input); + + $encoder->encodeByteStream($is, $os); + + $this->assertEquals( + $input, $os->read(PHP_INT_MAX) + ); + } + + // -- Creation Methods + + private function _createCharacterStream($stub = false) + { + return $this->getMockery('Swift_CharacterStream')->shouldIgnoreMissing(); + } + + private function createEncoder() + { + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $charStream = new Swift_CharacterStream_NgCharacterStream($factory, 'utf-8'); + + return new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + } + + private function _createOutputByteStream($stub = false) + { + return $this->getMockery('Swift_OutputByteStream')->shouldIgnoreMissing(); + } + + private function _createInputByteStream($stub = false) + { + return $this->getMockery('Swift_InputByteStream')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/EmbeddedFileTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/EmbeddedFileTest.php new file mode 100644 index 00000000..f4c3ac89 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/EmbeddedFileTest.php @@ -0,0 +1,57 @@ +<?php + +class Swift_Mime_EmbeddedFileTest extends Swift_Mime_AttachmentTest +{ + public function testNestingLevelIsAttachment() + { + //Overridden + } + + public function testNestingLevelIsEmbedded() + { + $file = $this->_createEmbeddedFile($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals( + Swift_Mime_MimeEntity::LEVEL_RELATED, $file->getNestingLevel() + ); + } + + public function testIdIsAutoGenerated() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addIdHeader') + ->once() + ->with('Content-ID', '/^.*?@.*?$/D'); + + $file = $this->_createEmbeddedFile($headers, $this->_createEncoder(), + $this->_createCache() + ); + } + + public function testDefaultDispositionIsInline() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addParameterizedHeader') + ->once() + ->with('Content-Disposition', 'inline'); + $headers->shouldReceive('addParameterizedHeader') + ->zeroOrMoreTimes(); + + $file = $this->_createEmbeddedFile($headers, $this->_createEncoder(), + $this->_createCache() + ); + } + + // -- Private helpers + + protected function _createAttachment($headers, $encoder, $cache, $mimeTypes = array()) + { + return $this->_createEmbeddedFile($headers, $encoder, $cache, $mimeTypes); + } + + private function _createEmbeddedFile($headers, $encoder, $cache) + { + return new Swift_Mime_EmbeddedFile($headers, $encoder, $cache, new Swift_Mime_Grammar()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/Base64HeaderEncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/Base64HeaderEncoderTest.php new file mode 100644 index 00000000..35801556 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/Base64HeaderEncoderTest.php @@ -0,0 +1,13 @@ +<?php + +class Swift_Mime_HeaderEncoder_Base64HeaderEncoderTest extends \PHPUnit_Framework_TestCase +{ + //Most tests are already covered in Base64EncoderTest since this subclass only + // adds a getName() method + + public function testNameIsB() + { + $encoder = new Swift_Mime_HeaderEncoder_Base64HeaderEncoder(); + $this->assertEquals('B', $encoder->getName()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/QpHeaderEncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/QpHeaderEncoderTest.php new file mode 100644 index 00000000..54a792a6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/QpHeaderEncoderTest.php @@ -0,0 +1,223 @@ +<?php + +class Swift_Mime_HeaderEncoder_QpHeaderEncoderTest extends \SwiftMailerTestCase +{ + //Most tests are already covered in QpEncoderTest since this subclass only + // adds a getName() method + + public function testNameIsQ() + { + $encoder = $this->_createEncoder( + $this->_createCharacterStream(true) + ); + $this->assertEquals('Q', $encoder->getName()); + } + + public function testSpaceAndTabNeverAppear() + { + /* -- RFC 2047, 4. + Only a subset of the printable ASCII characters may be used in + 'encoded-text'. Space and tab characters are not allowed, so that + the beginning and end of an 'encoded-word' are obvious. + */ + + $charStream = $this->_createCharacterStream(); + $charStream->shouldReceive('readBytes') + ->atLeast()->times(6) + ->andReturn(array(ord('a')), array(0x20), array(0x09), array(0x20), array(ord('b')), false); + + $encoder = $this->_createEncoder($charStream); + $this->assertNotRegExp('~[ \t]~', $encoder->encodeString("a \t b"), + '%s: encoded-words in headers cannot contain LWSP as per RFC 2047.' + ); + } + + public function testSpaceIsRepresentedByUnderscore() + { + /* -- RFC 2047, 4.2. + (2) The 8-bit hexadecimal value 20 (e.g., ISO-8859-1 SPACE) may be + represented as "_" (underscore, ASCII 95.). (This character may + not pass through some internetwork mail gateways, but its use + will greatly enhance readability of "Q" encoded data with mail + readers that do not support this encoding.) Note that the "_" + always represents hexadecimal 20, even if the SPACE character + occupies a different code position in the character set in use. + */ + $charStream = $this->_createCharacterStream(); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x20)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = $this->_createEncoder($charStream); + $this->assertEquals('a_b', $encoder->encodeString('a b'), + '%s: Spaces can be represented by more readable underscores as per RFC 2047.' + ); + } + + public function testEqualsAndQuestionAndUnderscoreAreEncoded() + { + /* -- RFC 2047, 4.2. + (3) 8-bit values which correspond to printable ASCII characters other + than "=", "?", and "_" (underscore), MAY be represented as those + characters. (But see section 5 for restrictions.) In + particular, SPACE and TAB MUST NOT be represented as themselves + within encoded words. + */ + $charStream = $this->_createCharacterStream(); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('='))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('?'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('_'))); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = $this->_createEncoder($charStream); + $this->assertEquals('=3D=3F=5F', $encoder->encodeString('=?_'), + '%s: Chars =, ? and _ (underscore) may not appear as per RFC 2047.' + ); + } + + public function testParensAndQuotesAreEncoded() + { + /* -- RFC 2047, 5 (2). + A "Q"-encoded 'encoded-word' which appears in a 'comment' MUST NOT + contain the characters "(", ")" or " + */ + + $charStream = $this->_createCharacterStream(); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('('))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('"'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord(')'))); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = $this->_createEncoder($charStream); + $this->assertEquals('=28=22=29', $encoder->encodeString('(")'), + '%s: Chars (, " (DQUOTE) and ) may not appear as per RFC 2047.' + ); + } + + public function testOnlyCharactersAllowedInPhrasesAreUsed() + { + /* -- RFC 2047, 5. + (3) As a replacement for a 'word' entity within a 'phrase', for example, + one that precedes an address in a From, To, or Cc header. The ABNF + definition for 'phrase' from RFC 822 thus becomes: + + phrase = 1*( encoded-word / word ) + + In this case the set of characters that may be used in a "Q"-encoded + 'encoded-word' is restricted to: <upper and lower case ASCII + letters, decimal digits, "!", "*", "+", "-", "/", "=", and "_" + (underscore, ASCII 95.)>. An 'encoded-word' that appears within a + 'phrase' MUST be separated from any adjacent 'word', 'text' or + 'special' by 'linear-white-space'. + */ + + $allowedBytes = array_merge( + range(ord('a'), ord('z')), range(ord('A'), ord('Z')), + range(ord('0'), ord('9')), + array(ord('!'), ord('*'), ord('+'), ord('-'), ord('/')) + ); + + foreach (range(0x00, 0xFF) as $byte) { + $char = pack('C', $byte); + + $charStream = $this->_createCharacterStream(); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($byte)); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = $this->_createEncoder($charStream); + $encodedChar = $encoder->encodeString($char); + + if (in_array($byte, $allowedBytes)) { + $this->assertEquals($char, $encodedChar, + '%s: Character '.$char.' should not be encoded.' + ); + } elseif (0x20 == $byte) { + //Special case + $this->assertEquals('_', $encodedChar, + '%s: Space character should be replaced.' + ); + } else { + $this->assertEquals(sprintf('=%02X', $byte), $encodedChar, + '%s: Byte '.$byte.' should be encoded.' + ); + } + } + } + + public function testEqualsNeverAppearsAtEndOfLine() + { + /* -- RFC 2047, 5 (3). + The 'encoded-text' in an 'encoded-word' must be self-contained; + 'encoded-text' MUST NOT be continued from one 'encoded-word' to + another. This implies that the 'encoded-text' portion of a "B" + 'encoded-word' will be a multiple of 4 characters long; for a "Q" + 'encoded-word', any "=" character that appears in the 'encoded-text' + portion will be followed by two hexadecimal characters. + */ + + $input = str_repeat('a', 140); + + $charStream = $this->_createCharacterStream(); + + $output = ''; + $seq = 0; + for (; $seq < 140; ++$seq) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + + if (75 == $seq) { + $output .= "\r\n"; // =\r\n + } + $output .= 'a'; + } + + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = $this->_createEncoder($charStream); + $this->assertEquals($output, $encoder->encodeString($input)); + } + + // -- Creation Methods + + private function _createEncoder($charStream) + { + return new Swift_Mime_HeaderEncoder_QpHeaderEncoder($charStream); + } + + private function _createCharacterStream($stub = false) + { + return $this->getMockery('Swift_CharacterStream')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/DateHeaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/DateHeaderTest.php new file mode 100644 index 00000000..1822ea68 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/DateHeaderTest.php @@ -0,0 +1,69 @@ +<?php + +class Swift_Mime_Headers_DateHeaderTest extends \PHPUnit_Framework_TestCase +{ + /* -- + The following tests refer to RFC 2822, section 3.6.1 and 3.3. + */ + + public function testTypeIsDateHeader() + { + $header = $this->_getHeader('Date'); + $this->assertEquals(Swift_Mime_Header::TYPE_DATE, $header->getFieldType()); + } + + public function testGetTimestamp() + { + $timestamp = time(); + $header = $this->_getHeader('Date'); + $header->setTimestamp($timestamp); + $this->assertSame($timestamp, $header->getTimestamp()); + } + + public function testTimestampCanBeSetBySetter() + { + $timestamp = time(); + $header = $this->_getHeader('Date'); + $header->setTimestamp($timestamp); + $this->assertSame($timestamp, $header->getTimestamp()); + } + + public function testIntegerTimestampIsConvertedToRfc2822Date() + { + $timestamp = time(); + $header = $this->_getHeader('Date'); + $header->setTimestamp($timestamp); + $this->assertEquals(date('r', $timestamp), $header->getFieldBody()); + } + + public function testSetBodyModel() + { + $timestamp = time(); + $header = $this->_getHeader('Date'); + $header->setFieldBodyModel($timestamp); + $this->assertEquals(date('r', $timestamp), $header->getFieldBody()); + } + + public function testGetBodyModel() + { + $timestamp = time(); + $header = $this->_getHeader('Date'); + $header->setTimestamp($timestamp); + $this->assertEquals($timestamp, $header->getFieldBodyModel()); + } + + public function testToString() + { + $timestamp = time(); + $header = $this->_getHeader('Date'); + $header->setTimestamp($timestamp); + $this->assertEquals('Date: '.date('r', $timestamp)."\r\n", + $header->toString() + ); + } + + private function _getHeader($name) + { + return new Swift_Mime_Headers_DateHeader($name, new Swift_Mime_Grammar()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/IdentificationHeaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/IdentificationHeaderTest.php new file mode 100644 index 00000000..93b3f609 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/IdentificationHeaderTest.php @@ -0,0 +1,189 @@ +<?php + +class Swift_Mime_Headers_IdentificationHeaderTest extends \PHPUnit_Framework_TestCase +{ + public function testTypeIsIdHeader() + { + $header = $this->_getHeader('Message-ID'); + $this->assertEquals(Swift_Mime_Header::TYPE_ID, $header->getFieldType()); + } + + public function testValueMatchesMsgIdSpec() + { + /* -- RFC 2822, 3.6.4. + message-id = "Message-ID:" msg-id CRLF + + in-reply-to = "In-Reply-To:" 1*msg-id CRLF + + references = "References:" 1*msg-id CRLF + + msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS] + + id-left = dot-atom-text / no-fold-quote / obs-id-left + + id-right = dot-atom-text / no-fold-literal / obs-id-right + + no-fold-quote = DQUOTE *(qtext / quoted-pair) DQUOTE + + no-fold-literal = "[" *(dtext / quoted-pair) "]" + */ + + $header = $this->_getHeader('Message-ID'); + $header->setId('id-left@id-right'); + $this->assertEquals('<id-left@id-right>', $header->getFieldBody()); + } + + public function testIdCanBeRetrievedVerbatim() + { + $header = $this->_getHeader('Message-ID'); + $header->setId('id-left@id-right'); + $this->assertEquals('id-left@id-right', $header->getId()); + } + + public function testMultipleIdsCanBeSet() + { + $header = $this->_getHeader('References'); + $header->setIds(array('a@b', 'x@y')); + $this->assertEquals(array('a@b', 'x@y'), $header->getIds()); + } + + public function testSettingMultipleIdsProducesAListValue() + { + /* -- RFC 2822, 3.6.4. + The "References:" and "In-Reply-To:" field each contain one or more + unique message identifiers, optionally separated by CFWS. + + .. SNIP .. + + in-reply-to = "In-Reply-To:" 1*msg-id CRLF + + references = "References:" 1*msg-id CRLF + */ + + $header = $this->_getHeader('References'); + $header->setIds(array('a@b', 'x@y')); + $this->assertEquals('<a@b> <x@y>', $header->getFieldBody()); + } + + public function testIdLeftCanBeQuoted() + { + /* -- RFC 2822, 3.6.4. + id-left = dot-atom-text / no-fold-quote / obs-id-left + */ + + $header = $this->_getHeader('References'); + $header->setId('"ab"@c'); + $this->assertEquals('"ab"@c', $header->getId()); + $this->assertEquals('<"ab"@c>', $header->getFieldBody()); + } + + public function testIdLeftCanContainAnglesAsQuotedPairs() + { + /* -- RFC 2822, 3.6.4. + no-fold-quote = DQUOTE *(qtext / quoted-pair) DQUOTE + */ + + $header = $this->_getHeader('References'); + $header->setId('"a\\<\\>b"@c'); + $this->assertEquals('"a\\<\\>b"@c', $header->getId()); + $this->assertEquals('<"a\\<\\>b"@c>', $header->getFieldBody()); + } + + public function testIdLeftCanBeDotAtom() + { + $header = $this->_getHeader('References'); + $header->setId('a.b+&%$.c@d'); + $this->assertEquals('a.b+&%$.c@d', $header->getId()); + $this->assertEquals('<a.b+&%$.c@d>', $header->getFieldBody()); + } + + public function testInvalidIdLeftThrowsException() + { + try { + $header = $this->_getHeader('References'); + $header->setId('a b c@d'); + $this->fail( + 'Exception should be thrown since "a b c" is not valid id-left.' + ); + } catch (Exception $e) { + } + } + + public function testIdRightCanBeDotAtom() + { + /* -- RFC 2822, 3.6.4. + id-right = dot-atom-text / no-fold-literal / obs-id-right + */ + + $header = $this->_getHeader('References'); + $header->setId('a@b.c+&%$.d'); + $this->assertEquals('a@b.c+&%$.d', $header->getId()); + $this->assertEquals('<a@b.c+&%$.d>', $header->getFieldBody()); + } + + public function testIdRightCanBeLiteral() + { + /* -- RFC 2822, 3.6.4. + no-fold-literal = "[" *(dtext / quoted-pair) "]" + */ + + $header = $this->_getHeader('References'); + $header->setId('a@[1.2.3.4]'); + $this->assertEquals('a@[1.2.3.4]', $header->getId()); + $this->assertEquals('<a@[1.2.3.4]>', $header->getFieldBody()); + } + + public function testInvalidIdRightThrowsException() + { + try { + $header = $this->_getHeader('References'); + $header->setId('a@b c d'); + $this->fail( + 'Exception should be thrown since "b c d" is not valid id-right.' + ); + } catch (Exception $e) { + } + } + + public function testMissingAtSignThrowsException() + { + /* -- RFC 2822, 3.6.4. + msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS] + */ + + try { + $header = $this->_getHeader('References'); + $header->setId('abc'); + $this->fail( + 'Exception should be thrown since "abc" is does not contain @.' + ); + } catch (Exception $e) { + } + } + + public function testSetBodyModel() + { + $header = $this->_getHeader('Message-ID'); + $header->setFieldBodyModel('a@b'); + $this->assertEquals(array('a@b'), $header->getIds()); + } + + public function testGetBodyModel() + { + $header = $this->_getHeader('Message-ID'); + $header->setId('a@b'); + $this->assertEquals(array('a@b'), $header->getFieldBodyModel()); + } + + public function testStringValue() + { + $header = $this->_getHeader('References'); + $header->setIds(array('a@b', 'x@y')); + $this->assertEquals('References: <a@b> <x@y>'."\r\n", $header->toString()); + } + + private function _getHeader($name) + { + return new Swift_Mime_Headers_IdentificationHeader($name, new Swift_Mime_Grammar()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/MailboxHeaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/MailboxHeaderTest.php new file mode 100644 index 00000000..0713ff4e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/MailboxHeaderTest.php @@ -0,0 +1,327 @@ +<?php + +class Swift_Mime_Headers_MailboxHeaderTest extends \SwiftMailerTestCase +{ + /* -- RFC 2822, 3.6.2 for all tests. + */ + + private $_charset = 'utf-8'; + + public function testTypeIsMailboxHeader() + { + $header = $this->_getHeader('To', $this->_getEncoder('Q', true)); + $this->assertEquals(Swift_Mime_Header::TYPE_MAILBOX, $header->getFieldType()); + } + + public function testMailboxIsSetForAddress() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setAddresses('chris@swiftmailer.org'); + $this->assertEquals(array('chris@swiftmailer.org'), + $header->getNameAddressStrings() + ); + } + + public function testMailboxIsRenderedForNameAddress() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array('chris@swiftmailer.org' => 'Chris Corbyn')); + $this->assertEquals( + array('Chris Corbyn <chris@swiftmailer.org>'), $header->getNameAddressStrings() + ); + } + + public function testAddressCanBeReturnedForAddress() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setAddresses('chris@swiftmailer.org'); + $this->assertEquals(array('chris@swiftmailer.org'), $header->getAddresses()); + } + + public function testAddressCanBeReturnedForNameAddress() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array('chris@swiftmailer.org' => 'Chris Corbyn')); + $this->assertEquals(array('chris@swiftmailer.org'), $header->getAddresses()); + } + + public function testQuotesInNameAreQuoted() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn, "DHE"', + )); + $this->assertEquals( + array('"Chris Corbyn, \"DHE\"" <chris@swiftmailer.org>'), + $header->getNameAddressStrings() + ); + } + + public function testEscapeCharsInNameAreQuoted() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn, \\escaped\\', + )); + $this->assertEquals( + array('"Chris Corbyn, \\\\escaped\\\\" <chris@swiftmailer.org>'), + $header->getNameAddressStrings() + ); + } + + public function testGetMailboxesReturnsNameValuePairs() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn, DHE', + )); + $this->assertEquals( + array('chris@swiftmailer.org' => 'Chris Corbyn, DHE'), $header->getNameAddresses() + ); + } + + public function testMultipleAddressesCanBeSetAndFetched() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setAddresses(array( + 'chris@swiftmailer.org', 'mark@swiftmailer.org', + )); + $this->assertEquals( + array('chris@swiftmailer.org', 'mark@swiftmailer.org'), + $header->getAddresses() + ); + } + + public function testMultipleAddressesAsMailboxes() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setAddresses(array( + 'chris@swiftmailer.org', 'mark@swiftmailer.org', + )); + $this->assertEquals( + array('chris@swiftmailer.org' => null, 'mark@swiftmailer.org' => null), + $header->getNameAddresses() + ); + } + + public function testMultipleAddressesAsMailboxStrings() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setAddresses(array( + 'chris@swiftmailer.org', 'mark@swiftmailer.org', + )); + $this->assertEquals( + array('chris@swiftmailer.org', 'mark@swiftmailer.org'), + $header->getNameAddressStrings() + ); + } + + public function testMultipleNamedMailboxesReturnsMultipleAddresses() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $this->assertEquals( + array('chris@swiftmailer.org', 'mark@swiftmailer.org'), + $header->getAddresses() + ); + } + + public function testMultipleNamedMailboxesReturnsMultipleMailboxes() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $this->assertEquals(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + ), + $header->getNameAddresses() + ); + } + + public function testMultipleMailboxesProducesMultipleMailboxStrings() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $this->assertEquals(array( + 'Chris Corbyn <chris@swiftmailer.org>', + 'Mark Corbyn <mark@swiftmailer.org>', + ), + $header->getNameAddressStrings() + ); + } + + public function testSetAddressesOverwritesAnyMailboxes() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $this->assertEquals( + array('chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', ), + $header->getNameAddresses() + ); + $this->assertEquals( + array('chris@swiftmailer.org', 'mark@swiftmailer.org'), + $header->getAddresses() + ); + + $header->setAddresses(array('chris@swiftmailer.org', 'mark@swiftmailer.org')); + + $this->assertEquals( + array('chris@swiftmailer.org' => null, 'mark@swiftmailer.org' => null), + $header->getNameAddresses() + ); + $this->assertEquals( + array('chris@swiftmailer.org', 'mark@swiftmailer.org'), + $header->getAddresses() + ); + } + + public function testNameIsEncodedIfNonAscii() + { + $name = 'C'.pack('C', 0x8F).'rbyn'; + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($name, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('C=8Frbyn'); + + $header = $this->_getHeader('From', $encoder); + $header->setNameAddresses(array('chris@swiftmailer.org' => 'Chris '.$name)); + + $addresses = $header->getNameAddressStrings(); + $this->assertEquals( + 'Chris =?'.$this->_charset.'?Q?C=8Frbyn?= <chris@swiftmailer.org>', + array_shift($addresses) + ); + } + + public function testEncodingLineLengthCalculations() + { + /* -- RFC 2047, 2. + An 'encoded-word' may not be more than 75 characters long, including + 'charset', 'encoding', 'encoded-text', and delimiters. + */ + + $name = 'C'.pack('C', 0x8F).'rbyn'; + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($name, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('C=8Frbyn'); + + $header = $this->_getHeader('From', $encoder); + $header->setNameAddresses(array('chris@swiftmailer.org' => 'Chris '.$name)); + + $header->getNameAddressStrings(); + } + + public function testGetValueReturnsMailboxStringValue() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + )); + $this->assertEquals( + 'Chris Corbyn <chris@swiftmailer.org>', $header->getFieldBody() + ); + } + + public function testGetValueReturnsMailboxStringValueForMultipleMailboxes() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $this->assertEquals( + 'Chris Corbyn <chris@swiftmailer.org>, Mark Corbyn <mark@swiftmailer.org>', + $header->getFieldBody() + ); + } + + public function testRemoveAddressesWithSingleValue() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $header->removeAddresses('chris@swiftmailer.org'); + $this->assertEquals(array('mark@swiftmailer.org'), + $header->getAddresses() + ); + } + + public function testRemoveAddressesWithList() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $header->removeAddresses( + array('chris@swiftmailer.org', 'mark@swiftmailer.org') + ); + $this->assertEquals(array(), $header->getAddresses()); + } + + public function testSetBodyModel() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setFieldBodyModel('chris@swiftmailer.org'); + $this->assertEquals(array('chris@swiftmailer.org' => null), $header->getNameAddresses()); + } + + public function testGetBodyModel() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setAddresses(array('chris@swiftmailer.org')); + $this->assertEquals(array('chris@swiftmailer.org' => null), $header->getFieldBodyModel()); + } + + public function testToString() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $this->assertEquals( + 'From: Chris Corbyn <chris@swiftmailer.org>, '. + 'Mark Corbyn <mark@swiftmailer.org>'."\r\n", + $header->toString() + ); + } + + private function _getHeader($name, $encoder) + { + $header = new Swift_Mime_Headers_MailboxHeader($name, $encoder, new Swift_Mime_Grammar()); + $header->setCharset($this->_charset); + + return $header; + } + + private function _getEncoder($type, $stub = false) + { + $encoder = $this->getMockery('Swift_Mime_HeaderEncoder')->shouldIgnoreMissing(); + $encoder->shouldReceive('getName') + ->zeroOrMoreTimes() + ->andReturn($type); + + return $encoder; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/ParameterizedHeaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/ParameterizedHeaderTest.php new file mode 100644 index 00000000..0f3fe145 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/ParameterizedHeaderTest.php @@ -0,0 +1,400 @@ +<?php + +class Swift_Mime_Headers_ParameterizedHeaderTest extends \SwiftMailerTestCase +{ + private $_charset = 'utf-8'; + private $_lang = 'en-us'; + + public function testTypeIsParameterizedHeader() + { + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $this->assertEquals(Swift_Mime_Header::TYPE_PARAMETERIZED, $header->getFieldType()); + } + + public function testValueIsReturnedVerbatim() + { + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setValue('text/plain'); + $this->assertEquals('text/plain', $header->getValue()); + } + + public function testParametersAreAppended() + { + /* -- RFC 2045, 5.1 + parameter := attribute "=" value + + attribute := token + ; Matching of attributes + ; is ALWAYS case-insensitive. + + value := token / quoted-string + + token := 1*<any (US-ASCII) CHAR except SPACE, CTLs, + or tspecials> + + tspecials := "(" / ")" / "<" / ">" / "@" / + "," / ";" / ":" / "\" / <"> + "/" / "[" / "]" / "?" / "=" + ; Must be in quoted-string, + ; to use within parameter values + */ + + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setValue('text/plain'); + $header->setParameters(array('charset' => 'utf-8')); + $this->assertEquals('text/plain; charset=utf-8', $header->getFieldBody()); + } + + public function testSpaceInParamResultsInQuotedString() + { + $header = $this->_getHeader('Content-Disposition', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setValue('attachment'); + $header->setParameters(array('filename' => 'my file.txt')); + $this->assertEquals('attachment; filename="my file.txt"', + $header->getFieldBody() + ); + } + + public function testLongParamsAreBrokenIntoMultipleAttributeStrings() + { + /* -- RFC 2231, 3. + The asterisk character ("*") followed + by a decimal count is employed to indicate that multiple parameters + are being used to encapsulate a single parameter value. The count + starts at 0 and increments by 1 for each subsequent section of the + parameter value. Decimal values are used and neither leading zeroes + nor gaps in the sequence are allowed. + + The original parameter value is recovered by concatenating the + various sections of the parameter, in order. For example, the + content-type field + + Content-Type: message/external-body; access-type=URL; + URL*0="ftp://"; + URL*1="cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar" + + is semantically identical to + + Content-Type: message/external-body; access-type=URL; + URL="ftp://cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar" + + Note that quotes around parameter values are part of the value + syntax; they are NOT part of the value itself. Furthermore, it is + explicitly permitted to have a mixture of quoted and unquoted + continuation fields. + */ + + $value = str_repeat('a', 180); + + $encoder = $this->_getParameterEncoder(); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), 63, \Mockery::any()) + ->andReturn(str_repeat('a', 63)."\r\n". + str_repeat('a', 63)."\r\n".str_repeat('a', 54)); + + $header = $this->_getHeader('Content-Disposition', + $this->_getHeaderEncoder('Q', true), $encoder + ); + $header->setValue('attachment'); + $header->setParameters(array('filename' => $value)); + $header->setMaxLineLength(78); + $this->assertEquals( + 'attachment; '. + 'filename*0*=utf-8\'\''.str_repeat('a', 63).";\r\n ". + 'filename*1*='.str_repeat('a', 63).";\r\n ". + 'filename*2*='.str_repeat('a', 54), + $header->getFieldBody() + ); + } + + public function testEncodedParamDataIncludesCharsetAndLanguage() + { + /* -- RFC 2231, 4. + Asterisks ("*") are reused to provide the indicator that language and + character set information is present and encoding is being used. A + single quote ("'") is used to delimit the character set and language + information at the beginning of the parameter value. Percent signs + ("%") are used as the encoding flag, which agrees with RFC 2047. + + Specifically, an asterisk at the end of a parameter name acts as an + indicator that character set and language information may appear at + the beginning of the parameter value. A single quote is used to + separate the character set, language, and actual value information in + the parameter value string, and an percent sign is used to flag + octets encoded in hexadecimal. For example: + + Content-Type: application/x-stuff; + title*=us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A + + Note that it is perfectly permissible to leave either the character + set or language field blank. Note also that the single quote + delimiters MUST be present even when one of the field values is + omitted. + */ + + $value = str_repeat('a', 20).pack('C', 0x8F).str_repeat('a', 10); + + $encoder = $this->_getParameterEncoder(); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, 12, 62, \Mockery::any()) + ->andReturn(str_repeat('a', 20).'%8F'.str_repeat('a', 10)); + + $header = $this->_getHeader('Content-Disposition', + $this->_getHeaderEncoder('Q', true), $encoder + ); + $header->setValue('attachment'); + $header->setParameters(array('filename' => $value)); + $header->setMaxLineLength(78); + $header->setLanguage($this->_lang); + $this->assertEquals( + 'attachment; filename*='.$this->_charset."'".$this->_lang."'". + str_repeat('a', 20).'%8F'.str_repeat('a', 10), + $header->getFieldBody() + ); + } + + public function testMultipleEncodedParamLinesAreFormattedCorrectly() + { + /* -- RFC 2231, 4.1. + Character set and language information may be combined with the + parameter continuation mechanism. For example: + + Content-Type: application/x-stuff + title*0*=us-ascii'en'This%20is%20even%20more%20 + title*1*=%2A%2A%2Afun%2A%2A%2A%20 + title*2="isn't it!" + + Note that: + + (1) Language and character set information only appear at + the beginning of a given parameter value. + + (2) Continuations do not provide a facility for using more + than one character set or language in the same + parameter value. + + (3) A value presented using multiple continuations may + contain a mixture of encoded and unencoded segments. + + (4) The first segment of a continuation MUST be encoded if + language and character set information are given. + + (5) If the first segment of a continued parameter value is + encoded the language and character set field delimiters + MUST be present even when the fields are left blank. + */ + + $value = str_repeat('a', 20).pack('C', 0x8F).str_repeat('a', 60); + + $encoder = $this->_getParameterEncoder(); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, 12, 62, \Mockery::any()) + ->andReturn(str_repeat('a', 20).'%8F'.str_repeat('a', 28)."\r\n". + str_repeat('a', 32)); + + $header = $this->_getHeader('Content-Disposition', + $this->_getHeaderEncoder('Q', true), $encoder + ); + $header->setValue('attachment'); + $header->setParameters(array('filename' => $value)); + $header->setMaxLineLength(78); + $header->setLanguage($this->_lang); + $this->assertEquals( + 'attachment; filename*0*='.$this->_charset."'".$this->_lang."'". + str_repeat('a', 20).'%8F'.str_repeat('a', 28).";\r\n ". + 'filename*1*='.str_repeat('a', 32), + $header->getFieldBody() + ); + } + + public function testToString() + { + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setValue('text/html'); + $header->setParameters(array('charset' => 'utf-8')); + $this->assertEquals('Content-Type: text/html; charset=utf-8'."\r\n", + $header->toString() + ); + } + + public function testValueCanBeEncodedIfNonAscii() + { + $value = 'fo'.pack('C', 0x8F).'bar'; + + $encoder = $this->_getHeaderEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo=8Fbar'); + + $header = $this->_getHeader('X-Foo', $encoder, $this->_getParameterEncoder(true)); + $header->setValue($value); + $header->setParameters(array('lookslike' => 'foobar')); + $this->assertEquals('X-Foo: =?utf-8?Q?fo=8Fbar?=; lookslike=foobar'."\r\n", + $header->toString() + ); + } + + public function testValueAndParamCanBeEncodedIfNonAscii() + { + $value = 'fo'.pack('C', 0x8F).'bar'; + + $encoder = $this->_getHeaderEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo=8Fbar'); + + $paramEncoder = $this->_getParameterEncoder(); + $paramEncoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo%8Fbar'); + + $header = $this->_getHeader('X-Foo', $encoder, $paramEncoder); + $header->setValue($value); + $header->setParameters(array('says' => $value)); + $this->assertEquals("X-Foo: =?utf-8?Q?fo=8Fbar?=; says*=utf-8''fo%8Fbar\r\n", + $header->toString() + ); + } + + public function testParamsAreEncodedWithEncodedWordsIfNoParamEncoderSet() + { + $value = 'fo'.pack('C', 0x8F).'bar'; + + $encoder = $this->_getHeaderEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo=8Fbar'); + + $header = $this->_getHeader('X-Foo', $encoder, null); + $header->setValue('bar'); + $header->setParameters(array('says' => $value)); + $this->assertEquals("X-Foo: bar; says=\"=?utf-8?Q?fo=8Fbar?=\"\r\n", + $header->toString() + ); + } + + public function testLanguageInformationAppearsInEncodedWords() + { + /* -- RFC 2231, 5. + 5. Language specification in Encoded Words + + RFC 2047 provides support for non-US-ASCII character sets in RFC 822 + message header comments, phrases, and any unstructured text field. + This is done by defining an encoded word construct which can appear + in any of these places. Given that these are fields intended for + display, it is sometimes necessary to associate language information + with encoded words as well as just the character set. This + specification extends the definition of an encoded word to allow the + inclusion of such information. This is simply done by suffixing the + character set specification with an asterisk followed by the language + tag. For example: + + From: =?US-ASCII*EN?Q?Keith_Moore?= <moore@cs.utk.edu> + */ + + $value = 'fo'.pack('C', 0x8F).'bar'; + + $encoder = $this->_getHeaderEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo=8Fbar'); + + $paramEncoder = $this->_getParameterEncoder(); + $paramEncoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo%8Fbar'); + + $header = $this->_getHeader('X-Foo', $encoder, $paramEncoder); + $header->setLanguage('en'); + $header->setValue($value); + $header->setParameters(array('says' => $value)); + $this->assertEquals("X-Foo: =?utf-8*en?Q?fo=8Fbar?=; says*=utf-8'en'fo%8Fbar\r\n", + $header->toString() + ); + } + + public function testSetBodyModel() + { + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setFieldBodyModel('text/html'); + $this->assertEquals('text/html', $header->getValue()); + } + + public function testGetBodyModel() + { + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setValue('text/plain'); + $this->assertEquals('text/plain', $header->getFieldBodyModel()); + } + + public function testSetParameter() + { + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setParameters(array('charset' => 'utf-8', 'delsp' => 'yes')); + $header->setParameter('delsp', 'no'); + $this->assertEquals(array('charset' => 'utf-8', 'delsp' => 'no'), + $header->getParameters() + ); + } + + public function testGetParameter() + { + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setParameters(array('charset' => 'utf-8', 'delsp' => 'yes')); + $this->assertEquals('utf-8', $header->getParameter('charset')); + } + + // -- Private helper + + private function _getHeader($name, $encoder, $paramEncoder) + { + $header = new Swift_Mime_Headers_ParameterizedHeader($name, $encoder, + $paramEncoder, new Swift_Mime_Grammar() + ); + $header->setCharset($this->_charset); + + return $header; + } + + private function _getHeaderEncoder($type, $stub = false) + { + $encoder = $this->getMockery('Swift_Mime_HeaderEncoder')->shouldIgnoreMissing(); + $encoder->shouldReceive('getName') + ->zeroOrMoreTimes() + ->andReturn($type); + + return $encoder; + } + + private function _getParameterEncoder($stub = false) + { + return $this->getMockery('Swift_Encoder')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/PathHeaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/PathHeaderTest.php new file mode 100644 index 00000000..a9f35e9d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/PathHeaderTest.php @@ -0,0 +1,77 @@ +<?php + +class Swift_Mime_Headers_PathHeaderTest extends \PHPUnit_Framework_TestCase +{ + public function testTypeIsPathHeader() + { + $header = $this->_getHeader('Return-Path'); + $this->assertEquals(Swift_Mime_Header::TYPE_PATH, $header->getFieldType()); + } + + public function testSingleAddressCanBeSetAndFetched() + { + $header = $this->_getHeader('Return-Path'); + $header->setAddress('chris@swiftmailer.org'); + $this->assertEquals('chris@swiftmailer.org', $header->getAddress()); + } + + public function testAddressMustComplyWithRfc2822() + { + try { + $header = $this->_getHeader('Return-Path'); + $header->setAddress('chr is@swiftmailer.org'); + $this->fail('Addresses not valid according to RFC 2822 addr-spec grammar must be rejected.'); + } catch (Exception $e) { + } + } + + public function testValueIsAngleAddrWithValidAddress() + { + /* -- RFC 2822, 3.6.7. + + return = "Return-Path:" path CRLF + + path = ([CFWS] "<" ([CFWS] / addr-spec) ">" [CFWS]) / + obs-path + */ + + $header = $this->_getHeader('Return-Path'); + $header->setAddress('chris@swiftmailer.org'); + $this->assertEquals('<chris@swiftmailer.org>', $header->getFieldBody()); + } + + public function testValueIsEmptyAngleBracketsIfEmptyAddressSet() + { + $header = $this->_getHeader('Return-Path'); + $header->setAddress(''); + $this->assertEquals('<>', $header->getFieldBody()); + } + + public function testSetBodyModel() + { + $header = $this->_getHeader('Return-Path'); + $header->setFieldBodyModel('foo@bar.tld'); + $this->assertEquals('foo@bar.tld', $header->getAddress()); + } + + public function testGetBodyModel() + { + $header = $this->_getHeader('Return-Path'); + $header->setAddress('foo@bar.tld'); + $this->assertEquals('foo@bar.tld', $header->getFieldBodyModel()); + } + + public function testToString() + { + $header = $this->_getHeader('Return-Path'); + $header->setAddress('chris@swiftmailer.org'); + $this->assertEquals('Return-Path: <chris@swiftmailer.org>'."\r\n", + $header->toString() + ); + } + + private function _getHeader($name) + { + return new Swift_Mime_Headers_PathHeader($name, new Swift_Mime_Grammar()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/UnstructuredHeaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/UnstructuredHeaderTest.php new file mode 100644 index 00000000..2e1dc8ca --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/UnstructuredHeaderTest.php @@ -0,0 +1,355 @@ +<?php + +class Swift_Mime_Headers_UnstructuredHeaderTest extends \SwiftMailerTestCase +{ + private $_charset = 'utf-8'; + + public function testTypeIsTextHeader() + { + $header = $this->_getHeader('Subject', $this->_getEncoder('Q', true)); + $this->assertEquals(Swift_Mime_Header::TYPE_TEXT, $header->getFieldType()); + } + + public function testGetNameReturnsNameVerbatim() + { + $header = $this->_getHeader('Subject', $this->_getEncoder('Q', true)); + $this->assertEquals('Subject', $header->getFieldName()); + } + + public function testGetValueReturnsValueVerbatim() + { + $header = $this->_getHeader('Subject', $this->_getEncoder('Q', true)); + $header->setValue('Test'); + $this->assertEquals('Test', $header->getValue()); + } + + public function testBasicStructureIsKeyValuePair() + { + /* -- RFC 2822, 2.2 + Header fields are lines composed of a field name, followed by a colon + (":"), followed by a field body, and terminated by CRLF. + */ + $header = $this->_getHeader('Subject', $this->_getEncoder('Q', true)); + $header->setValue('Test'); + $this->assertEquals('Subject: Test'."\r\n", $header->toString()); + } + + public function testLongHeadersAreFoldedAtWordBoundary() + { + /* -- RFC 2822, 2.2.3 + Each header field is logically a single line of characters comprising + the field name, the colon, and the field body. For convenience + however, and to deal with the 998/78 character limitations per line, + the field body portion of a header field can be split into a multiple + line representation; this is called "folding". The general rule is + that wherever this standard allows for folding white space (not + simply WSP characters), a CRLF may be inserted before any WSP. + */ + + $value = 'The quick brown fox jumped over the fence, he was a very very '. + 'scary brown fox with a bushy tail'; + $header = $this->_getHeader('X-Custom-Header', + $this->_getEncoder('Q', true) + ); + $header->setValue($value); + $header->setMaxLineLength(78); //A safe [RFC 2822, 2.2.3] default + /* + X-Custom-Header: The quick brown fox jumped over the fence, he was a very very + scary brown fox with a bushy tail + */ + $this->assertEquals( + 'X-Custom-Header: The quick brown fox jumped over the fence, he was a'. + ' very very'."\r\n".//Folding + ' scary brown fox with a bushy tail'."\r\n", + $header->toString(), '%s: The header should have been folded at 78th char' + ); + } + + public function testPrintableAsciiOnlyAppearsInHeaders() + { + /* -- RFC 2822, 2.2. + A field name MUST be composed of printable US-ASCII characters (i.e., + characters that have values between 33 and 126, inclusive), except + colon. A field body may be composed of any US-ASCII characters, + except for CR and LF. + */ + + $nonAsciiChar = pack('C', 0x8F); + $header = $this->_getHeader('X-Test', $this->_getEncoder('Q', true)); + $header->setValue($nonAsciiChar); + $this->assertRegExp( + '~^[^:\x00-\x20\x80-\xFF]+: [^\x80-\xFF\r\n]+\r\n$~s', + $header->toString() + ); + } + + public function testEncodedWordsFollowGeneralStructure() + { + /* -- RFC 2047, 1. + Generally, an "encoded-word" is a sequence of printable ASCII + characters that begins with "=?", ends with "?=", and has two "?"s in + between. + */ + + $nonAsciiChar = pack('C', 0x8F); + $header = $this->_getHeader('X-Test', $this->_getEncoder('Q', true)); + $header->setValue($nonAsciiChar); + $this->assertRegExp( + '~^X-Test: \=?.*?\?.*?\?.*?\?=\r\n$~s', + $header->toString() + ); + } + + public function testEncodedWordIncludesCharsetAndEncodingMethodAndText() + { + /* -- RFC 2047, 2. + An 'encoded-word' is defined by the following ABNF grammar. The + notation of RFC 822 is used, with the exception that white space + characters MUST NOT appear between components of an 'encoded-word'. + + encoded-word = "=?" charset "?" encoding "?" encoded-text "?=" + */ + + $nonAsciiChar = pack('C', 0x8F); + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($nonAsciiChar, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('=8F'); + + $header = $this->_getHeader('X-Test', $encoder); + $header->setValue($nonAsciiChar); + $this->assertEquals( + 'X-Test: =?'.$this->_charset.'?Q?=8F?='."\r\n", + $header->toString() + ); + } + + public function testEncodedWordsAreUsedToEncodedNonPrintableAscii() + { + //SPACE and TAB permitted + $nonPrintableBytes = array_merge( + range(0x00, 0x08), range(0x10, 0x19), array(0x7F) + ); + + foreach ($nonPrintableBytes as $byte) { + $char = pack('C', $byte); + $encodedChar = sprintf('=%02X', $byte); + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($char, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn($encodedChar); + + $header = $this->_getHeader('X-A', $encoder); + $header->setValue($char); + + $this->assertEquals( + 'X-A: =?'.$this->_charset.'?Q?'.$encodedChar.'?='."\r\n", + $header->toString(), '%s: Non-printable ascii should be encoded' + ); + } + } + + public function testEncodedWordsAreUsedToEncode8BitOctets() + { + $_8BitBytes = range(0x80, 0xFF); + + foreach ($_8BitBytes as $byte) { + $char = pack('C', $byte); + $encodedChar = sprintf('=%02X', $byte); + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($char, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn($encodedChar); + + $header = $this->_getHeader('X-A', $encoder); + $header->setValue($char); + + $this->assertEquals( + 'X-A: =?'.$this->_charset.'?Q?'.$encodedChar.'?='."\r\n", + $header->toString(), '%s: 8-bit octets should be encoded' + ); + } + } + + public function testEncodedWordsAreNoMoreThan75CharsPerLine() + { + /* -- RFC 2047, 2. + An 'encoded-word' may not be more than 75 characters long, including + 'charset', 'encoding', 'encoded-text', and delimiters. + + ... SNIP ... + + While there is no limit to the length of a multiple-line header + field, each line of a header field that contains one or more + 'encoded-word's is limited to 76 characters. + */ + + $nonAsciiChar = pack('C', 0x8F); + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($nonAsciiChar, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('=8F'); + //Note that multi-line headers begin with LWSP which makes 75 + 1 = 76 + //Note also that =?utf-8?q??= is 12 chars which makes 75 - 12 = 63 + + //* X-Test: is 8 chars + $header = $this->_getHeader('X-Test', $encoder); + $header->setValue($nonAsciiChar); + + $this->assertEquals( + 'X-Test: =?'.$this->_charset.'?Q?=8F?='."\r\n", + $header->toString() + ); + } + + public function testFWSPIsUsedWhenEncoderReturnsMultipleLines() + { + /* --RFC 2047, 2. + If it is desirable to encode more text than will fit in an 'encoded-word' of + 75 characters, multiple 'encoded-word's (separated by CRLF SPACE) may + be used. + */ + + //Note the Mock does NOT return 8F encoded, the 8F merely triggers + // encoding for the sake of testing + $nonAsciiChar = pack('C', 0x8F); + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($nonAsciiChar, 8, 63, \Mockery::any()) + ->andReturn('line_one_here'."\r\n".'line_two_here'); + + //Note that multi-line headers begin with LWSP which makes 75 + 1 = 76 + //Note also that =?utf-8?q??= is 12 chars which makes 75 - 12 = 63 + + //* X-Test: is 8 chars + $header = $this->_getHeader('X-Test', $encoder); + $header->setValue($nonAsciiChar); + + $this->assertEquals( + 'X-Test: =?'.$this->_charset.'?Q?line_one_here?='."\r\n". + ' =?'.$this->_charset.'?Q?line_two_here?='."\r\n", + $header->toString() + ); + } + + public function testAdjacentWordsAreEncodedTogether() + { + /* -- RFC 2047, 5 (1) + Ordinary ASCII text and 'encoded-word's may appear together in the + same header field. However, an 'encoded-word' that appears in a + header field defined as '*text' MUST be separated from any adjacent + 'encoded-word' or 'text' by 'linear-white-space'. + + -- RFC 2047, 2. + IMPORTANT: 'encoded-word's are designed to be recognized as 'atom's + by an RFC 822 parser. As a consequence, unencoded white space + characters (such as SPACE and HTAB) are FORBIDDEN within an + 'encoded-word'. + */ + + //It would be valid to encode all words needed, however it's probably + // easiest to encode the longest amount required at a time + + $word = 'w'.pack('C', 0x8F).'rd'; + $text = 'start '.$word.' '.$word.' then end '.$word; + // 'start', ' word word', ' and end', ' word' + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($word.' '.$word, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('w=8Frd_w=8Frd'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($word, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('w=8Frd'); + + $header = $this->_getHeader('X-Test', $encoder); + $header->setValue($text); + + $headerString = $header->toString(); + + $this->assertEquals('X-Test: start =?'.$this->_charset.'?Q?'. + 'w=8Frd_w=8Frd?= then end =?'.$this->_charset.'?Q?'. + 'w=8Frd?='."\r\n", $headerString, + '%s: Adjacent encoded words should appear grouped with WSP encoded' + ); + } + + public function testLanguageInformationAppearsInEncodedWords() + { + /* -- RFC 2231, 5. + 5. Language specification in Encoded Words + + RFC 2047 provides support for non-US-ASCII character sets in RFC 822 + message header comments, phrases, and any unstructured text field. + This is done by defining an encoded word construct which can appear + in any of these places. Given that these are fields intended for + display, it is sometimes necessary to associate language information + with encoded words as well as just the character set. This + specification extends the definition of an encoded word to allow the + inclusion of such information. This is simply done by suffixing the + character set specification with an asterisk followed by the language + tag. For example: + + From: =?US-ASCII*EN?Q?Keith_Moore?= <moore@cs.utk.edu> + */ + + $value = 'fo'.pack('C', 0x8F).'bar'; + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo=8Fbar'); + + $header = $this->_getHeader('Subject', $encoder); + $header->setLanguage('en'); + $header->setValue($value); + $this->assertEquals("Subject: =?utf-8*en?Q?fo=8Fbar?=\r\n", + $header->toString() + ); + } + + public function testSetBodyModel() + { + $header = $this->_getHeader('Subject', $this->_getEncoder('Q', true)); + $header->setFieldBodyModel('test'); + $this->assertEquals('test', $header->getValue()); + } + + public function testGetBodyModel() + { + $header = $this->_getHeader('Subject', $this->_getEncoder('Q', true)); + $header->setValue('test'); + $this->assertEquals('test', $header->getFieldBodyModel()); + } + + private function _getHeader($name, $encoder) + { + $header = new Swift_Mime_Headers_UnstructuredHeader($name, $encoder, new Swift_Mime_Grammar()); + $header->setCharset($this->_charset); + + return $header; + } + + private function _getEncoder($type, $stub = false) + { + $encoder = $this->getMockery('Swift_Mime_HeaderEncoder')->shouldIgnoreMissing(); + $encoder->shouldReceive('getName') + ->zeroOrMoreTimes() + ->andReturn($type); + + return $encoder; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/MimePartTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/MimePartTest.php new file mode 100644 index 00000000..7e91134f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/MimePartTest.php @@ -0,0 +1,233 @@ +<?php + +class Swift_Mime_MimePartTest extends Swift_Mime_AbstractMimeEntityTest +{ + public function testNestingLevelIsSubpart() + { + $part = $this->_createMimePart($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals( + Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, $part->getNestingLevel() + ); + } + + public function testCharsetIsReturnedFromHeader() + { + /* -- RFC 2046, 4.1.2. + A critical parameter that may be specified in the Content-Type field + for "text/plain" data is the character set. This is specified with a + "charset" parameter, as in: + + Content-type: text/plain; charset=iso-8859-1 + + Unlike some other parameter values, the values of the charset + parameter are NOT case sensitive. The default character set, which + must be assumed in the absence of a charset parameter, is US-ASCII. + */ + + $cType = $this->_createHeader('Content-Type', 'text/plain', + array('charset' => 'iso-8859-1') + ); + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('iso-8859-1', $part->getCharset()); + } + + public function testCharsetIsSetInHeader() + { + $cType = $this->_createHeader('Content-Type', 'text/plain', + array('charset' => 'iso-8859-1'), false + ); + $cType->shouldReceive('setParameter')->once()->with('charset', 'utf-8'); + + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $part->setCharset('utf-8'); + } + + public function testCharsetIsSetInHeaderIfPassedToSetBody() + { + $cType = $this->_createHeader('Content-Type', 'text/plain', + array('charset' => 'iso-8859-1'), false + ); + $cType->shouldReceive('setParameter')->once()->with('charset', 'utf-8'); + + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $part->setBody('', 'text/plian', 'utf-8'); + } + + public function testSettingCharsetNotifiesEncoder() + { + $encoder = $this->_createEncoder('quoted-printable', false); + $encoder->expects($this->once()) + ->method('charsetChanged') + ->with('utf-8'); + + $part = $this->_createMimePart($this->_createHeaderSet(), + $encoder, $this->_createCache() + ); + $part->setCharset('utf-8'); + } + + public function testSettingCharsetNotifiesHeaders() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('charsetChanged') + ->zeroOrMoreTimes() + ->with('utf-8'); + + $part = $this->_createMimePart($headers, $this->_createEncoder(), + $this->_createCache() + ); + $part->setCharset('utf-8'); + } + + public function testSettingCharsetNotifiesChildren() + { + $child = $this->_createChild(0, '', false); + $child->shouldReceive('charsetChanged') + ->once() + ->with('windows-874'); + + $part = $this->_createMimePart($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $part->setChildren(array($child)); + $part->setCharset('windows-874'); + } + + public function testCharsetChangeUpdatesCharset() + { + $cType = $this->_createHeader('Content-Type', 'text/plain', + array('charset' => 'iso-8859-1'), false + ); + $cType->shouldReceive('setParameter')->once()->with('charset', 'utf-8'); + + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $part->charsetChanged('utf-8'); + } + + public function testSettingCharsetClearsCache() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $cache = $this->_createCache(false); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $cache + ); + + $entity->setBody("blah\r\nblah!"); + $entity->toString(); + + // Initialize the expectation here because we only care about what happens in setCharset() + $cache->shouldReceive('clearKey') + ->once() + ->with(\Mockery::any(), 'body'); + + $entity->setCharset('iso-2022'); + } + + public function testFormatIsReturnedFromHeader() + { + /* -- RFC 3676. + */ + + $cType = $this->_createHeader('Content-Type', 'text/plain', + array('format' => 'flowed') + ); + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('flowed', $part->getFormat()); + } + + public function testFormatIsSetInHeader() + { + $cType = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $cType->shouldReceive('setParameter')->once()->with('format', 'fixed'); + + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $part->setFormat('fixed'); + } + + public function testDelSpIsReturnedFromHeader() + { + /* -- RFC 3676. + */ + + $cType = $this->_createHeader('Content-Type', 'text/plain', + array('delsp' => 'no') + ); + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertFalse($part->getDelSp()); + } + + public function testDelSpIsSetInHeader() + { + $cType = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $cType->shouldReceive('setParameter')->once()->with('delsp', 'yes'); + + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $part->setDelSp(true); + } + + public function testFluidInterface() + { + $part = $this->_createMimePart($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + + $this->assertSame($part, + $part + ->setContentType('text/plain') + ->setEncoder($this->_createEncoder()) + ->setId('foo@bar') + ->setDescription('my description') + ->setMaxLineLength(998) + ->setBody('xx') + ->setBoundary('xyz') + ->setChildren(array()) + ->setCharset('utf-8') + ->setFormat('flowed') + ->setDelSp(true) + ); + } + + // -- Private helpers + + //abstract + protected function _createEntity($headers, $encoder, $cache) + { + return $this->_createMimePart($headers, $encoder, $cache); + } + + protected function _createMimePart($headers, $encoder, $cache) + { + return new Swift_Mime_MimePart($headers, $encoder, $cache, new Swift_Mime_Grammar()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderFactoryTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderFactoryTest.php new file mode 100644 index 00000000..4ee62963 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderFactoryTest.php @@ -0,0 +1,168 @@ +<?php + +class Swift_Mime_SimpleHeaderFactoryTest extends \PHPUnit_Framework_TestCase +{ + private $_factory; + + public function setUp() + { + $this->_factory = $this->_createFactory(); + } + + public function testMailboxHeaderIsCorrectType() + { + $header = $this->_factory->createMailboxHeader('X-Foo'); + $this->assertInstanceof('Swift_Mime_Headers_MailboxHeader', $header); + } + + public function testMailboxHeaderHasCorrectName() + { + $header = $this->_factory->createMailboxHeader('X-Foo'); + $this->assertEquals('X-Foo', $header->getFieldName()); + } + + public function testMailboxHeaderHasCorrectModel() + { + $header = $this->_factory->createMailboxHeader('X-Foo', + array('foo@bar' => 'FooBar') + ); + $this->assertEquals(array('foo@bar' => 'FooBar'), $header->getFieldBodyModel()); + } + + public function testDateHeaderHasCorrectType() + { + $header = $this->_factory->createDateHeader('X-Date'); + $this->assertInstanceof('Swift_Mime_Headers_DateHeader', $header); + } + + public function testDateHeaderHasCorrectName() + { + $header = $this->_factory->createDateHeader('X-Date'); + $this->assertEquals('X-Date', $header->getFieldName()); + } + + public function testDateHeaderHasCorrectModel() + { + $header = $this->_factory->createDateHeader('X-Date', 123); + $this->assertEquals(123, $header->getFieldBodyModel()); + } + + public function testTextHeaderHasCorrectType() + { + $header = $this->_factory->createTextHeader('X-Foo'); + $this->assertInstanceof('Swift_Mime_Headers_UnstructuredHeader', $header); + } + + public function testTextHeaderHasCorrectName() + { + $header = $this->_factory->createTextHeader('X-Foo'); + $this->assertEquals('X-Foo', $header->getFieldName()); + } + + public function testTextHeaderHasCorrectModel() + { + $header = $this->_factory->createTextHeader('X-Foo', 'bar'); + $this->assertEquals('bar', $header->getFieldBodyModel()); + } + + public function testParameterizedHeaderHasCorrectType() + { + $header = $this->_factory->createParameterizedHeader('X-Foo'); + $this->assertInstanceof('Swift_Mime_Headers_ParameterizedHeader', $header); + } + + public function testParameterizedHeaderHasCorrectName() + { + $header = $this->_factory->createParameterizedHeader('X-Foo'); + $this->assertEquals('X-Foo', $header->getFieldName()); + } + + public function testParameterizedHeaderHasCorrectModel() + { + $header = $this->_factory->createParameterizedHeader('X-Foo', 'bar'); + $this->assertEquals('bar', $header->getFieldBodyModel()); + } + + public function testParameterizedHeaderHasCorrectParams() + { + $header = $this->_factory->createParameterizedHeader('X-Foo', 'bar', + array('zip' => 'button') + ); + $this->assertEquals(array('zip' => 'button'), $header->getParameters()); + } + + public function testIdHeaderHasCorrectType() + { + $header = $this->_factory->createIdHeader('X-ID'); + $this->assertInstanceof('Swift_Mime_Headers_IdentificationHeader', $header); + } + + public function testIdHeaderHasCorrectName() + { + $header = $this->_factory->createIdHeader('X-ID'); + $this->assertEquals('X-ID', $header->getFieldName()); + } + + public function testIdHeaderHasCorrectModel() + { + $header = $this->_factory->createIdHeader('X-ID', 'xyz@abc'); + $this->assertEquals(array('xyz@abc'), $header->getFieldBodyModel()); + } + + public function testPathHeaderHasCorrectType() + { + $header = $this->_factory->createPathHeader('X-Path'); + $this->assertInstanceof('Swift_Mime_Headers_PathHeader', $header); + } + + public function testPathHeaderHasCorrectName() + { + $header = $this->_factory->createPathHeader('X-Path'); + $this->assertEquals('X-Path', $header->getFieldName()); + } + + public function testPathHeaderHasCorrectModel() + { + $header = $this->_factory->createPathHeader('X-Path', 'foo@bar'); + $this->assertEquals('foo@bar', $header->getFieldBodyModel()); + } + + public function testCharsetChangeNotificationNotifiesEncoders() + { + $encoder = $this->_createHeaderEncoder(); + $encoder->expects($this->once()) + ->method('charsetChanged') + ->with('utf-8'); + $paramEncoder = $this->_createParamEncoder(); + $paramEncoder->expects($this->once()) + ->method('charsetChanged') + ->with('utf-8'); + + $factory = $this->_createFactory($encoder, $paramEncoder); + + $factory->charsetChanged('utf-8'); + } + + // -- Creation methods + + private function _createFactory($encoder = null, $paramEncoder = null) + { + return new Swift_Mime_SimpleHeaderFactory( + $encoder + ? $encoder : $this->_createHeaderEncoder(), + $paramEncoder + ? $paramEncoder : $this->_createParamEncoder(), + new Swift_Mime_Grammar() + ); + } + + private function _createHeaderEncoder() + { + return $this->getMockBuilder('Swift_Mime_HeaderEncoder')->getMock(); + } + + private function _createParamEncoder() + { + return $this->getMockBuilder('Swift_Encoder')->getMock(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderSetTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderSetTest.php new file mode 100644 index 00000000..9682f37e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderSetTest.php @@ -0,0 +1,739 @@ +<?php + +class Swift_Mime_SimpleHeaderSetTest extends \PHPUnit_Framework_TestCase +{ + public function testAddMailboxHeaderDelegatesToFactory() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createMailboxHeader') + ->with('From', array('person@domain' => 'Person')) + ->will($this->returnValue($this->_createHeader('From'))); + + $set = $this->_createSet($factory); + $set->addMailboxHeader('From', array('person@domain' => 'Person')); + } + + public function testAddDateHeaderDelegatesToFactory() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createDateHeader') + ->with('Date', 1234) + ->will($this->returnValue($this->_createHeader('Date'))); + + $set = $this->_createSet($factory); + $set->addDateHeader('Date', 1234); + } + + public function testAddTextHeaderDelegatesToFactory() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createTextHeader') + ->with('Subject', 'some text') + ->will($this->returnValue($this->_createHeader('Subject'))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Subject', 'some text'); + } + + public function testAddParameterizedHeaderDelegatesToFactory() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createParameterizedHeader') + ->with('Content-Type', 'text/plain', array('charset' => 'utf-8')) + ->will($this->returnValue($this->_createHeader('Content-Type'))); + + $set = $this->_createSet($factory); + $set->addParameterizedHeader('Content-Type', 'text/plain', + array('charset' => 'utf-8') + ); + } + + public function testAddIdHeaderDelegatesToFactory() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($this->_createHeader('Message-ID'))); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + } + + public function testAddPathHeaderDelegatesToFactory() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createPathHeader') + ->with('Return-Path', 'some@path') + ->will($this->returnValue($this->_createHeader('Return-Path'))); + + $set = $this->_createSet($factory); + $set->addPathHeader('Return-Path', 'some@path'); + } + + public function testHasReturnsFalseWhenNoHeaders() + { + $set = $this->_createSet($this->_createFactory()); + $this->assertFalse($set->has('Some-Header')); + } + + public function testAddedMailboxHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createMailboxHeader') + ->with('From', array('person@domain' => 'Person')) + ->will($this->returnValue($this->_createHeader('From'))); + + $set = $this->_createSet($factory); + $set->addMailboxHeader('From', array('person@domain' => 'Person')); + $this->assertTrue($set->has('From')); + } + + public function testAddedDateHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createDateHeader') + ->with('Date', 1234) + ->will($this->returnValue($this->_createHeader('Date'))); + + $set = $this->_createSet($factory); + $set->addDateHeader('Date', 1234); + $this->assertTrue($set->has('Date')); + } + + public function testAddedTextHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createTextHeader') + ->with('Subject', 'some text') + ->will($this->returnValue($this->_createHeader('Subject'))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Subject', 'some text'); + $this->assertTrue($set->has('Subject')); + } + + public function testAddedParameterizedHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createParameterizedHeader') + ->with('Content-Type', 'text/plain', array('charset' => 'utf-8')) + ->will($this->returnValue($this->_createHeader('Content-Type'))); + + $set = $this->_createSet($factory); + $set->addParameterizedHeader('Content-Type', 'text/plain', + array('charset' => 'utf-8') + ); + $this->assertTrue($set->has('Content-Type')); + } + + public function testAddedIdHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($this->_createHeader('Message-ID'))); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertTrue($set->has('Message-ID')); + } + + public function testAddedPathHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createPathHeader') + ->with('Return-Path', 'some@path') + ->will($this->returnValue($this->_createHeader('Return-Path'))); + + $set = $this->_createSet($factory); + $set->addPathHeader('Return-Path', 'some@path'); + $this->assertTrue($set->has('Return-Path')); + } + + public function testNewlySetHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $header = $this->_createHeader('X-Foo', 'bar'); + $set = $this->_createSet($factory); + $set->set($header); + $this->assertTrue($set->has('X-Foo')); + } + + public function testHasCanAcceptOffset() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($this->_createHeader('Message-ID'))); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertTrue($set->has('Message-ID', 0)); + } + + public function testHasWithIllegalOffsetReturnsFalse() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($this->_createHeader('Message-ID'))); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertFalse($set->has('Message-ID', 1)); + } + + public function testHasCanDistinguishMultipleHeaders() + { + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($this->_createHeader('Message-ID'))); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Message-ID', 'other@id') + ->will($this->returnValue($this->_createHeader('Message-ID'))); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Message-ID', 'other@id'); + $this->assertTrue($set->has('Message-ID', 1)); + } + + public function testGetWithUnspecifiedOffset() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertSame($header, $set->get('Message-ID')); + } + + public function testGetWithSpeiciedOffset() + { + $header0 = $this->_createHeader('Message-ID'); + $header1 = $this->_createHeader('Message-ID'); + $header2 = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header0)); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Message-ID', 'other@id') + ->will($this->returnValue($header1)); + $factory->expects($this->at(2)) + ->method('createIdHeader') + ->with('Message-ID', 'more@id') + ->will($this->returnValue($header2)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Message-ID', 'other@id'); + $set->addIdHeader('Message-ID', 'more@id'); + $this->assertSame($header1, $set->get('Message-ID', 1)); + } + + public function testGetReturnsNullIfHeaderNotSet() + { + $set = $this->_createSet($this->_createFactory()); + $this->assertNull($set->get('Message-ID', 99)); + } + + public function testGetAllReturnsAllHeadersMatchingName() + { + $header0 = $this->_createHeader('Message-ID'); + $header1 = $this->_createHeader('Message-ID'); + $header2 = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header0)); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Message-ID', 'other@id') + ->will($this->returnValue($header1)); + $factory->expects($this->at(2)) + ->method('createIdHeader') + ->with('Message-ID', 'more@id') + ->will($this->returnValue($header2)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Message-ID', 'other@id'); + $set->addIdHeader('Message-ID', 'more@id'); + + $this->assertEquals(array($header0, $header1, $header2), + $set->getAll('Message-ID') + ); + } + + public function testGetAllReturnsAllHeadersIfNoArguments() + { + $header0 = $this->_createHeader('Message-ID'); + $header1 = $this->_createHeader('Subject'); + $header2 = $this->_createHeader('To'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header0)); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Subject', 'thing') + ->will($this->returnValue($header1)); + $factory->expects($this->at(2)) + ->method('createIdHeader') + ->with('To', 'person@example.org') + ->will($this->returnValue($header2)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Subject', 'thing'); + $set->addIdHeader('To', 'person@example.org'); + + $this->assertEquals(array($header0, $header1, $header2), + $set->getAll() + ); + } + + public function testGetAllReturnsEmptyArrayIfNoneSet() + { + $set = $this->_createSet($this->_createFactory()); + $this->assertEquals(array(), $set->getAll('Received')); + } + + public function testRemoveWithUnspecifiedOffset() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->remove('Message-ID'); + $this->assertFalse($set->has('Message-ID')); + } + + public function testRemoveWithSpecifiedIndexRemovesHeader() + { + $header0 = $this->_createHeader('Message-ID'); + $header1 = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header0)); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Message-ID', 'other@id') + ->will($this->returnValue($header1)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Message-ID', 'other@id'); + $set->remove('Message-ID', 0); + $this->assertFalse($set->has('Message-ID', 0)); + $this->assertTrue($set->has('Message-ID', 1)); + $this->assertTrue($set->has('Message-ID')); + $set->remove('Message-ID', 1); + $this->assertFalse($set->has('Message-ID', 1)); + $this->assertFalse($set->has('Message-ID')); + } + + public function testRemoveWithSpecifiedIndexLeavesOtherHeaders() + { + $header0 = $this->_createHeader('Message-ID'); + $header1 = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header0)); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Message-ID', 'other@id') + ->will($this->returnValue($header1)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Message-ID', 'other@id'); + $set->remove('Message-ID', 1); + $this->assertTrue($set->has('Message-ID', 0)); + } + + public function testRemoveWithInvalidOffsetDoesNothing() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->remove('Message-ID', 50); + $this->assertTrue($set->has('Message-ID')); + } + + public function testRemoveAllRemovesAllHeadersWithName() + { + $header0 = $this->_createHeader('Message-ID'); + $header1 = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header0)); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Message-ID', 'other@id') + ->will($this->returnValue($header1)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Message-ID', 'other@id'); + $set->removeAll('Message-ID'); + $this->assertFalse($set->has('Message-ID', 0)); + $this->assertFalse($set->has('Message-ID', 1)); + } + + public function testHasIsNotCaseSensitive() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertTrue($set->has('message-id')); + } + + public function testGetIsNotCaseSensitive() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertSame($header, $set->get('message-id')); + } + + public function testGetAllIsNotCaseSensitive() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertEquals(array($header), $set->getAll('message-id')); + } + + public function testRemoveIsNotCaseSensitive() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->remove('message-id'); + $this->assertFalse($set->has('Message-ID')); + } + + public function testRemoveAllIsNotCaseSensitive() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->removeAll('message-id'); + $this->assertFalse($set->has('Message-ID')); + } + + public function testNewInstance() + { + $set = $this->_createSet($this->_createFactory()); + $instance = $set->newInstance(); + $this->assertInstanceof('Swift_Mime_HeaderSet', $instance); + } + + public function testToStringJoinsHeadersTogether() + { + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Foo', 'bar') + ->will($this->returnValue($this->_createHeader('Foo', 'bar'))); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('Zip', 'buttons') + ->will($this->returnValue($this->_createHeader('Zip', 'buttons'))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Foo', 'bar'); + $set->addTextHeader('Zip', 'buttons'); + $this->assertEquals( + "Foo: bar\r\n". + "Zip: buttons\r\n", + $set->toString() + ); + } + + public function testHeadersWithoutBodiesAreNotDisplayed() + { + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Foo', 'bar') + ->will($this->returnValue($this->_createHeader('Foo', 'bar'))); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('Zip', '') + ->will($this->returnValue($this->_createHeader('Zip', ''))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Foo', 'bar'); + $set->addTextHeader('Zip', ''); + $this->assertEquals( + "Foo: bar\r\n", + $set->toString() + ); + } + + public function testHeadersWithoutBodiesCanBeForcedToDisplay() + { + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Foo', '') + ->will($this->returnValue($this->_createHeader('Foo', ''))); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('Zip', '') + ->will($this->returnValue($this->_createHeader('Zip', ''))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Foo', ''); + $set->addTextHeader('Zip', ''); + $set->setAlwaysDisplayed(array('Foo', 'Zip')); + $this->assertEquals( + "Foo: \r\n". + "Zip: \r\n", + $set->toString() + ); + } + + public function testHeaderSequencesCanBeSpecified() + { + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Third', 'three') + ->will($this->returnValue($this->_createHeader('Third', 'three'))); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('First', 'one') + ->will($this->returnValue($this->_createHeader('First', 'one'))); + $factory->expects($this->at(2)) + ->method('createTextHeader') + ->with('Second', 'two') + ->will($this->returnValue($this->_createHeader('Second', 'two'))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Third', 'three'); + $set->addTextHeader('First', 'one'); + $set->addTextHeader('Second', 'two'); + + $set->defineOrdering(array('First', 'Second', 'Third')); + + $this->assertEquals( + "First: one\r\n". + "Second: two\r\n". + "Third: three\r\n", + $set->toString() + ); + } + + public function testUnsortedHeadersAppearAtEnd() + { + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Fourth', 'four') + ->will($this->returnValue($this->_createHeader('Fourth', 'four'))); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('Fifth', 'five') + ->will($this->returnValue($this->_createHeader('Fifth', 'five'))); + $factory->expects($this->at(2)) + ->method('createTextHeader') + ->with('Third', 'three') + ->will($this->returnValue($this->_createHeader('Third', 'three'))); + $factory->expects($this->at(3)) + ->method('createTextHeader') + ->with('First', 'one') + ->will($this->returnValue($this->_createHeader('First', 'one'))); + $factory->expects($this->at(4)) + ->method('createTextHeader') + ->with('Second', 'two') + ->will($this->returnValue($this->_createHeader('Second', 'two'))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Fourth', 'four'); + $set->addTextHeader('Fifth', 'five'); + $set->addTextHeader('Third', 'three'); + $set->addTextHeader('First', 'one'); + $set->addTextHeader('Second', 'two'); + + $set->defineOrdering(array('First', 'Second', 'Third')); + + $this->assertEquals( + "First: one\r\n". + "Second: two\r\n". + "Third: three\r\n". + "Fourth: four\r\n". + "Fifth: five\r\n", + $set->toString() + ); + } + + public function testSettingCharsetNotifiesAlreadyExistingHeaders() + { + $subject = $this->_createHeader('Subject', 'some text'); + $xHeader = $this->_createHeader('X-Header', 'some text'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Subject', 'some text') + ->will($this->returnValue($subject)); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('X-Header', 'some text') + ->will($this->returnValue($xHeader)); + $subject->expects($this->once()) + ->method('setCharset') + ->with('utf-8'); + $xHeader->expects($this->once()) + ->method('setCharset') + ->with('utf-8'); + + $set = $this->_createSet($factory); + $set->addTextHeader('Subject', 'some text'); + $set->addTextHeader('X-Header', 'some text'); + + $set->setCharset('utf-8'); + } + + public function testCharsetChangeNotifiesAlreadyExistingHeaders() + { + $subject = $this->_createHeader('Subject', 'some text'); + $xHeader = $this->_createHeader('X-Header', 'some text'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Subject', 'some text') + ->will($this->returnValue($subject)); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('X-Header', 'some text') + ->will($this->returnValue($xHeader)); + $subject->expects($this->once()) + ->method('setCharset') + ->with('utf-8'); + $xHeader->expects($this->once()) + ->method('setCharset') + ->with('utf-8'); + + $set = $this->_createSet($factory); + $set->addTextHeader('Subject', 'some text'); + $set->addTextHeader('X-Header', 'some text'); + + $set->charsetChanged('utf-8'); + } + + public function testCharsetChangeNotifiesFactory() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('charsetChanged') + ->with('utf-8'); + + $set = $this->_createSet($factory); + + $set->setCharset('utf-8'); + } + + // -- Creation methods + + private function _createSet($factory) + { + return new Swift_Mime_SimpleHeaderSet($factory); + } + + private function _createFactory() + { + return $this->getMockBuilder('Swift_Mime_HeaderFactory')->getMock(); + } + + private function _createHeader($name, $body = '') + { + $header = $this->getMockBuilder('Swift_Mime_Header')->getMock(); + $header->expects($this->any()) + ->method('getFieldName') + ->will($this->returnValue($name)); + $header->expects($this->any()) + ->method('toString') + ->will($this->returnValue(sprintf("%s: %s\r\n", $name, $body))); + $header->expects($this->any()) + ->method('getFieldBody') + ->will($this->returnValue($body)); + + return $header; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMessageTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMessageTest.php new file mode 100644 index 00000000..267d2764 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMessageTest.php @@ -0,0 +1,829 @@ +<?php + +class Swift_Mime_SimpleMessageTest extends Swift_Mime_MimePartTest +{ + public function testNestingLevelIsSubpart() + { + //Overridden + } + + public function testNestingLevelIsTop() + { + $message = $this->_createMessage($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals( + Swift_Mime_MimeEntity::LEVEL_TOP, $message->getNestingLevel() + ); + } + + public function testDateIsReturnedFromHeader() + { + $date = $this->_createHeader('Date', 123); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Date' => $date)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(123, $message->getDate()); + } + + public function testDateIsSetInHeader() + { + $date = $this->_createHeader('Date', 123, array(), false); + $date->shouldReceive('setFieldBodyModel') + ->once() + ->with(1234); + $date->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Date' => $date)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setDate(1234); + } + + public function testDateHeaderIsCreatedIfNonePresent() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addDateHeader') + ->once() + ->with('Date', 1234); + $headers->shouldReceive('addDateHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setDate(1234); + } + + public function testDateHeaderIsAddedDuringConstruction() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addDateHeader') + ->once() + ->with('Date', '/^[0-9]+$/D'); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + } + + public function testIdIsReturnedFromHeader() + { + /* -- RFC 2045, 7. + In constructing a high-level user agent, it may be desirable to allow + one body to make reference to another. Accordingly, bodies may be + labelled using the "Content-ID" header field, which is syntactically + identical to the "Message-ID" header field + */ + + $messageId = $this->_createHeader('Message-ID', 'a@b'); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Message-ID' => $messageId)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('a@b', $message->getId()); + } + + public function testIdIsSetInHeader() + { + $messageId = $this->_createHeader('Message-ID', 'a@b', array(), false); + $messageId->shouldReceive('setFieldBodyModel') + ->once() + ->with('x@y'); + $messageId->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Message-ID' => $messageId)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setId('x@y'); + } + + public function testIdIsAutoGenerated() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addIdHeader') + ->once() + ->with('Message-ID', '/^.*?@.*?$/D'); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + } + + public function testSubjectIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.5. + */ + + $subject = $this->_createHeader('Subject', 'example subject'); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Subject' => $subject)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('example subject', $message->getSubject()); + } + + public function testSubjectIsSetInHeader() + { + $subject = $this->_createHeader('Subject', '', array(), false); + $subject->shouldReceive('setFieldBodyModel') + ->once() + ->with('foo'); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Subject' => $subject)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setSubject('foo'); + } + + public function testSubjectHeaderIsCreatedIfNotPresent() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addTextHeader') + ->once() + ->with('Subject', 'example subject'); + $headers->shouldReceive('addTextHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setSubject('example subject'); + } + + public function testReturnPathIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.7. + */ + + $path = $this->_createHeader('Return-Path', 'bounces@domain'); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Return-Path' => $path)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('bounces@domain', $message->getReturnPath()); + } + + public function testReturnPathIsSetInHeader() + { + $path = $this->_createHeader('Return-Path', '', array(), false); + $path->shouldReceive('setFieldBodyModel') + ->once() + ->with('bounces@domain'); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Return-Path' => $path)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setReturnPath('bounces@domain'); + } + + public function testReturnPathHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addPathHeader') + ->once() + ->with('Return-Path', 'bounces@domain'); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setReturnPath('bounces@domain'); + } + + public function testSenderIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.2. + */ + + $sender = $this->_createHeader('Sender', array('sender@domain' => 'Name')); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Sender' => $sender)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('sender@domain' => 'Name'), $message->getSender()); + } + + public function testSenderIsSetInHeader() + { + $sender = $this->_createHeader('Sender', array('sender@domain' => 'Name'), + array(), false + ); + $sender->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Sender' => $sender)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setSender(array('other@domain' => 'Other')); + } + + public function testSenderHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Sender', (array) 'sender@domain'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setSender('sender@domain'); + } + + public function testNameCanBeUsedInSenderHeader() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Sender', array('sender@domain' => 'Name')); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setSender('sender@domain', 'Name'); + } + + public function testFromIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.2. + */ + + $from = $this->_createHeader('From', array('from@domain' => 'Name')); + $message = $this->_createMessage( + $this->_createHeaderSet(array('From' => $from)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('from@domain' => 'Name'), $message->getFrom()); + } + + public function testFromIsSetInHeader() + { + $from = $this->_createHeader('From', array('from@domain' => 'Name'), + array(), false + ); + $from->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('From' => $from)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setFrom(array('other@domain' => 'Other')); + } + + public function testFromIsAddedToHeadersDuringAddFrom() + { + $from = $this->_createHeader('From', array('from@domain' => 'Name'), + array(), false + ); + $from->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('from@domain' => 'Name', 'other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('From' => $from)), + $this->_createEncoder(), $this->_createCache() + ); + $message->addFrom('other@domain', 'Other'); + } + + public function testFromHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('From', (array) 'from@domain'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setFrom('from@domain'); + } + + public function testPersonalNameCanBeUsedInFromAddress() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('From', array('from@domain' => 'Name')); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setFrom('from@domain', 'Name'); + } + + public function testReplyToIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.2. + */ + + $reply = $this->_createHeader('Reply-To', array('reply@domain' => 'Name')); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Reply-To' => $reply)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('reply@domain' => 'Name'), $message->getReplyTo()); + } + + public function testReplyToIsSetInHeader() + { + $reply = $this->_createHeader('Reply-To', array('reply@domain' => 'Name'), + array(), false + ); + $reply->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Reply-To' => $reply)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setReplyTo(array('other@domain' => 'Other')); + } + + public function testReplyToIsAddedToHeadersDuringAddReplyTo() + { + $replyTo = $this->_createHeader('Reply-To', array('from@domain' => 'Name'), + array(), false + ); + $replyTo->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('from@domain' => 'Name', 'other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Reply-To' => $replyTo)), + $this->_createEncoder(), $this->_createCache() + ); + $message->addReplyTo('other@domain', 'Other'); + } + + public function testReplyToHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Reply-To', (array) 'reply@domain'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setReplyTo('reply@domain'); + } + + public function testNameCanBeUsedInReplyTo() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Reply-To', array('reply@domain' => 'Name')); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setReplyTo('reply@domain', 'Name'); + } + + public function testToIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.3. + */ + + $to = $this->_createHeader('To', array('to@domain' => 'Name')); + $message = $this->_createMessage( + $this->_createHeaderSet(array('To' => $to)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('to@domain' => 'Name'), $message->getTo()); + } + + public function testToIsSetInHeader() + { + $to = $this->_createHeader('To', array('to@domain' => 'Name'), + array(), false + ); + $to->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('To' => $to)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setTo(array('other@domain' => 'Other')); + } + + public function testToIsAddedToHeadersDuringAddTo() + { + $to = $this->_createHeader('To', array('from@domain' => 'Name'), + array(), false + ); + $to->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('from@domain' => 'Name', 'other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('To' => $to)), + $this->_createEncoder(), $this->_createCache() + ); + $message->addTo('other@domain', 'Other'); + } + + public function testToHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('To', (array) 'to@domain'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setTo('to@domain'); + } + + public function testNameCanBeUsedInToHeader() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('To', array('to@domain' => 'Name')); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setTo('to@domain', 'Name'); + } + + public function testCcIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.3. + */ + + $cc = $this->_createHeader('Cc', array('cc@domain' => 'Name')); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Cc' => $cc)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('cc@domain' => 'Name'), $message->getCc()); + } + + public function testCcIsSetInHeader() + { + $cc = $this->_createHeader('Cc', array('cc@domain' => 'Name'), + array(), false + ); + $cc->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Cc' => $cc)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setCc(array('other@domain' => 'Other')); + } + + public function testCcIsAddedToHeadersDuringAddCc() + { + $cc = $this->_createHeader('Cc', array('from@domain' => 'Name'), + array(), false + ); + $cc->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('from@domain' => 'Name', 'other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Cc' => $cc)), + $this->_createEncoder(), $this->_createCache() + ); + $message->addCc('other@domain', 'Other'); + } + + public function testCcHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Cc', (array) 'cc@domain'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setCc('cc@domain'); + } + + public function testNameCanBeUsedInCcHeader() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Cc', array('cc@domain' => 'Name')); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setCc('cc@domain', 'Name'); + } + + public function testBccIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.3. + */ + + $bcc = $this->_createHeader('Bcc', array('bcc@domain' => 'Name')); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Bcc' => $bcc)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('bcc@domain' => 'Name'), $message->getBcc()); + } + + public function testBccIsSetInHeader() + { + $bcc = $this->_createHeader('Bcc', array('bcc@domain' => 'Name'), + array(), false + ); + $bcc->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Bcc' => $bcc)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setBcc(array('other@domain' => 'Other')); + } + + public function testBccIsAddedToHeadersDuringAddBcc() + { + $bcc = $this->_createHeader('Bcc', array('from@domain' => 'Name'), + array(), false + ); + $bcc->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('from@domain' => 'Name', 'other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Bcc' => $bcc)), + $this->_createEncoder(), $this->_createCache() + ); + $message->addBcc('other@domain', 'Other'); + } + + public function testBccHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Bcc', (array) 'bcc@domain'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setBcc('bcc@domain'); + } + + public function testNameCanBeUsedInBcc() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Bcc', array('bcc@domain' => 'Name')); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setBcc('bcc@domain', 'Name'); + } + + public function testPriorityIsReadFromHeader() + { + $prio = $this->_createHeader('X-Priority', '2 (High)'); + $message = $this->_createMessage( + $this->_createHeaderSet(array('X-Priority' => $prio)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(2, $message->getPriority()); + } + + public function testPriorityIsSetInHeader() + { + $prio = $this->_createHeader('X-Priority', '2 (High)', array(), false); + $prio->shouldReceive('setFieldBodyModel') + ->once() + ->with('5 (Lowest)'); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('X-Priority' => $prio)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setPriority($message::PRIORITY_LOWEST); + } + + public function testPriorityHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addTextHeader') + ->once() + ->with('X-Priority', '4 (Low)'); + $headers->shouldReceive('addTextHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setPriority($message::PRIORITY_LOW); + } + + public function testReadReceiptAddressReadFromHeader() + { + $rcpt = $this->_createHeader('Disposition-Notification-To', + array('chris@swiftmailer.org' => 'Chris') + ); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Disposition-Notification-To' => $rcpt)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('chris@swiftmailer.org' => 'Chris'), + $message->getReadReceiptTo() + ); + } + + public function testReadReceiptIsSetInHeader() + { + $rcpt = $this->_createHeader('Disposition-Notification-To', array(), array(), false); + $rcpt->shouldReceive('setFieldBodyModel') + ->once() + ->with('mark@swiftmailer.org'); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Disposition-Notification-To' => $rcpt)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setReadReceiptTo('mark@swiftmailer.org'); + } + + public function testReadReceiptHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Disposition-Notification-To', 'mark@swiftmailer.org'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setReadReceiptTo('mark@swiftmailer.org'); + } + + public function testChildrenCanBeAttached() + { + $child1 = $this->_createChild(); + $child2 = $this->_createChild(); + + $message = $this->_createMessage($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + + $message->attach($child1); + $message->attach($child2); + + $this->assertEquals(array($child1, $child2), $message->getChildren()); + } + + public function testChildrenCanBeDetached() + { + $child1 = $this->_createChild(); + $child2 = $this->_createChild(); + + $message = $this->_createMessage($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + + $message->attach($child1); + $message->attach($child2); + + $message->detach($child1); + + $this->assertEquals(array($child2), $message->getChildren()); + } + + public function testEmbedAttachesChild() + { + $child = $this->_createChild(); + + $message = $this->_createMessage($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + + $message->embed($child); + + $this->assertEquals(array($child), $message->getChildren()); + } + + public function testEmbedReturnsValidCid() + { + $child = $this->_createChild(Swift_Mime_MimeEntity::LEVEL_RELATED, '', + false + ); + $child->shouldReceive('getId') + ->zeroOrMoreTimes() + ->andReturn('foo@bar'); + + $message = $this->_createMessage($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + + $this->assertEquals('cid:foo@bar', $message->embed($child)); + } + + public function testFluidInterface() + { + $child = $this->_createChild(); + $message = $this->_createMessage($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertSame($message, + $message + ->setContentType('text/plain') + ->setEncoder($this->_createEncoder()) + ->setId('foo@bar') + ->setDescription('my description') + ->setMaxLineLength(998) + ->setBody('xx') + ->setBoundary('xyz') + ->setChildren(array()) + ->setCharset('iso-8859-1') + ->setFormat('flowed') + ->setDelSp(false) + ->setSubject('subj') + ->setDate(123) + ->setReturnPath('foo@bar') + ->setSender('foo@bar') + ->setFrom(array('x@y' => 'XY')) + ->setReplyTo(array('ab@cd' => 'ABCD')) + ->setTo(array('chris@site.tld', 'mark@site.tld')) + ->setCc('john@somewhere.tld') + ->setBcc(array('one@site', 'two@site' => 'Two')) + ->setPriority($message::PRIORITY_LOW) + ->setReadReceiptTo('a@b') + ->attach($child) + ->detach($child) + ); + } + + // -- Private helpers + + //abstract + protected function _createEntity($headers, $encoder, $cache) + { + return $this->_createMessage($headers, $encoder, $cache); + } + + protected function _createMimePart($headers, $encoder, $cache) + { + return $this->_createMessage($headers, $encoder, $cache); + } + + private function _createMessage($headers, $encoder, $cache) + { + return new Swift_Mime_SimpleMessage($headers, $encoder, $cache, new Swift_Mime_Grammar()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMimeEntityTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMimeEntityTest.php new file mode 100644 index 00000000..b54d7f87 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMimeEntityTest.php @@ -0,0 +1,11 @@ +<?php + +class Swift_Mime_SimpleMimeEntityTest extends Swift_Mime_AbstractMimeEntityTest +{ + // -- Private helpers + + protected function _createEntity($headerFactory, $encoder, $cache) + { + return new Swift_Mime_SimpleMimeEntity($headerFactory, $encoder, $cache, new Swift_Mime_Grammar()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/AntiFloodPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/AntiFloodPluginTest.php new file mode 100644 index 00000000..8f014405 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/AntiFloodPluginTest.php @@ -0,0 +1,95 @@ +<?php + +class Swift_Plugins_AntiFloodPluginTest extends \PHPUnit_Framework_TestCase +{ + public function testThresholdCanBeSetAndFetched() + { + $plugin = new Swift_Plugins_AntiFloodPlugin(10); + $this->assertEquals(10, $plugin->getThreshold()); + $plugin->setThreshold(100); + $this->assertEquals(100, $plugin->getThreshold()); + } + + public function testSleepTimeCanBeSetAndFetched() + { + $plugin = new Swift_Plugins_AntiFloodPlugin(10, 5); + $this->assertEquals(5, $plugin->getSleepTime()); + $plugin->setSleepTime(1); + $this->assertEquals(1, $plugin->getSleepTime()); + } + + public function testPluginStopsConnectionAfterThreshold() + { + $transport = $this->_createTransport(); + $transport->expects($this->once()) + ->method('start'); + $transport->expects($this->once()) + ->method('stop'); + + $evt = $this->_createSendEvent($transport); + + $plugin = new Swift_Plugins_AntiFloodPlugin(10); + for ($i = 0; $i < 12; ++$i) { + $plugin->sendPerformed($evt); + } + } + + public function testPluginCanStopAndStartMultipleTimes() + { + $transport = $this->_createTransport(); + $transport->expects($this->exactly(5)) + ->method('start'); + $transport->expects($this->exactly(5)) + ->method('stop'); + + $evt = $this->_createSendEvent($transport); + + $plugin = new Swift_Plugins_AntiFloodPlugin(2); + for ($i = 0; $i < 11; ++$i) { + $plugin->sendPerformed($evt); + } + } + + public function testPluginCanSleepDuringRestart() + { + $sleeper = $this->getMockBuilder('Swift_Plugins_Sleeper')->getMock(); + $sleeper->expects($this->once()) + ->method('sleep') + ->with(10); + + $transport = $this->_createTransport(); + $transport->expects($this->once()) + ->method('start'); + $transport->expects($this->once()) + ->method('stop'); + + $evt = $this->_createSendEvent($transport); + + $plugin = new Swift_Plugins_AntiFloodPlugin(99, 10, $sleeper); + for ($i = 0; $i < 101; ++$i) { + $plugin->sendPerformed($evt); + } + } + + // -- Creation Methods + + private function _createTransport() + { + return $this->getMockBuilder('Swift_Transport')->getMock(); + } + + private function _createSendEvent($transport) + { + $evt = $this->getMockBuilder('Swift_Events_SendEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getSource') + ->will($this->returnValue($transport)); + $evt->expects($this->any()) + ->method('getTransport') + ->will($this->returnValue($transport)); + + return $evt; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/BandwidthMonitorPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/BandwidthMonitorPluginTest.php new file mode 100644 index 00000000..bb01a92c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/BandwidthMonitorPluginTest.php @@ -0,0 +1,130 @@ +<?php + +class Swift_Plugins_BandwidthMonitorPluginTest extends \PHPUnit_Framework_TestCase +{ + private $_monitor; + + private $_bytes = 0; + + public function setUp() + { + $this->_monitor = new Swift_Plugins_BandwidthMonitorPlugin(); + } + + public function testBytesOutIncreasesWhenCommandsSent() + { + $evt = $this->_createCommandEvent("RCPT TO:<foo@bar.com>\r\n"); + + $this->assertEquals(0, $this->_monitor->getBytesOut()); + $this->_monitor->commandSent($evt); + $this->assertEquals(23, $this->_monitor->getBytesOut()); + $this->_monitor->commandSent($evt); + $this->assertEquals(46, $this->_monitor->getBytesOut()); + } + + public function testBytesInIncreasesWhenResponsesReceived() + { + $evt = $this->_createResponseEvent("250 Ok\r\n"); + + $this->assertEquals(0, $this->_monitor->getBytesIn()); + $this->_monitor->responseReceived($evt); + $this->assertEquals(8, $this->_monitor->getBytesIn()); + $this->_monitor->responseReceived($evt); + $this->assertEquals(16, $this->_monitor->getBytesIn()); + } + + public function testCountersCanBeReset() + { + $evt = $this->_createResponseEvent("250 Ok\r\n"); + + $this->assertEquals(0, $this->_monitor->getBytesIn()); + $this->_monitor->responseReceived($evt); + $this->assertEquals(8, $this->_monitor->getBytesIn()); + $this->_monitor->responseReceived($evt); + $this->assertEquals(16, $this->_monitor->getBytesIn()); + + $evt = $this->_createCommandEvent("RCPT TO:<foo@bar.com>\r\n"); + + $this->assertEquals(0, $this->_monitor->getBytesOut()); + $this->_monitor->commandSent($evt); + $this->assertEquals(23, $this->_monitor->getBytesOut()); + $this->_monitor->commandSent($evt); + $this->assertEquals(46, $this->_monitor->getBytesOut()); + + $this->_monitor->reset(); + + $this->assertEquals(0, $this->_monitor->getBytesOut()); + $this->assertEquals(0, $this->_monitor->getBytesIn()); + } + + public function testBytesOutIncreasesAccordingToMessageLength() + { + $message = $this->_createMessageWithByteCount(6); + $evt = $this->_createSendEvent($message); + + $this->assertEquals(0, $this->_monitor->getBytesOut()); + $this->_monitor->sendPerformed($evt); + $this->assertEquals(6, $this->_monitor->getBytesOut()); + $this->_monitor->sendPerformed($evt); + $this->assertEquals(12, $this->_monitor->getBytesOut()); + } + + // -- Creation Methods + + private function _createSendEvent($message) + { + $evt = $this->getMockBuilder('Swift_Events_SendEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getMessage') + ->will($this->returnValue($message)); + + return $evt; + } + + private function _createCommandEvent($command) + { + $evt = $this->getMockBuilder('Swift_Events_CommandEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getCommand') + ->will($this->returnValue($command)); + + return $evt; + } + + private function _createResponseEvent($response) + { + $evt = $this->getMockBuilder('Swift_Events_ResponseEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getResponse') + ->will($this->returnValue($response)); + + return $evt; + } + + private function _createMessageWithByteCount($bytes) + { + $this->_bytes = $bytes; + $msg = $this->getMockBuilder('Swift_Mime_Message')->getMock(); + $msg->expects($this->any()) + ->method('toByteStream') + ->will($this->returnCallback(array($this, '_write'))); + /* $this->_checking(Expectations::create() + -> ignoring($msg)->toByteStream(any()) -> calls(array($this, '_write')) + ); */ + + return $msg; + } + + public function _write($is) + { + for ($i = 0; $i < $this->_bytes; ++$i) { + $is->write('x'); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/DecoratorPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/DecoratorPluginTest.php new file mode 100644 index 00000000..7f4cdef1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/DecoratorPluginTest.php @@ -0,0 +1,269 @@ +<?php + +class Swift_Plugins_DecoratorPluginTest extends \SwiftMailerTestCase +{ + public function testMessageBodyReceivesReplacements() + { + $message = $this->_createMessage( + $this->_createHeaders(), + array('zip@button.tld' => 'Zipathon'), + array('chris.corbyn@swiftmailer.org' => 'Chris'), + 'Subject', + 'Hello {name}, you are customer #{id}' + ); + $message->shouldReceive('setBody') + ->once() + ->with('Hello Zip, you are customer #456'); + $message->shouldReceive('setBody') + ->zeroOrMoreTimes(); + + $plugin = $this->_createPlugin( + array('zip@button.tld' => array('{name}' => 'Zip', '{id}' => '456')) + ); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + + public function testReplacementsCanBeAppliedToSameMessageMultipleTimes() + { + $message = $this->_createMessage( + $this->_createHeaders(), + array('zip@button.tld' => 'Zipathon', 'foo@bar.tld' => 'Foo'), + array('chris.corbyn@swiftmailer.org' => 'Chris'), + 'Subject', + 'Hello {name}, you are customer #{id}' + ); + $message->shouldReceive('setBody') + ->once() + ->with('Hello Zip, you are customer #456'); + $message->shouldReceive('setBody') + ->once() + ->with('Hello {name}, you are customer #{id}'); + $message->shouldReceive('setBody') + ->once() + ->with('Hello Foo, you are customer #123'); + $message->shouldReceive('setBody') + ->zeroOrMoreTimes(); + + $plugin = $this->_createPlugin( + array( + 'foo@bar.tld' => array('{name}' => 'Foo', '{id}' => '123'), + 'zip@button.tld' => array('{name}' => 'Zip', '{id}' => '456'), + ) + ); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + + public function testReplacementsCanBeMadeInHeaders() + { + $headers = $this->_createHeaders(array( + $returnPathHeader = $this->_createHeader('Return-Path', 'foo-{id}@swiftmailer.org'), + $toHeader = $this->_createHeader('Subject', 'A message for {name}!'), + )); + + $message = $this->_createMessage( + $headers, + array('zip@button.tld' => 'Zipathon'), + array('chris.corbyn@swiftmailer.org' => 'Chris'), + 'A message for {name}!', + 'Hello {name}, you are customer #{id}' + ); + + $message->shouldReceive('setBody') + ->once() + ->with('Hello Zip, you are customer #456'); + $toHeader->shouldReceive('setFieldBodyModel') + ->once() + ->with('A message for Zip!'); + $returnPathHeader->shouldReceive('setFieldBodyModel') + ->once() + ->with('foo-456@swiftmailer.org'); + $message->shouldReceive('setBody') + ->zeroOrMoreTimes(); + $toHeader->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + $returnPathHeader->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $plugin = $this->_createPlugin( + array('zip@button.tld' => array('{name}' => 'Zip', '{id}' => '456')) + ); + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + + public function testReplacementsAreMadeOnSubparts() + { + $part1 = $this->_createPart('text/plain', 'Your name is {name}?', '1@x'); + $part2 = $this->_createPart('text/html', 'Your <em>name</em> is {name}?', '2@x'); + $message = $this->_createMessage( + $this->_createHeaders(), + array('zip@button.tld' => 'Zipathon'), + array('chris.corbyn@swiftmailer.org' => 'Chris'), + 'A message for {name}!', + 'Subject' + ); + $message->shouldReceive('getChildren') + ->zeroOrMoreTimes() + ->andReturn(array($part1, $part2)); + $part1->shouldReceive('setBody') + ->once() + ->with('Your name is Zip?'); + $part2->shouldReceive('setBody') + ->once() + ->with('Your <em>name</em> is Zip?'); + $part1->shouldReceive('setBody') + ->zeroOrMoreTimes(); + $part2->shouldReceive('setBody') + ->zeroOrMoreTimes(); + + $plugin = $this->_createPlugin( + array('zip@button.tld' => array('{name}' => 'Zip', '{id}' => '456')) + ); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + + public function testReplacementsCanBeTakenFromCustomReplacementsObject() + { + $message = $this->_createMessage( + $this->_createHeaders(), + array('foo@bar' => 'Foobar', 'zip@zap' => 'Zip zap'), + array('chris.corbyn@swiftmailer.org' => 'Chris'), + 'Subject', + 'Something {a}' + ); + + $replacements = $this->_createReplacements(); + + $message->shouldReceive('setBody') + ->once() + ->with('Something b'); + $message->shouldReceive('setBody') + ->once() + ->with('Something c'); + $message->shouldReceive('setBody') + ->zeroOrMoreTimes(); + $replacements->shouldReceive('getReplacementsFor') + ->once() + ->with('foo@bar') + ->andReturn(array('{a}' => 'b')); + $replacements->shouldReceive('getReplacementsFor') + ->once() + ->with('zip@zap') + ->andReturn(array('{a}' => 'c')); + + $plugin = $this->_createPlugin($replacements); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + + // -- Creation methods + + private function _createMessage($headers, $to = array(), $from = null, $subject = null, + $body = null) + { + $message = $this->getMockery('Swift_Mime_Message')->shouldIgnoreMissing(); + foreach ($to as $addr => $name) { + $message->shouldReceive('getTo') + ->once() + ->andReturn(array($addr => $name)); + } + $message->shouldReceive('getHeaders') + ->zeroOrMoreTimes() + ->andReturn($headers); + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn($from); + $message->shouldReceive('getSubject') + ->zeroOrMoreTimes() + ->andReturn($subject); + $message->shouldReceive('getBody') + ->zeroOrMoreTimes() + ->andReturn($body); + + return $message; + } + + private function _createPlugin($replacements) + { + return new Swift_Plugins_DecoratorPlugin($replacements); + } + + private function _createReplacements() + { + return $this->getMockery('Swift_Plugins_Decorator_Replacements')->shouldIgnoreMissing(); + } + + private function _createSendEvent(Swift_Mime_Message $message) + { + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + $evt->shouldReceive('getMessage') + ->zeroOrMoreTimes() + ->andReturn($message); + + return $evt; + } + + private function _createPart($type, $body, $id) + { + $part = $this->getMockery('Swift_Mime_MimeEntity')->shouldIgnoreMissing(); + $part->shouldReceive('getContentType') + ->zeroOrMoreTimes() + ->andReturn($type); + $part->shouldReceive('getBody') + ->zeroOrMoreTimes() + ->andReturn($body); + $part->shouldReceive('getId') + ->zeroOrMoreTimes() + ->andReturn($id); + + return $part; + } + + private function _createHeaders($headers = array()) + { + $set = $this->getMockery('Swift_Mime_HeaderSet')->shouldIgnoreMissing(); + $set->shouldReceive('getAll') + ->zeroOrMoreTimes() + ->andReturn($headers); + + foreach ($headers as $header) { + $set->set($header); + } + + return $set; + } + + private function _createHeader($name, $body = '') + { + $header = $this->getMockery('Swift_Mime_Header')->shouldIgnoreMissing(); + $header->shouldReceive('getFieldName') + ->zeroOrMoreTimes() + ->andReturn($name); + $header->shouldReceive('getFieldBodyModel') + ->zeroOrMoreTimes() + ->andReturn($body); + + return $header; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/LoggerPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/LoggerPluginTest.php new file mode 100644 index 00000000..15d7014b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/LoggerPluginTest.php @@ -0,0 +1,190 @@ +<?php + +class Swift_Plugins_LoggerPluginTest extends \SwiftMailerTestCase +{ + public function testLoggerDelegatesAddingEntries() + { + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with('foo'); + + $plugin = $this->_createPlugin($logger); + $plugin->add('foo'); + } + + public function testLoggerDelegatesDumpingEntries() + { + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('dump') + ->will($this->returnValue('foobar')); + + $plugin = $this->_createPlugin($logger); + $this->assertEquals('foobar', $plugin->dump()); + } + + public function testLoggerDelegatesClearingEntries() + { + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('clear'); + + $plugin = $this->_createPlugin($logger); + $plugin->clear(); + } + + public function testCommandIsSentToLogger() + { + $evt = $this->_createCommandEvent("foo\r\n"); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->regExp('~foo\r\n~')); + + $plugin = $this->_createPlugin($logger); + $plugin->commandSent($evt); + } + + public function testResponseIsSentToLogger() + { + $evt = $this->_createResponseEvent("354 Go ahead\r\n"); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->regExp('~354 Go ahead\r\n~')); + + $plugin = $this->_createPlugin($logger); + $plugin->responseReceived($evt); + } + + public function testTransportBeforeStartChangeIsSentToLogger() + { + $evt = $this->_createTransportChangeEvent(); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->anything()); + + $plugin = $this->_createPlugin($logger); + $plugin->beforeTransportStarted($evt); + } + + public function testTransportStartChangeIsSentToLogger() + { + $evt = $this->_createTransportChangeEvent(); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->anything()); + + $plugin = $this->_createPlugin($logger); + $plugin->transportStarted($evt); + } + + public function testTransportStopChangeIsSentToLogger() + { + $evt = $this->_createTransportChangeEvent(); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->anything()); + + $plugin = $this->_createPlugin($logger); + $plugin->transportStopped($evt); + } + + public function testTransportBeforeStopChangeIsSentToLogger() + { + $evt = $this->_createTransportChangeEvent(); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->anything()); + + $plugin = $this->_createPlugin($logger); + $plugin->beforeTransportStopped($evt); + } + + public function testExceptionsArePassedToDelegateAndLeftToBubbleUp() + { + $transport = $this->_createTransport(); + $evt = $this->_createTransportExceptionEvent(); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->anything()); + + $plugin = $this->_createPlugin($logger); + try { + $plugin->exceptionThrown($evt); + $this->fail('Exception should bubble up.'); + } catch (Swift_TransportException $ex) { + } + } + + // -- Creation Methods + + private function _createLogger() + { + return $this->getMockBuilder('Swift_Plugins_Logger')->getMock(); + } + + private function _createPlugin($logger) + { + return new Swift_Plugins_LoggerPlugin($logger); + } + + private function _createCommandEvent($command) + { + $evt = $this->getMockBuilder('Swift_Events_CommandEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getCommand') + ->will($this->returnValue($command)); + + return $evt; + } + + private function _createResponseEvent($response) + { + $evt = $this->getMockBuilder('Swift_Events_ResponseEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getResponse') + ->will($this->returnValue($response)); + + return $evt; + } + + private function _createTransport() + { + return $this->getMockBuilder('Swift_Transport')->getMock(); + } + + private function _createTransportChangeEvent() + { + $evt = $this->getMockBuilder('Swift_Events_TransportChangeEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getSource') + ->will($this->returnValue($this->_createTransport())); + + return $evt; + } + + public function _createTransportExceptionEvent() + { + $evt = $this->getMockBuilder('Swift_Events_TransportExceptionEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getException') + ->will($this->returnValue(new Swift_TransportException(''))); + + return $evt; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/ArrayLoggerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/ArrayLoggerTest.php new file mode 100644 index 00000000..880bb32c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/ArrayLoggerTest.php @@ -0,0 +1,65 @@ +<?php + +class Swift_Plugins_Loggers_ArrayLoggerTest extends \PHPUnit_Framework_TestCase +{ + public function testAddingSingleEntryDumpsSingleLine() + { + $logger = new Swift_Plugins_Loggers_ArrayLogger(); + $logger->add(">> Foo\r\n"); + $this->assertEquals(">> Foo\r\n", $logger->dump()); + } + + public function testAddingMultipleEntriesDumpsMultipleLines() + { + $logger = new Swift_Plugins_Loggers_ArrayLogger(); + $logger->add(">> FOO\r\n"); + $logger->add("<< 502 That makes no sense\r\n"); + $logger->add(">> RSET\r\n"); + $logger->add("<< 250 OK\r\n"); + + $this->assertEquals( + ">> FOO\r\n".PHP_EOL. + "<< 502 That makes no sense\r\n".PHP_EOL. + ">> RSET\r\n".PHP_EOL. + "<< 250 OK\r\n", + $logger->dump() + ); + } + + public function testLogCanBeCleared() + { + $logger = new Swift_Plugins_Loggers_ArrayLogger(); + $logger->add(">> FOO\r\n"); + $logger->add("<< 502 That makes no sense\r\n"); + $logger->add(">> RSET\r\n"); + $logger->add("<< 250 OK\r\n"); + + $this->assertEquals( + ">> FOO\r\n".PHP_EOL. + "<< 502 That makes no sense\r\n".PHP_EOL. + ">> RSET\r\n".PHP_EOL. + "<< 250 OK\r\n", + $logger->dump() + ); + + $logger->clear(); + + $this->assertEquals('', $logger->dump()); + } + + public function testLengthCanBeTruncated() + { + $logger = new Swift_Plugins_Loggers_ArrayLogger(2); + $logger->add(">> FOO\r\n"); + $logger->add("<< 502 That makes no sense\r\n"); + $logger->add(">> RSET\r\n"); + $logger->add("<< 250 OK\r\n"); + + $this->assertEquals( + ">> RSET\r\n".PHP_EOL. + "<< 250 OK\r\n", + $logger->dump(), + '%s: Log should be truncated to last 2 entries' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/EchoLoggerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/EchoLoggerTest.php new file mode 100644 index 00000000..6134fe64 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/EchoLoggerTest.php @@ -0,0 +1,24 @@ +<?php + +class Swift_Plugins_Loggers_EchoLoggerTest extends \PHPUnit_Framework_TestCase +{ + public function testAddingEntryDumpsSingleLineWithoutHtml() + { + $logger = new Swift_Plugins_Loggers_EchoLogger(false); + ob_start(); + $logger->add('>> Foo'); + $data = ob_get_clean(); + + $this->assertEquals('>> Foo'.PHP_EOL, $data); + } + + public function testAddingEntryDumpsEscapedLineWithHtml() + { + $logger = new Swift_Plugins_Loggers_EchoLogger(true); + ob_start(); + $logger->add('>> Foo'); + $data = ob_get_clean(); + + $this->assertEquals('>> Foo<br />'.PHP_EOL, $data); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/PopBeforeSmtpPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/PopBeforeSmtpPluginTest.php new file mode 100644 index 00000000..d7c55afc --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/PopBeforeSmtpPluginTest.php @@ -0,0 +1,103 @@ +<?php + +class Swift_Plugins_PopBeforeSmtpPluginTest extends \PHPUnit_Framework_TestCase +{ + public function testPluginConnectsToPop3HostBeforeTransportStarts() + { + $connection = $this->_createConnection(); + $connection->expects($this->once()) + ->method('connect'); + + $plugin = $this->_createPlugin('pop.host.tld', 110); + $plugin->setConnection($connection); + + $transport = $this->_createTransport(); + $evt = $this->_createTransportChangeEvent($transport); + + $plugin->beforeTransportStarted($evt); + } + + public function testPluginDisconnectsFromPop3HostBeforeTransportStarts() + { + $connection = $this->_createConnection(); + $connection->expects($this->once()) + ->method('disconnect'); + + $plugin = $this->_createPlugin('pop.host.tld', 110); + $plugin->setConnection($connection); + + $transport = $this->_createTransport(); + $evt = $this->_createTransportChangeEvent($transport); + + $plugin->beforeTransportStarted($evt); + } + + public function testPluginDoesNotConnectToSmtpIfBoundToDifferentTransport() + { + $connection = $this->_createConnection(); + $connection->expects($this->never()) + ->method('disconnect'); + $connection->expects($this->never()) + ->method('connect'); + + $smtp = $this->_createTransport(); + + $plugin = $this->_createPlugin('pop.host.tld', 110); + $plugin->setConnection($connection); + $plugin->bindSmtp($smtp); + + $transport = $this->_createTransport(); + $evt = $this->_createTransportChangeEvent($transport); + + $plugin->beforeTransportStarted($evt); + } + + public function testPluginCanBindToSpecificTransport() + { + $connection = $this->_createConnection(); + $connection->expects($this->once()) + ->method('connect'); + + $smtp = $this->_createTransport(); + + $plugin = $this->_createPlugin('pop.host.tld', 110); + $plugin->setConnection($connection); + $plugin->bindSmtp($smtp); + + $evt = $this->_createTransportChangeEvent($smtp); + + $plugin->beforeTransportStarted($evt); + } + + // -- Creation Methods + + private function _createTransport() + { + return $this->getMockBuilder('Swift_Transport')->getMock(); + } + + private function _createTransportChangeEvent($transport) + { + $evt = $this->getMockBuilder('Swift_Events_TransportChangeEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getSource') + ->will($this->returnValue($transport)); + $evt->expects($this->any()) + ->method('getTransport') + ->will($this->returnValue($transport)); + + return $evt; + } + + public function _createConnection() + { + return $this->getMockBuilder('Swift_Plugins_Pop_Pop3Connection')->getMock(); + } + + public function _createPlugin($host, $port, $crypto = null) + { + return new Swift_Plugins_PopBeforeSmtpPlugin($host, $port, $crypto); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/RedirectingPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/RedirectingPluginTest.php new file mode 100644 index 00000000..4cc7e89f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/RedirectingPluginTest.php @@ -0,0 +1,185 @@ +<?php + +class Swift_Plugins_RedirectingPluginTest extends \PHPUnit_Framework_TestCase +{ + public function testRecipientCanBeSetAndFetched() + { + $plugin = new Swift_Plugins_RedirectingPlugin('fabien@example.com'); + $this->assertEquals('fabien@example.com', $plugin->getRecipient()); + $plugin->setRecipient('chris@example.com'); + $this->assertEquals('chris@example.com', $plugin->getRecipient()); + } + + public function testPluginChangesRecipients() + { + $message = Swift_Message::newInstance() + ->setSubject('...') + ->setFrom(array('john@example.com' => 'John Doe')) + ->setTo($to = array( + 'fabien-to@example.com' => 'Fabien (To)', + 'chris-to@example.com' => 'Chris (To)', + )) + ->setCc($cc = array( + 'fabien-cc@example.com' => 'Fabien (Cc)', + 'chris-cc@example.com' => 'Chris (Cc)', + )) + ->setBcc($bcc = array( + 'fabien-bcc@example.com' => 'Fabien (Bcc)', + 'chris-bcc@example.com' => 'Chris (Bcc)', + )) + ->setBody('...') + ; + + $plugin = new Swift_Plugins_RedirectingPlugin('god@example.com'); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + + $this->assertEquals($message->getTo(), array('god@example.com' => '')); + $this->assertEquals($message->getCc(), array()); + $this->assertEquals($message->getBcc(), array()); + + $plugin->sendPerformed($evt); + + $this->assertEquals($message->getTo(), $to); + $this->assertEquals($message->getCc(), $cc); + $this->assertEquals($message->getBcc(), $bcc); + } + + public function testPluginRespectsUnsetToList() + { + $message = Swift_Message::newInstance() + ->setSubject('...') + ->setFrom(array('john@example.com' => 'John Doe')) + ->setCc($cc = array( + 'fabien-cc@example.com' => 'Fabien (Cc)', + 'chris-cc@example.com' => 'Chris (Cc)', + )) + ->setBcc($bcc = array( + 'fabien-bcc@example.com' => 'Fabien (Bcc)', + 'chris-bcc@example.com' => 'Chris (Bcc)', + )) + ->setBody('...') + ; + + $plugin = new Swift_Plugins_RedirectingPlugin('god@example.com'); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + + $this->assertEquals($message->getTo(), array('god@example.com' => '')); + $this->assertEquals($message->getCc(), array()); + $this->assertEquals($message->getBcc(), array()); + + $plugin->sendPerformed($evt); + + $this->assertEquals($message->getTo(), array()); + $this->assertEquals($message->getCc(), $cc); + $this->assertEquals($message->getBcc(), $bcc); + } + + public function testPluginRespectsAWhitelistOfPatterns() + { + $message = Swift_Message::newInstance() + ->setSubject('...') + ->setFrom(array('john@example.com' => 'John Doe')) + ->setTo($to = array( + 'fabien-to@example.com' => 'Fabien (To)', + 'chris-to@example.com' => 'Chris (To)', + 'lars-to@internal.com' => 'Lars (To)', + )) + ->setCc($cc = array( + 'fabien-cc@example.com' => 'Fabien (Cc)', + 'chris-cc@example.com' => 'Chris (Cc)', + 'lars-cc@internal.org' => 'Lars (Cc)', + )) + ->setBcc($bcc = array( + 'fabien-bcc@example.com' => 'Fabien (Bcc)', + 'chris-bcc@example.com' => 'Chris (Bcc)', + 'john-bcc@example.org' => 'John (Bcc)', + )) + ->setBody('...') + ; + + $recipient = 'god@example.com'; + $patterns = array('/^.*@internal.[a-z]+$/', '/^john-.*$/'); + + $plugin = new Swift_Plugins_RedirectingPlugin($recipient, $patterns); + + $this->assertEquals($recipient, $plugin->getRecipient()); + $this->assertEquals($plugin->getWhitelist(), $patterns); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + + $this->assertEquals($message->getTo(), array('lars-to@internal.com' => 'Lars (To)', 'god@example.com' => null)); + $this->assertEquals($message->getCc(), array('lars-cc@internal.org' => 'Lars (Cc)')); + $this->assertEquals($message->getBcc(), array('john-bcc@example.org' => 'John (Bcc)')); + + $plugin->sendPerformed($evt); + + $this->assertEquals($message->getTo(), $to); + $this->assertEquals($message->getCc(), $cc); + $this->assertEquals($message->getBcc(), $bcc); + } + + public function testArrayOfRecipientsCanBeExplicitlyDefined() + { + $message = Swift_Message::newInstance() + ->setSubject('...') + ->setFrom(array('john@example.com' => 'John Doe')) + ->setTo(array( + 'fabien@example.com' => 'Fabien', + 'chris@example.com' => 'Chris (To)', + 'lars-to@internal.com' => 'Lars (To)', + )) + ->setCc(array( + 'fabien@example.com' => 'Fabien', + 'chris-cc@example.com' => 'Chris (Cc)', + 'lars-cc@internal.org' => 'Lars (Cc)', + )) + ->setBcc(array( + 'fabien@example.com' => 'Fabien', + 'chris-bcc@example.com' => 'Chris (Bcc)', + 'john-bcc@example.org' => 'John (Bcc)', + )) + ->setBody('...') + ; + + $recipients = array('god@example.com', 'fabien@example.com'); + $patterns = array('/^.*@internal.[a-z]+$/'); + + $plugin = new Swift_Plugins_RedirectingPlugin($recipients, $patterns); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + + $this->assertEquals( + $message->getTo(), + array('fabien@example.com' => 'Fabien', 'lars-to@internal.com' => 'Lars (To)', 'god@example.com' => null) + ); + $this->assertEquals( + $message->getCc(), + array('fabien@example.com' => 'Fabien', 'lars-cc@internal.org' => 'Lars (Cc)') + ); + $this->assertEquals($message->getBcc(), array('fabien@example.com' => 'Fabien')); + } + + // -- Creation Methods + + private function _createSendEvent(Swift_Mime_Message $message) + { + $evt = $this->getMockBuilder('Swift_Events_SendEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getMessage') + ->will($this->returnValue($message)); + + return $evt; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ReporterPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ReporterPluginTest.php new file mode 100644 index 00000000..81018830 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ReporterPluginTest.php @@ -0,0 +1,88 @@ +<?php + +class Swift_Plugins_ReporterPluginTest extends \SwiftMailerTestCase +{ + public function testReportingPasses() + { + $message = $this->_createMessage(); + $evt = $this->_createSendEvent(); + $reporter = $this->_createReporter(); + + $message->shouldReceive('getTo')->zeroOrMoreTimes()->andReturn(array('foo@bar.tld' => 'Foo')); + $evt->shouldReceive('getMessage')->zeroOrMoreTimes()->andReturn($message); + $evt->shouldReceive('getFailedRecipients')->zeroOrMoreTimes()->andReturn(array()); + $reporter->shouldReceive('notify')->once()->with($message, 'foo@bar.tld', Swift_Plugins_Reporter::RESULT_PASS); + + $plugin = new Swift_Plugins_ReporterPlugin($reporter); + $plugin->sendPerformed($evt); + } + + public function testReportingFailedTo() + { + $message = $this->_createMessage(); + $evt = $this->_createSendEvent(); + $reporter = $this->_createReporter(); + + $message->shouldReceive('getTo')->zeroOrMoreTimes()->andReturn(array('foo@bar.tld' => 'Foo', 'zip@button' => 'Zip')); + $evt->shouldReceive('getMessage')->zeroOrMoreTimes()->andReturn($message); + $evt->shouldReceive('getFailedRecipients')->zeroOrMoreTimes()->andReturn(array('zip@button')); + $reporter->shouldReceive('notify')->once()->with($message, 'foo@bar.tld', Swift_Plugins_Reporter::RESULT_PASS); + $reporter->shouldReceive('notify')->once()->with($message, 'zip@button', Swift_Plugins_Reporter::RESULT_FAIL); + + $plugin = new Swift_Plugins_ReporterPlugin($reporter); + $plugin->sendPerformed($evt); + } + + public function testReportingFailedCc() + { + $message = $this->_createMessage(); + $evt = $this->_createSendEvent(); + $reporter = $this->_createReporter(); + + $message->shouldReceive('getTo')->zeroOrMoreTimes()->andReturn(array('foo@bar.tld' => 'Foo')); + $message->shouldReceive('getCc')->zeroOrMoreTimes()->andReturn(array('zip@button' => 'Zip', 'test@test.com' => 'Test')); + $evt->shouldReceive('getMessage')->zeroOrMoreTimes()->andReturn($message); + $evt->shouldReceive('getFailedRecipients')->zeroOrMoreTimes()->andReturn(array('zip@button')); + $reporter->shouldReceive('notify')->once()->with($message, 'foo@bar.tld', Swift_Plugins_Reporter::RESULT_PASS); + $reporter->shouldReceive('notify')->once()->with($message, 'zip@button', Swift_Plugins_Reporter::RESULT_FAIL); + $reporter->shouldReceive('notify')->once()->with($message, 'test@test.com', Swift_Plugins_Reporter::RESULT_PASS); + + $plugin = new Swift_Plugins_ReporterPlugin($reporter); + $plugin->sendPerformed($evt); + } + + public function testReportingFailedBcc() + { + $message = $this->_createMessage(); + $evt = $this->_createSendEvent(); + $reporter = $this->_createReporter(); + + $message->shouldReceive('getTo')->zeroOrMoreTimes()->andReturn(array('foo@bar.tld' => 'Foo')); + $message->shouldReceive('getBcc')->zeroOrMoreTimes()->andReturn(array('zip@button' => 'Zip', 'test@test.com' => 'Test')); + $evt->shouldReceive('getMessage')->zeroOrMoreTimes()->andReturn($message); + $evt->shouldReceive('getFailedRecipients')->zeroOrMoreTimes()->andReturn(array('zip@button')); + $reporter->shouldReceive('notify')->once()->with($message, 'foo@bar.tld', Swift_Plugins_Reporter::RESULT_PASS); + $reporter->shouldReceive('notify')->once()->with($message, 'zip@button', Swift_Plugins_Reporter::RESULT_FAIL); + $reporter->shouldReceive('notify')->once()->with($message, 'test@test.com', Swift_Plugins_Reporter::RESULT_PASS); + + $plugin = new Swift_Plugins_ReporterPlugin($reporter); + $plugin->sendPerformed($evt); + } + + // -- Creation Methods + + private function _createMessage() + { + return $this->getMockery('Swift_Mime_Message')->shouldIgnoreMissing(); + } + + private function _createSendEvent() + { + return $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + } + + private function _createReporter() + { + return $this->getMockery('Swift_Plugins_Reporter')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HitReporterTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HitReporterTest.php new file mode 100644 index 00000000..33dcc750 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HitReporterTest.php @@ -0,0 +1,64 @@ +<?php + +class Swift_Plugins_Reporters_HitReporterTest extends \PHPUnit_Framework_TestCase +{ + private $_hitReporter; + private $_message; + + public function setUp() + { + $this->_hitReporter = new Swift_Plugins_Reporters_HitReporter(); + $this->_message = $this->getMockBuilder('Swift_Mime_Message')->getMock(); + } + + public function testReportingFail() + { + $this->_hitReporter->notify($this->_message, 'foo@bar.tld', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $this->assertEquals(array('foo@bar.tld'), + $this->_hitReporter->getFailedRecipients() + ); + } + + public function testMultipleReports() + { + $this->_hitReporter->notify($this->_message, 'foo@bar.tld', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $this->_hitReporter->notify($this->_message, 'zip@button', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $this->assertEquals(array('foo@bar.tld', 'zip@button'), + $this->_hitReporter->getFailedRecipients() + ); + } + + public function testReportingPassIsIgnored() + { + $this->_hitReporter->notify($this->_message, 'foo@bar.tld', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $this->_hitReporter->notify($this->_message, 'zip@button', + Swift_Plugins_Reporter::RESULT_PASS + ); + $this->assertEquals(array('foo@bar.tld'), + $this->_hitReporter->getFailedRecipients() + ); + } + + public function testBufferCanBeCleared() + { + $this->_hitReporter->notify($this->_message, 'foo@bar.tld', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $this->_hitReporter->notify($this->_message, 'zip@button', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $this->assertEquals(array('foo@bar.tld', 'zip@button'), + $this->_hitReporter->getFailedRecipients() + ); + $this->_hitReporter->clear(); + $this->assertEquals(array(), $this->_hitReporter->getFailedRecipients()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HtmlReporterTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HtmlReporterTest.php new file mode 100644 index 00000000..a6c6a277 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HtmlReporterTest.php @@ -0,0 +1,54 @@ +<?php + +class Swift_Plugins_Reporters_HtmlReporterTest extends \PHPUnit_Framework_TestCase +{ + private $_html; + private $_message; + + public function setUp() + { + $this->_html = new Swift_Plugins_Reporters_HtmlReporter(); + $this->_message = $this->getMockBuilder('Swift_Mime_Message')->getMock(); + } + + public function testReportingPass() + { + ob_start(); + $this->_html->notify($this->_message, 'foo@bar.tld', + Swift_Plugins_Reporter::RESULT_PASS + ); + $html = ob_get_clean(); + + $this->assertRegExp('~ok|pass~i', $html, '%s: Reporter should indicate pass'); + $this->assertRegExp('~foo@bar\.tld~', $html, '%s: Reporter should show address'); + } + + public function testReportingFail() + { + ob_start(); + $this->_html->notify($this->_message, 'zip@button', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $html = ob_get_clean(); + + $this->assertRegExp('~fail~i', $html, '%s: Reporter should indicate fail'); + $this->assertRegExp('~zip@button~', $html, '%s: Reporter should show address'); + } + + public function testMultipleReports() + { + ob_start(); + $this->_html->notify($this->_message, 'foo@bar.tld', + Swift_Plugins_Reporter::RESULT_PASS + ); + $this->_html->notify($this->_message, 'zip@button', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $html = ob_get_clean(); + + $this->assertRegExp('~ok|pass~i', $html, '%s: Reporter should indicate pass'); + $this->assertRegExp('~foo@bar\.tld~', $html, '%s: Reporter should show address'); + $this->assertRegExp('~fail~i', $html, '%s: Reporter should indicate fail'); + $this->assertRegExp('~zip@button~', $html, '%s: Reporter should show address'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ThrottlerPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ThrottlerPluginTest.php new file mode 100644 index 00000000..a50f14f9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ThrottlerPluginTest.php @@ -0,0 +1,104 @@ +<?php + +class Swift_Plugins_ThrottlerPluginTest extends \SwiftMailerTestCase +{ + public function testBytesPerMinuteThrottling() + { + $sleeper = $this->_createSleeper(); + $timer = $this->_createTimer(); + + //10MB/min + $plugin = new Swift_Plugins_ThrottlerPlugin( + 10000000, Swift_Plugins_ThrottlerPlugin::BYTES_PER_MINUTE, + $sleeper, $timer + ); + + $timer->shouldReceive('getTimestamp')->once()->andReturn(0); + $timer->shouldReceive('getTimestamp')->once()->andReturn(1); //expected 0.6 + $timer->shouldReceive('getTimestamp')->once()->andReturn(1); //expected 1.2 (sleep 1) + $timer->shouldReceive('getTimestamp')->once()->andReturn(2); //expected 1.8 + $timer->shouldReceive('getTimestamp')->once()->andReturn(2); //expected 2.4 (sleep 1) + $sleeper->shouldReceive('sleep')->twice()->with(1); + + //10,000,000 bytes per minute + //100,000 bytes per email + + // .: (10,000,000/100,000)/60 emails per second = 1.667 emais/sec + + $message = $this->_createMessageWithByteCount(100000); //100KB + + $evt = $this->_createSendEvent($message); + + for ($i = 0; $i < 5; ++$i) { + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + } + + public function testMessagesPerMinuteThrottling() + { + $sleeper = $this->_createSleeper(); + $timer = $this->_createTimer(); + + //60/min + $plugin = new Swift_Plugins_ThrottlerPlugin( + 60, Swift_Plugins_ThrottlerPlugin::MESSAGES_PER_MINUTE, + $sleeper, $timer + ); + + $timer->shouldReceive('getTimestamp')->once()->andReturn(0); + $timer->shouldReceive('getTimestamp')->once()->andReturn(0); //expected 1 (sleep 1) + $timer->shouldReceive('getTimestamp')->once()->andReturn(2); //expected 2 + $timer->shouldReceive('getTimestamp')->once()->andReturn(2); //expected 3 (sleep 1) + $timer->shouldReceive('getTimestamp')->once()->andReturn(4); //expected 4 + $sleeper->shouldReceive('sleep')->twice()->with(1); + + //60 messages per minute + //1 message per second + + $message = $this->_createMessageWithByteCount(10); + + $evt = $this->_createSendEvent($message); + + for ($i = 0; $i < 5; ++$i) { + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + } + + // -- Creation Methods + + private function _createSleeper() + { + return $this->getMockery('Swift_Plugins_Sleeper'); + } + + private function _createTimer() + { + return $this->getMockery('Swift_Plugins_Timer'); + } + + private function _createMessageWithByteCount($bytes) + { + $msg = $this->getMockery('Swift_Mime_Message'); + $msg->shouldReceive('toByteStream') + ->zeroOrMoreTimes() + ->andReturnUsing(function ($is) use ($bytes) { + for ($i = 0; $i < $bytes; ++$i) { + $is->write('x'); + } + }); + + return $msg; + } + + private function _createSendEvent($message) + { + $evt = $this->getMockery('Swift_Events_SendEvent'); + $evt->shouldReceive('getMessage') + ->zeroOrMoreTimes() + ->andReturn($message); + + return $evt; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/DKIMSignerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/DKIMSignerTest.php new file mode 100644 index 00000000..13c1b4c1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/DKIMSignerTest.php @@ -0,0 +1,227 @@ +<?php + +class Swift_Signers_DKIMSignerTest extends \SwiftMailerTestCase +{ + public function setUp() + { + if (version_compare(phpversion(), '5.4', '<') && !defined('OPENSSL_ALGO_SHA256')) { + $this->markTestSkipped( + 'skipping because of https://bugs.php.net/bug.php?id=61421' + ); + } + } + + public function testBasicSigningHeaderManipulation() + { + $headers = $this->_createHeaders(); + $messageContent = 'Hello World'; + $signer = new Swift_Signers_DKIMSigner(file_get_contents(dirname(dirname(dirname(__DIR__))).'/_samples/dkim/dkim.test.priv'), 'dummy.nxdomain.be', 'dummySelector'); + /* @var $signer Swift_Signers_HeaderSigner */ + $altered = $signer->getAlteredHeaders(); + $signer->reset(); + // Headers + $signer->setHeaders($headers); + // Body + $signer->startBody(); + $signer->write($messageContent); + $signer->endBody(); + // Signing + $signer->addSignature($headers); + } + + // Default Signing + public function testSigningDefaults() + { + $headerSet = $this->_createHeaderSet(); + $messageContent = 'Hello World'; + $signer = new Swift_Signers_DKIMSigner(file_get_contents(dirname(dirname(dirname(__DIR__))).'/_samples/dkim/dkim.test.priv'), 'dummy.nxdomain.be', 'dummySelector'); + $signer->setSignatureTimestamp('1299879181'); + $altered = $signer->getAlteredHeaders(); + $this->assertEquals(array('DKIM-Signature'), $altered); + $signer->reset(); + $signer->setHeaders($headerSet); + $this->assertFalse($headerSet->has('DKIM-Signature')); + $signer->startBody(); + $signer->write($messageContent); + $signer->endBody(); + $signer->addSignature($headerSet); + $this->assertTrue($headerSet->has('DKIM-Signature')); + $dkim = $headerSet->getAll('DKIM-Signature'); + $sig = reset($dkim); + $this->assertEquals($sig->getValue(), 'v=1; a=rsa-sha1; bh=wlbYcY9O9OPInGJ4D0E/rGsvMLE=; d=dummy.nxdomain.be; h=; i=@dummy.nxdomain.be; s=dummySelector; t=1299879181; b=RMSNelzM2O5MAAnMjT3G3/VF36S3DGJXoPCXR001F1WDReu0prGphWjuzK/m6V1pwqQL8cCNg Hi74mTx2bvyAvmkjvQtJf1VMUOCc9WHGcm1Yec66I3ZWoNMGSWZ1EKAm2CtTzyG0IFw4ml9DI wSkyAFxlgicckDD6FibhqwX4w='); + } + + // SHA256 Signing + public function testSigning256() + { + $headerSet = $this->_createHeaderSet(); + $messageContent = 'Hello World'; + $signer = new Swift_Signers_DKIMSigner(file_get_contents(dirname(dirname(dirname(__DIR__))).'/_samples/dkim/dkim.test.priv'), 'dummy.nxdomain.be', 'dummySelector'); + $signer->setHashAlgorithm('rsa-sha256'); + $signer->setSignatureTimestamp('1299879181'); + $altered = $signer->getAlteredHeaders(); + $this->assertEquals(array('DKIM-Signature'), $altered); + $signer->reset(); + $signer->setHeaders($headerSet); + $this->assertFalse($headerSet->has('DKIM-Signature')); + $signer->startBody(); + $signer->write($messageContent); + $signer->endBody(); + $signer->addSignature($headerSet); + $this->assertTrue($headerSet->has('DKIM-Signature')); + $dkim = $headerSet->getAll('DKIM-Signature'); + $sig = reset($dkim); + $this->assertEquals($sig->getValue(), 'v=1; a=rsa-sha256; bh=f+W+hu8dIhf2VAni89o8lF6WKTXi7nViA4RrMdpD5/U=; d=dummy.nxdomain.be; h=; i=@dummy.nxdomain.be; s=dummySelector; t=1299879181; b=jqPmieHzF5vR9F4mXCAkowuphpO4iJ8IAVuioh1BFZ3VITXZj5jlOFxULJMBiiApm2keJirnh u4mzogj444QkpT3lJg8/TBGAYQPdcvkG3KC0jdyN6QpSgpITBJG2BwWa+keXsv2bkQgLRAzNx qRhP45vpHCKun0Tg9LrwW/KCg='); + } + + // Relaxed/Relaxed Hash Signing + public function testSigningRelaxedRelaxed256() + { + $headerSet = $this->_createHeaderSet(); + $messageContent = 'Hello World'; + $signer = new Swift_Signers_DKIMSigner(file_get_contents(dirname(dirname(dirname(__DIR__))).'/_samples/dkim/dkim.test.priv'), 'dummy.nxdomain.be', 'dummySelector'); + $signer->setHashAlgorithm('rsa-sha256'); + $signer->setSignatureTimestamp('1299879181'); + $signer->setBodyCanon('relaxed'); + $signer->setHeaderCanon('relaxed'); + $altered = $signer->getAlteredHeaders(); + $this->assertEquals(array('DKIM-Signature'), $altered); + $signer->reset(); + $signer->setHeaders($headerSet); + $this->assertFalse($headerSet->has('DKIM-Signature')); + $signer->startBody(); + $signer->write($messageContent); + $signer->endBody(); + $signer->addSignature($headerSet); + $this->assertTrue($headerSet->has('DKIM-Signature')); + $dkim = $headerSet->getAll('DKIM-Signature'); + $sig = reset($dkim); + $this->assertEquals($sig->getValue(), 'v=1; a=rsa-sha256; bh=f+W+hu8dIhf2VAni89o8lF6WKTXi7nViA4RrMdpD5/U=; d=dummy.nxdomain.be; h=; i=@dummy.nxdomain.be; s=dummySelector; c=relaxed/relaxed; t=1299879181; b=gzOI+PX6HpZKQFzwwmxzcVJsyirdLXOS+4pgfCpVHQIdqYusKLrhlLeFBTNoz75HrhNvGH6T0 Rt3w5aTqkrWfUuAEYt0Ns14GowLM7JojaFN+pZ4eYnRB3CBBgW6fee4NEMD5WPca3uS09tr1E 10RYh9ILlRtl+84sovhx5id3Y='); + } + + // Relaxed/Simple Hash Signing + public function testSigningRelaxedSimple256() + { + $headerSet = $this->_createHeaderSet(); + $messageContent = 'Hello World'; + $signer = new Swift_Signers_DKIMSigner(file_get_contents(dirname(dirname(dirname(__DIR__))).'/_samples/dkim/dkim.test.priv'), 'dummy.nxdomain.be', 'dummySelector'); + $signer->setHashAlgorithm('rsa-sha256'); + $signer->setSignatureTimestamp('1299879181'); + $signer->setHeaderCanon('relaxed'); + $altered = $signer->getAlteredHeaders(); + $this->assertEquals(array('DKIM-Signature'), $altered); + $signer->reset(); + $signer->setHeaders($headerSet); + $this->assertFalse($headerSet->has('DKIM-Signature')); + $signer->startBody(); + $signer->write($messageContent); + $signer->endBody(); + $signer->addSignature($headerSet); + $this->assertTrue($headerSet->has('DKIM-Signature')); + $dkim = $headerSet->getAll('DKIM-Signature'); + $sig = reset($dkim); + $this->assertEquals($sig->getValue(), 'v=1; a=rsa-sha256; bh=f+W+hu8dIhf2VAni89o8lF6WKTXi7nViA4RrMdpD5/U=; d=dummy.nxdomain.be; h=; i=@dummy.nxdomain.be; s=dummySelector; c=relaxed; t=1299879181; b=dLPJNec5v81oelyzGOY0qPqTlGnQeNfUNBOrV/JKbStr3NqWGI9jH4JAe2YvO2V32lfPNoby1 4MMzZ6EPkaZkZDDSPa+53YbCPQAlqiD9QZZIUe2UNM33HN8yAMgiWEF5aP7MbQnxeVZMfVLEl 9S8qOImu+K5JZqhQQTL0dgLwA='); + } + + // Simple/Relaxed Hash Signing + public function testSigningSimpleRelaxed256() + { + $headerSet = $this->_createHeaderSet(); + $messageContent = 'Hello World'; + $signer = new Swift_Signers_DKIMSigner(file_get_contents(dirname(dirname(dirname(__DIR__))).'/_samples/dkim/dkim.test.priv'), 'dummy.nxdomain.be', 'dummySelector'); + $signer->setHashAlgorithm('rsa-sha256'); + $signer->setSignatureTimestamp('1299879181'); + $signer->setBodyCanon('relaxed'); + $altered = $signer->getAlteredHeaders(); + $this->assertEquals(array('DKIM-Signature'), $altered); + $signer->reset(); + $signer->setHeaders($headerSet); + $this->assertFalse($headerSet->has('DKIM-Signature')); + $signer->startBody(); + $signer->write($messageContent); + $signer->endBody(); + $signer->addSignature($headerSet); + $this->assertTrue($headerSet->has('DKIM-Signature')); + $dkim = $headerSet->getAll('DKIM-Signature'); + $sig = reset($dkim); + $this->assertEquals($sig->getValue(), 'v=1; a=rsa-sha256; bh=f+W+hu8dIhf2VAni89o8lF6WKTXi7nViA4RrMdpD5/U=; d=dummy.nxdomain.be; h=; i=@dummy.nxdomain.be; s=dummySelector; c=simple/relaxed; t=1299879181; b=M5eomH/zamyzix9kOes+6YLzQZxuJdBP4x3nP9zF2N26eMLG2/cBKbnNyqiOTDhJdYfWPbLIa 1CWnjST0j5p4CpeOkGYuiE+M4TWEZwhRmRWootlPO3Ii6XpbBJKFk1o9zviS7OmXblUUE4aqb yRSIMDhtLdCK5GlaCneFLN7RQ='); + } + + // -- Creation Methods + private function _createHeaderSet() + { + $cache = new Swift_KeyCache_ArrayKeyCache(new Swift_KeyCache_SimpleKeyCacheInputStream()); + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $contentEncoder = new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + + $headerEncoder = new Swift_Mime_HeaderEncoder_QpHeaderEncoder(new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8')); + $paramEncoder = new Swift_Encoder_Rfc2231Encoder(new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8')); + $grammar = new Swift_Mime_Grammar(); + $headers = new Swift_Mime_SimpleHeaderSet(new Swift_Mime_SimpleHeaderFactory($headerEncoder, $paramEncoder, $grammar)); + + return $headers; + } + + /** + * @return Swift_Mime_Headers + */ + private function _createHeaders() + { + $x = 0; + $cache = new Swift_KeyCache_ArrayKeyCache(new Swift_KeyCache_SimpleKeyCacheInputStream()); + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $contentEncoder = new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + + $headerEncoder = new Swift_Mime_HeaderEncoder_QpHeaderEncoder(new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8')); + $paramEncoder = new Swift_Encoder_Rfc2231Encoder(new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8')); + $grammar = new Swift_Mime_Grammar(); + $headerFactory = new Swift_Mime_SimpleHeaderFactory($headerEncoder, $paramEncoder, $grammar); + $headers = $this->getMockery('Swift_Mime_HeaderSet'); + + $headers->shouldReceive('listAll') + ->zeroOrMoreTimes() + ->andReturn(array('From', 'To', 'Date', 'Subject')); + $headers->shouldReceive('has') + ->zeroOrMoreTimes() + ->with('From') + ->andReturn(true); + $headers->shouldReceive('getAll') + ->zeroOrMoreTimes() + ->with('From') + ->andReturn(array($headerFactory->createMailboxHeader('From', 'test@test.test'))); + $headers->shouldReceive('has') + ->zeroOrMoreTimes() + ->with('To') + ->andReturn(true); + $headers->shouldReceive('getAll') + ->zeroOrMoreTimes() + ->with('To') + ->andReturn(array($headerFactory->createMailboxHeader('To', 'test@test.test'))); + $headers->shouldReceive('has') + ->zeroOrMoreTimes() + ->with('Date') + ->andReturn(true); + $headers->shouldReceive('getAll') + ->zeroOrMoreTimes() + ->with('Date') + ->andReturn(array($headerFactory->createTextHeader('Date', 'Fri, 11 Mar 2011 20:56:12 +0000 (GMT)'))); + $headers->shouldReceive('has') + ->zeroOrMoreTimes() + ->with('Subject') + ->andReturn(true); + $headers->shouldReceive('getAll') + ->zeroOrMoreTimes() + ->with('Subject') + ->andReturn(array($headerFactory->createTextHeader('Subject', 'Foo Bar Text Message'))); + $headers->shouldReceive('addTextHeader') + ->zeroOrMoreTimes() + ->with('DKIM-Signature', \Mockery::any()) + ->andReturn(true); + $headers->shouldReceive('getAll') + ->zeroOrMoreTimes() + ->with('DKIM-Signature') + ->andReturn(array($headerFactory->createTextHeader('DKIM-Signature', 'Foo Bar Text Message'))); + + return $headers; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/OpenDKIMSignerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/OpenDKIMSignerTest.php new file mode 100644 index 00000000..00e48c13 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/OpenDKIMSignerTest.php @@ -0,0 +1,45 @@ +<?php + +/** + * @todo + */ +class Swift_Signers_OpenDKIMSignerTest extends \SwiftMailerTestCase +{ + public function setUp() + { + if (!extension_loaded('opendkim')) { + $this->markTestSkipped( + 'Need OpenDKIM extension run these tests.' + ); + } + } + + public function testBasicSigningHeaderManipulation() + { + } + + // Default Signing + public function testSigningDefaults() + { + } + + // SHA256 Signing + public function testSigning256() + { + } + + // Relaxed/Relaxed Hash Signing + public function testSigningRelaxedRelaxed256() + { + } + + // Relaxed/Simple Hash Signing + public function testSigningRelaxedSimple256() + { + } + + // Simple/Relaxed Hash Signing + public function testSigningSimpleRelaxed256() + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/SMimeSignerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/SMimeSignerTest.php new file mode 100644 index 00000000..df745ef2 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/SMimeSignerTest.php @@ -0,0 +1,554 @@ +<?php + +class Swift_Signers_SMimeSignerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var Swift_StreamFilters_StringReplacementFilterFactory + */ + protected $replacementFactory; + + protected $samplesDir; + + public function setUp() + { + $this->replacementFactory = Swift_DependencyContainer::getInstance() + ->lookup('transport.replacementfactory'); + + $this->samplesDir = str_replace('\\', '/', realpath(__DIR__.'/../../../_samples/')).'/'; + } + + public function testUnSingedMessage() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $this->assertEquals('Here is the message itself', $message->getBody()); + } + + public function testSingedMessage() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setSignCertificate($this->samplesDir.'smime/sign.crt', $this->samplesDir.'smime/sign.key'); + $message->attachSigner($signer); + + $messageStream = $this->newFilteredStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!($boundary = $this->getBoundary($headers['content-type']))) { + return false; + } + + $expectedBody = <<<OEL +This is an S/MIME signed message + +--$boundary +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: quoted-printable + +Here is the message itself +--$boundary +Content-Type: application/(x\-)?pkcs7-signature; name="smime\.p7s" +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; filename="smime\.p7s" + +(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2}) + +--$boundary-- +OEL; + $this->assertValidVerify($expectedBody, $messageStream); + unset($messageStream); + } + + public function testSingedMessageExtraCerts() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setSignCertificate($this->samplesDir.'smime/sign2.crt', $this->samplesDir.'smime/sign2.key', PKCS7_DETACHED, $this->samplesDir.'smime/intermediate.crt'); + $message->attachSigner($signer); + + $messageStream = $this->newFilteredStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!($boundary = $this->getBoundary($headers['content-type']))) { + return false; + } + + $expectedBody = <<<OEL +This is an S/MIME signed message + +--$boundary +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: quoted-printable + +Here is the message itself +--$boundary +Content-Type: application/(x\-)?pkcs7-signature; name="smime\.p7s" +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; filename="smime\.p7s" + +(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2}) + +--$boundary-- +OEL; + $this->assertValidVerify($expectedBody, $messageStream); + unset($messageStream); + } + + public function testSingedMessageBinary() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setSignCertificate($this->samplesDir.'smime/sign.crt', $this->samplesDir.'smime/sign.key', PKCS7_BINARY); + $message->attachSigner($signer); + + $messageStream = $this->newFilteredStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!preg_match('#^application/(x\-)?pkcs7-mime; smime-type=signed\-data;#', $headers['content-type'])) { + $this->fail('Content-type does not match.'); + + return false; + } + + $this->assertEquals($headers['content-transfer-encoding'], 'base64'); + $this->assertEquals($headers['content-disposition'], 'attachment; filename="smime.p7m"'); + + $expectedBody = '(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})'; + + $messageStreamClean = $this->newFilteredStream(); + + $this->assertValidVerify($expectedBody, $messageStream); + unset($messageStreamClean, $messageStream); + } + + public function testSingedMessageWithAttachments() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $message->attach(Swift_Attachment::fromPath($this->samplesDir.'/files/textfile.zip')); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setSignCertificate($this->samplesDir.'smime/sign.crt', $this->samplesDir.'smime/sign.key'); + $message->attachSigner($signer); + + $messageStream = $this->newFilteredStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!($boundary = $this->getBoundary($headers['content-type']))) { + return false; + } + + $expectedBody = <<<OEL +This is an S/MIME signed message + +--$boundary +Content-Type: multipart/mixed; + boundary="([a-z0-9\\'\\(\\)\\+_\\-,\\.\\/:=\\?\\ ]{0,69}[a-z0-9\\'\\(\\)\\+_\\-,\\.\\/:=\\?])" + + +--\\1 +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: quoted-printable + +Here is the message itself + +--\\1 +Content-Type: application/zip; name=textfile\\.zip +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; filename=textfile\\.zip + +UEsDBAoAAgAAAMi6VjiOTiKwLgAAAC4AAAAMABUAdGV4dGZpbGUudHh0VVQJAAN3vr5Hd76\\+R1V4 +BAD1AfUBVGhpcyBpcyBwYXJ0IG9mIGEgU3dpZnQgTWFpbGVyIHY0IHNtb2tlIHRlc3QuClBLAQIX +AwoAAgAAAMi6VjiOTiKwLgAAAC4AAAAMAA0AAAAAAAEAAACkgQAAAAB0ZXh0ZmlsZS50eHRVVAUA +A3e\\+vkdVeAAAUEsFBgAAAAABAAEARwAAAG0AAAAAAA== + +--\\1-- + +--$boundary +Content-Type: application/(x\-)?pkcs7-signature; name="smime\\.p7s" +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; filename="smime\\.p7s" + +(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2}) + +--$boundary-- +OEL; + + $this->assertValidVerify($expectedBody, $messageStream); + unset($messageStream); + } + + public function testEncryptedMessage() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $originalMessage = $this->cleanMessage($message->toString()); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setEncryptCertificate($this->samplesDir.'smime/encrypt.crt'); + $message->attachSigner($signer); + + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!preg_match('#^application/(x\-)?pkcs7-mime; smime-type=enveloped\-data;#', $headers['content-type'])) { + $this->fail('Content-type does not match.'); + + return false; + } + + $expectedBody = '(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})'; + + $decryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if (!openssl_pkcs7_decrypt($messageStream->getPath(), $decryptedMessageStream->getPath(), 'file://'.$this->samplesDir.'smime/encrypt.crt', array('file://'.$this->samplesDir.'smime/encrypt.key', 'swift'))) { + $this->fail(sprintf('Decrypt of the message failed. Internal error "%s".', openssl_error_string())); + } + + $this->assertEquals($originalMessage, $decryptedMessageStream->getContent()); + unset($decryptedMessageStream, $messageStream); + } + + public function testEncryptedMessageWithMultipleCerts() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $originalMessage = $this->cleanMessage($message->toString()); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setEncryptCertificate(array($this->samplesDir.'smime/encrypt.crt', $this->samplesDir.'smime/encrypt2.crt')); + $message->attachSigner($signer); + + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!preg_match('#^application/(x\-)?pkcs7-mime; smime-type=enveloped\-data;#', $headers['content-type'])) { + $this->fail('Content-type does not match.'); + + return false; + } + + $expectedBody = '(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})'; + + $decryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if (!openssl_pkcs7_decrypt($messageStream->getPath(), $decryptedMessageStream->getPath(), 'file://'.$this->samplesDir.'smime/encrypt.crt', array('file://'.$this->samplesDir.'smime/encrypt.key', 'swift'))) { + $this->fail(sprintf('Decrypt of the message failed. Internal error "%s".', openssl_error_string())); + } + + $this->assertEquals($originalMessage, $decryptedMessageStream->getContent()); + unset($decryptedMessageStream); + + $decryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if (!openssl_pkcs7_decrypt($messageStream->getPath(), $decryptedMessageStream->getPath(), 'file://'.$this->samplesDir.'smime/encrypt2.crt', array('file://'.$this->samplesDir.'smime/encrypt2.key', 'swift'))) { + $this->fail(sprintf('Decrypt of the message failed. Internal error "%s".', openssl_error_string())); + } + + $this->assertEquals($originalMessage, $decryptedMessageStream->getContent()); + unset($decryptedMessageStream, $messageStream); + } + + public function testSignThenEncryptedMessage() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setSignCertificate($this->samplesDir.'smime/sign.crt', $this->samplesDir.'smime/sign.key'); + $signer->setEncryptCertificate($this->samplesDir.'smime/encrypt.crt'); + $message->attachSigner($signer); + + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!preg_match('#^application/(x\-)?pkcs7-mime; smime-type=enveloped\-data;#', $headers['content-type'])) { + $this->fail('Content-type does not match.'); + + return false; + } + + $expectedBody = '(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})'; + + $decryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if (!openssl_pkcs7_decrypt($messageStream->getPath(), $decryptedMessageStream->getPath(), 'file://'.$this->samplesDir.'smime/encrypt.crt', array('file://'.$this->samplesDir.'smime/encrypt.key', 'swift'))) { + $this->fail(sprintf('Decrypt of the message failed. Internal error "%s".', openssl_error_string())); + } + + $entityString = $decryptedMessageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!($boundary = $this->getBoundary($headers['content-type']))) { + return false; + } + + $expectedBody = <<<OEL +This is an S/MIME signed message + +--$boundary +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: quoted-printable + +Here is the message itself +--$boundary +Content-Type: application/(x\-)?pkcs7-signature; name="smime\.p7s" +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; filename="smime\.p7s" + +(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2}) + +--$boundary-- +OEL; + + if (!$this->assertValidVerify($expectedBody, $decryptedMessageStream)) { + return false; + } + + unset($decryptedMessageStream, $messageStream); + } + + public function testEncryptThenSignMessage() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $originalMessage = $this->cleanMessage($message->toString()); + + $signer = Swift_Signers_SMimeSigner::newInstance(); + $signer->setSignCertificate($this->samplesDir.'smime/sign.crt', $this->samplesDir.'smime/sign.key'); + $signer->setEncryptCertificate($this->samplesDir.'smime/encrypt.crt'); + $signer->setSignThenEncrypt(false); + $message->attachSigner($signer); + + $messageStream = $this->newFilteredStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!($boundary = $this->getBoundary($headers['content-type']))) { + return false; + } + + $expectedBody = <<<OEL +This is an S/MIME signed message + +--$boundary +(?P<encrypted_message>MIME-Version: 1\.0 +Content-Disposition: attachment; filename="smime\.p7m" +Content-Type: application/(x\-)?pkcs7-mime; smime-type=enveloped-data; name="smime\.p7m" +Content-Transfer-Encoding: base64 + +(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2}) + + +)--$boundary +Content-Type: application/(x\-)?pkcs7-signature; name="smime\.p7s" +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; filename="smime\.p7s" + +(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2}) + +--$boundary-- +OEL; + + if (!$this->assertValidVerify($expectedBody, $messageStream)) { + return false; + } + + $expectedBody = str_replace("\n", "\r\n", $expectedBody); + if (!preg_match('%'.$expectedBody.'*%m', $entityString, $entities)) { + $this->fail('Failed regex match.'); + + return false; + } + + $messageStreamClean = new Swift_ByteStream_TemporaryFileByteStream(); + $messageStreamClean->write($entities['encrypted_message']); + + $decryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if (!openssl_pkcs7_decrypt($messageStreamClean->getPath(), $decryptedMessageStream->getPath(), 'file://'.$this->samplesDir.'smime/encrypt.crt', array('file://'.$this->samplesDir.'smime/encrypt.key', 'swift'))) { + $this->fail(sprintf('Decrypt of the message failed. Internal error "%s".', openssl_error_string())); + } + + $this->assertEquals($originalMessage, $decryptedMessageStream->getContent()); + unset($messageStreamClean, $messageStream, $decryptedMessageStream); + } + + protected function assertValidVerify($expected, Swift_ByteStream_TemporaryFileByteStream $messageStream) + { + $actual = $messageStream->getContent(); + + // File is UNIX encoded so convert them to correct line ending + $expected = str_replace("\n", "\r\n", $expected); + + $actual = trim(self::getBodyOfMessage($actual)); + if (!$this->assertRegExp('%^'.$expected.'$\s*%m', $actual)) { + return false; + } + + $opensslOutput = new Swift_ByteStream_TemporaryFileByteStream(); + $verify = openssl_pkcs7_verify($messageStream->getPath(), null, $opensslOutput->getPath(), array($this->samplesDir.'smime/ca.crt')); + + if (false === $verify) { + $this->fail('Verification of the message failed.'); + + return false; + } elseif (-1 === $verify) { + $this->fail(sprintf('Verification of the message failed. Internal error "%s".', openssl_error_string())); + + return false; + } + + return true; + } + + protected function getBoundary($contentType) + { + if (!preg_match('/boundary=("[^"]+"|(?:[^\s]+|$))/is', $contentType, $contentTypeData)) { + $this->fail('Failed to find Boundary parameter'); + + return false; + } + + return trim($contentTypeData[1], '"'); + } + + protected function newFilteredStream() + { + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + $messageStream->addFilter($this->replacementFactory->createFilter("\r\n", "\n"), 'CRLF to LF'); + $messageStream->addFilter($this->replacementFactory->createFilter("\n", "\r\n"), 'LF to CRLF'); + + return $messageStream; + } + + protected static function getBodyOfMessage($message) + { + return substr($message, strpos($message, "\r\n\r\n")); + } + + /** + * Strips of the sender headers and Mime-Version. + * + * @param Swift_ByteStream_TemporaryFileByteStream $messageStream + * @param Swift_ByteStream_TemporaryFileByteStream $inputStream + */ + protected function cleanMessage($content) + { + $newContent = ''; + + $headers = self::getHeadersOfMessage($content); + foreach ($headers as $headerName => $value) { + if (!in_array($headerName, array('content-type', 'content-transfer-encoding', 'content-disposition'))) { + continue; + } + + $headerName = explode('-', $headerName); + $headerName = array_map('ucfirst', $headerName); + $headerName = implode('-', $headerName); + + if (strlen($value) > 62) { + $value = wordwrap($value, 62, "\n "); + } + + $newContent .= "$headerName: $value\r\n"; + } + + return $newContent."\r\n".ltrim(self::getBodyOfMessage($content)); + } + + /** + * Returns the headers of the message. + * + * Header-names are lowercase. + * + * @param string $message + * + * @return array + */ + protected static function getHeadersOfMessage($message) + { + $headersPosEnd = strpos($message, "\r\n\r\n"); + $headerData = substr($message, 0, $headersPosEnd); + $headerLines = explode("\r\n", $headerData); + + if (empty($headerLines)) { + return array(); + } + + $headers = array(); + + foreach ($headerLines as $headerLine) { + if (ctype_space($headerLines[0]) || false === strpos($headerLine, ':')) { + $headers[$currentHeaderName] .= ' '.trim($headerLine); + continue; + } + + $header = explode(':', $headerLine, 2); + $currentHeaderName = strtolower($header[0]); + $headers[$currentHeaderName] = trim($header[1]); + } + + return $headers; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/ByteArrayReplacementFilterTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/ByteArrayReplacementFilterTest.php new file mode 100644 index 00000000..e4d4f48d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/ByteArrayReplacementFilterTest.php @@ -0,0 +1,131 @@ +<?php + +class Swift_StreamFilters_ByteArrayReplacementFilterTest extends \PHPUnit_Framework_TestCase +{ + public function testBasicReplacementsAreMade() + { + $filter = $this->_createFilter(array(0x61, 0x62), array(0x63, 0x64)); + $this->assertEquals( + array(0x59, 0x60, 0x63, 0x64, 0x65), + $filter->filter(array(0x59, 0x60, 0x61, 0x62, 0x65)) + ); + } + + public function testShouldBufferReturnsTrueIfPartialMatchAtEndOfBuffer() + { + $filter = $this->_createFilter(array(0x61, 0x62), array(0x63, 0x64)); + $this->assertTrue($filter->shouldBuffer(array(0x59, 0x60, 0x61)), + '%s: Filter should buffer since 0x61 0x62 is the needle and the ending '. + '0x61 could be from 0x61 0x62' + ); + } + + public function testFilterCanMakeMultipleReplacements() + { + $filter = $this->_createFilter(array(array(0x61), array(0x62)), array(0x63)); + $this->assertEquals( + array(0x60, 0x63, 0x60, 0x63, 0x60), + $filter->filter(array(0x60, 0x61, 0x60, 0x62, 0x60)) + ); + } + + public function testMultipleReplacementsCanBeDifferent() + { + $filter = $this->_createFilter(array(array(0x61), array(0x62)), array(array(0x63), array(0x64))); + $this->assertEquals( + array(0x60, 0x63, 0x60, 0x64, 0x60), + $filter->filter(array(0x60, 0x61, 0x60, 0x62, 0x60)) + ); + } + + public function testShouldBufferReturnsFalseIfPartialMatchNotAtEndOfString() + { + $filter = $this->_createFilter(array(0x0D, 0x0A), array(0x0A)); + $this->assertFalse($filter->shouldBuffer(array(0x61, 0x62, 0x0D, 0x0A, 0x63)), + '%s: Filter should not buffer since x0Dx0A is the needle and is not at EOF' + ); + } + + public function testShouldBufferReturnsTrueIfAnyOfMultipleMatchesAtEndOfString() + { + $filter = $this->_createFilter(array(array(0x61, 0x62), array(0x63)), array(0x64)); + $this->assertTrue($filter->shouldBuffer(array(0x59, 0x60, 0x61)), + '%s: Filter should buffer since 0x61 0x62 is a needle and the ending '. + '0x61 could be from 0x61 0x62' + ); + } + + public function testConvertingAllLineEndingsToCRLFWhenInputIsLF() + { + $filter = $this->_createFilter( + array(array(0x0D, 0x0A), array(0x0D), array(0x0A)), + array(array(0x0A), array(0x0A), array(0x0D, 0x0A)) + ); + + $this->assertEquals( + array(0x60, 0x0D, 0x0A, 0x61, 0x0D, 0x0A, 0x62, 0x0D, 0x0A, 0x63), + $filter->filter(array(0x60, 0x0A, 0x61, 0x0A, 0x62, 0x0A, 0x63)) + ); + } + + public function testConvertingAllLineEndingsToCRLFWhenInputIsCR() + { + $filter = $this->_createFilter( + array(array(0x0D, 0x0A), array(0x0D), array(0x0A)), + array(array(0x0A), array(0x0A), array(0x0D, 0x0A)) + ); + + $this->assertEquals( + array(0x60, 0x0D, 0x0A, 0x61, 0x0D, 0x0A, 0x62, 0x0D, 0x0A, 0x63), + $filter->filter(array(0x60, 0x0D, 0x61, 0x0D, 0x62, 0x0D, 0x63)) + ); + } + + public function testConvertingAllLineEndingsToCRLFWhenInputIsCRLF() + { + $filter = $this->_createFilter( + array(array(0x0D, 0x0A), array(0x0D), array(0x0A)), + array(array(0x0A), array(0x0A), array(0x0D, 0x0A)) + ); + + $this->assertEquals( + array(0x60, 0x0D, 0x0A, 0x61, 0x0D, 0x0A, 0x62, 0x0D, 0x0A, 0x63), + $filter->filter(array(0x60, 0x0D, 0x0A, 0x61, 0x0D, 0x0A, 0x62, 0x0D, 0x0A, 0x63)) + ); + } + + public function testConvertingAllLineEndingsToCRLFWhenInputIsLFCR() + { + $filter = $this->_createFilter( + array(array(0x0D, 0x0A), array(0x0D), array(0x0A)), + array(array(0x0A), array(0x0A), array(0x0D, 0x0A)) + ); + + $this->assertEquals( + array(0x60, 0x0D, 0x0A, 0x0D, 0x0A, 0x61, 0x0D, 0x0A, 0x0D, 0x0A, 0x62, 0x0D, 0x0A, 0x0D, 0x0A, 0x63), + $filter->filter(array(0x60, 0x0A, 0x0D, 0x61, 0x0A, 0x0D, 0x62, 0x0A, 0x0D, 0x63)) + ); + } + + public function testConvertingAllLineEndingsToCRLFWhenInputContainsLFLF() + { + //Lighthouse Bug #23 + + $filter = $this->_createFilter( + array(array(0x0D, 0x0A), array(0x0D), array(0x0A)), + array(array(0x0A), array(0x0A), array(0x0D, 0x0A)) + ); + + $this->assertEquals( + array(0x60, 0x0D, 0x0A, 0x0D, 0x0A, 0x61, 0x0D, 0x0A, 0x0D, 0x0A, 0x62, 0x0D, 0x0A, 0x0D, 0x0A, 0x63), + $filter->filter(array(0x60, 0x0A, 0x0A, 0x61, 0x0A, 0x0A, 0x62, 0x0A, 0x0A, 0x63)) + ); + } + + // -- Creation methods + + private function _createFilter($search, $replace) + { + return new Swift_StreamFilters_ByteArrayReplacementFilter($search, $replace); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterFactoryTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterFactoryTest.php new file mode 100644 index 00000000..bd44f5c7 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterFactoryTest.php @@ -0,0 +1,38 @@ +<?php + +class Swift_StreamFilters_StringReplacementFilterFactoryTest extends \PHPUnit_Framework_TestCase +{ + public function testInstancesOfStringReplacementFilterAreCreated() + { + $factory = $this->_createFactory(); + $this->assertInstanceof( + 'Swift_StreamFilters_StringReplacementFilter', + $factory->createFilter('a', 'b') + ); + } + + public function testSameInstancesAreCached() + { + $factory = $this->_createFactory(); + $filter1 = $factory->createFilter('a', 'b'); + $filter2 = $factory->createFilter('a', 'b'); + $this->assertSame($filter1, $filter2, '%s: Instances should be cached'); + } + + public function testDifferingInstancesAreNotCached() + { + $factory = $this->_createFactory(); + $filter1 = $factory->createFilter('a', 'b'); + $filter2 = $factory->createFilter('a', 'c'); + $this->assertNotEquals($filter1, $filter2, + '%s: Differing instances should not be cached' + ); + } + + // -- Creation methods + + private function _createFactory() + { + return new Swift_StreamFilters_StringReplacementFilterFactory(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterTest.php new file mode 100644 index 00000000..7a98a2f2 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterTest.php @@ -0,0 +1,55 @@ +<?php + +class Swift_StreamFilters_StringReplacementFilterTest extends \PHPUnit_Framework_TestCase +{ + public function testBasicReplacementsAreMade() + { + $filter = $this->_createFilter('foo', 'bar'); + $this->assertEquals('XbarYbarZ', $filter->filter('XfooYfooZ')); + } + + public function testShouldBufferReturnsTrueIfPartialMatchAtEndOfBuffer() + { + $filter = $this->_createFilter('foo', 'bar'); + $this->assertTrue($filter->shouldBuffer('XfooYf'), + '%s: Filter should buffer since "foo" is the needle and the ending '. + '"f" could be from "foo"' + ); + } + + public function testFilterCanMakeMultipleReplacements() + { + $filter = $this->_createFilter(array('a', 'b'), 'foo'); + $this->assertEquals('XfooYfooZ', $filter->filter('XaYbZ')); + } + + public function testMultipleReplacementsCanBeDifferent() + { + $filter = $this->_createFilter(array('a', 'b'), array('foo', 'zip')); + $this->assertEquals('XfooYzipZ', $filter->filter('XaYbZ')); + } + + public function testShouldBufferReturnsFalseIfPartialMatchNotAtEndOfString() + { + $filter = $this->_createFilter("\r\n", "\n"); + $this->assertFalse($filter->shouldBuffer("foo\r\nbar"), + '%s: Filter should not buffer since x0Dx0A is the needle and is not at EOF' + ); + } + + public function testShouldBufferReturnsTrueIfAnyOfMultipleMatchesAtEndOfString() + { + $filter = $this->_createFilter(array('foo', 'zip'), 'bar'); + $this->assertTrue($filter->shouldBuffer('XfooYzi'), + '%s: Filter should buffer since "zip" is a needle and the ending '. + '"zi" could be from "zip"' + ); + } + + // -- Creation methods + + private function _createFilter($search, $replace) + { + return new Swift_StreamFilters_StringReplacementFilter($search, $replace); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpEventSupportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpEventSupportTest.php new file mode 100644 index 00000000..121aaba0 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpEventSupportTest.php @@ -0,0 +1,560 @@ +<?php + +require_once __DIR__.'/AbstractSmtpTest.php'; + +abstract class Swift_Transport_AbstractSmtpEventSupportTest extends Swift_Transport_AbstractSmtpTest +{ + public function testRegisterPluginLoadsPluginInEventDispatcher() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $listener = $this->getMockery('Swift_Events_EventListener'); + $smtp = $this->_getTransport($buf, $dispatcher); + $dispatcher->shouldReceive('bindEventListener') + ->once() + ->with($listener); + + $smtp->registerPlugin($listener); + } + + public function testSendingDispatchesBeforeSendEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $message = $this->_createMessage(); + $smtp = $this->_getTransport($buf, $dispatcher); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('mark@swiftmailer.org' => 'Mark')); + $dispatcher->shouldReceive('createSendEvent') + ->once() + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'beforeSendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(1, $smtp->send($message)); + } + + public function testSendingDispatchesSendEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $message = $this->_createMessage(); + $smtp = $this->_getTransport($buf, $dispatcher); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('mark@swiftmailer.org' => 'Mark')); + $dispatcher->shouldReceive('createSendEvent') + ->once() + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'sendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(1, $smtp->send($message)); + } + + public function testSendEventCapturesFailures() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('mark@swiftmailer.org' => 'Mark')); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:<chris@swiftmailer.org>\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:<mark@swiftmailer.org>\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("500 Not now\r\n"); + $dispatcher->shouldReceive('createSendEvent') + ->zeroOrMoreTimes() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'sendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturn(false); + $evt->shouldReceive('setFailedRecipients') + ->once() + ->with(array('mark@swiftmailer.org')); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(0, $smtp->send($message)); + } + + public function testSendEventHasResultFailedIfAllFailures() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('mark@swiftmailer.org' => 'Mark')); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:<chris@swiftmailer.org>\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:<mark@swiftmailer.org>\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("500 Not now\r\n"); + $dispatcher->shouldReceive('createSendEvent') + ->zeroOrMoreTimes() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'sendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturn(false); + $evt->shouldReceive('setResult') + ->once() + ->with(Swift_Events_SendEvent::RESULT_FAILED); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(0, $smtp->send($message)); + } + + public function testSendEventHasResultTentativeIfSomeFailures() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array( + 'mark@swiftmailer.org' => 'Mark', + 'chris@site.tld' => 'Chris', + )); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:<chris@swiftmailer.org>\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:<mark@swiftmailer.org>\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("500 Not now\r\n"); + $dispatcher->shouldReceive('createSendEvent') + ->zeroOrMoreTimes() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'sendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturn(false); + $evt->shouldReceive('setResult') + ->once() + ->with(Swift_Events_SendEvent::RESULT_TENTATIVE); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(1, $smtp->send($message)); + } + + public function testSendEventHasResultSuccessIfNoFailures() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array( + 'mark@swiftmailer.org' => 'Mark', + 'chris@site.tld' => 'Chris', + )); + $dispatcher->shouldReceive('createSendEvent') + ->zeroOrMoreTimes() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'sendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturn(false); + $evt->shouldReceive('setResult') + ->once() + ->with(Swift_Events_SendEvent::RESULT_SUCCESS); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(2, $smtp->send($message)); + } + + public function testCancellingEventBubbleBeforeSendStopsEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('mark@swiftmailer.org' => 'Mark')); + $dispatcher->shouldReceive('createSendEvent') + ->zeroOrMoreTimes() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'beforeSendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->atLeast()->once() + ->andReturn(true); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(0, $smtp->send($message)); + } + + public function testStartingTransportDispatchesTransportChangeEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportChangeEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createTransportChangeEvent') + ->atLeast()->once() + ->with($smtp) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'transportStarted'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->atLeast()->once() + ->andReturn(false); + + $this->_finishBuffer($buf); + $smtp->start(); + } + + public function testStartingTransportDispatchesBeforeTransportChangeEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportChangeEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createTransportChangeEvent') + ->atLeast()->once() + ->with($smtp) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'beforeTransportStarted'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->atLeast()->once() + ->andReturn(false); + + $this->_finishBuffer($buf); + $smtp->start(); + } + + public function testCancellingBubbleBeforeTransportStartStopsEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportChangeEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createTransportChangeEvent') + ->atLeast()->once() + ->with($smtp) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'beforeTransportStarted'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->atLeast()->once() + ->andReturn(true); + + $this->_finishBuffer($buf); + $smtp->start(); + + $this->assertFalse($smtp->isStarted(), + '%s: Transport should not be started since event bubble was cancelled' + ); + } + + public function testStoppingTransportDispatchesTransportChangeEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportChangeEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createTransportChangeEvent') + ->atLeast()->once() + ->with($smtp) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'transportStopped'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->stop(); + } + + public function testStoppingTransportDispatchesBeforeTransportChangeEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportChangeEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createTransportChangeEvent') + ->atLeast()->once() + ->with($smtp) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'beforeTransportStopped'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->stop(); + } + + public function testCancellingBubbleBeforeTransportStoppedStopsEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportChangeEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $hasRun = false; + $dispatcher->shouldReceive('createTransportChangeEvent') + ->atLeast()->once() + ->with($smtp) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'beforeTransportStopped') + ->andReturnUsing(function () use (&$hasRun) { + $hasRun = true; + }); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$hasRun) { + return $hasRun; + }); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->stop(); + + $this->assertTrue($smtp->isStarted(), + '%s: Transport should not be stopped since event bubble was cancelled' + ); + } + + public function testResponseEventsAreGenerated() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_ResponseEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createResponseEvent') + ->atLeast()->once() + ->with($smtp, \Mockery::any(), \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->atLeast()->once() + ->with($evt, 'responseReceived'); + + $this->_finishBuffer($buf); + $smtp->start(); + } + + public function testCommandEventsAreGenerated() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_CommandEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createCommandEvent') + ->once() + ->with($smtp, \Mockery::any(), \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'commandSent'); + + $this->_finishBuffer($buf); + $smtp->start(); + } + + public function testExceptionsCauseExceptionEvents() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportExceptionEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $buf->shouldReceive('readLine') + ->atLeast()->once() + ->andReturn("503 I'm sleepy, go away!\r\n"); + $dispatcher->shouldReceive('createTransportExceptionEvent') + ->zeroOrMoreTimes() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'exceptionThrown'); + $evt->shouldReceive('bubbleCancelled') + ->atLeast()->once() + ->andReturn(false); + + try { + $smtp->start(); + $this->fail('TransportException should be thrown on invalid response'); + } catch (Swift_TransportException $e) { + } + } + + public function testExceptionBubblesCanBeCancelled() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportExceptionEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $buf->shouldReceive('readLine') + ->atLeast()->once() + ->andReturn("503 I'm sleepy, go away!\r\n"); + $dispatcher->shouldReceive('createTransportExceptionEvent') + ->twice() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->twice() + ->with($evt, 'exceptionThrown'); + $evt->shouldReceive('bubbleCancelled') + ->atLeast()->once() + ->andReturn(true); + + $this->_finishBuffer($buf); + $smtp->start(); + } + + // -- Creation Methods + + protected function _createEventDispatcher($stub = true) + { + return $this->getMockery('Swift_Events_EventDispatcher')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpTest.php new file mode 100644 index 00000000..f49b489e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpTest.php @@ -0,0 +1,1249 @@ +<?php + +abstract class Swift_Transport_AbstractSmtpTest extends \SwiftMailerTestCase +{ + /** Abstract test method */ + abstract protected function _getTransport($buf); + + public function testStartAccepts220ServiceGreeting() + { + /* -- RFC 2821, 4.2. + + Greeting = "220 " Domain [ SP text ] CRLF + + -- RFC 2822, 4.3.2. + + CONNECTION ESTABLISHMENT + S: 220 + E: 554 + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + + $this->_finishBuffer($buf); + try { + $this->assertFalse($smtp->isStarted(), '%s: SMTP should begin non-started'); + $smtp->start(); + $this->assertTrue($smtp->isStarted(), '%s: start() should have started connection'); + } catch (Exception $e) { + $this->fail('220 is a valid SMTP greeting and should be accepted'); + } + } + + public function testBadGreetingCausesException() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("554 I'm busy\r\n"); + $this->_finishBuffer($buf); + try { + $this->assertFalse($smtp->isStarted(), '%s: SMTP should begin non-started'); + $smtp->start(); + $this->fail('554 greeting indicates an error and should cause an exception'); + } catch (Exception $e) { + $this->assertFalse($smtp->isStarted(), '%s: start() should have failed'); + } + } + + public function testStartSendsHeloToInitiate() + { + /* -- RFC 2821, 3.2. + + 3.2 Client Initiation + + Once the server has sent the welcoming message and the client has + received it, the client normally sends the EHLO command to the + server, indicating the client's identity. In addition to opening the + session, use of EHLO indicates that the client is able to process + service extensions and requests that the server provide a list of the + extensions it supports. Older SMTP systems which are unable to + support service extensions and contemporary clients which do not + require service extensions in the mail session being initiated, MAY + use HELO instead of EHLO. Servers MUST NOT return the extended + EHLO-style response to a HELO command. For a particular connection + attempt, if the server returns a "command not recognized" response to + EHLO, the client SHOULD be able to fall back and send HELO. + + In the EHLO command the host sending the command identifies itself; + the command may be interpreted as saying "Hello, I am <domain>" (and, + in the case of EHLO, "and I support service extension requests"). + + -- RFC 2281, 4.1.1.1. + + ehlo = "EHLO" SP Domain CRLF + helo = "HELO" SP Domain CRLF + + -- RFC 2821, 4.3.2. + + EHLO or HELO + S: 250 + E: 504, 550 + + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^HELO .*?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 ServerName'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + } catch (Exception $e) { + $this->fail('Starting SMTP should send HELO and accept 250 response'); + } + } + + public function testInvalidHeloResponseCausesException() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^HELO .*?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('504 WTF'."\r\n"); + + $this->_finishBuffer($buf); + try { + $this->assertFalse($smtp->isStarted(), '%s: SMTP should begin non-started'); + $smtp->start(); + $this->fail('Non 250 HELO response should raise Exception'); + } catch (Exception $e) { + $this->assertFalse($smtp->isStarted(), '%s: SMTP start() should have failed'); + } + } + + public function testDomainNameIsPlacedInHelo() + { + /* -- RFC 2821, 4.1.4. + + The SMTP client MUST, if possible, ensure that the domain parameter + to the EHLO command is a valid principal host name (not a CNAME or MX + name) for its host. If this is not possible (e.g., when the client's + address is dynamically assigned and the client does not have an + obvious name), an address literal SHOULD be substituted for the + domain name and supplemental information provided that will assist in + identifying the client. + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("HELO mydomain.com\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 ServerName'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->setLocalDomain('mydomain.com'); + $smtp->start(); + } + + public function testSuccessfulMailCommand() + { + /* -- RFC 2821, 3.3. + + There are three steps to SMTP mail transactions. The transaction + starts with a MAIL command which gives the sender identification. + + ..... + + The first step in the procedure is the MAIL command. + + MAIL FROM:<reverse-path> [SP <mail-parameters> ] <CRLF> + + -- RFC 2821, 4.1.1.2. + + Syntax: + + "MAIL FROM:" ("<>" / Reverse-Path) + [SP Mail-parameters] CRLF + -- RFC 2821, 4.1.2. + + Reverse-path = Path + Forward-path = Path + Path = "<" [ A-d-l ":" ] Mailbox ">" + A-d-l = At-domain *( "," A-d-l ) + ; Note that this form, the so-called "source route", + ; MUST BE accepted, SHOULD NOT be generated, and SHOULD be + ; ignored. + At-domain = "@" domain + + -- RFC 2821, 4.3.2. + + MAIL + S: 250 + E: 552, 451, 452, 550, 553, 503 + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:<me@domain.com>\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 OK\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + $smtp->send($message); + } catch (Exception $e) { + $this->fail('MAIL FROM should accept a 250 response'); + } + } + + public function testInvalidResponseCodeFromMailCausesException() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:<me@domain.com>\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('553 Bad'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + $smtp->send($message); + $this->fail('MAIL FROM should accept a 250 response'); + } catch (Exception $e) { + } + } + + public function testSenderIsPreferredOverFrom() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getSender') + ->once() + ->andReturn(array('another@domain.com' => 'Someone')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:<another@domain.com>\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testReturnPathIsPreferredOverSender() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getSender') + ->once() + ->andReturn(array('another@domain.com' => 'Someone')); + $message->shouldReceive('getReturnPath') + ->once() + ->andReturn('more@domain.com'); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:<more@domain.com>\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testSuccessfulRcptCommandWith250Response() + { + /* -- RFC 2821, 3.3. + + The second step in the procedure is the RCPT command. + + RCPT TO:<forward-path> [ SP <rcpt-parameters> ] <CRLF> + + The first or only argument to this command includes a forward-path + (normally a mailbox and domain, always surrounded by "<" and ">" + brackets) identifying one recipient. If accepted, the SMTP server + returns a 250 OK reply and stores the forward-path. If the recipient + is known not to be a deliverable address, the SMTP server returns a + 550 reply, typically with a string such as "no such user - " and the + mailbox name (other circumstances and reply codes are possible). + This step of the procedure can be repeated any number of times. + + -- RFC 2821, 4.1.1.3. + + This command is used to identify an individual recipient of the mail + data; multiple recipients are specified by multiple use of this + command. The argument field contains a forward-path and may contain + optional parameters. + + The forward-path normally consists of the required destination + mailbox. Sending systems SHOULD not generate the optional list of + hosts known as a source route. + + ....... + + "RCPT TO:" ("<Postmaster@" domain ">" / "<Postmaster>" / Forward-Path) + [SP Rcpt-parameters] CRLF + + -- RFC 2821, 4.2.2. + + 250 Requested mail action okay, completed + 251 User not local; will forward to <forward-path> + (See section 3.4) + 252 Cannot VRFY user, but will accept message and attempt + delivery + + -- RFC 2821, 4.3.2. + + RCPT + S: 250, 251 (but see section 3.4 for discussion of 251 and 551) + E: 550, 551, 552, 553, 450, 451, 452, 503, 550 + */ + + //We'll treat 252 as accepted since it isn't really a failure + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:<me@domain.com>\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:<foo@bar>\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + $smtp->send($message); + } catch (Exception $e) { + $this->fail('RCPT TO should accept a 250 response'); + } + } + + public function testMailFromCommandIsOnlySentOncePerMessage() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:<me@domain.com>\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:<foo@bar>\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->never() + ->with("MAIL FROM:<me@domain.com>\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testMultipleRecipientsSendsMultipleRcpt() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array( + 'foo@bar' => null, + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:<foo@bar>\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:<zip@button>\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:<test@domain>\r\n") + ->andReturn(3); + $buf->shouldReceive('readLine') + ->once() + ->with(3) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testCcRecipientsSendsMultipleRcpt() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('getCc') + ->once() + ->andReturn(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:<foo@bar>\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:<zip@button>\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:<test@domain>\r\n") + ->andReturn(3); + $buf->shouldReceive('readLine') + ->once() + ->with(3) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testSendReturnsNumberOfSuccessfulRecipients() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('getCc') + ->once() + ->andReturn(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:<foo@bar>\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:<zip@button>\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('501 Nobody here'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:<test@domain>\r\n") + ->andReturn(3); + $buf->shouldReceive('readLine') + ->once() + ->with(3) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(2, $smtp->send($message), + '%s: 1 of 3 recipients failed so 2 should be returned' + ); + } + + public function testRsetIsSentIfNoSuccessfulRecipients() + { + /* --RFC 2821, 4.1.1.5. + + This command specifies that the current mail transaction will be + aborted. Any stored sender, recipients, and mail data MUST be + discarded, and all buffers and state tables cleared. The receiver + MUST send a "250 OK" reply to a RSET command with no arguments. A + reset command may be issued by the client at any time. + + -- RFC 2821, 4.3.2. + + RSET + S: 250 + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:<foo@bar>\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('503 Bad'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RSET\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(0, $smtp->send($message), + '%s: 1 of 1 recipients failed so 0 should be returned' + ); + } + + public function testSuccessfulDataCommand() + { + /* -- RFC 2821, 3.3. + + The third step in the procedure is the DATA command (or some + alternative specified in a service extension). + + DATA <CRLF> + + If accepted, the SMTP server returns a 354 Intermediate reply and + considers all succeeding lines up to but not including the end of + mail data indicator to be the message text. + + -- RFC 2821, 4.1.1.4. + + The receiver normally sends a 354 response to DATA, and then treats + the lines (strings ending in <CRLF> sequences, as described in + section 2.3.7) following the command as mail data from the sender. + This command causes the mail data to be appended to the mail data + buffer. The mail data may contain any of the 128 ASCII character + codes, although experience has indicated that use of control + characters other than SP, HT, CR, and LF may cause problems and + SHOULD be avoided when possible. + + -- RFC 2821, 4.3.2. + + DATA + I: 354 -> data -> S: 250 + E: 552, 554, 451, 452 + E: 451, 554, 503 + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("DATA\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('354 Go ahead'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + $smtp->send($message); + } catch (Exception $e) { + $this->fail('354 is the expected response to DATA'); + } + } + + public function testBadDataResponseCausesException() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("DATA\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('451 Bad'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + $smtp->send($message); + $this->fail('354 is the expected response to DATA (not observed)'); + } catch (Exception $e) { + } + } + + public function testMessageIsStreamedToBufferForData() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("DATA\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('354 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("\r\n.\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testBadResponseAfterDataTransmissionCausesException() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("DATA\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('354 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("\r\n.\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('554 Error'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + $smtp->send($message); + $this->fail('250 is the expected response after a DATA transmission (not observed)'); + } catch (Exception $e) { + } + } + + public function testBccRecipientsAreRemovedFromHeaders() + { + /* -- RFC 2821, 7.2. + + Addresses that do not appear in the message headers may appear in the + RCPT commands to an SMTP server for a number of reasons. The two + most common involve the use of a mailing address as a "list exploder" + (a single address that resolves into multiple addresses) and the + appearance of "blind copies". Especially when more than one RCPT + command is present, and in order to avoid defeating some of the + purpose of these mechanisms, SMTP clients and servers SHOULD NOT copy + the full set of RCPT command arguments into the headers, either as + part of trace headers or as informational or private-extension + headers. Since this rule is often violated in practice, and cannot + be enforced, sending SMTP systems that are aware of "bcc" use MAY + find it helpful to send each blind copy as a separate message + transaction containing only a single RCPT command. + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('getBcc') + ->zeroOrMoreTimes() + ->andReturn(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $message->shouldReceive('setBcc') + ->once() + ->with(array()); + $message->shouldReceive('setBcc') + ->zeroOrMoreTimes(); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testEachBccRecipientIsSentASeparateMessage() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('getBcc') + ->zeroOrMoreTimes() + ->andReturn(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $message->shouldReceive('setBcc') + ->atLeast()->once() + ->with(array()); + $message->shouldReceive('setBcc') + ->once() + ->with(array('zip@button' => 'Zip Button')); + $message->shouldReceive('setBcc') + ->once() + ->with(array('test@domain' => 'Test user')); + $message->shouldReceive('setBcc') + ->atLeast()->once() + ->with(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + + $buf->shouldReceive('write')->once()->with("MAIL FROM:<me@domain.com>\r\n")->andReturn(1); + $buf->shouldReceive('readLine')->once()->with(1)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("RCPT TO:<foo@bar>\r\n")->andReturn(2); + $buf->shouldReceive('readLine')->once()->with(2)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("DATA\r\n")->andReturn(3); + $buf->shouldReceive('readLine')->once()->with(3)->andReturn("354 OK\r\n"); + $buf->shouldReceive('write')->once()->with("\r\n.\r\n")->andReturn(4); + $buf->shouldReceive('readLine')->once()->with(4)->andReturn("250 OK\r\n"); + + $buf->shouldReceive('write')->once()->with("MAIL FROM:<me@domain.com>\r\n")->andReturn(5); + $buf->shouldReceive('readLine')->once()->with(5)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("RCPT TO:<zip@button>\r\n")->andReturn(6); + $buf->shouldReceive('readLine')->once()->with(6)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("DATA\r\n")->andReturn(7); + $buf->shouldReceive('readLine')->once()->with(7)->andReturn("354 OK\r\n"); + $buf->shouldReceive('write')->once()->with("\r\n.\r\n")->andReturn(8); + $buf->shouldReceive('readLine')->once()->with(8)->andReturn("250 OK\r\n"); + + $buf->shouldReceive('write')->once()->with("MAIL FROM:<me@domain.com>\r\n")->andReturn(9); + $buf->shouldReceive('readLine')->once()->with(9)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("RCPT TO:<test@domain>\r\n")->andReturn(10); + $buf->shouldReceive('readLine')->once()->with(10)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("DATA\r\n")->andReturn(11); + $buf->shouldReceive('readLine')->once()->with(11)->andReturn("354 OK\r\n"); + $buf->shouldReceive('write')->once()->with("\r\n.\r\n")->andReturn(12); + $buf->shouldReceive('readLine')->once()->with(12)->andReturn("250 OK\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(3, $smtp->send($message)); + } + + public function testMessageStateIsRestoredOnFailure() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('getBcc') + ->zeroOrMoreTimes() + ->andReturn(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $message->shouldReceive('setBcc') + ->once() + ->with(array()); + $message->shouldReceive('setBcc') + ->once() + ->with(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:<me@domain.com>\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:<foo@bar>\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("DATA\r\n") + ->andReturn(3); + $buf->shouldReceive('readLine') + ->once() + ->with(3) + ->andReturn("451 No\r\n"); + + $this->_finishBuffer($buf); + + $smtp->start(); + try { + $smtp->send($message); + $this->fail('A bad response was given so exception is expected'); + } catch (Exception $e) { + } + } + + public function testStopSendsQuitCommand() + { + /* -- RFC 2821, 4.1.1.10. + + This command specifies that the receiver MUST send an OK reply, and + then close the transmission channel. + + The receiver MUST NOT intentionally close the transmission channel + until it receives and replies to a QUIT command (even if there was an + error). The sender MUST NOT intentionally close the transmission + channel until it sends a QUIT command and SHOULD wait until it + receives the reply (even if there was an error response to a previous + command). If the connection is closed prematurely due to violations + of the above or system or network failure, the server MUST cancel any + pending transaction, but not undo any previously completed + transaction, and generally MUST act as if the command or transaction + in progress had received a temporary error (i.e., a 4yz response). + + The QUIT command may be issued at any time. + + Syntax: + "QUIT" CRLF + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('write') + ->once() + ->with("QUIT\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("221 Bye\r\n"); + $buf->shouldReceive('terminate') + ->once(); + + $this->_finishBuffer($buf); + + $this->assertFalse($smtp->isStarted()); + $smtp->start(); + $this->assertTrue($smtp->isStarted()); + $smtp->stop(); + $this->assertFalse($smtp->isStarted()); + } + + public function testBufferCanBeFetched() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ref = $smtp->getBuffer(); + $this->assertEquals($buf, $ref); + } + + public function testBufferCanBeWrittenToUsingExecuteCommand() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with("FOO\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with(1) + ->andReturn("250 OK\r\n"); + + $res = $smtp->executeCommand("FOO\r\n"); + $this->assertEquals("250 OK\r\n", $res); + } + + public function testResponseCodesAreValidated() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with("FOO\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with(1) + ->andReturn("551 Not ok\r\n"); + + try { + $smtp->executeCommand("FOO\r\n", array(250, 251)); + $this->fail('A 250 or 251 response was needed but 551 was returned.'); + } catch (Exception $e) { + } + } + + public function testFailedRecipientsCanBeCollectedByReference() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('getBcc') + ->zeroOrMoreTimes() + ->andReturn(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $message->shouldReceive('setBcc') + ->atLeast()->once() + ->with(array()); + $message->shouldReceive('setBcc') + ->once() + ->with(array('zip@button' => 'Zip Button')); + $message->shouldReceive('setBcc') + ->once() + ->with(array('test@domain' => 'Test user')); + $message->shouldReceive('setBcc') + ->atLeast()->once() + ->with(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + + $buf->shouldReceive('write')->once()->with("MAIL FROM:<me@domain.com>\r\n")->andReturn(1); + $buf->shouldReceive('readLine')->once()->with(1)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("RCPT TO:<foo@bar>\r\n")->andReturn(2); + $buf->shouldReceive('readLine')->once()->with(2)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("DATA\r\n")->andReturn(3); + $buf->shouldReceive('readLine')->once()->with(3)->andReturn("354 OK\r\n"); + $buf->shouldReceive('write')->once()->with("\r\n.\r\n")->andReturn(4); + $buf->shouldReceive('readLine')->once()->with(4)->andReturn("250 OK\r\n"); + + $buf->shouldReceive('write')->once()->with("MAIL FROM:<me@domain.com>\r\n")->andReturn(5); + $buf->shouldReceive('readLine')->once()->with(5)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("RCPT TO:<zip@button>\r\n")->andReturn(6); + $buf->shouldReceive('readLine')->once()->with(6)->andReturn("500 Bad\r\n"); + $buf->shouldReceive('write')->once()->with("RSET\r\n")->andReturn(7); + $buf->shouldReceive('readLine')->once()->with(7)->andReturn("250 OK\r\n"); + + $buf->shouldReceive('write')->once()->with("MAIL FROM:<me@domain.com>\r\n")->andReturn(9); + $buf->shouldReceive('readLine')->once()->with(9)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("RCPT TO:<test@domain>\r\n")->andReturn(10); + $buf->shouldReceive('readLine')->once()->with(10)->andReturn("500 Bad\r\n"); + $buf->shouldReceive('write')->once()->with("RSET\r\n")->andReturn(11); + $buf->shouldReceive('readLine')->once()->with(11)->andReturn("250 OK\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(1, $smtp->send($message, $failures)); + $this->assertEquals(array('zip@button', 'test@domain'), $failures, + '%s: Failures should be caught in an array' + ); + } + + public function testSendingRegeneratesMessageId() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('generateId') + ->once(); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + protected function _getBuffer() + { + return $this->getMockery('Swift_Transport_IoBuffer')->shouldIgnoreMissing(); + } + + protected function _createMessage() + { + return $this->getMockery('Swift_Mime_Message')->shouldIgnoreMissing(); + } + + protected function _finishBuffer($buf) + { + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with(0) + ->andReturn('220 server.com foo'."\r\n"); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with('~^(EH|HE)LO .*?\r\n$~D') + ->andReturn($x = uniqid()); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with($x) + ->andReturn('250 ServerName'."\r\n"); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with('~^MAIL FROM:<.*?>\r\n$~D') + ->andReturn($x = uniqid()); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with($x) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with('~^RCPT TO:<.*?>\r\n$~D') + ->andReturn($x = uniqid()); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with($x) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with("DATA\r\n") + ->andReturn($x = uniqid()); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with($x) + ->andReturn("354 OK\r\n"); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with("\r\n.\r\n") + ->andReturn($x = uniqid()); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with($x) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with("RSET\r\n") + ->andReturn($x = uniqid()); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with($x) + ->andReturn("250 OK\r\n"); + + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturn(false); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->andReturn(false); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/CramMd5AuthenticatorTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/CramMd5AuthenticatorTest.php new file mode 100644 index 00000000..f64b0716 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/CramMd5AuthenticatorTest.php @@ -0,0 +1,66 @@ +<?php + +class Swift_Transport_Esmtp_Auth_CramMd5AuthenticatorTest extends \SwiftMailerTestCase +{ + private $_agent; + + public function setUp() + { + $this->_agent = $this->getMockery('Swift_Transport_SmtpAgent')->shouldIgnoreMissing(); + } + + public function testKeywordIsCramMd5() + { + /* -- RFC 2195, 2. + The authentication type associated with CRAM is "CRAM-MD5". + */ + + $cram = $this->_getAuthenticator(); + $this->assertEquals('CRAM-MD5', $cram->getAuthKeyword()); + } + + public function testSuccessfulAuthentication() + { + $cram = $this->_getAuthenticator(); + + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("AUTH CRAM-MD5\r\n", array(334)) + ->andReturn('334 '.base64_encode('<foo@bar>')."\r\n"); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with(\Mockery::any(), array(235)); + + $this->assertTrue($cram->authenticate($this->_agent, 'jack', 'pass'), + '%s: The buffer accepted all commands authentication should succeed' + ); + } + + public function testAuthenticationFailureSendRsetAndReturnFalse() + { + $cram = $this->_getAuthenticator(); + + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("AUTH CRAM-MD5\r\n", array(334)) + ->andReturn('334 '.base64_encode('<foo@bar>')."\r\n"); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with(\Mockery::any(), array(235)) + ->andThrow(new Swift_TransportException('')); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("RSET\r\n", array(250)); + + $this->assertFalse($cram->authenticate($this->_agent, 'jack', 'pass'), + '%s: Authentication fails, so RSET should be sent' + ); + } + + // -- Private helpers + + private function _getAuthenticator() + { + return new Swift_Transport_Esmtp_Auth_CramMd5Authenticator(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/LoginAuthenticatorTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/LoginAuthenticatorTest.php new file mode 100644 index 00000000..fc6e8069 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/LoginAuthenticatorTest.php @@ -0,0 +1,66 @@ +<?php + +class Swift_Transport_Esmtp_Auth_LoginAuthenticatorTest extends \SwiftMailerTestCase +{ + private $_agent; + + public function setUp() + { + $this->_agent = $this->getMockery('Swift_Transport_SmtpAgent')->shouldIgnoreMissing(); + } + + public function testKeywordIsLogin() + { + $login = $this->_getAuthenticator(); + $this->assertEquals('LOGIN', $login->getAuthKeyword()); + } + + public function testSuccessfulAuthentication() + { + $login = $this->_getAuthenticator(); + + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("AUTH LOGIN\r\n", array(334)); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with(base64_encode('jack')."\r\n", array(334)); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with(base64_encode('pass')."\r\n", array(235)); + + $this->assertTrue($login->authenticate($this->_agent, 'jack', 'pass'), + '%s: The buffer accepted all commands authentication should succeed' + ); + } + + public function testAuthenticationFailureSendRsetAndReturnFalse() + { + $login = $this->_getAuthenticator(); + + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("AUTH LOGIN\r\n", array(334)); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with(base64_encode('jack')."\r\n", array(334)); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with(base64_encode('pass')."\r\n", array(235)) + ->andThrow(new Swift_TransportException('')); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("RSET\r\n", array(250)); + + $this->assertFalse($login->authenticate($this->_agent, 'jack', 'pass'), + '%s: Authentication fails, so RSET should be sent' + ); + } + + // -- Private helpers + + private function _getAuthenticator() + { + return new Swift_Transport_Esmtp_Auth_LoginAuthenticator(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/NTLMAuthenticatorTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/NTLMAuthenticatorTest.php new file mode 100644 index 00000000..09ace834 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/NTLMAuthenticatorTest.php @@ -0,0 +1,235 @@ +<?php + +class Swift_Transport_Esmtp_Auth_NTLMAuthenticatorTest extends \SwiftMailerTestCase +{ + private $_message1 = '4e544c4d535350000100000007020000'; + private $_message2 = '4e544c4d53535000020000000c000c003000000035828980514246973ea892c10000000000000000460046003c00000054004500530054004e00540002000c0054004500530054004e00540001000c004d0045004d0042004500520003001e006d0065006d006200650072002e0074006500730074002e0063006f006d0000000000'; + private $_message3 = '4e544c4d5353500003000000180018006000000076007600780000000c000c0040000000080008004c0000000c000c0054000000000000009a0000000102000054004500530054004e00540074006500730074004d0045004d00420045005200bf2e015119f6bdb3f6fdb768aa12d478f5ce3d2401c8f6e9caa4da8f25d5e840974ed8976d3ada46010100000000000030fa7e3c677bc301f5ce3d2401c8f6e90000000002000c0054004500530054004e00540001000c004d0045004d0042004500520003001e006d0065006d006200650072002e0074006500730074002e0063006f006d000000000000000000'; + + public function setUp() + { + if (!function_exists('openssl_encrypt') || !function_exists('openssl_random_pseudo_bytes') || !function_exists('bcmul') || !function_exists('iconv')) { + $this->markTestSkipped( + 'One of the required functions is not available.' + ); + } + } + + public function testKeywordIsNtlm() + { + $login = $this->_getAuthenticator(); + $this->assertEquals('NTLM', $login->getAuthKeyword()); + } + + public function testMessage1Generator() + { + $login = $this->_getAuthenticator(); + $message1 = $this->_invokePrivateMethod('createMessage1', $login); + + $this->assertEquals($this->_message1, bin2hex($message1), + '%s: We send the smallest ntlm message which should never fail.' + ); + } + + public function testLMv1Generator() + { + $password = 'test1234'; + $challenge = 'b019d38bad875c9d'; + $lmv1 = '1879f60127f8a877022132ec221bcbf3ca016a9f76095606'; + + $login = $this->_getAuthenticator(); + $lmv1Result = $this->_invokePrivateMethod('createLMPassword', $login, array($password, $this->hex2bin($challenge))); + + $this->assertEquals($lmv1, bin2hex($lmv1Result), + '%s: The keys should be the same cause we use the same values to generate them.' + ); + } + + public function testLMv2Generator() + { + $username = 'user'; + $password = 'SecREt01'; + $domain = 'DOMAIN'; + $challenge = '0123456789abcdef'; + $lmv2 = 'd6e6152ea25d03b7c6ba6629c2d6aaf0ffffff0011223344'; + + $login = $this->_getAuthenticator(); + $lmv2Result = $this->_invokePrivateMethod('createLMv2Password', $login, array($password, $username, $domain, $this->hex2bin($challenge), $this->hex2bin('ffffff0011223344'))); + + $this->assertEquals($lmv2, bin2hex($lmv2Result), + '%s: The keys should be the same cause we use the same values to generate them.' + ); + } + + public function testMessage3v1Generator() + { + $username = 'test'; + $domain = 'TESTNT'; + $workstation = 'MEMBER'; + $lmResponse = '1879f60127f8a877022132ec221bcbf3ca016a9f76095606'; + $ntlmResponse = 'e6285df3287c5d194f84df1a94817c7282d09754b6f9e02a'; + $message3T = '4e544c4d5353500003000000180018006000000018001800780000000c000c0040000000080008004c0000000c000c0054000000000000009a0000000102000054004500530054004e00540074006500730074004d0045004d004200450052001879f60127f8a877022132ec221bcbf3ca016a9f76095606e6285df3287c5d194f84df1a94817c7282d09754b6f9e02a'; + + $login = $this->_getAuthenticator(); + $message3 = $this->_invokePrivateMethod('createMessage3', $login, array($domain, $username, $workstation, $this->hex2bin($lmResponse), $this->hex2bin($ntlmResponse))); + + $this->assertEquals($message3T, bin2hex($message3), + '%s: We send the same information as the example is created with so this should be the same' + ); + } + + public function testMessage3v2Generator() + { + $username = 'test'; + $domain = 'TESTNT'; + $workstation = 'MEMBER'; + $lmResponse = 'bf2e015119f6bdb3f6fdb768aa12d478f5ce3d2401c8f6e9'; + $ntlmResponse = 'caa4da8f25d5e840974ed8976d3ada46010100000000000030fa7e3c677bc301f5ce3d2401c8f6e90000000002000c0054004500530054004e00540001000c004d0045004d0042004500520003001e006d0065006d006200650072002e0074006500730074002e0063006f006d000000000000000000'; + + $login = $this->_getAuthenticator(); + $message3 = $this->_invokePrivateMethod('createMessage3', $login, array($domain, $username, $workstation, $this->hex2bin($lmResponse), $this->hex2bin($ntlmResponse))); + + $this->assertEquals($this->_message3, bin2hex($message3), + '%s: We send the same information as the example is created with so this should be the same' + ); + } + + public function testGetDomainAndUsername() + { + $username = "DOMAIN\user"; + + $login = $this->_getAuthenticator(); + list($domain, $user) = $this->_invokePrivateMethod('getDomainAndUsername', $login, array($username)); + + $this->assertEquals('DOMAIN', $domain, + '%s: the fetched domain did not match' + ); + $this->assertEquals('user', $user, + '%s: the fetched user did not match' + ); + } + + public function testGetDomainAndUsernameWithExtension() + { + $username = "domain.com\user"; + + $login = $this->_getAuthenticator(); + list($domain, $user) = $this->_invokePrivateMethod('getDomainAndUsername', $login, array($username)); + + $this->assertEquals('domain.com', $domain, + '%s: the fetched domain did not match' + ); + $this->assertEquals('user', $user, + '%s: the fetched user did not match' + ); + } + + public function testGetDomainAndUsernameWithAtSymbol() + { + $username = 'user@DOMAIN'; + + $login = $this->_getAuthenticator(); + list($domain, $user) = $this->_invokePrivateMethod('getDomainAndUsername', $login, array($username)); + + $this->assertEquals('DOMAIN', $domain, + '%s: the fetched domain did not match' + ); + $this->assertEquals('user', $user, + '%s: the fetched user did not match' + ); + } + + public function testGetDomainAndUsernameWithAtSymbolAndExtension() + { + $username = 'user@domain.com'; + + $login = $this->_getAuthenticator(); + list($domain, $user) = $this->_invokePrivateMethod('getDomainAndUsername', $login, array($username)); + + $this->assertEquals('domain.com', $domain, + '%s: the fetched domain did not match' + ); + $this->assertEquals('user', $user, + '%s: the fetched user did not match' + ); + } + + public function testSuccessfulAuthentication() + { + $domain = 'TESTNT'; + $username = 'test'; + $secret = 'test1234'; + + $ntlm = $this->_getAuthenticator(); + $agent = $this->_getAgent(); + $agent->shouldReceive('executeCommand') + ->once() + ->with('AUTH NTLM '.base64_encode( + $this->_invokePrivateMethod('createMessage1', $ntlm) + )."\r\n", array(334)) + ->andReturn('334 '.base64_encode($this->hex2bin('4e544c4d53535000020000000c000c003000000035828980514246973ea892c10000000000000000460046003c00000054004500530054004e00540002000c0054004500530054004e00540001000c004d0045004d0042004500520003001e006d0065006d006200650072002e0074006500730074002e0063006f006d0000000000'))); + $agent->shouldReceive('executeCommand') + ->once() + ->with(base64_encode( + $this->_invokePrivateMethod('createMessage3', $ntlm, array($domain, $username, $this->hex2bin('4d0045004d00420045005200'), $this->hex2bin('bf2e015119f6bdb3f6fdb768aa12d478f5ce3d2401c8f6e9'), $this->hex2bin('caa4da8f25d5e840974ed8976d3ada46010100000000000030fa7e3c677bc301f5ce3d2401c8f6e90000000002000c0054004500530054004e00540001000c004d0045004d0042004500520003001e006d0065006d006200650072002e0074006500730074002e0063006f006d000000000000000000')) + ))."\r\n", array(235)); + + $this->assertTrue($ntlm->authenticate($agent, $username.'@'.$domain, $secret, $this->hex2bin('30fa7e3c677bc301'), $this->hex2bin('f5ce3d2401c8f6e9')), + '%s: The buffer accepted all commands authentication should succeed' + ); + } + + public function testAuthenticationFailureSendRsetAndReturnFalse() + { + $domain = 'TESTNT'; + $username = 'test'; + $secret = 'test1234'; + + $ntlm = $this->_getAuthenticator(); + $agent = $this->_getAgent(); + $agent->shouldReceive('executeCommand') + ->once() + ->with('AUTH NTLM '.base64_encode( + $this->_invokePrivateMethod('createMessage1', $ntlm) + )."\r\n", array(334)) + ->andThrow(new Swift_TransportException('')); + $agent->shouldReceive('executeCommand') + ->once() + ->with("RSET\r\n", array(250)); + + $this->assertFalse($ntlm->authenticate($agent, $username.'@'.$domain, $secret, $this->hex2bin('30fa7e3c677bc301'), $this->hex2bin('f5ce3d2401c8f6e9')), + '%s: Authentication fails, so RSET should be sent' + ); + } + + // -- Private helpers + private function _getAuthenticator() + { + return new Swift_Transport_Esmtp_Auth_NTLMAuthenticator(); + } + + private function _getAgent() + { + return $this->getMockery('Swift_Transport_SmtpAgent')->shouldIgnoreMissing(); + } + + private function _invokePrivateMethod($method, $instance, array $args = array()) + { + $methodC = new ReflectionMethod($instance, trim($method)); + $methodC->setAccessible(true); + + return $methodC->invokeArgs($instance, $args); + } + + /** + * Hex2bin replacement for < PHP 5.4. + * + * @param string $hex + * + * @return string Binary + */ + protected function hex2bin($hex) + { + return function_exists('hex2bin') ? hex2bin($hex) : pack('H*', $hex); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/PlainAuthenticatorTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/PlainAuthenticatorTest.php new file mode 100644 index 00000000..4fe9db80 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/PlainAuthenticatorTest.php @@ -0,0 +1,69 @@ +<?php + +class Swift_Transport_Esmtp_Auth_PlainAuthenticatorTest extends \SwiftMailerTestCase +{ + private $_agent; + + public function setUp() + { + $this->_agent = $this->getMockery('Swift_Transport_SmtpAgent')->shouldIgnoreMissing(); + } + + public function testKeywordIsPlain() + { + /* -- RFC 4616, 1. + The name associated with this mechanism is "PLAIN". + */ + + $login = $this->_getAuthenticator(); + $this->assertEquals('PLAIN', $login->getAuthKeyword()); + } + + public function testSuccessfulAuthentication() + { + /* -- RFC 4616, 2. + The client presents the authorization identity (identity to act as), + followed by a NUL (U+0000) character, followed by the authentication + identity (identity whose password will be used), followed by a NUL + (U+0000) character, followed by the clear-text password. + */ + + $plain = $this->_getAuthenticator(); + + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with('AUTH PLAIN '.base64_encode( + 'jack'.chr(0).'jack'.chr(0).'pass' + )."\r\n", array(235)); + + $this->assertTrue($plain->authenticate($this->_agent, 'jack', 'pass'), + '%s: The buffer accepted all commands authentication should succeed' + ); + } + + public function testAuthenticationFailureSendRsetAndReturnFalse() + { + $plain = $this->_getAuthenticator(); + + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with('AUTH PLAIN '.base64_encode( + 'jack'.chr(0).'jack'.chr(0).'pass' + )."\r\n", array(235)) + ->andThrow(new Swift_TransportException('')); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("RSET\r\n", array(250)); + + $this->assertFalse($plain->authenticate($this->_agent, 'jack', 'pass'), + '%s: Authentication fails, so RSET should be sent' + ); + } + + // -- Private helpers + + private function _getAuthenticator() + { + return new Swift_Transport_Esmtp_Auth_PlainAuthenticator(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/AuthHandlerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/AuthHandlerTest.php new file mode 100644 index 00000000..64327d47 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/AuthHandlerTest.php @@ -0,0 +1,167 @@ +<?php + +class Swift_Transport_Esmtp_AuthHandlerTest extends \SwiftMailerTestCase +{ + private $_agent; + + public function setUp() + { + $this->_agent = $this->getMockery('Swift_Transport_SmtpAgent')->shouldIgnoreMissing(); + } + + public function testKeywordIsAuth() + { + $auth = $this->_createHandler(array()); + $this->assertEquals('AUTH', $auth->getHandledKeyword()); + } + + public function testUsernameCanBeSetAndFetched() + { + $auth = $this->_createHandler(array()); + $auth->setUsername('jack'); + $this->assertEquals('jack', $auth->getUsername()); + } + + public function testPasswordCanBeSetAndFetched() + { + $auth = $this->_createHandler(array()); + $auth->setPassword('pass'); + $this->assertEquals('pass', $auth->getPassword()); + } + + public function testAuthModeCanBeSetAndFetched() + { + $auth = $this->_createHandler(array()); + $auth->setAuthMode('PLAIN'); + $this->assertEquals('PLAIN', $auth->getAuthMode()); + } + + public function testMixinMethods() + { + $auth = $this->_createHandler(array()); + $mixins = $auth->exposeMixinMethods(); + $this->assertTrue(in_array('getUsername', $mixins), + '%s: getUsername() should be accessible via mixin' + ); + $this->assertTrue(in_array('setUsername', $mixins), + '%s: setUsername() should be accessible via mixin' + ); + $this->assertTrue(in_array('getPassword', $mixins), + '%s: getPassword() should be accessible via mixin' + ); + $this->assertTrue(in_array('setPassword', $mixins), + '%s: setPassword() should be accessible via mixin' + ); + $this->assertTrue(in_array('setAuthMode', $mixins), + '%s: setAuthMode() should be accessible via mixin' + ); + $this->assertTrue(in_array('getAuthMode', $mixins), + '%s: getAuthMode() should be accessible via mixin' + ); + } + + public function testAuthenticatorsAreCalledAccordingToParamsAfterEhlo() + { + $a1 = $this->_createMockAuthenticator('PLAIN'); + $a2 = $this->_createMockAuthenticator('LOGIN'); + + $a1->shouldReceive('authenticate') + ->never() + ->with($this->_agent, 'jack', 'pass'); + $a2->shouldReceive('authenticate') + ->once() + ->with($this->_agent, 'jack', 'pass') + ->andReturn(true); + + $auth = $this->_createHandler(array($a1, $a2)); + $auth->setUsername('jack'); + $auth->setPassword('pass'); + + $auth->setKeywordParams(array('CRAM-MD5', 'LOGIN')); + $auth->afterEhlo($this->_agent); + } + + public function testAuthenticatorsAreNotUsedIfNoUsernameSet() + { + $a1 = $this->_createMockAuthenticator('PLAIN'); + $a2 = $this->_createMockAuthenticator('LOGIN'); + + $a1->shouldReceive('authenticate') + ->never() + ->with($this->_agent, 'jack', 'pass'); + $a2->shouldReceive('authenticate') + ->never() + ->with($this->_agent, 'jack', 'pass') + ->andReturn(true); + + $auth = $this->_createHandler(array($a1, $a2)); + + $auth->setKeywordParams(array('CRAM-MD5', 'LOGIN')); + $auth->afterEhlo($this->_agent); + } + + public function testSeveralAuthenticatorsAreTriedIfNeeded() + { + $a1 = $this->_createMockAuthenticator('PLAIN'); + $a2 = $this->_createMockAuthenticator('LOGIN'); + + $a1->shouldReceive('authenticate') + ->once() + ->with($this->_agent, 'jack', 'pass') + ->andReturn(false); + $a2->shouldReceive('authenticate') + ->once() + ->with($this->_agent, 'jack', 'pass') + ->andReturn(true); + + $auth = $this->_createHandler(array($a1, $a2)); + $auth->setUsername('jack'); + $auth->setPassword('pass'); + + $auth->setKeywordParams(array('PLAIN', 'LOGIN')); + $auth->afterEhlo($this->_agent); + } + + public function testFirstAuthenticatorToPassBreaksChain() + { + $a1 = $this->_createMockAuthenticator('PLAIN'); + $a2 = $this->_createMockAuthenticator('LOGIN'); + $a3 = $this->_createMockAuthenticator('CRAM-MD5'); + + $a1->shouldReceive('authenticate') + ->once() + ->with($this->_agent, 'jack', 'pass') + ->andReturn(false); + $a2->shouldReceive('authenticate') + ->once() + ->with($this->_agent, 'jack', 'pass') + ->andReturn(true); + $a3->shouldReceive('authenticate') + ->never() + ->with($this->_agent, 'jack', 'pass'); + + $auth = $this->_createHandler(array($a1, $a2)); + $auth->setUsername('jack'); + $auth->setPassword('pass'); + + $auth->setKeywordParams(array('PLAIN', 'LOGIN', 'CRAM-MD5')); + $auth->afterEhlo($this->_agent); + } + + // -- Private helpers + + private function _createHandler($authenticators) + { + return new Swift_Transport_Esmtp_AuthHandler($authenticators); + } + + private function _createMockAuthenticator($type) + { + $authenticator = $this->getMockery('Swift_Transport_Esmtp_Authenticator')->shouldIgnoreMissing(); + $authenticator->shouldReceive('getAuthKeyword') + ->zeroOrMoreTimes() + ->andReturn($type); + + return $authenticator; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransport/ExtensionSupportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransport/ExtensionSupportTest.php new file mode 100644 index 00000000..8d6321f3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransport/ExtensionSupportTest.php @@ -0,0 +1,528 @@ +<?php + +require_once dirname(__DIR__).'/EsmtpTransportTest.php'; + +interface Swift_Transport_EsmtpHandlerMixin extends Swift_Transport_EsmtpHandler +{ + public function setUsername($user); + public function setPassword($pass); +} + +class Swift_Transport_EsmtpTransport_ExtensionSupportTest extends Swift_Transport_EsmtpTransportTest +{ + public function testExtensionHandlersAreSortedAsNeeded() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('getPriorityOver') + ->zeroOrMoreTimes() + ->with('STARTTLS') + ->andReturn(1); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $ext2->shouldReceive('getPriorityOver') + ->zeroOrMoreTimes() + ->with('AUTH') + ->andReturn(-1); + $this->_finishBuffer($buf); + + $smtp->setExtensionHandlers(array($ext1, $ext2)); + $this->assertEquals(array($ext2, $ext1), $smtp->getExtensionHandlers()); + } + + public function testHandlersAreNotifiedOfParams() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 server.com foo\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .*?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-ServerName.tld\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-AUTH PLAIN LOGIN\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 SIZE=123456\r\n"); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('setKeywordParams') + ->once() + ->with(array('PLAIN', 'LOGIN')); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('SIZE'); + $ext2->shouldReceive('setKeywordParams') + ->zeroOrMoreTimes() + ->with(array('123456')); + $this->_finishBuffer($buf); + + $smtp->setExtensionHandlers(array($ext1, $ext2)); + $smtp->start(); + } + + public function testSupportedExtensionHandlersAreRunAfterEhlo() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext3 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 server.com foo\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .*?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-ServerName.tld\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-AUTH PLAIN LOGIN\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 SIZE=123456\r\n"); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('afterEhlo') + ->once() + ->with($smtp); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('SIZE'); + $ext2->shouldReceive('afterEhlo') + ->zeroOrMoreTimes() + ->with($smtp); + $ext3->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $ext3->shouldReceive('afterEhlo') + ->never() + ->with($smtp); + $this->_finishBuffer($buf); + + $smtp->setExtensionHandlers(array($ext1, $ext2, $ext3)); + $smtp->start(); + } + + public function testExtensionsCanModifyMailFromParams() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(); + $smtp = new Swift_Transport_EsmtpTransport($buf, array(), $dispatcher); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext3 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 server.com foo\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .*?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-ServerName.tld\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-AUTH PLAIN LOGIN\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 SIZE=123456\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:<me@domain> FOO ZIP\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:<foo@bar>\r\n") + ->andReturn(3); + $buf->shouldReceive('readLine') + ->once() + ->with(3) + ->andReturn("250 OK\r\n"); + $this->_finishBuffer($buf); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('getMailParams') + ->once() + ->andReturn('FOO'); + $ext1->shouldReceive('getPriorityOver') + ->zeroOrMoreTimes() + ->with('AUTH') + ->andReturn(-1); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('SIZE'); + $ext2->shouldReceive('getMailParams') + ->once() + ->andReturn('ZIP'); + $ext2->shouldReceive('getPriorityOver') + ->zeroOrMoreTimes() + ->with('AUTH') + ->andReturn(1); + $ext3->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $ext3->shouldReceive('getMailParams') + ->never(); + + $smtp->setExtensionHandlers(array($ext1, $ext2, $ext3)); + $smtp->start(); + $smtp->send($message); + } + + public function testExtensionsCanModifyRcptParams() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(); + $smtp = new Swift_Transport_EsmtpTransport($buf, array(), $dispatcher); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext3 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 server.com foo\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-ServerName.tld\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-AUTH PLAIN LOGIN\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 SIZE=123456\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:<me@domain>\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:<foo@bar> FOO ZIP\r\n") + ->andReturn(3); + $buf->shouldReceive('readLine') + ->once() + ->with(3) + ->andReturn("250 OK\r\n"); + $this->_finishBuffer($buf); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('getRcptParams') + ->once() + ->andReturn('FOO'); + $ext1->shouldReceive('getPriorityOver') + ->zeroOrMoreTimes() + ->with('AUTH') + ->andReturn(-1); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('SIZE'); + $ext2->shouldReceive('getRcptParams') + ->once() + ->andReturn('ZIP'); + $ext2->shouldReceive('getPriorityOver') + ->zeroOrMoreTimes() + ->with('AUTH') + ->andReturn(1); + $ext3->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $ext3->shouldReceive('getRcptParams') + ->never(); + + $smtp->setExtensionHandlers(array($ext1, $ext2, $ext3)); + $smtp->start(); + $smtp->send($message); + } + + public function testExtensionsAreNotifiedOnCommand() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext3 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 server.com foo\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-ServerName.tld\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-AUTH PLAIN LOGIN\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 SIZE=123456\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("FOO\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("250 Cool\r\n"); + $this->_finishBuffer($buf); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('onCommand') + ->once() + ->with($smtp, "FOO\r\n", array(250, 251), \Mockery::any(), \Mockery::any()); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('SIZE'); + $ext2->shouldReceive('onCommand') + ->once() + ->with($smtp, "FOO\r\n", array(250, 251), \Mockery::any(), \Mockery::any()); + $ext3->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $ext3->shouldReceive('onCommand') + ->never() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $smtp->setExtensionHandlers(array($ext1, $ext2, $ext3)); + $smtp->start(); + $smtp->executeCommand("FOO\r\n", array(250, 251)); + } + + public function testChainOfCommandAlgorithmWhenNotifyingExtensions() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext3 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 server.com foo\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-ServerName.tld\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-AUTH PLAIN LOGIN\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 SIZE=123456\r\n"); + $buf->shouldReceive('write') + ->never() + ->with("FOO\r\n"); + $this->_finishBuffer($buf); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('onCommand') + ->once() + ->with($smtp, "FOO\r\n", array(250, 251), \Mockery::any(), \Mockery::any()) + ->andReturnUsing(function ($a, $b, $c, $d, &$e) { + $e = true; + + return '250 ok'; + }); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('SIZE'); + $ext2->shouldReceive('onCommand') + ->never() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $ext3->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $ext3->shouldReceive('onCommand') + ->never() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $smtp->setExtensionHandlers(array($ext1, $ext2, $ext3)); + $smtp->start(); + $smtp->executeCommand("FOO\r\n", array(250, 251)); + } + + public function testExtensionsCanExposeMixinMethods() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandlerMixin')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('exposeMixinMethods') + ->zeroOrMoreTimes() + ->andReturn(array('setUsername', 'setPassword')); + $ext1->shouldReceive('setUsername') + ->once() + ->with('mick'); + $ext1->shouldReceive('setPassword') + ->once() + ->with('pass'); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $this->_finishBuffer($buf); + + $smtp->setExtensionHandlers(array($ext1, $ext2)); + $smtp->setUsername('mick'); + $smtp->setPassword('pass'); + } + + public function testMixinMethodsBeginningWithSetAndNullReturnAreFluid() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandlerMixin')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('exposeMixinMethods') + ->zeroOrMoreTimes() + ->andReturn(array('setUsername', 'setPassword')); + $ext1->shouldReceive('setUsername') + ->once() + ->with('mick') + ->andReturn(null); + $ext1->shouldReceive('setPassword') + ->once() + ->with('pass') + ->andReturn(null); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $this->_finishBuffer($buf); + + $smtp->setExtensionHandlers(array($ext1, $ext2)); + $ret = $smtp->setUsername('mick'); + $this->assertEquals($smtp, $ret); + $ret = $smtp->setPassword('pass'); + $this->assertEquals($smtp, $ret); + } + + public function testMixinSetterWhichReturnValuesAreNotFluid() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandlerMixin')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('exposeMixinMethods') + ->zeroOrMoreTimes() + ->andReturn(array('setUsername', 'setPassword')); + $ext1->shouldReceive('setUsername') + ->once() + ->with('mick') + ->andReturn('x'); + $ext1->shouldReceive('setPassword') + ->once() + ->with('pass') + ->andReturn('x'); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $this->_finishBuffer($buf); + + $smtp->setExtensionHandlers(array($ext1, $ext2)); + $this->assertEquals('x', $smtp->setUsername('mick')); + $this->assertEquals('x', $smtp->setPassword('pass')); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransportTest.php new file mode 100644 index 00000000..e6cca15b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransportTest.php @@ -0,0 +1,297 @@ +<?php + +class Swift_Transport_EsmtpTransportTest extends Swift_Transport_AbstractSmtpEventSupportTest +{ + protected function _getTransport($buf, $dispatcher = null) + { + if (!$dispatcher) { + $dispatcher = $this->_createEventDispatcher(); + } + + return new Swift_Transport_EsmtpTransport($buf, array(), $dispatcher); + } + + public function testHostCanBeSetAndFetched() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $smtp->setHost('foo'); + $this->assertEquals('foo', $smtp->getHost(), '%s: Host should be returned'); + } + + public function testPortCanBeSetAndFetched() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $smtp->setPort(25); + $this->assertEquals(25, $smtp->getPort(), '%s: Port should be returned'); + } + + public function testTimeoutCanBeSetAndFetched() + { + $buf = $this->_getBuffer(); + $buf->shouldReceive('setParam') + ->once() + ->with('timeout', 10); + + $smtp = $this->_getTransport($buf); + $smtp->setTimeout(10); + $this->assertEquals(10, $smtp->getTimeout(), '%s: Timeout should be returned'); + } + + public function testEncryptionCanBeSetAndFetched() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $smtp->setEncryption('tls'); + $this->assertEquals('tls', $smtp->getEncryption(), '%s: Crypto should be returned'); + } + + public function testStartSendsHeloToInitiate() + { + //Overridden for EHLO instead + } + + public function testStartSendsEhloToInitiate() + { + /* -- RFC 2821, 3.2. + + 3.2 Client Initiation + + Once the server has sent the welcoming message and the client has + received it, the client normally sends the EHLO command to the + server, indicating the client's identity. In addition to opening the + session, use of EHLO indicates that the client is able to process + service extensions and requests that the server provide a list of the + extensions it supports. Older SMTP systems which are unable to + support service extensions and contemporary clients which do not + require service extensions in the mail session being initiated, MAY + use HELO instead of EHLO. Servers MUST NOT return the extended + EHLO-style response to a HELO command. For a particular connection + attempt, if the server returns a "command not recognized" response to + EHLO, the client SHOULD be able to fall back and send HELO. + + In the EHLO command the host sending the command identifies itself; + the command may be interpreted as saying "Hello, I am <domain>" (and, + in the case of EHLO, "and I support service extension requests"). + + -- RFC 2281, 4.1.1.1. + + ehlo = "EHLO" SP Domain CRLF + helo = "HELO" SP Domain CRLF + + -- RFC 2821, 4.3.2. + + EHLO or HELO + S: 250 + E: 504, 550 + + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 ServerName'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + } catch (Exception $e) { + $this->fail('Starting Esmtp should send EHLO and accept 250 response'); + } + } + + public function testHeloIsUsedAsFallback() + { + /* -- RFC 2821, 4.1.4. + + If the EHLO command is not acceptable to the SMTP server, 501, 500, + or 502 failure replies MUST be returned as appropriate. The SMTP + server MUST stay in the same state after transmitting these replies + that it was in before the EHLO was received. + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('501 WTF'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^HELO .+?\r\n$~D') + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 HELO'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + } catch (Exception $e) { + $this->fail( + 'Starting Esmtp should fallback to HELO if needed and accept 250 response' + ); + } + } + + public function testInvalidHeloResponseCausesException() + { + //Overridden to first try EHLO + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('501 WTF'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^HELO .+?\r\n$~D') + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('504 WTF'."\r\n"); + $this->_finishBuffer($buf); + + try { + $this->assertFalse($smtp->isStarted(), '%s: SMTP should begin non-started'); + $smtp->start(); + $this->fail('Non 250 HELO response should raise Exception'); + } catch (Exception $e) { + $this->assertFalse($smtp->isStarted(), '%s: SMTP start() should have failed'); + } + } + + public function testDomainNameIsPlacedInEhlo() + { + /* -- RFC 2821, 4.1.4. + + The SMTP client MUST, if possible, ensure that the domain parameter + to the EHLO command is a valid principal host name (not a CNAME or MX + name) for its host. If this is not possible (e.g., when the client's + address is dynamically assigned and the client does not have an + obvious name), an address literal SHOULD be substituted for the + domain name and supplemental information provided that will assist in + identifying the client. + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("EHLO mydomain.com\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 ServerName'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->setLocalDomain('mydomain.com'); + $smtp->start(); + } + + public function testDomainNameIsPlacedInHelo() + { + //Overridden to include ESMTP + /* -- RFC 2821, 4.1.4. + + The SMTP client MUST, if possible, ensure that the domain parameter + to the EHLO command is a valid principal host name (not a CNAME or MX + name) for its host. If this is not possible (e.g., when the client's + address is dynamically assigned and the client does not have an + obvious name), an address literal SHOULD be substituted for the + domain name and supplemental information provided that will assist in + identifying the client. + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('501 WTF'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("HELO mydomain.com\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 ServerName'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->setLocalDomain('mydomain.com'); + $smtp->start(); + } + + public function testFluidInterface() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $buf->shouldReceive('setParam') + ->once() + ->with('timeout', 30); + + $ref = $smtp + ->setHost('foo') + ->setPort(25) + ->setEncryption('tls') + ->setTimeout(30) + ; + $this->assertEquals($ref, $smtp); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/FailoverTransportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/FailoverTransportTest.php new file mode 100644 index 00000000..8d80f359 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/FailoverTransportTest.php @@ -0,0 +1,520 @@ +<?php + +class Swift_Transport_FailoverTransportTest extends \SwiftMailerTestCase +{ + public function testFirstTransportIsUsed() + { + $message1 = $this->getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState) { + return $connectionState; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState) { + if (!$connectionState) { + $connectionState = true; + } + }); + $t1->shouldReceive('send') + ->twice() + ->with(\Mockery::anyOf($message1, $message2), \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState) { + if ($connectionState) { + return 1; + } + }); + $t2->shouldReceive('start')->never(); + $t2->shouldReceive('send')->never(); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message1)); + $this->assertEquals(1, $transport->send($message2)); + } + + public function testMessageCanBeTriedOnNextTransportIfExceptionThrown() + { + $e = new Swift_TransportException('b0rken'); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + throw $e; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + return 1; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message)); + } + + public function testZeroIsReturnedIfTransportReturnsZero() + { + $message = $this->getMockery('Swift_Mime_Message')->shouldIgnoreMissing(); + $t1 = $this->getMockery('Swift_Transport')->shouldIgnoreMissing(); + + $connectionState = false; + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState) { + return $connectionState; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState) { + if (!$connectionState) { + $connectionState = true; + } + }); + $testCase = $this; + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState, $testCase) { + if (!$connectionState) { + $testCase->fail(); + } + + return 0; + }); + + $transport = $this->_getTransport(array($t1)); + $transport->start(); + $this->assertEquals(0, $transport->send($message)); + } + + public function testTransportsWhichThrowExceptionsAreNotRetried() + { + $e = new Swift_TransportException('maur b0rken'); + + $message1 = $this->getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $message3 = $this->getMockery('Swift_Mime_Message'); + $message4 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + throw $e; + } + }); + $t1->shouldReceive('send') + ->never() + ->with($message2, \Mockery::any()); + $t1->shouldReceive('send') + ->never() + ->with($message3, \Mockery::any()); + $t1->shouldReceive('send') + ->never() + ->with($message4, \Mockery::any()); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->times(4) + ->with(\Mockery::anyOf($message1, $message2, $message3, $message4), \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + return 1; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message1)); + $this->assertEquals(1, $transport->send($message2)); + $this->assertEquals(1, $transport->send($message3)); + $this->assertEquals(1, $transport->send($message4)); + } + + public function testExceptionIsThrownIfAllTransportsDie() + { + $e = new Swift_TransportException('b0rken'); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + throw $e; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + throw $e; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + try { + $transport->send($message); + $this->fail('All transports failed so Exception should be thrown'); + } catch (Exception $e) { + } + } + + public function testStoppingTransportStopsAllDelegates() + { + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + + $connectionState1 = true; + $connectionState2 = true; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('stop') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if ($connectionState1) { + $connectionState1 = false; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('stop') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if ($connectionState2) { + $connectionState2 = false; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $transport->stop(); + } + + public function testTransportShowsAsNotStartedIfAllDelegatesDead() + { + $e = new Swift_TransportException('b0rken'); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + $connectionState1 = false; + throw $e; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + $connectionState2 = false; + throw $e; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertTrue($transport->isStarted()); + try { + $transport->send($message); + $this->fail('All transports failed so Exception should be thrown'); + } catch (Exception $e) { + $this->assertFalse($transport->isStarted()); + } + } + + public function testRestartingTransportRestartsDeadDelegates() + { + $e = new Swift_TransportException('b0rken'); + + $message1 = $this->getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->twice() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + $connectionState1 = false; + throw $e; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message2, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1) { + if ($connectionState1) { + return 10; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + $connectionState2 = false; + throw $e; + } + }); + $t2->shouldReceive('send') + ->never() + ->with($message2, \Mockery::any()); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertTrue($transport->isStarted()); + try { + $transport->send($message1); + $this->fail('All transports failed so Exception should be thrown'); + } catch (Exception $e) { + $this->assertFalse($transport->isStarted()); + } + //Restart and re-try + $transport->start(); + $this->assertTrue($transport->isStarted()); + $this->assertEquals(10, $transport->send($message2)); + } + + public function testFailureReferenceIsPassedToDelegates() + { + $failures = array(); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + + $connectionState = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use ($connectionState) { + return $connectionState; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use ($connectionState) { + if (!$connectionState) { + $connectionState = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, $failures) + ->andReturnUsing(function () use ($connectionState) { + if ($connectionState) { + return 1; + } + }); + + $transport = $this->_getTransport(array($t1)); + $transport->start(); + $transport->send($message, $failures); + } + + public function testRegisterPluginDelegatesToLoadedTransports() + { + $plugin = $this->_createPlugin(); + + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $t1->shouldReceive('registerPlugin') + ->once() + ->with($plugin); + $t2->shouldReceive('registerPlugin') + ->once() + ->with($plugin); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->registerPlugin($plugin); + } + + // -- Private helpers + + private function _getTransport(array $transports) + { + $transport = new Swift_Transport_FailoverTransport(); + $transport->setTransports($transports); + + return $transport; + } + + private function _createPlugin() + { + return $this->getMockery('Swift_Events_EventListener'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/LoadBalancedTransportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/LoadBalancedTransportTest.php new file mode 100644 index 00000000..cc7297b1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/LoadBalancedTransportTest.php @@ -0,0 +1,751 @@ +<?php + +class Swift_Transport_LoadBalancedTransportTest extends \SwiftMailerTestCase +{ + public function testEachTransportIsUsedInTurn() + { + $message1 = $this->getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $testCase = $this; + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $testCase) { + if ($connectionState1) { + return 1; + } + $testCase->fail(); + }); + $t1->shouldReceive('send') + ->never() + ->with($message2, \Mockery::any()); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message2, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $testCase) { + if ($connectionState2) { + return 1; + } + $testCase->fail(); + }); + $t2->shouldReceive('send') + ->never() + ->with($message1, \Mockery::any()); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message1)); + $this->assertEquals(1, $transport->send($message2)); + } + + public function testTransportsAreReusedInRotatingFashion() + { + $message1 = $this->getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $message3 = $this->getMockery('Swift_Mime_Message'); + $message4 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $testCase = $this; + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $testCase) { + if ($connectionState1) { + return 1; + } + $testCase->fail(); + }); + $t1->shouldReceive('send') + ->never() + ->with($message2, \Mockery::any()); + $t1->shouldReceive('send') + ->once() + ->with($message3, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $testCase) { + if ($connectionState1) { + return 1; + } + $testCase->fail(); + }); + $t1->shouldReceive('send') + ->never() + ->with($message4, \Mockery::any()); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message2, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $testCase) { + if ($connectionState2) { + return 1; + } + $testCase->fail(); + }); + $t2->shouldReceive('send') + ->never() + ->with($message1, \Mockery::any()); + $t2->shouldReceive('send') + ->once() + ->with($message4, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $testCase) { + if ($connectionState2) { + return 1; + } + $testCase->fail(); + }); + $t2->shouldReceive('send') + ->never() + ->with($message3, \Mockery::any()); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + + $this->assertEquals(1, $transport->send($message1)); + $this->assertEquals(1, $transport->send($message2)); + $this->assertEquals(1, $transport->send($message3)); + $this->assertEquals(1, $transport->send($message4)); + } + + public function testMessageCanBeTriedOnNextTransportIfExceptionThrown() + { + $e = new Swift_TransportException('b0rken'); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $testCase = $this; + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e, $testCase) { + if ($connectionState1) { + throw $e; + } + $testCase->fail(); + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $testCase) { + if ($connectionState2) { + return 1; + } + $testCase->fail(); + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message)); + } + + public function testMessageIsTriedOnNextTransportIfZeroReturned() + { + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1) { + if ($connectionState1) { + return 0; + } + + return 1; + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2) { + if ($connectionState2) { + return 1; + } + + return 0; + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message)); + } + + public function testZeroIsReturnedIfAllTransportsReturnZero() + { + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1) { + if ($connectionState1) { + return 0; + } + + return 1; + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2) { + if ($connectionState2) { + return 0; + } + + return 1; + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(0, $transport->send($message)); + } + + public function testTransportsWhichThrowExceptionsAreNotRetried() + { + $e = new Swift_TransportException('maur b0rken'); + + $message1 = $this->getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $message3 = $this->getMockery('Swift_Mime_Message'); + $message4 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $testCase = $this; + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e, $testCase) { + if ($connectionState1) { + throw $e; + } + $testCase->fail(); + }); + $t1->shouldReceive('send') + ->never() + ->with($message2, \Mockery::any()); + $t1->shouldReceive('send') + ->never() + ->with($message3, \Mockery::any()); + $t1->shouldReceive('send') + ->never() + ->with($message4, \Mockery::any()); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->times(4) + ->with(\Mockery::anyOf($message1, $message3, $message3, $message4), \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $testCase) { + if ($connectionState2) { + return 1; + } + $testCase->fail(); + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message1)); + $this->assertEquals(1, $transport->send($message2)); + $this->assertEquals(1, $transport->send($message3)); + $this->assertEquals(1, $transport->send($message4)); + } + + public function testExceptionIsThrownIfAllTransportsDie() + { + $e = new Swift_TransportException('b0rken'); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + throw $e; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + throw $e; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + try { + $transport->send($message); + $this->fail('All transports failed so Exception should be thrown'); + } catch (Exception $e) { + } + } + + public function testStoppingTransportStopsAllDelegates() + { + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = true; + $connectionState2 = true; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('stop') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if ($connectionState1) { + $connectionState1 = false; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('stop') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if ($connectionState2) { + $connectionState2 = false; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $transport->stop(); + } + + public function testTransportShowsAsNotStartedIfAllDelegatesDead() + { + $e = new Swift_TransportException('b0rken'); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + throw $e; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + throw $e; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertTrue($transport->isStarted()); + try { + $transport->send($message); + $this->fail('All transports failed so Exception should be thrown'); + } catch (Exception $e) { + $this->assertFalse($transport->isStarted()); + } + } + + public function testRestartingTransportRestartsDeadDelegates() + { + $e = new Swift_TransportException('b0rken'); + + $message1 = $this->getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->twice() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + $connectionState1 = false; + throw $e; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message2, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + return 10; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + throw $e; + } + }); + $t2->shouldReceive('send') + ->never() + ->with($message2, \Mockery::any()); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertTrue($transport->isStarted()); + try { + $transport->send($message1); + $this->fail('All transports failed so Exception should be thrown'); + } catch (Exception $e) { + $this->assertFalse($transport->isStarted()); + } + //Restart and re-try + $transport->start(); + $this->assertTrue($transport->isStarted()); + $this->assertEquals(10, $transport->send($message2)); + } + + public function testFailureReferenceIsPassedToDelegates() + { + $failures = array(); + $testCase = $this; + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $connectionState = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState) { + return $connectionState; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState) { + if (!$connectionState) { + $connectionState = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::on(function (&$var) use (&$failures, $testCase) { + return $testCase->varsAreReferences($var, $failures); + })) + ->andReturnUsing(function () use (&$connectionState) { + if ($connectionState) { + return 1; + } + }); + + $transport = $this->_getTransport(array($t1)); + $transport->start(); + $transport->send($message, $failures); + } + + public function testRegisterPluginDelegatesToLoadedTransports() + { + $plugin = $this->_createPlugin(); + + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + + $t1->shouldReceive('registerPlugin') + ->once() + ->with($plugin); + $t2->shouldReceive('registerPlugin') + ->once() + ->with($plugin); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->registerPlugin($plugin); + } + + /** + * Adapted from Yay_Matchers_ReferenceMatcher. + */ + public function varsAreReferences(&$ref1, &$ref2) + { + if (is_object($ref2)) { + return ($ref1 === $ref2); + } + if ($ref1 !== $ref2) { + return false; + } + + $copy = $ref2; + $randomString = uniqid('yay'); + $ref2 = $randomString; + $isRef = ($ref1 === $ref2); + $ref2 = $copy; + + return $isRef; + } + + // -- Private helpers + + private function _getTransport(array $transports) + { + $transport = new Swift_Transport_LoadBalancedTransport(); + $transport->setTransports($transports); + + return $transport; + } + + private function _createPlugin() + { + return $this->getMockery('Swift_Events_EventListener'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/MailTransportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/MailTransportTest.php new file mode 100644 index 00000000..96e9943d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/MailTransportTest.php @@ -0,0 +1,535 @@ +<?php + +/** + * @group legacy + */ +class Swift_Transport_MailTransportTest extends \SwiftMailerTestCase +{ + public function testTransportInvokesMailOncePerMessage() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessageWithRecipient($headers); + + $invoker->shouldReceive('mail') + ->once(); + + $transport->send($message); + } + + public function testTransportUsesToFieldBodyInSending() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $to = $this->_createHeader(); + $headers = $this->_createHeaders(array( + 'To' => $to, + )); + $message = $this->_createMessageWithRecipient($headers); + + $to->shouldReceive('getFieldBody') + ->zeroOrMoreTimes() + ->andReturn('Foo <foo@bar>'); + $invoker->shouldReceive('mail') + ->once() + ->with('Foo <foo@bar>', \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + public function testTransportUsesSubjectFieldBodyInSending() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $subj = $this->_createHeader(); + $headers = $this->_createHeaders(array( + 'Subject' => $subj, + )); + $message = $this->_createMessageWithRecipient($headers); + + $subj->shouldReceive('getFieldBody') + ->zeroOrMoreTimes() + ->andReturn('Thing'); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), 'Thing', \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + public function testTransportUsesBodyOfMessage() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessageWithRecipient($headers); + + $message->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn( + "To: Foo <foo@bar>\r\n". + "\r\n". + 'This body' + ); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), 'This body', \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + public function testTransportSettingUsingReturnPathForExtraParams() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessageWithRecipient($headers); + + $message->shouldReceive('getReturnPath') + ->zeroOrMoreTimes() + ->andReturn( + 'foo@bar' + ); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), '-ffoo@bar'); + + $transport->send($message); + } + + public function testTransportSettingEmptyExtraParams() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessageWithRecipient($headers); + + $message->shouldReceive('getReturnPath') + ->zeroOrMoreTimes() + ->andReturn(null); + $message->shouldReceive('getSender') + ->zeroOrMoreTimes() + ->andReturn(null); + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(null); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), null); + + $transport->send($message); + } + + public function testTransportSettingSettingExtraParamsWithF() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + $transport->setExtraParams('-x\'foo\' -f%s'); + + $headers = $this->_createHeaders(); + $message = $this->_createMessageWithRecipient($headers); + + $message->shouldReceive('getReturnPath') + ->zeroOrMoreTimes() + ->andReturn( + 'foo@bar' + ); + $message->shouldReceive('getSender') + ->zeroOrMoreTimes() + ->andReturn(null); + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(null); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), '-x\'foo\' -ffoo@bar'); + + $transport->send($message); + } + + public function testTransportSettingSettingExtraParamsWithoutF() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + $transport->setExtraParams('-x\'foo\''); + + $headers = $this->_createHeaders(); + $message = $this->_createMessageWithRecipient($headers); + + $message->shouldReceive('getReturnPath') + ->zeroOrMoreTimes() + ->andReturn( + 'foo@bar' + ); + $message->shouldReceive('getSender') + ->zeroOrMoreTimes() + ->andReturn(null); + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(null); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), '-x\'foo\''); + + $transport->send($message); + } + + public function testTransportSettingInvalidFromEmail() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessageWithRecipient($headers); + + $message->shouldReceive('getReturnPath') + ->zeroOrMoreTimes() + ->andReturn( + '"attacker\" -oQ/tmp/ -X/var/www/cache/phpcode.php "@email.com' + ); + $message->shouldReceive('getSender') + ->zeroOrMoreTimes() + ->andReturn(null); + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(null); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), null); + + $transport->send($message); + } + + public function testTransportUsesHeadersFromMessage() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessageWithRecipient($headers); + + $message->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn( + "Subject: Stuff\r\n". + "\r\n". + 'This body' + ); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), 'Subject: Stuff'.PHP_EOL, \Mockery::any()); + + $transport->send($message); + } + + public function testTransportReturnsCountOfAllRecipientsIfInvokerReturnsTrue() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessage($headers); + + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null, 'zip@button' => null)); + $message->shouldReceive('getCc') + ->zeroOrMoreTimes() + ->andReturn(array('test@test' => null)); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn(true); + + $this->assertEquals(3, $transport->send($message)); + } + + public function testTransportReturnsZeroIfInvokerReturnsFalse() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessage($headers); + + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null, 'zip@button' => null)); + $message->shouldReceive('getCc') + ->zeroOrMoreTimes() + ->andReturn(array('test@test' => null)); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn(false); + + $this->assertEquals(0, $transport->send($message)); + } + + public function testToHeaderIsRemovedFromHeaderSetDuringSending() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $to = $this->_createHeader(); + $headers = $this->_createHeaders(array( + 'To' => $to, + )); + $message = $this->_createMessageWithRecipient($headers); + + $headers->shouldReceive('remove') + ->once() + ->with('To'); + $headers->shouldReceive('remove') + ->zeroOrMoreTimes(); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + public function testSubjectHeaderIsRemovedFromHeaderSetDuringSending() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $subject = $this->_createHeader(); + $headers = $this->_createHeaders(array( + 'Subject' => $subject, + )); + $message = $this->_createMessageWithRecipient($headers); + + $headers->shouldReceive('remove') + ->once() + ->with('Subject'); + $headers->shouldReceive('remove') + ->zeroOrMoreTimes(); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + public function testToHeaderIsPutBackAfterSending() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $to = $this->_createHeader(); + $headers = $this->_createHeaders(array( + 'To' => $to, + )); + $message = $this->_createMessageWithRecipient($headers); + + $headers->shouldReceive('set') + ->once() + ->with($to); + $headers->shouldReceive('set') + ->zeroOrMoreTimes(); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + public function testSubjectHeaderIsPutBackAfterSending() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $subject = $this->_createHeader(); + $headers = $this->_createHeaders(array( + 'Subject' => $subject, + )); + $message = $this->_createMessageWithRecipient($headers); + + $headers->shouldReceive('set') + ->once() + ->with($subject); + $headers->shouldReceive('set') + ->zeroOrMoreTimes(); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + public function testMessageHeadersOnlyHavePHPEolsDuringSending() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $subject = $this->_createHeader(); + $subject->shouldReceive('getFieldBody')->andReturn("Foo\r\nBar"); + + $headers = $this->_createHeaders(array( + 'Subject' => $subject, + )); + $message = $this->_createMessageWithRecipient($headers); + $message->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn( + "From: Foo\r\n<foo@bar>\r\n". + "\r\n". + "This\r\n". + 'body' + ); + + if ("\r\n" != PHP_EOL) { + $expectedHeaders = "From: Foo\n<foo@bar>\n"; + $expectedSubject = "Foo\nBar"; + $expectedBody = "This\nbody"; + } else { + $expectedHeaders = "From: Foo\r\n<foo@bar>\r\n"; + $expectedSubject = "Foo\r\nBar"; + $expectedBody = "This\r\nbody"; + } + + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), $expectedSubject, $expectedBody, $expectedHeaders, \Mockery::any()); + + $transport->send($message); + } + + /** + * @expectedException Swift_TransportException + * @expectedExceptionMessage Cannot send message without a recipient + */ + public function testExceptionWhenNoRecipients() + { + $invoker = $this->_createInvoker(); + $invoker->shouldReceive('mail'); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessage($headers); + + $transport->send($message); + } + + public function noExceptionWhenRecipientsExistProvider() + { + return array( + array('To'), + array('Cc'), + array('Bcc'), + ); + } + + /** + * @dataProvider noExceptionWhenRecipientsExistProvider + * + * @param string $header + */ + public function testNoExceptionWhenRecipientsExist($header) + { + $invoker = $this->_createInvoker(); + $invoker->shouldReceive('mail'); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessage($headers); + $message->shouldReceive(sprintf('get%s', $header))->andReturn(array('foo@bar' => 'Foo')); + + $transport->send($message); + } + + // -- Creation Methods + + private function _createTransport($invoker, $dispatcher) + { + return new Swift_Transport_MailTransport($invoker, $dispatcher); + } + + private function _createEventDispatcher() + { + return $this->getMockery('Swift_Events_EventDispatcher')->shouldIgnoreMissing(); + } + + private function _createInvoker() + { + return $this->getMockery('Swift_Transport_MailInvoker'); + } + + private function _createMessage($headers) + { + $message = $this->getMockery('Swift_Mime_Message')->shouldIgnoreMissing(); + $message->shouldReceive('getHeaders') + ->zeroOrMoreTimes() + ->andReturn($headers); + + return $message; + } + + private function _createMessageWithRecipient($headers, $recipient = array('foo@bar' => 'Foo')) + { + $message = $this->_createMessage($headers); + $message->shouldReceive('getTo')->andReturn($recipient); + + return $message; + } + + private function _createHeaders($headers = array()) + { + $set = $this->getMockery('Swift_Mime_HeaderSet')->shouldIgnoreMissing(); + + if (count($headers) > 0) { + foreach ($headers as $name => $header) { + $set->shouldReceive('get') + ->zeroOrMoreTimes() + ->with($name) + ->andReturn($header); + $set->shouldReceive('has') + ->zeroOrMoreTimes() + ->with($name) + ->andReturn(true); + } + } + + $header = $this->_createHeader(); + $set->shouldReceive('get') + ->zeroOrMoreTimes() + ->andReturn($header); + $set->shouldReceive('has') + ->zeroOrMoreTimes() + ->andReturn(true); + + return $set; + } + + private function _createHeader() + { + return $this->getMockery('Swift_Mime_Header')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/SendmailTransportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/SendmailTransportTest.php new file mode 100644 index 00000000..9040f9ea --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/SendmailTransportTest.php @@ -0,0 +1,151 @@ +<?php + +class Swift_Transport_SendmailTransportTest extends Swift_Transport_AbstractSmtpEventSupportTest +{ + protected function _getTransport($buf, $dispatcher = null, $command = '/usr/sbin/sendmail -bs') + { + if (!$dispatcher) { + $dispatcher = $this->_createEventDispatcher(); + } + $transport = new Swift_Transport_SendmailTransport($buf, $dispatcher); + $transport->setCommand($command); + + return $transport; + } + + protected function _getSendmail($buf, $dispatcher = null) + { + if (!$dispatcher) { + $dispatcher = $this->_createEventDispatcher(); + } + $sendmail = new Swift_Transport_SendmailTransport($buf, $dispatcher); + + return $sendmail; + } + + public function testCommandCanBeSetAndFetched() + { + $buf = $this->_getBuffer(); + $sendmail = $this->_getSendmail($buf); + + $sendmail->setCommand('/usr/sbin/sendmail -bs'); + $this->assertEquals('/usr/sbin/sendmail -bs', $sendmail->getCommand()); + $sendmail->setCommand('/usr/sbin/sendmail -oi -t'); + $this->assertEquals('/usr/sbin/sendmail -oi -t', $sendmail->getCommand()); + } + + public function testSendingMessageIn_t_ModeUsesSimplePipe() + { + $buf = $this->_getBuffer(); + $sendmail = $this->_getSendmail($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => 'Foobar', 'zip@button' => 'Zippy')); + $message->shouldReceive('toByteStream') + ->once() + ->with($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('terminate') + ->once(); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array("\r\n" => "\n", "\n." => "\n..")); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array()); + + $sendmail->setCommand('/usr/sbin/sendmail -t'); + $this->assertEquals(2, $sendmail->send($message)); + } + + public function testSendingIn_t_ModeWith_i_FlagDoesntEscapeDot() + { + $buf = $this->_getBuffer(); + $sendmail = $this->_getSendmail($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => 'Foobar', 'zip@button' => 'Zippy')); + $message->shouldReceive('toByteStream') + ->once() + ->with($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('terminate') + ->once(); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array("\r\n" => "\n")); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array()); + + $sendmail->setCommand('/usr/sbin/sendmail -i -t'); + $this->assertEquals(2, $sendmail->send($message)); + } + + public function testSendingInTModeWith_oi_FlagDoesntEscapeDot() + { + $buf = $this->_getBuffer(); + $sendmail = $this->_getSendmail($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => 'Foobar', 'zip@button' => 'Zippy')); + $message->shouldReceive('toByteStream') + ->once() + ->with($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('terminate') + ->once(); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array("\r\n" => "\n")); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array()); + + $sendmail->setCommand('/usr/sbin/sendmail -oi -t'); + $this->assertEquals(2, $sendmail->send($message)); + } + + public function testSendingMessageRegeneratesId() + { + $buf = $this->_getBuffer(); + $sendmail = $this->_getSendmail($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => 'Foobar', 'zip@button' => 'Zippy')); + $message->shouldReceive('generateId'); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('terminate') + ->once(); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array("\r\n" => "\n", "\n." => "\n..")); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array()); + + $sendmail->setCommand('/usr/sbin/sendmail -t'); + $this->assertEquals(2, $sendmail->send($message)); + } + + public function testFluidInterface() + { + $buf = $this->_getBuffer(); + $sendmail = $this->_getTransport($buf); + + $ref = $sendmail->setCommand('/foo'); + $this->assertEquals($ref, $sendmail); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/StreamBufferTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/StreamBufferTest.php new file mode 100644 index 00000000..6108a954 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/StreamBufferTest.php @@ -0,0 +1,45 @@ +<?php + +class Swift_Transport_StreamBufferTest extends \PHPUnit_Framework_TestCase +{ + public function testSettingWriteTranslationsCreatesFilters() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createFilter') + ->with('a', 'b') + ->will($this->returnCallback(array($this, '_createFilter'))); + + $buffer = $this->_createBuffer($factory); + $buffer->setWriteTranslations(array('a' => 'b')); + } + + public function testOverridingTranslationsOnlyAddsNeededFilters() + { + $factory = $this->_createFactory(); + $factory->expects($this->exactly(2)) + ->method('createFilter') + ->will($this->returnCallback(array($this, '_createFilter'))); + + $buffer = $this->_createBuffer($factory); + $buffer->setWriteTranslations(array('a' => 'b')); + $buffer->setWriteTranslations(array('x' => 'y', 'a' => 'b')); + } + + // -- Creation methods + + private function _createBuffer($replacementFactory) + { + return new Swift_Transport_StreamBuffer($replacementFactory); + } + + private function _createFactory() + { + return $this->getMockBuilder('Swift_ReplacementFilterFactory')->getMock(); + } + + public function _createFilter() + { + return $this->getMockBuilder('Swift_StreamFilter')->getMock(); + } +} diff --git a/vendor/symfony/console/.gitignore b/vendor/symfony/console/.gitignore new file mode 100644 index 00000000..c49a5d8d --- /dev/null +++ b/vendor/symfony/console/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/console/Application.php b/vendor/symfony/console/Application.php new file mode 100644 index 00000000..603559f0 --- /dev/null +++ b/vendor/symfony/console/Application.php @@ -0,0 +1,1144 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Descriptor\TextDescriptor; +use Symfony\Component\Console\Descriptor\XmlDescriptor; +use Symfony\Component\Console\Exception\ExceptionInterface; +use Symfony\Component\Console\Helper\DebugFormatterHelper; +use Symfony\Component\Console\Helper\ProcessHelper; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputAwareInterface; +use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Command\HelpCommand; +use Symfony\Component\Console\Command\ListCommand; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Helper\DialogHelper; +use Symfony\Component\Console\Helper\ProgressHelper; +use Symfony\Component\Console\Helper\TableHelper; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleExceptionEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\Console\Exception\CommandNotFoundException; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * An Application is the container for a collection of commands. + * + * It is the main entry point of a Console application. + * + * This class is optimized for a standard CLI environment. + * + * Usage: + * + * $app = new Application('myapp', '1.0 (stable)'); + * $app->add(new SimpleCommand()); + * $app->run(); + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class Application +{ + private $commands = array(); + private $wantHelps = false; + private $runningCommand; + private $name; + private $version; + private $catchExceptions = true; + private $autoExit = true; + private $definition; + private $helperSet; + private $dispatcher; + private $terminalDimensions; + private $defaultCommand; + + /** + * Constructor. + * + * @param string $name The name of the application + * @param string $version The version of the application + */ + public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') + { + $this->name = $name; + $this->version = $version; + $this->defaultCommand = 'list'; + $this->helperSet = $this->getDefaultHelperSet(); + $this->definition = $this->getDefaultInputDefinition(); + + foreach ($this->getDefaultCommands() as $command) { + $this->add($command); + } + } + + public function setDispatcher(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return int 0 if everything went fine, or an error code + * + * @throws \Exception When doRun returns Exception + */ + public function run(InputInterface $input = null, OutputInterface $output = null) + { + if (null === $input) { + $input = new ArgvInput(); + } + + if (null === $output) { + $output = new ConsoleOutput(); + } + + $this->configureIO($input, $output); + + try { + $exitCode = $this->doRun($input, $output); + } catch (\Exception $e) { + if (!$this->catchExceptions) { + throw $e; + } + + if ($output instanceof ConsoleOutputInterface) { + $this->renderException($e, $output->getErrorOutput()); + } else { + $this->renderException($e, $output); + } + + $exitCode = $e->getCode(); + if (is_numeric($exitCode)) { + $exitCode = (int) $exitCode; + if (0 === $exitCode) { + $exitCode = 1; + } + } else { + $exitCode = 1; + } + } + + if ($this->autoExit) { + if ($exitCode > 255) { + $exitCode = 255; + } + + exit($exitCode); + } + + return $exitCode; + } + + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return int 0 if everything went fine, or an error code + */ + public function doRun(InputInterface $input, OutputInterface $output) + { + if (true === $input->hasParameterOption(array('--version', '-V'))) { + $output->writeln($this->getLongVersion()); + + return 0; + } + + $name = $this->getCommandName($input); + if (true === $input->hasParameterOption(array('--help', '-h'))) { + if (!$name) { + $name = 'help'; + $input = new ArrayInput(array('command' => 'help')); + } else { + $this->wantHelps = true; + } + } + + if (!$name) { + $name = $this->defaultCommand; + $input = new ArrayInput(array('command' => $this->defaultCommand)); + } + + // the command name MUST be the first element of the input + $command = $this->find($name); + + $this->runningCommand = $command; + $exitCode = $this->doRunCommand($command, $input, $output); + $this->runningCommand = null; + + return $exitCode; + } + + /** + * Set a helper set to be used with the command. + * + * @param HelperSet $helperSet The helper set + */ + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Get the helper set associated with the command. + * + * @return HelperSet The HelperSet instance associated with this command + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Set an input definition set to be used with this application. + * + * @param InputDefinition $definition The input definition + */ + public function setDefinition(InputDefinition $definition) + { + $this->definition = $definition; + } + + /** + * Gets the InputDefinition related to this Application. + * + * @return InputDefinition The InputDefinition instance + */ + public function getDefinition() + { + return $this->definition; + } + + /** + * Gets the help message. + * + * @return string A help message. + */ + public function getHelp() + { + return $this->getLongVersion(); + } + + /** + * Sets whether to catch exceptions or not during commands execution. + * + * @param bool $boolean Whether to catch exceptions or not during commands execution + */ + public function setCatchExceptions($boolean) + { + $this->catchExceptions = (bool) $boolean; + } + + /** + * Sets whether to automatically exit after a command execution or not. + * + * @param bool $boolean Whether to automatically exit after a command execution or not + */ + public function setAutoExit($boolean) + { + $this->autoExit = (bool) $boolean; + } + + /** + * Gets the name of the application. + * + * @return string The application name + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the application name. + * + * @param string $name The application name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Gets the application version. + * + * @return string The application version + */ + public function getVersion() + { + return $this->version; + } + + /** + * Sets the application version. + * + * @param string $version The application version + */ + public function setVersion($version) + { + $this->version = $version; + } + + /** + * Returns the long version of the application. + * + * @return string The long application version + */ + public function getLongVersion() + { + if ('UNKNOWN' !== $this->getName()) { + if ('UNKNOWN' !== $this->getVersion()) { + return sprintf('<info>%s</info> version <comment>%s</comment>', $this->getName(), $this->getVersion()); + } + + return sprintf('<info>%s</info>', $this->getName()); + } + + return '<info>Console Tool</info>'; + } + + /** + * Registers a new command. + * + * @param string $name The command name + * + * @return Command The newly created command + */ + public function register($name) + { + return $this->add(new Command($name)); + } + + /** + * Adds an array of command objects. + * + * @param Command[] $commands An array of commands + */ + public function addCommands(array $commands) + { + foreach ($commands as $command) { + $this->add($command); + } + } + + /** + * Adds a command object. + * + * If a command with the same name already exists, it will be overridden. + * + * @param Command $command A Command object + * + * @return Command The registered command + */ + public function add(Command $command) + { + $command->setApplication($this); + + if (!$command->isEnabled()) { + $command->setApplication(null); + + return; + } + + if (null === $command->getDefinition()) { + throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command))); + } + + $this->commands[$command->getName()] = $command; + + foreach ($command->getAliases() as $alias) { + $this->commands[$alias] = $command; + } + + return $command; + } + + /** + * Returns a registered command by name or alias. + * + * @param string $name The command name or alias + * + * @return Command A Command object + * + * @throws CommandNotFoundException When command name given does not exist + */ + public function get($name) + { + if (!isset($this->commands[$name])) { + throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name)); + } + + $command = $this->commands[$name]; + + if ($this->wantHelps) { + $this->wantHelps = false; + + $helpCommand = $this->get('help'); + $helpCommand->setCommand($command); + + return $helpCommand; + } + + return $command; + } + + /** + * Returns true if the command exists, false otherwise. + * + * @param string $name The command name or alias + * + * @return bool true if the command exists, false otherwise + */ + public function has($name) + { + return isset($this->commands[$name]); + } + + /** + * Returns an array of all unique namespaces used by currently registered commands. + * + * It does not returns the global namespace which always exists. + * + * @return array An array of namespaces + */ + public function getNamespaces() + { + $namespaces = array(); + foreach ($this->all() as $command) { + $namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName())); + + foreach ($command->getAliases() as $alias) { + $namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias)); + } + } + + return array_values(array_unique(array_filter($namespaces))); + } + + /** + * Finds a registered namespace by a name or an abbreviation. + * + * @param string $namespace A namespace or abbreviation to search for + * + * @return string A registered namespace + * + * @throws CommandNotFoundException When namespace is incorrect or ambiguous + */ + public function findNamespace($namespace) + { + $allNamespaces = $this->getNamespaces(); + $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $namespace); + $namespaces = preg_grep('{^'.$expr.'}', $allNamespaces); + + if (empty($namespaces)) { + $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace); + + if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) { + if (1 == count($alternatives)) { + $message .= "\n\nDid you mean this?\n "; + } else { + $message .= "\n\nDid you mean one of these?\n "; + } + + $message .= implode("\n ", $alternatives); + } + + throw new CommandNotFoundException($message, $alternatives); + } + + $exact = in_array($namespace, $namespaces, true); + if (count($namespaces) > 1 && !$exact) { + throw new CommandNotFoundException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces)); + } + + return $exact ? $namespace : reset($namespaces); + } + + /** + * Finds a command by name or alias. + * + * Contrary to get, this command tries to find the best + * match if you give it an abbreviation of a name or alias. + * + * @param string $name A command name or a command alias + * + * @return Command A Command instance + * + * @throws CommandNotFoundException When command name is incorrect or ambiguous + */ + public function find($name) + { + $allCommands = array_keys($this->commands); + $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name); + $commands = preg_grep('{^'.$expr.'}', $allCommands); + + if (empty($commands) || count(preg_grep('{^'.$expr.'$}', $commands)) < 1) { + if (false !== $pos = strrpos($name, ':')) { + // check if a namespace exists and contains commands + $this->findNamespace(substr($name, 0, $pos)); + } + + $message = sprintf('Command "%s" is not defined.', $name); + + if ($alternatives = $this->findAlternatives($name, $allCommands)) { + if (1 == count($alternatives)) { + $message .= "\n\nDid you mean this?\n "; + } else { + $message .= "\n\nDid you mean one of these?\n "; + } + $message .= implode("\n ", $alternatives); + } + + throw new CommandNotFoundException($message, $alternatives); + } + + // filter out aliases for commands which are already on the list + if (count($commands) > 1) { + $commandList = $this->commands; + $commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) { + $commandName = $commandList[$nameOrAlias]->getName(); + + return $commandName === $nameOrAlias || !in_array($commandName, $commands); + }); + } + + $exact = in_array($name, $commands, true); + if (count($commands) > 1 && !$exact) { + $suggestions = $this->getAbbreviationSuggestions(array_values($commands)); + + throw new CommandNotFoundException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions), array_values($commands)); + } + + return $this->get($exact ? $name : reset($commands)); + } + + /** + * Gets the commands (registered in the given namespace if provided). + * + * The array keys are the full names and the values the command instances. + * + * @param string $namespace A namespace name + * + * @return Command[] An array of Command instances + */ + public function all($namespace = null) + { + if (null === $namespace) { + return $this->commands; + } + + $commands = array(); + foreach ($this->commands as $name => $command) { + if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) { + $commands[$name] = $command; + } + } + + return $commands; + } + + /** + * Returns an array of possible abbreviations given a set of names. + * + * @param array $names An array of names + * + * @return array An array of abbreviations + */ + public static function getAbbreviations($names) + { + $abbrevs = array(); + foreach ($names as $name) { + for ($len = strlen($name); $len > 0; --$len) { + $abbrev = substr($name, 0, $len); + $abbrevs[$abbrev][] = $name; + } + } + + return $abbrevs; + } + + /** + * Returns a text representation of the Application. + * + * @param string $namespace An optional namespace name + * @param bool $raw Whether to return raw command list + * + * @return string A string representing the Application + * + * @deprecated since version 2.3, to be removed in 3.0. + */ + public function asText($namespace = null, $raw = false) + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.3 and will be removed in 3.0.', E_USER_DEPRECATED); + + $descriptor = new TextDescriptor(); + $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, !$raw); + $descriptor->describe($output, $this, array('namespace' => $namespace, 'raw_output' => true)); + + return $output->fetch(); + } + + /** + * Returns an XML representation of the Application. + * + * @param string $namespace An optional namespace name + * @param bool $asDom Whether to return a DOM or an XML string + * + * @return string|\DOMDocument An XML string representing the Application + * + * @deprecated since version 2.3, to be removed in 3.0. + */ + public function asXml($namespace = null, $asDom = false) + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.3 and will be removed in 3.0.', E_USER_DEPRECATED); + + $descriptor = new XmlDescriptor(); + + if ($asDom) { + return $descriptor->getApplicationDocument($this, $namespace); + } + + $output = new BufferedOutput(); + $descriptor->describe($output, $this, array('namespace' => $namespace)); + + return $output->fetch(); + } + + /** + * Renders a caught exception. + * + * @param \Exception $e An exception instance + * @param OutputInterface $output An OutputInterface instance + */ + public function renderException($e, $output) + { + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + + do { + $title = sprintf(' [%s] ', get_class($e)); + + $len = $this->stringWidth($title); + + $width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX; + // HHVM only accepts 32 bits integer in str_split, even when PHP_INT_MAX is a 64 bit integer: https://github.com/facebook/hhvm/issues/1327 + if (defined('HHVM_VERSION') && $width > 1 << 31) { + $width = 1 << 31; + } + $formatter = $output->getFormatter(); + $lines = array(); + foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) { + foreach ($this->splitStringByWidth($line, $width - 4) as $line) { + // pre-format lines to get the right string length + $lineLength = $this->stringWidth(preg_replace('/\[[^m]*m/', '', $formatter->format($line))) + 4; + $lines[] = array($line, $lineLength); + + $len = max($lineLength, $len); + } + } + + $messages = array(); + $messages[] = $emptyLine = $formatter->format(sprintf('<error>%s</error>', str_repeat(' ', $len))); + $messages[] = $formatter->format(sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title))))); + foreach ($lines as $line) { + $messages[] = $formatter->format(sprintf('<error> %s %s</error>', $line[0], str_repeat(' ', $len - $line[1]))); + } + $messages[] = $emptyLine; + $messages[] = ''; + + $output->writeln($messages, OutputInterface::OUTPUT_RAW | OutputInterface::VERBOSITY_QUIET); + + if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $output->writeln('<comment>Exception trace:</comment>', OutputInterface::VERBOSITY_QUIET); + + // exception related properties + $trace = $e->getTrace(); + array_unshift($trace, array( + 'function' => '', + 'file' => $e->getFile() !== null ? $e->getFile() : 'n/a', + 'line' => $e->getLine() !== null ? $e->getLine() : 'n/a', + 'args' => array(), + )); + + for ($i = 0, $count = count($trace); $i < $count; ++$i) { + $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; + $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; + $function = $trace[$i]['function']; + $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a'; + $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a'; + + $output->writeln(sprintf(' %s%s%s() at <info>%s:%s</info>', $class, $type, $function, $file, $line), OutputInterface::VERBOSITY_QUIET); + } + + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + } + } while ($e = $e->getPrevious()); + + if (null !== $this->runningCommand) { + $output->writeln(sprintf('<info>%s</info>', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET); + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + } + } + + /** + * Tries to figure out the terminal width in which this application runs. + * + * @return int|null + */ + protected function getTerminalWidth() + { + $dimensions = $this->getTerminalDimensions(); + + return $dimensions[0]; + } + + /** + * Tries to figure out the terminal height in which this application runs. + * + * @return int|null + */ + protected function getTerminalHeight() + { + $dimensions = $this->getTerminalDimensions(); + + return $dimensions[1]; + } + + /** + * Tries to figure out the terminal dimensions based on the current environment. + * + * @return array Array containing width and height + */ + public function getTerminalDimensions() + { + if ($this->terminalDimensions) { + return $this->terminalDimensions; + } + + if ('\\' === DIRECTORY_SEPARATOR) { + // extract [w, H] from "wxh (WxH)" + if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) { + return array((int) $matches[1], (int) $matches[2]); + } + // extract [w, h] from "wxh" + if (preg_match('/^(\d+)x(\d+)$/', $this->getConsoleMode(), $matches)) { + return array((int) $matches[1], (int) $matches[2]); + } + } + + if ($sttyString = $this->getSttyColumns()) { + // extract [w, h] from "rows h; columns w;" + if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) { + return array((int) $matches[2], (int) $matches[1]); + } + // extract [w, h] from "; h rows; w columns" + if (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) { + return array((int) $matches[2], (int) $matches[1]); + } + } + + return array(null, null); + } + + /** + * Sets terminal dimensions. + * + * Can be useful to force terminal dimensions for functional tests. + * + * @param int $width The width + * @param int $height The height + * + * @return Application The current application + */ + public function setTerminalDimensions($width, $height) + { + $this->terminalDimensions = array($width, $height); + + return $this; + } + + /** + * Configures the input and output instances based on the user arguments and options. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function configureIO(InputInterface $input, OutputInterface $output) + { + if (true === $input->hasParameterOption(array('--ansi'))) { + $output->setDecorated(true); + } elseif (true === $input->hasParameterOption(array('--no-ansi'))) { + $output->setDecorated(false); + } + + if (true === $input->hasParameterOption(array('--no-interaction', '-n'))) { + $input->setInteractive(false); + } elseif (function_exists('posix_isatty') && $this->getHelperSet()->has('question')) { + $inputStream = $this->getHelperSet()->get('question')->getInputStream(); + if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) { + $input->setInteractive(false); + } + } + + if (true === $input->hasParameterOption(array('--quiet', '-q'))) { + $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); + } else { + if ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || $input->getParameterOption('--verbose') === 3) { + $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); + } elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || $input->getParameterOption('--verbose') === 2) { + $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); + } elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) { + $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + } + } + } + + /** + * Runs the current command. + * + * If an event dispatcher has been attached to the application, + * events are also dispatched during the life-cycle of the command. + * + * @param Command $command A Command instance + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return int 0 if everything went fine, or an error code + * + * @throws \Exception when the command being run threw an exception + */ + protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output) + { + foreach ($command->getHelperSet() as $helper) { + if ($helper instanceof InputAwareInterface) { + $helper->setInput($input); + } + } + + if (null === $this->dispatcher) { + return $command->run($input, $output); + } + + // bind before the console.command event, so the listeners have access to input options/arguments + try { + $command->mergeApplicationDefinition(); + $input->bind($command->getDefinition()); + } catch (ExceptionInterface $e) { + // ignore invalid options/arguments for now, to allow the event listeners to customize the InputDefinition + } + + $event = new ConsoleCommandEvent($command, $input, $output); + $this->dispatcher->dispatch(ConsoleEvents::COMMAND, $event); + + if ($event->commandShouldRun()) { + try { + $exitCode = $command->run($input, $output); + } catch (\Exception $e) { + $event = new ConsoleExceptionEvent($command, $input, $output, $e, $e->getCode()); + $this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event); + + $e = $event->getException(); + + $event = new ConsoleTerminateEvent($command, $input, $output, $e->getCode()); + $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); + + throw $e; + } + } else { + $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED; + } + + $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode); + $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); + + return $event->getExitCode(); + } + + /** + * Gets the name of the command based on input. + * + * @param InputInterface $input The input interface + * + * @return string The command name + */ + protected function getCommandName(InputInterface $input) + { + return $input->getFirstArgument(); + } + + /** + * Gets the default input definition. + * + * @return InputDefinition An InputDefinition instance + */ + protected function getDefaultInputDefinition() + { + return new InputDefinition(array( + new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), + + new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'), + new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'), + new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'), + new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'), + new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'), + new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'), + new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'), + )); + } + + /** + * Gets the default commands that should always be available. + * + * @return Command[] An array of default Command instances + */ + protected function getDefaultCommands() + { + return array(new HelpCommand(), new ListCommand()); + } + + /** + * Gets the default helper set with the helpers that should always be available. + * + * @return HelperSet A HelperSet instance + */ + protected function getDefaultHelperSet() + { + return new HelperSet(array( + new FormatterHelper(), + new DialogHelper(false), + new ProgressHelper(false), + new TableHelper(false), + new DebugFormatterHelper(), + new ProcessHelper(), + new QuestionHelper(), + )); + } + + /** + * Runs and parses stty -a if it's available, suppressing any error output. + * + * @return string + */ + private function getSttyColumns() + { + if (!function_exists('proc_open')) { + return; + } + + $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')); + $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, array('suppress_errors' => true)); + if (is_resource($process)) { + $info = stream_get_contents($pipes[1]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + return $info; + } + } + + /** + * Runs and parses mode CON if it's available, suppressing any error output. + * + * @return string <width>x<height> or null if it could not be parsed + */ + private function getConsoleMode() + { + if (!function_exists('proc_open')) { + return; + } + + $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')); + $process = proc_open('mode CON', $descriptorspec, $pipes, null, null, array('suppress_errors' => true)); + if (is_resource($process)) { + $info = stream_get_contents($pipes[1]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) { + return $matches[2].'x'.$matches[1]; + } + } + } + + /** + * Returns abbreviated suggestions in string format. + * + * @param array $abbrevs Abbreviated suggestions to convert + * + * @return string A formatted string of abbreviated suggestions + */ + private function getAbbreviationSuggestions($abbrevs) + { + return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : ''); + } + + /** + * Returns the namespace part of the command name. + * + * This method is not part of public API and should not be used directly. + * + * @param string $name The full name of the command + * @param string $limit The maximum number of parts of the namespace + * + * @return string The namespace of the command + */ + public function extractNamespace($name, $limit = null) + { + $parts = explode(':', $name); + array_pop($parts); + + return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit)); + } + + /** + * Finds alternative of $name among $collection, + * if nothing is found in $collection, try in $abbrevs. + * + * @param string $name The string + * @param array|\Traversable $collection The collection + * + * @return array A sorted array of similar string + */ + private function findAlternatives($name, $collection) + { + $threshold = 1e3; + $alternatives = array(); + + $collectionParts = array(); + foreach ($collection as $item) { + $collectionParts[$item] = explode(':', $item); + } + + foreach (explode(':', $name) as $i => $subname) { + foreach ($collectionParts as $collectionName => $parts) { + $exists = isset($alternatives[$collectionName]); + if (!isset($parts[$i]) && $exists) { + $alternatives[$collectionName] += $threshold; + continue; + } elseif (!isset($parts[$i])) { + continue; + } + + $lev = levenshtein($subname, $parts[$i]); + if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) { + $alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev; + } elseif ($exists) { + $alternatives[$collectionName] += $threshold; + } + } + } + + foreach ($collection as $item) { + $lev = levenshtein($name, $item); + if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) { + $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev; + } + } + + $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; }); + asort($alternatives); + + return array_keys($alternatives); + } + + /** + * Sets the default Command name. + * + * @param string $commandName The Command name + */ + public function setDefaultCommand($commandName) + { + $this->defaultCommand = $commandName; + } + + private function stringWidth($string) + { + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return strlen($string); + } + + return mb_strwidth($string, $encoding); + } + + private function splitStringByWidth($string, $width) + { + // str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly. + // additionally, array_slice() is not enough as some character has doubled width. + // we need a function to split string not by character count but by string width + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return str_split($string, $width); + } + + $utf8String = mb_convert_encoding($string, 'utf8', $encoding); + $lines = array(); + $line = ''; + foreach (preg_split('//u', $utf8String) as $char) { + // test if $char could be appended to current line + if (mb_strwidth($line.$char, 'utf8') <= $width) { + $line .= $char; + continue; + } + // if not, push current line to array and make new line + $lines[] = str_pad($line, $width); + $line = $char; + } + if ('' !== $line) { + $lines[] = count($lines) ? str_pad($line, $width) : $line; + } + + mb_convert_variables($encoding, 'utf8', $lines); + + return $lines; + } + + /** + * Returns all namespaces of the command name. + * + * @param string $name The full name of the command + * + * @return array The namespaces of the command + */ + private function extractAllNamespaces($name) + { + // -1 as third argument is needed to skip the command short name when exploding + $parts = explode(':', $name, -1); + $namespaces = array(); + + foreach ($parts as $part) { + if (count($namespaces)) { + $namespaces[] = end($namespaces).':'.$part; + } else { + $namespaces[] = $part; + } + } + + return $namespaces; + } +} diff --git a/vendor/symfony/console/CHANGELOG.md b/vendor/symfony/console/CHANGELOG.md new file mode 100644 index 00000000..8021068e --- /dev/null +++ b/vendor/symfony/console/CHANGELOG.md @@ -0,0 +1,73 @@ +CHANGELOG +========= + +2.8.3 +----- + + * remove readline support from the question helper as it caused issues + +2.8.0 +----- + + * use readline for user input in the question helper when available to allow + the use of arrow keys + +2.6.0 +----- + + * added a Process helper + * added a DebugFormatter helper + +2.5.0 +----- + + * deprecated the dialog helper (use the question helper instead) + * deprecated TableHelper in favor of Table + * deprecated ProgressHelper in favor of ProgressBar + * added ConsoleLogger + * added a question helper + * added a way to set the process name of a command + * added a way to set a default command instead of `ListCommand` + +2.4.0 +----- + + * added a way to force terminal dimensions + * added a convenient method to detect verbosity level + * [BC BREAK] made descriptors use output instead of returning a string + +2.3.0 +----- + + * added multiselect support to the select dialog helper + * added Table Helper for tabular data rendering + * added support for events in `Application` + * added a way to normalize EOLs in `ApplicationTester::getDisplay()` and `CommandTester::getDisplay()` + * added a way to set the progress bar progress via the `setCurrent` method + * added support for multiple InputOption shortcuts, written as `'-a|-b|-c'` + * added two additional verbosity levels, VERBOSITY_VERY_VERBOSE and VERBOSITY_DEBUG + +2.2.0 +----- + + * added support for colorization on Windows via ConEmu + * add a method to Dialog Helper to ask for a question and hide the response + * added support for interactive selections in console (DialogHelper::select()) + * added support for autocompletion as you type in Dialog Helper + +2.1.0 +----- + + * added ConsoleOutputInterface + * added the possibility to disable a command (Command::isEnabled()) + * added suggestions when a command does not exist + * added a --raw option to the list command + * added support for STDERR in the console output class (errors are now sent + to STDERR) + * made the defaults (helper set, commands, input definition) in Application + more easily customizable + * added support for the shell even if readline is not available + * added support for process isolation in Symfony shell via + `--process-isolation` switch + * added support for `--`, which disables options parsing after that point + (tokens will be parsed as arguments) diff --git a/vendor/symfony/console/Command/Command.php b/vendor/symfony/console/Command/Command.php new file mode 100644 index 00000000..6acbe219 --- /dev/null +++ b/vendor/symfony/console/Command/Command.php @@ -0,0 +1,681 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Descriptor\TextDescriptor; +use Symfony\Component\Console\Descriptor\XmlDescriptor; +use Symfony\Component\Console\Exception\ExceptionInterface; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Base class for all commands. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class Command +{ + private $application; + private $name; + private $processTitle; + private $aliases = array(); + private $definition; + private $help; + private $description; + private $ignoreValidationErrors = false; + private $applicationDefinitionMerged = false; + private $applicationDefinitionMergedWithArgs = false; + private $code; + private $synopsis = array(); + private $usages = array(); + private $helperSet; + + /** + * Constructor. + * + * @param string|null $name The name of the command; passing null means it must be set in configure() + * + * @throws LogicException When the command name is empty + */ + public function __construct($name = null) + { + $this->definition = new InputDefinition(); + + if (null !== $name) { + $this->setName($name); + } + + $this->configure(); + + if (!$this->name) { + throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($this))); + } + } + + /** + * Ignores validation errors. + * + * This is mainly useful for the help command. + */ + public function ignoreValidationErrors() + { + $this->ignoreValidationErrors = true; + } + + /** + * Sets the application instance for this command. + * + * @param Application $application An Application instance + */ + public function setApplication(Application $application = null) + { + $this->application = $application; + if ($application) { + $this->setHelperSet($application->getHelperSet()); + } else { + $this->helperSet = null; + } + } + + /** + * Sets the helper set. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Gets the helper set. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Gets the application instance for this command. + * + * @return Application An Application instance + */ + public function getApplication() + { + return $this->application; + } + + /** + * Checks whether the command is enabled or not in the current environment. + * + * Override this to check for x or y and return false if the command can not + * run properly under the current conditions. + * + * @return bool + */ + public function isEnabled() + { + return true; + } + + /** + * Configures the current command. + */ + protected function configure() + { + } + + /** + * Executes the current command. + * + * This method is not abstract because you can use this class + * as a concrete class. In this case, instead of defining the + * execute() method, you set the code to execute by passing + * a Closure to the setCode() method. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @return null|int null or 0 if everything went fine, or an error code + * + * @throws LogicException When this abstract method is not implemented + * + * @see setCode() + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + throw new LogicException('You must override the execute() method in the concrete command class.'); + } + + /** + * Interacts with the user. + * + * This method is executed before the InputDefinition is validated. + * This means that this is the only place where the command can + * interactively ask for values of missing required arguments. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function interact(InputInterface $input, OutputInterface $output) + { + } + + /** + * Initializes the command just after the input has been validated. + * + * This is mainly useful when a lot of commands extends one main command + * where some things need to be initialized based on the input arguments and options. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function initialize(InputInterface $input, OutputInterface $output) + { + } + + /** + * Runs the command. + * + * The code to execute is either defined directly with the + * setCode() method or by overriding the execute() method + * in a sub-class. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @return int The command exit code + * + * @throws \Exception + * + * @see setCode() + * @see execute() + */ + public function run(InputInterface $input, OutputInterface $output) + { + // force the creation of the synopsis before the merge with the app definition + $this->getSynopsis(true); + $this->getSynopsis(false); + + // add the application arguments and options + $this->mergeApplicationDefinition(); + + // bind the input against the command specific arguments/options + try { + $input->bind($this->definition); + } catch (ExceptionInterface $e) { + if (!$this->ignoreValidationErrors) { + throw $e; + } + } + + $this->initialize($input, $output); + + if (null !== $this->processTitle) { + if (function_exists('cli_set_process_title')) { + cli_set_process_title($this->processTitle); + } elseif (function_exists('setproctitle')) { + setproctitle($this->processTitle); + } elseif (OutputInterface::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) { + $output->writeln('<comment>Install the proctitle PECL to be able to change the process title.</comment>'); + } + } + + if ($input->isInteractive()) { + $this->interact($input, $output); + } + + // The command name argument is often omitted when a command is executed directly with its run() method. + // It would fail the validation if we didn't make sure the command argument is present, + // since it's required by the application. + if ($input->hasArgument('command') && null === $input->getArgument('command')) { + $input->setArgument('command', $this->getName()); + } + + $input->validate(); + + if ($this->code) { + $statusCode = call_user_func($this->code, $input, $output); + } else { + $statusCode = $this->execute($input, $output); + } + + return is_numeric($statusCode) ? (int) $statusCode : 0; + } + + /** + * Sets the code to execute when running this command. + * + * If this method is used, it overrides the code defined + * in the execute() method. + * + * @param callable $code A callable(InputInterface $input, OutputInterface $output) + * + * @return Command The current instance + * + * @throws InvalidArgumentException + * + * @see execute() + */ + public function setCode($code) + { + if (!is_callable($code)) { + throw new InvalidArgumentException('Invalid callable provided to Command::setCode.'); + } + + if (PHP_VERSION_ID >= 50400 && $code instanceof \Closure) { + $r = new \ReflectionFunction($code); + if (null === $r->getClosureThis()) { + $code = \Closure::bind($code, $this); + } + } + + $this->code = $code; + + return $this; + } + + /** + * Merges the application definition with the command definition. + * + * This method is not part of public API and should not be used directly. + * + * @param bool $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments + */ + public function mergeApplicationDefinition($mergeArgs = true) + { + if (null === $this->application || (true === $this->applicationDefinitionMerged && ($this->applicationDefinitionMergedWithArgs || !$mergeArgs))) { + return; + } + + $this->definition->addOptions($this->application->getDefinition()->getOptions()); + + if ($mergeArgs) { + $currentArguments = $this->definition->getArguments(); + $this->definition->setArguments($this->application->getDefinition()->getArguments()); + $this->definition->addArguments($currentArguments); + } + + $this->applicationDefinitionMerged = true; + if ($mergeArgs) { + $this->applicationDefinitionMergedWithArgs = true; + } + } + + /** + * Sets an array of argument and option instances. + * + * @param array|InputDefinition $definition An array of argument and option instances or a definition instance + * + * @return Command The current instance + */ + public function setDefinition($definition) + { + if ($definition instanceof InputDefinition) { + $this->definition = $definition; + } else { + $this->definition->setDefinition($definition); + } + + $this->applicationDefinitionMerged = false; + + return $this; + } + + /** + * Gets the InputDefinition attached to this Command. + * + * @return InputDefinition An InputDefinition instance + */ + public function getDefinition() + { + return $this->definition; + } + + /** + * Gets the InputDefinition to be used to create XML and Text representations of this Command. + * + * Can be overridden to provide the original command representation when it would otherwise + * be changed by merging with the application InputDefinition. + * + * This method is not part of public API and should not be used directly. + * + * @return InputDefinition An InputDefinition instance + */ + public function getNativeDefinition() + { + return $this->getDefinition(); + } + + /** + * Adds an argument. + * + * @param string $name The argument name + * @param int $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL + * @param string $description A description text + * @param mixed $default The default value (for InputArgument::OPTIONAL mode only) + * + * @return Command The current instance + */ + public function addArgument($name, $mode = null, $description = '', $default = null) + { + $this->definition->addArgument(new InputArgument($name, $mode, $description, $default)); + + return $this; + } + + /** + * Adds an option. + * + * @param string $name The option name + * @param string $shortcut The shortcut (can be null) + * @param int $mode The option mode: One of the InputOption::VALUE_* constants + * @param string $description A description text + * @param mixed $default The default value (must be null for InputOption::VALUE_NONE) + * + * @return Command The current instance + */ + public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default)); + + return $this; + } + + /** + * Sets the name of the command. + * + * This method can set both the namespace and the name if + * you separate them by a colon (:) + * + * $command->setName('foo:bar'); + * + * @param string $name The command name + * + * @return Command The current instance + * + * @throws InvalidArgumentException When the name is invalid + */ + public function setName($name) + { + $this->validateName($name); + + $this->name = $name; + + return $this; + } + + /** + * Sets the process title of the command. + * + * This feature should be used only when creating a long process command, + * like a daemon. + * + * PHP 5.5+ or the proctitle PECL library is required + * + * @param string $title The process title + * + * @return Command The current instance + */ + public function setProcessTitle($title) + { + $this->processTitle = $title; + + return $this; + } + + /** + * Returns the command name. + * + * @return string The command name + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the description for the command. + * + * @param string $description The description for the command + * + * @return Command The current instance + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * Returns the description for the command. + * + * @return string The description for the command + */ + public function getDescription() + { + return $this->description; + } + + /** + * Sets the help for the command. + * + * @param string $help The help for the command + * + * @return Command The current instance + */ + public function setHelp($help) + { + $this->help = $help; + + return $this; + } + + /** + * Returns the help for the command. + * + * @return string The help for the command + */ + public function getHelp() + { + return $this->help; + } + + /** + * Returns the processed help for the command replacing the %command.name% and + * %command.full_name% patterns with the real values dynamically. + * + * @return string The processed help for the command + */ + public function getProcessedHelp() + { + $name = $this->name; + + $placeholders = array( + '%command.name%', + '%command.full_name%', + ); + $replacements = array( + $name, + $_SERVER['PHP_SELF'].' '.$name, + ); + + return str_replace($placeholders, $replacements, $this->getHelp() ?: $this->getDescription()); + } + + /** + * Sets the aliases for the command. + * + * @param string[] $aliases An array of aliases for the command + * + * @return Command The current instance + * + * @throws InvalidArgumentException When an alias is invalid + */ + public function setAliases($aliases) + { + if (!is_array($aliases) && !$aliases instanceof \Traversable) { + throw new InvalidArgumentException('$aliases must be an array or an instance of \Traversable'); + } + + foreach ($aliases as $alias) { + $this->validateName($alias); + } + + $this->aliases = $aliases; + + return $this; + } + + /** + * Returns the aliases for the command. + * + * @return array An array of aliases for the command + */ + public function getAliases() + { + return $this->aliases; + } + + /** + * Returns the synopsis for the command. + * + * @param bool $short Whether to show the short version of the synopsis (with options folded) or not + * + * @return string The synopsis + */ + public function getSynopsis($short = false) + { + $key = $short ? 'short' : 'long'; + + if (!isset($this->synopsis[$key])) { + $this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short))); + } + + return $this->synopsis[$key]; + } + + /** + * Add a command usage example. + * + * @param string $usage The usage, it'll be prefixed with the command name + */ + public function addUsage($usage) + { + if (0 !== strpos($usage, $this->name)) { + $usage = sprintf('%s %s', $this->name, $usage); + } + + $this->usages[] = $usage; + + return $this; + } + + /** + * Returns alternative usages of the command. + * + * @return array + */ + public function getUsages() + { + return $this->usages; + } + + /** + * Gets a helper instance by name. + * + * @param string $name The helper name + * + * @return mixed The helper value + * + * @throws LogicException if no HelperSet is defined + * @throws InvalidArgumentException if the helper is not defined + */ + public function getHelper($name) + { + if (null === $this->helperSet) { + throw new LogicException(sprintf('Cannot retrieve helper "%s" because there is no HelperSet defined. Did you forget to add your command to the application or to set the application on the command using the setApplication() method? You can also set the HelperSet directly using the setHelperSet() method.', $name)); + } + + return $this->helperSet->get($name); + } + + /** + * Returns a text representation of the command. + * + * @return string A string representing the command + * + * @deprecated since version 2.3, to be removed in 3.0. + */ + public function asText() + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.3 and will be removed in 3.0.', E_USER_DEPRECATED); + + $descriptor = new TextDescriptor(); + $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); + $descriptor->describe($output, $this, array('raw_output' => true)); + + return $output->fetch(); + } + + /** + * Returns an XML representation of the command. + * + * @param bool $asDom Whether to return a DOM or an XML string + * + * @return string|\DOMDocument An XML string representing the command + * + * @deprecated since version 2.3, to be removed in 3.0. + */ + public function asXml($asDom = false) + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.3 and will be removed in 3.0.', E_USER_DEPRECATED); + + $descriptor = new XmlDescriptor(); + + if ($asDom) { + return $descriptor->getCommandDocument($this); + } + + $output = new BufferedOutput(); + $descriptor->describe($output, $this); + + return $output->fetch(); + } + + /** + * Validates a command name. + * + * It must be non-empty and parts can optionally be separated by ":". + * + * @param string $name + * + * @throws InvalidArgumentException When the name is invalid + */ + private function validateName($name) + { + if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) { + throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name)); + } + } +} diff --git a/vendor/symfony/console/Command/HelpCommand.php b/vendor/symfony/console/Command/HelpCommand.php new file mode 100644 index 00000000..c0e7b388 --- /dev/null +++ b/vendor/symfony/console/Command/HelpCommand.php @@ -0,0 +1,93 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Helper\DescriptorHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * HelpCommand displays the help for a given command. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class HelpCommand extends Command +{ + private $command; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->ignoreValidationErrors(); + + $this + ->setName('help') + ->setDefinition(array( + new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'), + new InputOption('xml', null, InputOption::VALUE_NONE, 'To output help as XML'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'), + )) + ->setDescription('Displays help for a command') + ->setHelp(<<<'EOF' +The <info>%command.name%</info> command displays help for a given command: + + <info>php %command.full_name% list</info> + +You can also output the help in other formats by using the <comment>--format</comment> option: + + <info>php %command.full_name% --format=xml list</info> + +To display the list of available commands, please use the <info>list</info> command. +EOF + ) + ; + } + + /** + * Sets the command. + * + * @param Command $command The command to set + */ + public function setCommand(Command $command) + { + $this->command = $command; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + if (null === $this->command) { + $this->command = $this->getApplication()->find($input->getArgument('command_name')); + } + + if ($input->getOption('xml')) { + @trigger_error('The --xml option was deprecated in version 2.7 and will be removed in version 3.0. Use the --format option instead.', E_USER_DEPRECATED); + + $input->setOption('format', 'xml'); + } + + $helper = new DescriptorHelper(); + $helper->describe($output, $this->command, array( + 'format' => $input->getOption('format'), + 'raw_text' => $input->getOption('raw'), + )); + + $this->command = null; + } +} diff --git a/vendor/symfony/console/Command/ListCommand.php b/vendor/symfony/console/Command/ListCommand.php new file mode 100644 index 00000000..5e1b926a --- /dev/null +++ b/vendor/symfony/console/Command/ListCommand.php @@ -0,0 +1,97 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Helper\DescriptorHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputDefinition; + +/** + * ListCommand displays the list of all available commands for the application. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class ListCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('list') + ->setDefinition($this->createDefinition()) + ->setDescription('Lists commands') + ->setHelp(<<<'EOF' +The <info>%command.name%</info> command lists all commands: + + <info>php %command.full_name%</info> + +You can also display the commands for a specific namespace: + + <info>php %command.full_name% test</info> + +You can also output the information in other formats by using the <comment>--format</comment> option: + + <info>php %command.full_name% --format=xml</info> + +It's also possible to get raw list of commands (useful for embedding command runner): + + <info>php %command.full_name% --raw</info> +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + public function getNativeDefinition() + { + return $this->createDefinition(); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + if ($input->getOption('xml')) { + @trigger_error('The --xml option was deprecated in version 2.7 and will be removed in version 3.0. Use the --format option instead.', E_USER_DEPRECATED); + + $input->setOption('format', 'xml'); + } + + $helper = new DescriptorHelper(); + $helper->describe($output, $this->getApplication(), array( + 'format' => $input->getOption('format'), + 'raw_text' => $input->getOption('raw'), + 'namespace' => $input->getArgument('namespace'), + )); + } + + /** + * {@inheritdoc} + */ + private function createDefinition() + { + return new InputDefinition(array( + new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'), + new InputOption('xml', null, InputOption::VALUE_NONE, 'To output list as XML'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + )); + } +} diff --git a/vendor/symfony/console/ConsoleEvents.php b/vendor/symfony/console/ConsoleEvents.php new file mode 100644 index 00000000..1ed41b7d --- /dev/null +++ b/vendor/symfony/console/ConsoleEvents.php @@ -0,0 +1,61 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +/** + * Contains all events dispatched by an Application. + * + * @author Francesco Levorato <git@flevour.net> + */ +final class ConsoleEvents +{ + /** + * The COMMAND event allows you to attach listeners before any command is + * executed by the console. It also allows you to modify the command, input and output + * before they are handled to the command. + * + * The event listener method receives a Symfony\Component\Console\Event\ConsoleCommandEvent + * instance. + * + * @Event + * + * @var string + */ + const COMMAND = 'console.command'; + + /** + * The TERMINATE event allows you to attach listeners after a command is + * executed by the console. + * + * The event listener method receives a Symfony\Component\Console\Event\ConsoleTerminateEvent + * instance. + * + * @Event + * + * @var string + */ + const TERMINATE = 'console.terminate'; + + /** + * The EXCEPTION event occurs when an uncaught exception appears. + * + * This event allows you to deal with the exception or + * to modify the thrown exception. The event listener method receives + * a Symfony\Component\Console\Event\ConsoleExceptionEvent + * instance. + * + * @Event + * + * @var string + */ + const EXCEPTION = 'console.exception'; +} diff --git a/vendor/symfony/console/Descriptor/ApplicationDescription.php b/vendor/symfony/console/Descriptor/ApplicationDescription.php new file mode 100644 index 00000000..89961b9c --- /dev/null +++ b/vendor/symfony/console/Descriptor/ApplicationDescription.php @@ -0,0 +1,160 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\CommandNotFoundException; + +/** + * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> + * + * @internal + */ +class ApplicationDescription +{ + const GLOBAL_NAMESPACE = '_global'; + + /** + * @var Application + */ + private $application; + + /** + * @var null|string + */ + private $namespace; + + /** + * @var array + */ + private $namespaces; + + /** + * @var Command[] + */ + private $commands; + + /** + * @var Command[] + */ + private $aliases; + + /** + * Constructor. + * + * @param Application $application + * @param string|null $namespace + */ + public function __construct(Application $application, $namespace = null) + { + $this->application = $application; + $this->namespace = $namespace; + } + + /** + * @return array + */ + public function getNamespaces() + { + if (null === $this->namespaces) { + $this->inspectApplication(); + } + + return $this->namespaces; + } + + /** + * @return Command[] + */ + public function getCommands() + { + if (null === $this->commands) { + $this->inspectApplication(); + } + + return $this->commands; + } + + /** + * @param string $name + * + * @return Command + * + * @throws CommandNotFoundException + */ + public function getCommand($name) + { + if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) { + throw new CommandNotFoundException(sprintf('Command %s does not exist.', $name)); + } + + return isset($this->commands[$name]) ? $this->commands[$name] : $this->aliases[$name]; + } + + private function inspectApplication() + { + $this->commands = array(); + $this->namespaces = array(); + + $all = $this->application->all($this->namespace ? $this->application->findNamespace($this->namespace) : null); + foreach ($this->sortCommands($all) as $namespace => $commands) { + $names = array(); + + /** @var Command $command */ + foreach ($commands as $name => $command) { + if (!$command->getName()) { + continue; + } + + if ($command->getName() === $name) { + $this->commands[$name] = $command; + } else { + $this->aliases[$name] = $command; + } + + $names[] = $name; + } + + $this->namespaces[$namespace] = array('id' => $namespace, 'commands' => $names); + } + } + + /** + * @param array $commands + * + * @return array + */ + private function sortCommands(array $commands) + { + $namespacedCommands = array(); + $globalCommands = array(); + foreach ($commands as $name => $command) { + $key = $this->application->extractNamespace($name, 1); + if (!$key) { + $globalCommands['_global'][$name] = $command; + } else { + $namespacedCommands[$key][$name] = $command; + } + } + ksort($namespacedCommands); + $namespacedCommands = array_merge($globalCommands, $namespacedCommands); + + foreach ($namespacedCommands as &$commandsSet) { + ksort($commandsSet); + } + // unset reference to keep scope clear + unset($commandsSet); + + return $namespacedCommands; + } +} diff --git a/vendor/symfony/console/Descriptor/Descriptor.php b/vendor/symfony/console/Descriptor/Descriptor.php new file mode 100644 index 00000000..43a7a0a1 --- /dev/null +++ b/vendor/symfony/console/Descriptor/Descriptor.php @@ -0,0 +1,122 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> + * + * @internal + */ +abstract class Descriptor implements DescriptorInterface +{ + /** + * @var OutputInterface + */ + private $output; + + /** + * {@inheritdoc} + */ + public function describe(OutputInterface $output, $object, array $options = array()) + { + $this->output = $output; + + switch (true) { + case $object instanceof InputArgument: + $this->describeInputArgument($object, $options); + break; + case $object instanceof InputOption: + $this->describeInputOption($object, $options); + break; + case $object instanceof InputDefinition: + $this->describeInputDefinition($object, $options); + break; + case $object instanceof Command: + $this->describeCommand($object, $options); + break; + case $object instanceof Application: + $this->describeApplication($object, $options); + break; + default: + throw new InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object))); + } + } + + /** + * Writes content to output. + * + * @param string $content + * @param bool $decorated + */ + protected function write($content, $decorated = false) + { + $this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW); + } + + /** + * Describes an InputArgument instance. + * + * @param InputArgument $argument + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeInputArgument(InputArgument $argument, array $options = array()); + + /** + * Describes an InputOption instance. + * + * @param InputOption $option + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeInputOption(InputOption $option, array $options = array()); + + /** + * Describes an InputDefinition instance. + * + * @param InputDefinition $definition + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeInputDefinition(InputDefinition $definition, array $options = array()); + + /** + * Describes a Command instance. + * + * @param Command $command + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeCommand(Command $command, array $options = array()); + + /** + * Describes an Application instance. + * + * @param Application $application + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeApplication(Application $application, array $options = array()); +} diff --git a/vendor/symfony/console/Descriptor/DescriptorInterface.php b/vendor/symfony/console/Descriptor/DescriptorInterface.php new file mode 100644 index 00000000..3929b6d9 --- /dev/null +++ b/vendor/symfony/console/Descriptor/DescriptorInterface.php @@ -0,0 +1,31 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Descriptor interface. + * + * @author Jean-François Simon <contact@jfsimon.fr> + */ +interface DescriptorInterface +{ + /** + * Describes an InputArgument instance. + * + * @param OutputInterface $output + * @param object $object + * @param array $options + */ + public function describe(OutputInterface $output, $object, array $options = array()); +} diff --git a/vendor/symfony/console/Descriptor/JsonDescriptor.php b/vendor/symfony/console/Descriptor/JsonDescriptor.php new file mode 100644 index 00000000..87e38fdb --- /dev/null +++ b/vendor/symfony/console/Descriptor/JsonDescriptor.php @@ -0,0 +1,166 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * JSON descriptor. + * + * @author Jean-François Simon <contact@jfsimon.fr> + * + * @internal + */ +class JsonDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + $this->writeData($this->getInputArgumentData($argument), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + $this->writeData($this->getInputOptionData($option), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + $this->writeData($this->getInputDefinitionData($definition), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $this->writeData($this->getCommandData($command), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + $commands = array(); + + foreach ($description->getCommands() as $command) { + $commands[] = $this->getCommandData($command); + } + + $data = $describedNamespace + ? array('commands' => $commands, 'namespace' => $describedNamespace) + : array('commands' => $commands, 'namespaces' => array_values($description->getNamespaces())); + + $this->writeData($data, $options); + } + + /** + * Writes data as json. + * + * @param array $data + * @param array $options + * + * @return array|string + */ + private function writeData(array $data, array $options) + { + $this->write(json_encode($data, isset($options['json_encoding']) ? $options['json_encoding'] : 0)); + } + + /** + * @param InputArgument $argument + * + * @return array + */ + private function getInputArgumentData(InputArgument $argument) + { + return array( + 'name' => $argument->getName(), + 'is_required' => $argument->isRequired(), + 'is_array' => $argument->isArray(), + 'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $argument->getDescription()), + 'default' => $argument->getDefault(), + ); + } + + /** + * @param InputOption $option + * + * @return array + */ + private function getInputOptionData(InputOption $option) + { + return array( + 'name' => '--'.$option->getName(), + 'shortcut' => $option->getShortcut() ? '-'.implode('|-', explode('|', $option->getShortcut())) : '', + 'accept_value' => $option->acceptValue(), + 'is_value_required' => $option->isValueRequired(), + 'is_multiple' => $option->isArray(), + 'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $option->getDescription()), + 'default' => $option->getDefault(), + ); + } + + /** + * @param InputDefinition $definition + * + * @return array + */ + private function getInputDefinitionData(InputDefinition $definition) + { + $inputArguments = array(); + foreach ($definition->getArguments() as $name => $argument) { + $inputArguments[$name] = $this->getInputArgumentData($argument); + } + + $inputOptions = array(); + foreach ($definition->getOptions() as $name => $option) { + $inputOptions[$name] = $this->getInputOptionData($option); + } + + return array('arguments' => $inputArguments, 'options' => $inputOptions); + } + + /** + * @param Command $command + * + * @return array + */ + private function getCommandData(Command $command) + { + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + return array( + 'name' => $command->getName(), + 'usage' => array_merge(array($command->getSynopsis()), $command->getUsages(), $command->getAliases()), + 'description' => $command->getDescription(), + 'help' => $command->getProcessedHelp(), + 'definition' => $this->getInputDefinitionData($command->getNativeDefinition()), + ); + } +} diff --git a/vendor/symfony/console/Descriptor/MarkdownDescriptor.php b/vendor/symfony/console/Descriptor/MarkdownDescriptor.php new file mode 100644 index 00000000..d3d76a42 --- /dev/null +++ b/vendor/symfony/console/Descriptor/MarkdownDescriptor.php @@ -0,0 +1,143 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * Markdown descriptor. + * + * @author Jean-François Simon <contact@jfsimon.fr> + * + * @internal + */ +class MarkdownDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + $this->write( + '**'.$argument->getName().':**'."\n\n" + .'* Name: '.($argument->getName() ?: '<none>')."\n" + .'* Is required: '.($argument->isRequired() ? 'yes' : 'no')."\n" + .'* Is array: '.($argument->isArray() ? 'yes' : 'no')."\n" + .'* Description: '.preg_replace('/\s*[\r\n]\s*/', "\n ", $argument->getDescription() ?: '<none>')."\n" + .'* Default: `'.str_replace("\n", '', var_export($argument->getDefault(), true)).'`' + ); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + $this->write( + '**'.$option->getName().':**'."\n\n" + .'* Name: `--'.$option->getName().'`'."\n" + .'* Shortcut: '.($option->getShortcut() ? '`-'.implode('|-', explode('|', $option->getShortcut())).'`' : '<none>')."\n" + .'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n" + .'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n" + .'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n" + .'* Description: '.preg_replace('/\s*[\r\n]\s*/', "\n ", $option->getDescription() ?: '<none>')."\n" + .'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`' + ); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + if ($showArguments = count($definition->getArguments()) > 0) { + $this->write('### Arguments:'); + foreach ($definition->getArguments() as $argument) { + $this->write("\n\n"); + $this->write($this->describeInputArgument($argument)); + } + } + + if (count($definition->getOptions()) > 0) { + if ($showArguments) { + $this->write("\n\n"); + } + + $this->write('### Options:'); + foreach ($definition->getOptions() as $option) { + $this->write("\n\n"); + $this->write($this->describeInputOption($option)); + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + $this->write( + $command->getName()."\n" + .str_repeat('-', strlen($command->getName()))."\n\n" + .'* Description: '.($command->getDescription() ?: '<none>')."\n" + .'* Usage:'."\n\n" + .array_reduce(array_merge(array($command->getSynopsis()), $command->getAliases(), $command->getUsages()), function ($carry, $usage) { + return $carry .= ' * `'.$usage.'`'."\n"; + }) + ); + + if ($help = $command->getProcessedHelp()) { + $this->write("\n"); + $this->write($help); + } + + if ($command->getNativeDefinition()) { + $this->write("\n\n"); + $this->describeInputDefinition($command->getNativeDefinition()); + } + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + + $this->write($application->getName()."\n".str_repeat('=', strlen($application->getName()))); + + foreach ($description->getNamespaces() as $namespace) { + if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) { + $this->write("\n\n"); + $this->write('**'.$namespace['id'].':**'); + } + + $this->write("\n\n"); + $this->write(implode("\n", array_map(function ($commandName) { + return '* '.$commandName; + }, $namespace['commands']))); + } + + foreach ($description->getCommands() as $command) { + $this->write("\n\n"); + $this->write($this->describeCommand($command)); + } + } +} diff --git a/vendor/symfony/console/Descriptor/TextDescriptor.php b/vendor/symfony/console/Descriptor/TextDescriptor.php new file mode 100644 index 00000000..64b53971 --- /dev/null +++ b/vendor/symfony/console/Descriptor/TextDescriptor.php @@ -0,0 +1,287 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * Text descriptor. + * + * @author Jean-François Simon <contact@jfsimon.fr> + * + * @internal + */ +class TextDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + if (null !== $argument->getDefault() && (!is_array($argument->getDefault()) || count($argument->getDefault()))) { + $default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($argument->getDefault())); + } else { + $default = ''; + } + + $totalWidth = isset($options['total_width']) ? $options['total_width'] : strlen($argument->getName()); + $spacingWidth = $totalWidth - strlen($argument->getName()) + 2; + + $this->writeText(sprintf(' <info>%s</info>%s%s%s', + $argument->getName(), + str_repeat(' ', $spacingWidth), + // + 17 = 2 spaces + <info> + </info> + 2 spaces + preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 17), $argument->getDescription()), + $default + ), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + if ($option->acceptValue() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault()))) { + $default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($option->getDefault())); + } else { + $default = ''; + } + + $value = ''; + if ($option->acceptValue()) { + $value = '='.strtoupper($option->getName()); + + if ($option->isValueOptional()) { + $value = '['.$value.']'; + } + } + + $totalWidth = isset($options['total_width']) ? $options['total_width'] : $this->calculateTotalWidthForOptions(array($option)); + $synopsis = sprintf('%s%s', + $option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : ' ', + sprintf('--%s%s', $option->getName(), $value) + ); + + $spacingWidth = $totalWidth - strlen($synopsis) + 2; + + $this->writeText(sprintf(' <info>%s</info>%s%s%s%s', + $synopsis, + str_repeat(' ', $spacingWidth), + // + 17 = 2 spaces + <info> + </info> + 2 spaces + preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 17), $option->getDescription()), + $default, + $option->isArray() ? '<comment> (multiple values allowed)</comment>' : '' + ), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + $totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions()); + foreach ($definition->getArguments() as $argument) { + $totalWidth = max($totalWidth, strlen($argument->getName())); + } + + if ($definition->getArguments()) { + $this->writeText('<comment>Arguments:</comment>', $options); + $this->writeText("\n"); + foreach ($definition->getArguments() as $argument) { + $this->describeInputArgument($argument, array_merge($options, array('total_width' => $totalWidth))); + $this->writeText("\n"); + } + } + + if ($definition->getArguments() && $definition->getOptions()) { + $this->writeText("\n"); + } + + if ($definition->getOptions()) { + $laterOptions = array(); + + $this->writeText('<comment>Options:</comment>', $options); + foreach ($definition->getOptions() as $option) { + if (strlen($option->getShortcut()) > 1) { + $laterOptions[] = $option; + continue; + } + $this->writeText("\n"); + $this->describeInputOption($option, array_merge($options, array('total_width' => $totalWidth))); + } + foreach ($laterOptions as $option) { + $this->writeText("\n"); + $this->describeInputOption($option, array_merge($options, array('total_width' => $totalWidth))); + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $command->getSynopsis(true); + $command->getSynopsis(false); + $command->mergeApplicationDefinition(false); + + $this->writeText('<comment>Usage:</comment>', $options); + foreach (array_merge(array($command->getSynopsis(true)), $command->getAliases(), $command->getUsages()) as $usage) { + $this->writeText("\n"); + $this->writeText(' '.$usage, $options); + } + $this->writeText("\n"); + + $definition = $command->getNativeDefinition(); + if ($definition->getOptions() || $definition->getArguments()) { + $this->writeText("\n"); + $this->describeInputDefinition($definition, $options); + $this->writeText("\n"); + } + + if ($help = $command->getProcessedHelp()) { + $this->writeText("\n"); + $this->writeText('<comment>Help:</comment>', $options); + $this->writeText("\n"); + $this->writeText(' '.str_replace("\n", "\n ", $help), $options); + $this->writeText("\n"); + } + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + + if (isset($options['raw_text']) && $options['raw_text']) { + $width = $this->getColumnWidth($description->getCommands()); + + foreach ($description->getCommands() as $command) { + $this->writeText(sprintf("%-{$width}s %s", $command->getName(), $command->getDescription()), $options); + $this->writeText("\n"); + } + } else { + if ('' != $help = $application->getHelp()) { + $this->writeText("$help\n\n", $options); + } + + $this->writeText("<comment>Usage:</comment>\n", $options); + $this->writeText(" command [options] [arguments]\n\n", $options); + + $this->describeInputDefinition(new InputDefinition($application->getDefinition()->getOptions()), $options); + + $this->writeText("\n"); + $this->writeText("\n"); + + $width = $this->getColumnWidth($description->getCommands()); + + if ($describedNamespace) { + $this->writeText(sprintf('<comment>Available commands for the "%s" namespace:</comment>', $describedNamespace), $options); + } else { + $this->writeText('<comment>Available commands:</comment>', $options); + } + + // add commands by namespace + foreach ($description->getNamespaces() as $namespace) { + if (!$describedNamespace && ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) { + $this->writeText("\n"); + $this->writeText(' <comment>'.$namespace['id'].'</comment>', $options); + } + + foreach ($namespace['commands'] as $name) { + $this->writeText("\n"); + $spacingWidth = $width - strlen($name); + $this->writeText(sprintf(' <info>%s</info>%s%s', $name, str_repeat(' ', $spacingWidth), $description->getCommand($name)->getDescription()), $options); + } + } + + $this->writeText("\n"); + } + } + + /** + * {@inheritdoc} + */ + private function writeText($content, array $options = array()) + { + $this->write( + isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content, + isset($options['raw_output']) ? !$options['raw_output'] : true + ); + } + + /** + * Formats input option/argument default value. + * + * @param mixed $default + * + * @return string + */ + private function formatDefaultValue($default) + { + if (PHP_VERSION_ID < 50400) { + return str_replace(array('\/', '\\\\'), array('/', '\\'), json_encode($default)); + } + + return str_replace('\\\\', '\\', json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); + } + + /** + * @param Command[] $commands + * + * @return int + */ + private function getColumnWidth(array $commands) + { + $widths = array(); + + foreach ($commands as $command) { + $widths[] = strlen($command->getName()); + foreach ($command->getAliases() as $alias) { + $widths[] = strlen($alias); + } + } + + return max($widths) + 2; + } + + /** + * @param InputOption[] $options + * + * @return int + */ + private function calculateTotalWidthForOptions($options) + { + $totalWidth = 0; + foreach ($options as $option) { + // "-" + shortcut + ", --" + name + $nameLength = 1 + max(strlen($option->getShortcut()), 1) + 4 + strlen($option->getName()); + + if ($option->acceptValue()) { + $valueLength = 1 + strlen($option->getName()); // = + value + $valueLength += $option->isValueOptional() ? 2 : 0; // [ + ] + + $nameLength += $valueLength; + } + $totalWidth = max($totalWidth, $nameLength); + } + + return $totalWidth; + } +} diff --git a/vendor/symfony/console/Descriptor/XmlDescriptor.php b/vendor/symfony/console/Descriptor/XmlDescriptor.php new file mode 100644 index 00000000..b5676beb --- /dev/null +++ b/vendor/symfony/console/Descriptor/XmlDescriptor.php @@ -0,0 +1,263 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * XML descriptor. + * + * @author Jean-François Simon <contact@jfsimon.fr> + * + * @internal + */ +class XmlDescriptor extends Descriptor +{ + /** + * @param InputDefinition $definition + * + * @return \DOMDocument + */ + public function getInputDefinitionDocument(InputDefinition $definition) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($definitionXML = $dom->createElement('definition')); + + $definitionXML->appendChild($argumentsXML = $dom->createElement('arguments')); + foreach ($definition->getArguments() as $argument) { + $this->appendDocument($argumentsXML, $this->getInputArgumentDocument($argument)); + } + + $definitionXML->appendChild($optionsXML = $dom->createElement('options')); + foreach ($definition->getOptions() as $option) { + $this->appendDocument($optionsXML, $this->getInputOptionDocument($option)); + } + + return $dom; + } + + /** + * @param Command $command + * + * @return \DOMDocument + */ + public function getCommandDocument(Command $command) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($commandXML = $dom->createElement('command')); + + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + $commandXML->setAttribute('id', $command->getName()); + $commandXML->setAttribute('name', $command->getName()); + + $commandXML->appendChild($usagesXML = $dom->createElement('usages')); + + foreach (array_merge(array($command->getSynopsis()), $command->getAliases(), $command->getUsages()) as $usage) { + $usagesXML->appendChild($dom->createElement('usage', $usage)); + } + + $commandXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getDescription()))); + + $commandXML->appendChild($helpXML = $dom->createElement('help')); + $helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getProcessedHelp()))); + + $definitionXML = $this->getInputDefinitionDocument($command->getNativeDefinition()); + $this->appendDocument($commandXML, $definitionXML->getElementsByTagName('definition')->item(0)); + + return $dom; + } + + /** + * @param Application $application + * @param string|null $namespace + * + * @return \DOMDocument + */ + public function getApplicationDocument(Application $application, $namespace = null) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($rootXml = $dom->createElement('symfony')); + + if ($application->getName() !== 'UNKNOWN') { + $rootXml->setAttribute('name', $application->getName()); + if ($application->getVersion() !== 'UNKNOWN') { + $rootXml->setAttribute('version', $application->getVersion()); + } + } + + $rootXml->appendChild($commandsXML = $dom->createElement('commands')); + + $description = new ApplicationDescription($application, $namespace); + + if ($namespace) { + $commandsXML->setAttribute('namespace', $namespace); + } + + foreach ($description->getCommands() as $command) { + $this->appendDocument($commandsXML, $this->getCommandDocument($command)); + } + + if (!$namespace) { + $rootXml->appendChild($namespacesXML = $dom->createElement('namespaces')); + + foreach ($description->getNamespaces() as $namespaceDescription) { + $namespacesXML->appendChild($namespaceArrayXML = $dom->createElement('namespace')); + $namespaceArrayXML->setAttribute('id', $namespaceDescription['id']); + + foreach ($namespaceDescription['commands'] as $name) { + $namespaceArrayXML->appendChild($commandXML = $dom->createElement('command')); + $commandXML->appendChild($dom->createTextNode($name)); + } + } + } + + return $dom; + } + + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + $this->writeDocument($this->getInputArgumentDocument($argument)); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + $this->writeDocument($this->getInputOptionDocument($option)); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + $this->writeDocument($this->getInputDefinitionDocument($definition)); + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $this->writeDocument($this->getCommandDocument($command)); + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $this->writeDocument($this->getApplicationDocument($application, isset($options['namespace']) ? $options['namespace'] : null)); + } + + /** + * Appends document children to parent node. + * + * @param \DOMNode $parentNode + * @param \DOMNode $importedParent + */ + private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent) + { + foreach ($importedParent->childNodes as $childNode) { + $parentNode->appendChild($parentNode->ownerDocument->importNode($childNode, true)); + } + } + + /** + * Writes DOM document. + * + * @param \DOMDocument $dom + * + * @return \DOMDocument|string + */ + private function writeDocument(\DOMDocument $dom) + { + $dom->formatOutput = true; + $this->write($dom->saveXML()); + } + + /** + * @param InputArgument $argument + * + * @return \DOMDocument + */ + private function getInputArgumentDocument(InputArgument $argument) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + + $dom->appendChild($objectXML = $dom->createElement('argument')); + $objectXML->setAttribute('name', $argument->getName()); + $objectXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0); + $objectXML->setAttribute('is_array', $argument->isArray() ? 1 : 0); + $objectXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($argument->getDescription())); + + $objectXML->appendChild($defaultsXML = $dom->createElement('defaults')); + $defaults = is_array($argument->getDefault()) ? $argument->getDefault() : (is_bool($argument->getDefault()) ? array(var_export($argument->getDefault(), true)) : ($argument->getDefault() ? array($argument->getDefault()) : array())); + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + + return $dom; + } + + /** + * @param InputOption $option + * + * @return \DOMDocument + */ + private function getInputOptionDocument(InputOption $option) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + + $dom->appendChild($objectXML = $dom->createElement('option')); + $objectXML->setAttribute('name', '--'.$option->getName()); + $pos = strpos($option->getShortcut(), '|'); + if (false !== $pos) { + $objectXML->setAttribute('shortcut', '-'.substr($option->getShortcut(), 0, $pos)); + $objectXML->setAttribute('shortcuts', '-'.implode('|-', explode('|', $option->getShortcut()))); + } else { + $objectXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : ''); + } + $objectXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0); + $objectXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0); + $objectXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0); + $objectXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($option->getDescription())); + + if ($option->acceptValue()) { + $defaults = is_array($option->getDefault()) ? $option->getDefault() : (is_bool($option->getDefault()) ? array(var_export($option->getDefault(), true)) : ($option->getDefault() ? array($option->getDefault()) : array())); + $objectXML->appendChild($defaultsXML = $dom->createElement('defaults')); + + if (!empty($defaults)) { + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + } + } + + return $dom; + } +} diff --git a/vendor/symfony/console/Event/ConsoleCommandEvent.php b/vendor/symfony/console/Event/ConsoleCommandEvent.php new file mode 100644 index 00000000..92adf1ef --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleCommandEvent.php @@ -0,0 +1,62 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +/** + * Allows to do things before the command is executed, like skipping the command or changing the input. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class ConsoleCommandEvent extends ConsoleEvent +{ + /** + * The return code for skipped commands, this will also be passed into the terminate event. + */ + const RETURN_CODE_DISABLED = 113; + + /** + * Indicates if the command should be run or skipped. + * + * @var bool + */ + private $commandShouldRun = true; + + /** + * Disables the command, so it won't be run. + * + * @return bool + */ + public function disableCommand() + { + return $this->commandShouldRun = false; + } + + /** + * Enables the command. + * + * @return bool + */ + public function enableCommand() + { + return $this->commandShouldRun = true; + } + + /** + * Returns true if the command is runnable, false otherwise. + * + * @return bool + */ + public function commandShouldRun() + { + return $this->commandShouldRun; + } +} diff --git a/vendor/symfony/console/Event/ConsoleEvent.php b/vendor/symfony/console/Event/ConsoleEvent.php new file mode 100644 index 00000000..ab620c46 --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleEvent.php @@ -0,0 +1,67 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\EventDispatcher\Event; + +/** + * Allows to inspect input and output of a command. + * + * @author Francesco Levorato <git@flevour.net> + */ +class ConsoleEvent extends Event +{ + protected $command; + + private $input; + private $output; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output) + { + $this->command = $command; + $this->input = $input; + $this->output = $output; + } + + /** + * Gets the command that is executed. + * + * @return Command A Command instance + */ + public function getCommand() + { + return $this->command; + } + + /** + * Gets the input instance. + * + * @return InputInterface An InputInterface instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance. + * + * @return OutputInterface An OutputInterface instance + */ + public function getOutput() + { + return $this->output; + } +} diff --git a/vendor/symfony/console/Event/ConsoleExceptionEvent.php b/vendor/symfony/console/Event/ConsoleExceptionEvent.php new file mode 100644 index 00000000..603b7eed --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleExceptionEvent.php @@ -0,0 +1,67 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Allows to handle exception thrown in a command. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class ConsoleExceptionEvent extends ConsoleEvent +{ + private $exception; + private $exitCode; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output, \Exception $exception, $exitCode) + { + parent::__construct($command, $input, $output); + + $this->setException($exception); + $this->exitCode = (int) $exitCode; + } + + /** + * Returns the thrown exception. + * + * @return \Exception The thrown exception + */ + public function getException() + { + return $this->exception; + } + + /** + * Replaces the thrown exception. + * + * This exception will be thrown if no response is set in the event. + * + * @param \Exception $exception The thrown exception + */ + public function setException(\Exception $exception) + { + $this->exception = $exception; + } + + /** + * Gets the exit code. + * + * @return int The command exit code + */ + public function getExitCode() + { + return $this->exitCode; + } +} diff --git a/vendor/symfony/console/Event/ConsoleTerminateEvent.php b/vendor/symfony/console/Event/ConsoleTerminateEvent.php new file mode 100644 index 00000000..b6a5d7c0 --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleTerminateEvent.php @@ -0,0 +1,58 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Allows to manipulate the exit code of a command after its execution. + * + * @author Francesco Levorato <git@flevour.net> + */ +class ConsoleTerminateEvent extends ConsoleEvent +{ + /** + * The exit code of the command. + * + * @var int + */ + private $exitCode; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output, $exitCode) + { + parent::__construct($command, $input, $output); + + $this->setExitCode($exitCode); + } + + /** + * Sets the exit code. + * + * @param int $exitCode The command exit code + */ + public function setExitCode($exitCode) + { + $this->exitCode = (int) $exitCode; + } + + /** + * Gets the exit code. + * + * @return int The command exit code + */ + public function getExitCode() + { + return $this->exitCode; + } +} diff --git a/vendor/symfony/console/Exception/CommandNotFoundException.php b/vendor/symfony/console/Exception/CommandNotFoundException.php new file mode 100644 index 00000000..ce6fefe3 --- /dev/null +++ b/vendor/symfony/console/Exception/CommandNotFoundException.php @@ -0,0 +1,43 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * Represents an incorrect command name typed in the console. + * + * @author Jérôme Tamarelle <jerome@tamarelle.net> + */ +class CommandNotFoundException extends \InvalidArgumentException implements ExceptionInterface +{ + private $alternatives; + + /** + * @param string $message Exception message to throw. + * @param array $alternatives List of similar defined names. + * @param int $code Exception code. + * @param Exception $previous previous exception used for the exception chaining. + */ + public function __construct($message, array $alternatives = array(), $code = 0, \Exception $previous = null) + { + parent::__construct($message, $code, $previous); + + $this->alternatives = $alternatives; + } + + /** + * @return array A list of similar defined names. + */ + public function getAlternatives() + { + return $this->alternatives; + } +} diff --git a/vendor/symfony/console/Exception/ExceptionInterface.php b/vendor/symfony/console/Exception/ExceptionInterface.php new file mode 100644 index 00000000..491cc4c6 --- /dev/null +++ b/vendor/symfony/console/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * ExceptionInterface. + * + * @author Jérôme Tamarelle <jerome@tamarelle.net> + */ +interface ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Exception/InvalidArgumentException.php b/vendor/symfony/console/Exception/InvalidArgumentException.php new file mode 100644 index 00000000..07cc0b61 --- /dev/null +++ b/vendor/symfony/console/Exception/InvalidArgumentException.php @@ -0,0 +1,19 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle <jerome@tamarelle.net> + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Exception/InvalidOptionException.php b/vendor/symfony/console/Exception/InvalidOptionException.php new file mode 100644 index 00000000..b2eec616 --- /dev/null +++ b/vendor/symfony/console/Exception/InvalidOptionException.php @@ -0,0 +1,21 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * Represents an incorrect option name typed in the console. + * + * @author Jérôme Tamarelle <jerome@tamarelle.net> + */ +class InvalidOptionException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Exception/LogicException.php b/vendor/symfony/console/Exception/LogicException.php new file mode 100644 index 00000000..fc37b8d8 --- /dev/null +++ b/vendor/symfony/console/Exception/LogicException.php @@ -0,0 +1,19 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle <jerome@tamarelle.net> + */ +class LogicException extends \LogicException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Exception/RuntimeException.php b/vendor/symfony/console/Exception/RuntimeException.php new file mode 100644 index 00000000..51d7d80a --- /dev/null +++ b/vendor/symfony/console/Exception/RuntimeException.php @@ -0,0 +1,19 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle <jerome@tamarelle.net> + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Formatter/OutputFormatter.php b/vendor/symfony/console/Formatter/OutputFormatter.php new file mode 100644 index 00000000..56cd5e56 --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatter.php @@ -0,0 +1,240 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Formatter class for console output. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +class OutputFormatter implements OutputFormatterInterface +{ + private $decorated; + private $styles = array(); + private $styleStack; + + /** + * Escapes "<" special char in given text. + * + * @param string $text Text to escape + * + * @return string Escaped text + */ + public static function escape($text) + { + $text = preg_replace('/([^\\\\]?)</', '$1\\<', $text); + + if ('\\' === substr($text, -1)) { + $len = strlen($text); + $text = rtrim($text, '\\'); + $text .= str_repeat('<<', $len - strlen($text)); + } + + return $text; + } + + /** + * Initializes console output formatter. + * + * @param bool $decorated Whether this formatter should actually decorate strings + * @param OutputFormatterStyleInterface[] $styles Array of "name => FormatterStyle" instances + */ + public function __construct($decorated = false, array $styles = array()) + { + $this->decorated = (bool) $decorated; + + $this->setStyle('error', new OutputFormatterStyle('white', 'red')); + $this->setStyle('info', new OutputFormatterStyle('green')); + $this->setStyle('comment', new OutputFormatterStyle('yellow')); + $this->setStyle('question', new OutputFormatterStyle('black', 'cyan')); + + foreach ($styles as $name => $style) { + $this->setStyle($name, $style); + } + + $this->styleStack = new OutputFormatterStyleStack(); + } + + /** + * Sets the decorated flag. + * + * @param bool $decorated Whether to decorate the messages or not + */ + public function setDecorated($decorated) + { + $this->decorated = (bool) $decorated; + } + + /** + * Gets the decorated flag. + * + * @return bool true if the output will decorate messages, false otherwise + */ + public function isDecorated() + { + return $this->decorated; + } + + /** + * Sets a new style. + * + * @param string $name The style name + * @param OutputFormatterStyleInterface $style The style instance + */ + public function setStyle($name, OutputFormatterStyleInterface $style) + { + $this->styles[strtolower($name)] = $style; + } + + /** + * Checks if output formatter has style with specified name. + * + * @param string $name + * + * @return bool + */ + public function hasStyle($name) + { + return isset($this->styles[strtolower($name)]); + } + + /** + * Gets style options from style with specified name. + * + * @param string $name + * + * @return OutputFormatterStyleInterface + * + * @throws InvalidArgumentException When style isn't defined + */ + public function getStyle($name) + { + if (!$this->hasStyle($name)) { + throw new InvalidArgumentException(sprintf('Undefined style: %s', $name)); + } + + return $this->styles[strtolower($name)]; + } + + /** + * Formats a message according to the given styles. + * + * @param string $message The message to style + * + * @return string The styled message + */ + public function format($message) + { + $message = (string) $message; + $offset = 0; + $output = ''; + $tagRegex = '[a-z][a-z0-9_=;-]*+'; + preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, PREG_OFFSET_CAPTURE); + foreach ($matches[0] as $i => $match) { + $pos = $match[1]; + $text = $match[0]; + + if (0 != $pos && '\\' == $message[$pos - 1]) { + continue; + } + + // add the text up to the next tag + $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset)); + $offset = $pos + strlen($text); + + // opening tag? + if ($open = '/' != $text[1]) { + $tag = $matches[1][$i][0]; + } else { + $tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : ''; + } + + if (!$open && !$tag) { + // </> + $this->styleStack->pop(); + } elseif (false === $style = $this->createStyleFromString(strtolower($tag))) { + $output .= $this->applyCurrentStyle($text); + } elseif ($open) { + $this->styleStack->push($style); + } else { + $this->styleStack->pop($style); + } + } + + $output .= $this->applyCurrentStyle(substr($message, $offset)); + + if (false !== strpos($output, '<<')) { + return strtr($output, array('\\<' => '<', '<<' => '\\')); + } + + return str_replace('\\<', '<', $output); + } + + /** + * @return OutputFormatterStyleStack + */ + public function getStyleStack() + { + return $this->styleStack; + } + + /** + * Tries to create new style instance from string. + * + * @param string $string + * + * @return OutputFormatterStyle|bool false if string is not format string + */ + private function createStyleFromString($string) + { + if (isset($this->styles[$string])) { + return $this->styles[$string]; + } + + if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) { + return false; + } + + $style = new OutputFormatterStyle(); + foreach ($matches as $match) { + array_shift($match); + + if ('fg' == $match[0]) { + $style->setForeground($match[1]); + } elseif ('bg' == $match[0]) { + $style->setBackground($match[1]); + } else { + try { + $style->setOption($match[1]); + } catch (\InvalidArgumentException $e) { + return false; + } + } + } + + return $style; + } + + /** + * Applies current style from stack to text, if must be applied. + * + * @param string $text Input text + * + * @return string Styled text + */ + private function applyCurrentStyle($text) + { + return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text; + } +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterInterface.php b/vendor/symfony/console/Formatter/OutputFormatterInterface.php new file mode 100644 index 00000000..5a52ba09 --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterInterface.php @@ -0,0 +1,69 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter interface for console output. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +interface OutputFormatterInterface +{ + /** + * Sets the decorated flag. + * + * @param bool $decorated Whether to decorate the messages or not + */ + public function setDecorated($decorated); + + /** + * Gets the decorated flag. + * + * @return bool true if the output will decorate messages, false otherwise + */ + public function isDecorated(); + + /** + * Sets a new style. + * + * @param string $name The style name + * @param OutputFormatterStyleInterface $style The style instance + */ + public function setStyle($name, OutputFormatterStyleInterface $style); + + /** + * Checks if output formatter has style with specified name. + * + * @param string $name + * + * @return bool + */ + public function hasStyle($name); + + /** + * Gets style options from style with specified name. + * + * @param string $name + * + * @return OutputFormatterStyleInterface + */ + public function getStyle($name); + + /** + * Formats a message according to the given styles. + * + * @param string $message The message to style + * + * @return string The styled message + */ + public function format($message); +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterStyle.php b/vendor/symfony/console/Formatter/OutputFormatterStyle.php new file mode 100644 index 00000000..c7c6b4a0 --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterStyle.php @@ -0,0 +1,221 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Formatter style class for defining styles. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +class OutputFormatterStyle implements OutputFormatterStyleInterface +{ + private static $availableForegroundColors = array( + 'black' => array('set' => 30, 'unset' => 39), + 'red' => array('set' => 31, 'unset' => 39), + 'green' => array('set' => 32, 'unset' => 39), + 'yellow' => array('set' => 33, 'unset' => 39), + 'blue' => array('set' => 34, 'unset' => 39), + 'magenta' => array('set' => 35, 'unset' => 39), + 'cyan' => array('set' => 36, 'unset' => 39), + 'white' => array('set' => 37, 'unset' => 39), + 'default' => array('set' => 39, 'unset' => 39), + ); + private static $availableBackgroundColors = array( + 'black' => array('set' => 40, 'unset' => 49), + 'red' => array('set' => 41, 'unset' => 49), + 'green' => array('set' => 42, 'unset' => 49), + 'yellow' => array('set' => 43, 'unset' => 49), + 'blue' => array('set' => 44, 'unset' => 49), + 'magenta' => array('set' => 45, 'unset' => 49), + 'cyan' => array('set' => 46, 'unset' => 49), + 'white' => array('set' => 47, 'unset' => 49), + 'default' => array('set' => 49, 'unset' => 49), + ); + private static $availableOptions = array( + 'bold' => array('set' => 1, 'unset' => 22), + 'underscore' => array('set' => 4, 'unset' => 24), + 'blink' => array('set' => 5, 'unset' => 25), + 'reverse' => array('set' => 7, 'unset' => 27), + 'conceal' => array('set' => 8, 'unset' => 28), + ); + + private $foreground; + private $background; + private $options = array(); + + /** + * Initializes output formatter style. + * + * @param string|null $foreground The style foreground color name + * @param string|null $background The style background color name + * @param array $options The style options + */ + public function __construct($foreground = null, $background = null, array $options = array()) + { + if (null !== $foreground) { + $this->setForeground($foreground); + } + if (null !== $background) { + $this->setBackground($background); + } + if (count($options)) { + $this->setOptions($options); + } + } + + /** + * Sets style foreground color. + * + * @param string|null $color The color name + * + * @throws InvalidArgumentException When the color name isn't defined + */ + public function setForeground($color = null) + { + if (null === $color) { + $this->foreground = null; + + return; + } + + if (!isset(static::$availableForegroundColors[$color])) { + throw new InvalidArgumentException(sprintf( + 'Invalid foreground color specified: "%s". Expected one of (%s)', + $color, + implode(', ', array_keys(static::$availableForegroundColors)) + )); + } + + $this->foreground = static::$availableForegroundColors[$color]; + } + + /** + * Sets style background color. + * + * @param string|null $color The color name + * + * @throws InvalidArgumentException When the color name isn't defined + */ + public function setBackground($color = null) + { + if (null === $color) { + $this->background = null; + + return; + } + + if (!isset(static::$availableBackgroundColors[$color])) { + throw new InvalidArgumentException(sprintf( + 'Invalid background color specified: "%s". Expected one of (%s)', + $color, + implode(', ', array_keys(static::$availableBackgroundColors)) + )); + } + + $this->background = static::$availableBackgroundColors[$color]; + } + + /** + * Sets some specific style option. + * + * @param string $option The option name + * + * @throws InvalidArgumentException When the option name isn't defined + */ + public function setOption($option) + { + if (!isset(static::$availableOptions[$option])) { + throw new InvalidArgumentException(sprintf( + 'Invalid option specified: "%s". Expected one of (%s)', + $option, + implode(', ', array_keys(static::$availableOptions)) + )); + } + + if (!in_array(static::$availableOptions[$option], $this->options)) { + $this->options[] = static::$availableOptions[$option]; + } + } + + /** + * Unsets some specific style option. + * + * @param string $option The option name + * + * @throws InvalidArgumentException When the option name isn't defined + */ + public function unsetOption($option) + { + if (!isset(static::$availableOptions[$option])) { + throw new InvalidArgumentException(sprintf( + 'Invalid option specified: "%s". Expected one of (%s)', + $option, + implode(', ', array_keys(static::$availableOptions)) + )); + } + + $pos = array_search(static::$availableOptions[$option], $this->options); + if (false !== $pos) { + unset($this->options[$pos]); + } + } + + /** + * Sets multiple style options at once. + * + * @param array $options + */ + public function setOptions(array $options) + { + $this->options = array(); + + foreach ($options as $option) { + $this->setOption($option); + } + } + + /** + * Applies the style to a given text. + * + * @param string $text The text to style + * + * @return string + */ + public function apply($text) + { + $setCodes = array(); + $unsetCodes = array(); + + if (null !== $this->foreground) { + $setCodes[] = $this->foreground['set']; + $unsetCodes[] = $this->foreground['unset']; + } + if (null !== $this->background) { + $setCodes[] = $this->background['set']; + $unsetCodes[] = $this->background['unset']; + } + if (count($this->options)) { + foreach ($this->options as $option) { + $setCodes[] = $option['set']; + $unsetCodes[] = $option['unset']; + } + } + + if (0 === count($setCodes)) { + return $text; + } + + return sprintf("\033[%sm%s\033[%sm", implode(';', $setCodes), $text, implode(';', $unsetCodes)); + } +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php b/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php new file mode 100644 index 00000000..c36fda80 --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php @@ -0,0 +1,64 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter style interface for defining styles. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +interface OutputFormatterStyleInterface +{ + /** + * Sets style foreground color. + * + * @param string $color The color name + */ + public function setForeground($color = null); + + /** + * Sets style background color. + * + * @param string $color The color name + */ + public function setBackground($color = null); + + /** + * Sets some specific style option. + * + * @param string $option The option name + */ + public function setOption($option); + + /** + * Unsets some specific style option. + * + * @param string $option The option name + */ + public function unsetOption($option); + + /** + * Sets multiple style options at once. + * + * @param array $options + */ + public function setOptions(array $options); + + /** + * Applies the style to a given text. + * + * @param string $text The text to style + * + * @return string + */ + public function apply($text); +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php b/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php new file mode 100644 index 00000000..e5d14ea3 --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php @@ -0,0 +1,123 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Jean-François Simon <contact@jfsimon.fr> + */ +class OutputFormatterStyleStack +{ + /** + * @var OutputFormatterStyleInterface[] + */ + private $styles; + + /** + * @var OutputFormatterStyleInterface + */ + private $emptyStyle; + + /** + * Constructor. + * + * @param OutputFormatterStyleInterface|null $emptyStyle + */ + public function __construct(OutputFormatterStyleInterface $emptyStyle = null) + { + $this->emptyStyle = $emptyStyle ?: new OutputFormatterStyle(); + $this->reset(); + } + + /** + * Resets stack (ie. empty internal arrays). + */ + public function reset() + { + $this->styles = array(); + } + + /** + * Pushes a style in the stack. + * + * @param OutputFormatterStyleInterface $style + */ + public function push(OutputFormatterStyleInterface $style) + { + $this->styles[] = $style; + } + + /** + * Pops a style from the stack. + * + * @param OutputFormatterStyleInterface|null $style + * + * @return OutputFormatterStyleInterface + * + * @throws InvalidArgumentException When style tags incorrectly nested + */ + public function pop(OutputFormatterStyleInterface $style = null) + { + if (empty($this->styles)) { + return $this->emptyStyle; + } + + if (null === $style) { + return array_pop($this->styles); + } + + foreach (array_reverse($this->styles, true) as $index => $stackedStyle) { + if ($style->apply('') === $stackedStyle->apply('')) { + $this->styles = array_slice($this->styles, 0, $index); + + return $stackedStyle; + } + } + + throw new InvalidArgumentException('Incorrectly nested style tag found.'); + } + + /** + * Computes current style with stacks top codes. + * + * @return OutputFormatterStyle + */ + public function getCurrent() + { + if (empty($this->styles)) { + return $this->emptyStyle; + } + + return $this->styles[count($this->styles) - 1]; + } + + /** + * @param OutputFormatterStyleInterface $emptyStyle + * + * @return OutputFormatterStyleStack + */ + public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle) + { + $this->emptyStyle = $emptyStyle; + + return $this; + } + + /** + * @return OutputFormatterStyleInterface + */ + public function getEmptyStyle() + { + return $this->emptyStyle; + } +} diff --git a/vendor/symfony/console/Helper/DebugFormatterHelper.php b/vendor/symfony/console/Helper/DebugFormatterHelper.php new file mode 100644 index 00000000..1119b795 --- /dev/null +++ b/vendor/symfony/console/Helper/DebugFormatterHelper.php @@ -0,0 +1,127 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * Helps outputting debug information when running an external program from a command. + * + * An external program can be a Process, an HTTP request, or anything else. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class DebugFormatterHelper extends Helper +{ + private $colors = array('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'default'); + private $started = array(); + private $count = -1; + + /** + * Starts a debug formatting session. + * + * @param string $id The id of the formatting session + * @param string $message The message to display + * @param string $prefix The prefix to use + * + * @return string + */ + public function start($id, $message, $prefix = 'RUN') + { + $this->started[$id] = array('border' => ++$this->count % count($this->colors)); + + return sprintf("%s<bg=blue;fg=white> %s </> <fg=blue>%s</>\n", $this->getBorder($id), $prefix, $message); + } + + /** + * Adds progress to a formatting session. + * + * @param string $id The id of the formatting session + * @param string $buffer The message to display + * @param bool $error Whether to consider the buffer as error + * @param string $prefix The prefix for output + * @param string $errorPrefix The prefix for error output + * + * @return string + */ + public function progress($id, $buffer, $error = false, $prefix = 'OUT', $errorPrefix = 'ERR') + { + $message = ''; + + if ($error) { + if (isset($this->started[$id]['out'])) { + $message .= "\n"; + unset($this->started[$id]['out']); + } + if (!isset($this->started[$id]['err'])) { + $message .= sprintf('%s<bg=red;fg=white> %s </> ', $this->getBorder($id), $errorPrefix); + $this->started[$id]['err'] = true; + } + + $message .= str_replace("\n", sprintf("\n%s<bg=red;fg=white> %s </> ", $this->getBorder($id), $errorPrefix), $buffer); + } else { + if (isset($this->started[$id]['err'])) { + $message .= "\n"; + unset($this->started[$id]['err']); + } + if (!isset($this->started[$id]['out'])) { + $message .= sprintf('%s<bg=green;fg=white> %s </> ', $this->getBorder($id), $prefix); + $this->started[$id]['out'] = true; + } + + $message .= str_replace("\n", sprintf("\n%s<bg=green;fg=white> %s </> ", $this->getBorder($id), $prefix), $buffer); + } + + return $message; + } + + /** + * Stops a formatting session. + * + * @param string $id The id of the formatting session + * @param string $message The message to display + * @param bool $successful Whether to consider the result as success + * @param string $prefix The prefix for the end output + * + * @return string + */ + public function stop($id, $message, $successful, $prefix = 'RES') + { + $trailingEOL = isset($this->started[$id]['out']) || isset($this->started[$id]['err']) ? "\n" : ''; + + if ($successful) { + return sprintf("%s%s<bg=green;fg=white> %s </> <fg=green>%s</>\n", $trailingEOL, $this->getBorder($id), $prefix, $message); + } + + $message = sprintf("%s%s<bg=red;fg=white> %s </> <fg=red>%s</>\n", $trailingEOL, $this->getBorder($id), $prefix, $message); + + unset($this->started[$id]['out'], $this->started[$id]['err']); + + return $message; + } + + /** + * @param string $id The id of the formatting session + * + * @return string + */ + private function getBorder($id) + { + return sprintf('<bg=%s> </>', $this->colors[$this->started[$id]['border']]); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'debug_formatter'; + } +} diff --git a/vendor/symfony/console/Helper/DescriptorHelper.php b/vendor/symfony/console/Helper/DescriptorHelper.php new file mode 100644 index 00000000..a53b476b --- /dev/null +++ b/vendor/symfony/console/Helper/DescriptorHelper.php @@ -0,0 +1,97 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Descriptor\DescriptorInterface; +use Symfony\Component\Console\Descriptor\JsonDescriptor; +use Symfony\Component\Console\Descriptor\MarkdownDescriptor; +use Symfony\Component\Console\Descriptor\TextDescriptor; +use Symfony\Component\Console\Descriptor\XmlDescriptor; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * This class adds helper method to describe objects in various formats. + * + * @author Jean-François Simon <contact@jfsimon.fr> + */ +class DescriptorHelper extends Helper +{ + /** + * @var DescriptorInterface[] + */ + private $descriptors = array(); + + /** + * Constructor. + */ + public function __construct() + { + $this + ->register('txt', new TextDescriptor()) + ->register('xml', new XmlDescriptor()) + ->register('json', new JsonDescriptor()) + ->register('md', new MarkdownDescriptor()) + ; + } + + /** + * Describes an object if supported. + * + * Available options are: + * * format: string, the output format name + * * raw_text: boolean, sets output type as raw + * + * @param OutputInterface $output + * @param object $object + * @param array $options + * + * @throws InvalidArgumentException when the given format is not supported + */ + public function describe(OutputInterface $output, $object, array $options = array()) + { + $options = array_merge(array( + 'raw_text' => false, + 'format' => 'txt', + ), $options); + + if (!isset($this->descriptors[$options['format']])) { + throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format'])); + } + + $descriptor = $this->descriptors[$options['format']]; + $descriptor->describe($output, $object, $options); + } + + /** + * Registers a descriptor. + * + * @param string $format + * @param DescriptorInterface $descriptor + * + * @return DescriptorHelper + */ + public function register($format, DescriptorInterface $descriptor) + { + $this->descriptors[$format] = $descriptor; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'descriptor'; + } +} diff --git a/vendor/symfony/console/Helper/DialogHelper.php b/vendor/symfony/console/Helper/DialogHelper.php new file mode 100644 index 00000000..9ce9f661 --- /dev/null +++ b/vendor/symfony/console/Helper/DialogHelper.php @@ -0,0 +1,502 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +/** + * The Dialog class provides helpers to interact with the user. + * + * @author Fabien Potencier <fabien@symfony.com> + * + * @deprecated since version 2.5, to be removed in 3.0. + * Use {@link \Symfony\Component\Console\Helper\QuestionHelper} instead. + */ +class DialogHelper extends InputAwareHelper +{ + private $inputStream; + private static $shell; + private static $stty; + + public function __construct($triggerDeprecationError = true) + { + if ($triggerDeprecationError) { + @trigger_error('"Symfony\Component\Console\Helper\DialogHelper" is deprecated since version 2.5 and will be removed in 3.0. Use "Symfony\Component\Console\Helper\QuestionHelper" instead.', E_USER_DEPRECATED); + } + } + + /** + * Asks the user to select a value. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param array $choices List of choices to pick from + * @param bool|string $default The default answer if the user enters nothing + * @param bool|int $attempts Max number of times to ask before giving up (false by default, which means infinite) + * @param string $errorMessage Message which will be shown if invalid value from choice list would be picked + * @param bool $multiselect Select more than one value separated by comma + * + * @return int|string|array The selected value or values (the key of the choices array) + * + * @throws InvalidArgumentException + */ + public function select(OutputInterface $output, $question, $choices, $default = null, $attempts = false, $errorMessage = 'Value "%s" is invalid', $multiselect = false) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $width = max(array_map('strlen', array_keys($choices))); + + $messages = (array) $question; + foreach ($choices as $key => $value) { + $messages[] = sprintf(" [<info>%-{$width}s</info>] %s", $key, $value); + } + + $output->writeln($messages); + + $result = $this->askAndValidate($output, '> ', function ($picked) use ($choices, $errorMessage, $multiselect) { + // Collapse all spaces. + $selectedChoices = str_replace(' ', '', $picked); + + if ($multiselect) { + // Check for a separated comma values + if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) { + throw new InvalidArgumentException(sprintf($errorMessage, $picked)); + } + $selectedChoices = explode(',', $selectedChoices); + } else { + $selectedChoices = array($picked); + } + + $multiselectChoices = array(); + + foreach ($selectedChoices as $value) { + if (empty($choices[$value])) { + throw new InvalidArgumentException(sprintf($errorMessage, $value)); + } + $multiselectChoices[] = $value; + } + + if ($multiselect) { + return $multiselectChoices; + } + + return $picked; + }, $attempts, $default); + + return $result; + } + + /** + * Asks a question to the user. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param string $default The default answer if none is given by the user + * @param array $autocomplete List of values to autocomplete + * + * @return string The user answer + * + * @throws RuntimeException If there is no data to read in the input stream + */ + public function ask(OutputInterface $output, $question, $default = null, array $autocomplete = null) + { + if ($this->input && !$this->input->isInteractive()) { + return $default; + } + + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $output->write($question); + + $inputStream = $this->inputStream ?: STDIN; + + if (null === $autocomplete || !$this->hasSttyAvailable()) { + $ret = fgets($inputStream, 4096); + if (false === $ret) { + throw new RuntimeException('Aborted'); + } + $ret = trim($ret); + } else { + $ret = ''; + + $i = 0; + $ofs = -1; + $matches = $autocomplete; + $numMatches = count($matches); + + $sttyMode = shell_exec('stty -g'); + + // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead) + shell_exec('stty -icanon -echo'); + + // Add highlighted text style + $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white')); + + // Read a keypress + while (!feof($inputStream)) { + $c = fread($inputStream, 1); + + // Backspace Character + if ("\177" === $c) { + if (0 === $numMatches && 0 !== $i) { + --$i; + // Move cursor backwards + $output->write("\033[1D"); + } + + if ($i === 0) { + $ofs = -1; + $matches = $autocomplete; + $numMatches = count($matches); + } else { + $numMatches = 0; + } + + // Pop the last character off the end of our string + $ret = substr($ret, 0, $i); + } elseif ("\033" === $c) { + // Did we read an escape sequence? + $c .= fread($inputStream, 2); + + // A = Up Arrow. B = Down Arrow + if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) { + if ('A' === $c[2] && -1 === $ofs) { + $ofs = 0; + } + + if (0 === $numMatches) { + continue; + } + + $ofs += ('A' === $c[2]) ? -1 : 1; + $ofs = ($numMatches + $ofs) % $numMatches; + } + } elseif (ord($c) < 32) { + if ("\t" === $c || "\n" === $c) { + if ($numMatches > 0 && -1 !== $ofs) { + $ret = $matches[$ofs]; + // Echo out remaining chars for current match + $output->write(substr($ret, $i)); + $i = strlen($ret); + } + + if ("\n" === $c) { + $output->write($c); + break; + } + + $numMatches = 0; + } + + continue; + } else { + $output->write($c); + $ret .= $c; + ++$i; + + $numMatches = 0; + $ofs = 0; + + foreach ($autocomplete as $value) { + // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle) + if (0 === strpos($value, $ret) && $i !== strlen($value)) { + $matches[$numMatches++] = $value; + } + } + } + + // Erase characters from cursor to end of line + $output->write("\033[K"); + + if ($numMatches > 0 && -1 !== $ofs) { + // Save cursor position + $output->write("\0337"); + // Write highlighted text + $output->write('<hl>'.substr($matches[$ofs], $i).'</hl>'); + // Restore cursor position + $output->write("\0338"); + } + } + + // Reset stty so it behaves normally again + shell_exec(sprintf('stty %s', $sttyMode)); + } + + return strlen($ret) > 0 ? $ret : $default; + } + + /** + * Asks a confirmation to the user. + * + * The question will be asked until the user answers by nothing, yes, or no. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param bool $default The default answer if the user enters nothing + * + * @return bool true if the user has confirmed, false otherwise + */ + public function askConfirmation(OutputInterface $output, $question, $default = true) + { + $answer = 'z'; + while ($answer && !in_array(strtolower($answer[0]), array('y', 'n'))) { + $answer = $this->ask($output, $question); + } + + if (false === $default) { + return $answer && 'y' == strtolower($answer[0]); + } + + return !$answer || 'y' == strtolower($answer[0]); + } + + /** + * Asks a question to the user, the response is hidden. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question + * @param bool $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not + * + * @return string The answer + * + * @throws RuntimeException In case the fallback is deactivated and the response can not be hidden + */ + public function askHiddenResponse(OutputInterface $output, $question, $fallback = true) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + if ('\\' === DIRECTORY_SEPARATOR) { + $exe = __DIR__.'/../Resources/bin/hiddeninput.exe'; + + // handle code running from a phar + if ('phar:' === substr(__FILE__, 0, 5)) { + $tmpExe = sys_get_temp_dir().'/hiddeninput.exe'; + copy($exe, $tmpExe); + $exe = $tmpExe; + } + + $output->write($question); + $value = rtrim(shell_exec($exe)); + $output->writeln(''); + + if (isset($tmpExe)) { + unlink($tmpExe); + } + + return $value; + } + + if ($this->hasSttyAvailable()) { + $output->write($question); + + $sttyMode = shell_exec('stty -g'); + + shell_exec('stty -echo'); + $value = fgets($this->inputStream ?: STDIN, 4096); + shell_exec(sprintf('stty %s', $sttyMode)); + + if (false === $value) { + throw new RuntimeException('Aborted'); + } + + $value = trim($value); + $output->writeln(''); + + return $value; + } + + if (false !== $shell = $this->getShell()) { + $output->write($question); + $readCmd = $shell === 'csh' ? 'set mypassword = $<' : 'read -r mypassword'; + $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd); + $value = rtrim(shell_exec($command)); + $output->writeln(''); + + return $value; + } + + if ($fallback) { + return $this->ask($output, $question); + } + + throw new RuntimeException('Unable to hide the response'); + } + + /** + * Asks for a value and validates the response. + * + * The validator receives the data to validate. It must return the + * validated data when the data is valid and throw an exception + * otherwise. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param callable $validator A PHP callback + * @param int|false $attempts Max number of times to ask before giving up (false by default, which means infinite) + * @param string $default The default answer if none is given by the user + * @param array $autocomplete List of values to autocomplete + * + * @return mixed + * + * @throws \Exception When any of the validators return an error + */ + public function askAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $default = null, array $autocomplete = null) + { + $that = $this; + + $interviewer = function () use ($output, $question, $default, $autocomplete, $that) { + return $that->ask($output, $question, $default, $autocomplete); + }; + + return $this->validateAttempts($interviewer, $output, $validator, $attempts); + } + + /** + * Asks for a value, hide and validates the response. + * + * The validator receives the data to validate. It must return the + * validated data when the data is valid and throw an exception + * otherwise. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param callable $validator A PHP callback + * @param int|false $attempts Max number of times to ask before giving up (false by default, which means infinite) + * @param bool $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not + * + * @return string The response + * + * @throws \Exception When any of the validators return an error + * @throws RuntimeException In case the fallback is deactivated and the response can not be hidden + */ + public function askHiddenResponseAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $fallback = true) + { + $that = $this; + + $interviewer = function () use ($output, $question, $fallback, $that) { + return $that->askHiddenResponse($output, $question, $fallback); + }; + + return $this->validateAttempts($interviewer, $output, $validator, $attempts); + } + + /** + * Sets the input stream to read from when interacting with the user. + * + * This is mainly useful for testing purpose. + * + * @param resource $stream The input stream + */ + public function setInputStream($stream) + { + $this->inputStream = $stream; + } + + /** + * Returns the helper's input stream. + * + * @return resource|null The input stream or null if the default STDIN is used + */ + public function getInputStream() + { + return $this->inputStream; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'dialog'; + } + + /** + * Return a valid Unix shell. + * + * @return string|bool The valid shell name, false in case no valid shell is found + */ + private function getShell() + { + if (null !== self::$shell) { + return self::$shell; + } + + self::$shell = false; + + if (file_exists('/usr/bin/env')) { + // handle other OSs with bash/zsh/ksh/csh if available to hide the answer + $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null"; + foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) { + if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) { + self::$shell = $sh; + break; + } + } + } + + return self::$shell; + } + + private function hasSttyAvailable() + { + if (null !== self::$stty) { + return self::$stty; + } + + exec('stty 2>&1', $output, $exitcode); + + return self::$stty = $exitcode === 0; + } + + /** + * Validate an attempt. + * + * @param callable $interviewer A callable that will ask for a question and return the result + * @param OutputInterface $output An Output instance + * @param callable $validator A PHP callback + * @param int|false $attempts Max number of times to ask before giving up; false will ask infinitely + * + * @return string The validated response + * + * @throws \Exception In case the max number of attempts has been reached and no valid response has been given + */ + private function validateAttempts($interviewer, OutputInterface $output, $validator, $attempts) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $e = null; + while (false === $attempts || $attempts--) { + if (null !== $e) { + $output->writeln($this->getHelperSet()->get('formatter')->formatBlock($e->getMessage(), 'error')); + } + + try { + return call_user_func($validator, $interviewer()); + } catch (\Exception $e) { + } + } + + throw $e; + } +} diff --git a/vendor/symfony/console/Helper/FormatterHelper.php b/vendor/symfony/console/Helper/FormatterHelper.php new file mode 100644 index 00000000..ac736f98 --- /dev/null +++ b/vendor/symfony/console/Helper/FormatterHelper.php @@ -0,0 +1,82 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Formatter\OutputFormatter; + +/** + * The Formatter class provides helpers to format messages. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class FormatterHelper extends Helper +{ + /** + * Formats a message within a section. + * + * @param string $section The section name + * @param string $message The message + * @param string $style The style to apply to the section + * + * @return string The format section + */ + public function formatSection($section, $message, $style = 'info') + { + return sprintf('<%s>[%s]</%s> %s', $style, $section, $style, $message); + } + + /** + * Formats a message as a block of text. + * + * @param string|array $messages The message to write in the block + * @param string $style The style to apply to the whole block + * @param bool $large Whether to return a large block + * + * @return string The formatter message + */ + public function formatBlock($messages, $style, $large = false) + { + if (!is_array($messages)) { + $messages = array($messages); + } + + $len = 0; + $lines = array(); + foreach ($messages as $message) { + $message = OutputFormatter::escape($message); + $lines[] = sprintf($large ? ' %s ' : ' %s ', $message); + $len = max($this->strlen($message) + ($large ? 4 : 2), $len); + } + + $messages = $large ? array(str_repeat(' ', $len)) : array(); + for ($i = 0; isset($lines[$i]); ++$i) { + $messages[] = $lines[$i].str_repeat(' ', $len - $this->strlen($lines[$i])); + } + if ($large) { + $messages[] = str_repeat(' ', $len); + } + + for ($i = 0; isset($messages[$i]); ++$i) { + $messages[$i] = sprintf('<%s>%s</%s>', $style, $messages[$i], $style); + } + + return implode("\n", $messages); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'formatter'; + } +} diff --git a/vendor/symfony/console/Helper/Helper.php b/vendor/symfony/console/Helper/Helper.php new file mode 100644 index 00000000..43f9a169 --- /dev/null +++ b/vendor/symfony/console/Helper/Helper.php @@ -0,0 +1,119 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * Helper is the base class for all helper classes. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +abstract class Helper implements HelperInterface +{ + protected $helperSet = null; + + /** + * Sets the helper set associated with this helper. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet = null) + { + $this->helperSet = $helperSet; + } + + /** + * Gets the helper set associated with this helper. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Returns the length of a string, using mb_strwidth if it is available. + * + * @param string $string The string to check its length + * + * @return int The length of the string + */ + public static function strlen($string) + { + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return strlen($string); + } + + return mb_strwidth($string, $encoding); + } + + public static function formatTime($secs) + { + static $timeFormats = array( + array(0, '< 1 sec'), + array(1, '1 sec'), + array(2, 'secs', 1), + array(60, '1 min'), + array(120, 'mins', 60), + array(3600, '1 hr'), + array(7200, 'hrs', 3600), + array(86400, '1 day'), + array(172800, 'days', 86400), + ); + + foreach ($timeFormats as $index => $format) { + if ($secs >= $format[0]) { + if ((isset($timeFormats[$index + 1]) && $secs < $timeFormats[$index + 1][0]) + || $index == count($timeFormats) - 1 + ) { + if (2 == count($format)) { + return $format[1]; + } + + return floor($secs / $format[2]).' '.$format[1]; + } + } + } + } + + public static function formatMemory($memory) + { + if ($memory >= 1024 * 1024 * 1024) { + return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024); + } + + if ($memory >= 1024 * 1024) { + return sprintf('%.1f MiB', $memory / 1024 / 1024); + } + + if ($memory >= 1024) { + return sprintf('%d KiB', $memory / 1024); + } + + return sprintf('%d B', $memory); + } + + public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, $string) + { + $isDecorated = $formatter->isDecorated(); + $formatter->setDecorated(false); + // remove <...> formatting + $string = $formatter->format($string); + // remove already formatted characters + $string = preg_replace("/\033\[[^m]*m/", '', $string); + $formatter->setDecorated($isDecorated); + + return self::strlen($string); + } +} diff --git a/vendor/symfony/console/Helper/HelperInterface.php b/vendor/symfony/console/Helper/HelperInterface.php new file mode 100644 index 00000000..5a923e0a --- /dev/null +++ b/vendor/symfony/console/Helper/HelperInterface.php @@ -0,0 +1,41 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * HelperInterface is the interface all helpers must implement. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +interface HelperInterface +{ + /** + * Sets the helper set associated with this helper. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet = null); + + /** + * Gets the helper set associated with this helper. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet(); + + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + */ + public function getName(); +} diff --git a/vendor/symfony/console/Helper/HelperSet.php b/vendor/symfony/console/Helper/HelperSet.php new file mode 100644 index 00000000..27fedcf7 --- /dev/null +++ b/vendor/symfony/console/Helper/HelperSet.php @@ -0,0 +1,117 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * HelperSet represents a set of helpers to be used with a command. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class HelperSet implements \IteratorAggregate +{ + private $helpers = array(); + private $command; + + /** + * Constructor. + * + * @param Helper[] $helpers An array of helper. + */ + public function __construct(array $helpers = array()) + { + foreach ($helpers as $alias => $helper) { + $this->set($helper, is_int($alias) ? null : $alias); + } + } + + /** + * Sets a helper. + * + * @param HelperInterface $helper The helper instance + * @param string $alias An alias + */ + public function set(HelperInterface $helper, $alias = null) + { + $this->helpers[$helper->getName()] = $helper; + if (null !== $alias) { + $this->helpers[$alias] = $helper; + } + + $helper->setHelperSet($this); + } + + /** + * Returns true if the helper if defined. + * + * @param string $name The helper name + * + * @return bool true if the helper is defined, false otherwise + */ + public function has($name) + { + return isset($this->helpers[$name]); + } + + /** + * Gets a helper value. + * + * @param string $name The helper name + * + * @return HelperInterface The helper instance + * + * @throws InvalidArgumentException if the helper is not defined + */ + public function get($name) + { + if (!$this->has($name)) { + throw new InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name)); + } + + if ('dialog' === $name && $this->helpers[$name] instanceof DialogHelper) { + @trigger_error('"Symfony\Component\Console\Helper\DialogHelper" is deprecated since version 2.5 and will be removed in 3.0. Use "Symfony\Component\Console\Helper\QuestionHelper" instead.', E_USER_DEPRECATED); + } elseif ('progress' === $name && $this->helpers[$name] instanceof ProgressHelper) { + @trigger_error('"Symfony\Component\Console\Helper\ProgressHelper" is deprecated since version 2.5 and will be removed in 3.0. Use "Symfony\Component\Console\Helper\ProgressBar" instead.', E_USER_DEPRECATED); + } elseif ('table' === $name && $this->helpers[$name] instanceof TableHelper) { + @trigger_error('"Symfony\Component\Console\Helper\TableHelper" is deprecated since version 2.5 and will be removed in 3.0. Use "Symfony\Component\Console\Helper\Table" instead.', E_USER_DEPRECATED); + } + + return $this->helpers[$name]; + } + + /** + * Sets the command associated with this helper set. + * + * @param Command $command A Command instance + */ + public function setCommand(Command $command = null) + { + $this->command = $command; + } + + /** + * Gets the command associated with this helper set. + * + * @return Command A Command instance + */ + public function getCommand() + { + return $this->command; + } + + public function getIterator() + { + return new \ArrayIterator($this->helpers); + } +} diff --git a/vendor/symfony/console/Helper/InputAwareHelper.php b/vendor/symfony/console/Helper/InputAwareHelper.php new file mode 100644 index 00000000..42617674 --- /dev/null +++ b/vendor/symfony/console/Helper/InputAwareHelper.php @@ -0,0 +1,33 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputAwareInterface; + +/** + * An implementation of InputAwareInterface for Helpers. + * + * @author Wouter J <waldio.webdesign@gmail.com> + */ +abstract class InputAwareHelper extends Helper implements InputAwareInterface +{ + protected $input; + + /** + * {@inheritdoc} + */ + public function setInput(InputInterface $input) + { + $this->input = $input; + } +} diff --git a/vendor/symfony/console/Helper/ProcessHelper.php b/vendor/symfony/console/Helper/ProcessHelper.php new file mode 100644 index 00000000..a811eb48 --- /dev/null +++ b/vendor/symfony/console/Helper/ProcessHelper.php @@ -0,0 +1,151 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\Exception\ProcessFailedException; +use Symfony\Component\Process\Process; +use Symfony\Component\Process\ProcessBuilder; + +/** + * The ProcessHelper class provides helpers to run external processes. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class ProcessHelper extends Helper +{ + /** + * Runs an external process. + * + * @param OutputInterface $output An OutputInterface instance + * @param string|array|Process $cmd An instance of Process or an array of arguments to escape and run or a command to run + * @param string|null $error An error message that must be displayed if something went wrong + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * @param int $verbosity The threshold for verbosity + * + * @return Process The process that ran + */ + public function run(OutputInterface $output, $cmd, $error = null, $callback = null, $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $formatter = $this->getHelperSet()->get('debug_formatter'); + + if (is_array($cmd)) { + $process = ProcessBuilder::create($cmd)->getProcess(); + } elseif ($cmd instanceof Process) { + $process = $cmd; + } else { + $process = new Process($cmd); + } + + if ($verbosity <= $output->getVerbosity()) { + $output->write($formatter->start(spl_object_hash($process), $this->escapeString($process->getCommandLine()))); + } + + if ($output->isDebug()) { + $callback = $this->wrapCallback($output, $process, $callback); + } + + $process->run($callback); + + if ($verbosity <= $output->getVerbosity()) { + $message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode()); + $output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful())); + } + + if (!$process->isSuccessful() && null !== $error) { + $output->writeln(sprintf('<error>%s</error>', $this->escapeString($error))); + } + + return $process; + } + + /** + * Runs the process. + * + * This is identical to run() except that an exception is thrown if the process + * exits with a non-zero exit code. + * + * @param OutputInterface $output An OutputInterface instance + * @param string|Process $cmd An instance of Process or a command to run + * @param string|null $error An error message that must be displayed if something went wrong + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * + * @return Process The process that ran + * + * @throws ProcessFailedException + * + * @see run() + */ + public function mustRun(OutputInterface $output, $cmd, $error = null, $callback = null) + { + $process = $this->run($output, $cmd, $error, $callback); + + if (!$process->isSuccessful()) { + throw new ProcessFailedException($process); + } + + return $process; + } + + /** + * Wraps a Process callback to add debugging output. + * + * @param OutputInterface $output An OutputInterface interface + * @param Process $process The Process + * @param callable|null $callback A PHP callable + * + * @return callable + */ + public function wrapCallback(OutputInterface $output, Process $process, $callback = null) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $formatter = $this->getHelperSet()->get('debug_formatter'); + + $that = $this; + + return function ($type, $buffer) use ($output, $process, $callback, $formatter, $that) { + $output->write($formatter->progress(spl_object_hash($process), $that->escapeString($buffer), Process::ERR === $type)); + + if (null !== $callback) { + call_user_func($callback, $type, $buffer); + } + }; + } + + /** + * This method is public for PHP 5.3 compatibility, it should be private. + * + * @internal + */ + public function escapeString($str) + { + return str_replace('<', '\\<', $str); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'process'; + } +} diff --git a/vendor/symfony/console/Helper/ProgressBar.php b/vendor/symfony/console/Helper/ProgressBar.php new file mode 100644 index 00000000..e7717a52 --- /dev/null +++ b/vendor/symfony/console/Helper/ProgressBar.php @@ -0,0 +1,621 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\LogicException; + +/** + * The ProgressBar provides helpers to display progress output. + * + * @author Fabien Potencier <fabien@symfony.com> + * @author Chris Jones <leeked@gmail.com> + */ +class ProgressBar +{ + // options + private $barWidth = 28; + private $barChar; + private $emptyBarChar = '-'; + private $progressChar = '>'; + private $format; + private $internalFormat; + private $redrawFreq = 1; + + /** + * @var OutputInterface + */ + private $output; + private $step = 0; + private $max; + private $startTime; + private $stepWidth; + private $percent = 0.0; + private $formatLineCount; + private $messages; + private $overwrite = true; + + private static $formatters; + private static $formats; + + /** + * Constructor. + * + * @param OutputInterface $output An OutputInterface instance + * @param int $max Maximum steps (0 if unknown) + */ + public function __construct(OutputInterface $output, $max = 0) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $this->output = $output; + $this->setMaxSteps($max); + + if (!$this->output->isDecorated()) { + // disable overwrite when output does not support ANSI codes. + $this->overwrite = false; + + // set a reasonable redraw frequency so output isn't flooded + $this->setRedrawFrequency($max / 10); + } + + $this->startTime = time(); + } + + /** + * Sets a placeholder formatter for a given name. + * + * This method also allow you to override an existing placeholder. + * + * @param string $name The placeholder name (including the delimiter char like %) + * @param callable $callable A PHP callable + */ + public static function setPlaceholderFormatterDefinition($name, $callable) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + self::$formatters[$name] = $callable; + } + + /** + * Gets the placeholder formatter for a given name. + * + * @param string $name The placeholder name (including the delimiter char like %) + * + * @return callable|null A PHP callable + */ + public static function getPlaceholderFormatterDefinition($name) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + return isset(self::$formatters[$name]) ? self::$formatters[$name] : null; + } + + /** + * Sets a format for a given name. + * + * This method also allow you to override an existing format. + * + * @param string $name The format name + * @param string $format A format string + */ + public static function setFormatDefinition($name, $format) + { + if (!self::$formats) { + self::$formats = self::initFormats(); + } + + self::$formats[$name] = $format; + } + + /** + * Gets the format for a given name. + * + * @param string $name The format name + * + * @return string|null A format string + */ + public static function getFormatDefinition($name) + { + if (!self::$formats) { + self::$formats = self::initFormats(); + } + + return isset(self::$formats[$name]) ? self::$formats[$name] : null; + } + + public function setMessage($message, $name = 'message') + { + $this->messages[$name] = $message; + } + + public function getMessage($name = 'message') + { + return $this->messages[$name]; + } + + /** + * Gets the progress bar start time. + * + * @return int The progress bar start time + */ + public function getStartTime() + { + return $this->startTime; + } + + /** + * Gets the progress bar maximal steps. + * + * @return int The progress bar max steps + */ + public function getMaxSteps() + { + return $this->max; + } + + /** + * Gets the progress bar step. + * + * @deprecated since version 2.6, to be removed in 3.0. Use {@link getProgress()} instead. + * + * @return int The progress bar step + */ + public function getStep() + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the getProgress() method instead.', E_USER_DEPRECATED); + + return $this->getProgress(); + } + + /** + * Gets the current step position. + * + * @return int The progress bar step + */ + public function getProgress() + { + return $this->step; + } + + /** + * Gets the progress bar step width. + * + * @internal This method is public for PHP 5.3 compatibility, it should not be used. + * + * @return int The progress bar step width + */ + public function getStepWidth() + { + return $this->stepWidth; + } + + /** + * Gets the current progress bar percent. + * + * @return float The current progress bar percent + */ + public function getProgressPercent() + { + return $this->percent; + } + + /** + * Sets the progress bar width. + * + * @param int $size The progress bar size + */ + public function setBarWidth($size) + { + $this->barWidth = (int) $size; + } + + /** + * Gets the progress bar width. + * + * @return int The progress bar size + */ + public function getBarWidth() + { + return $this->barWidth; + } + + /** + * Sets the bar character. + * + * @param string $char A character + */ + public function setBarCharacter($char) + { + $this->barChar = $char; + } + + /** + * Gets the bar character. + * + * @return string A character + */ + public function getBarCharacter() + { + if (null === $this->barChar) { + return $this->max ? '=' : $this->emptyBarChar; + } + + return $this->barChar; + } + + /** + * Sets the empty bar character. + * + * @param string $char A character + */ + public function setEmptyBarCharacter($char) + { + $this->emptyBarChar = $char; + } + + /** + * Gets the empty bar character. + * + * @return string A character + */ + public function getEmptyBarCharacter() + { + return $this->emptyBarChar; + } + + /** + * Sets the progress bar character. + * + * @param string $char A character + */ + public function setProgressCharacter($char) + { + $this->progressChar = $char; + } + + /** + * Gets the progress bar character. + * + * @return string A character + */ + public function getProgressCharacter() + { + return $this->progressChar; + } + + /** + * Sets the progress bar format. + * + * @param string $format The format + */ + public function setFormat($format) + { + $this->format = null; + $this->internalFormat = $format; + } + + /** + * Sets the redraw frequency. + * + * @param int|float $freq The frequency in steps + */ + public function setRedrawFrequency($freq) + { + $this->redrawFreq = max((int) $freq, 1); + } + + /** + * Starts the progress output. + * + * @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged + */ + public function start($max = null) + { + $this->startTime = time(); + $this->step = 0; + $this->percent = 0.0; + + if (null !== $max) { + $this->setMaxSteps($max); + } + + $this->display(); + } + + /** + * Advances the progress output X steps. + * + * @param int $step Number of steps to advance + * + * @throws LogicException + */ + public function advance($step = 1) + { + $this->setProgress($this->step + $step); + } + + /** + * Sets the current progress. + * + * @deprecated since version 2.6, to be removed in 3.0. Use {@link setProgress()} instead. + * + * @param int $step The current progress + * + * @throws LogicException + */ + public function setCurrent($step) + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the setProgress() method instead.', E_USER_DEPRECATED); + + $this->setProgress($step); + } + + /** + * Sets whether to overwrite the progressbar, false for new line. + * + * @param bool $overwrite + */ + public function setOverwrite($overwrite) + { + $this->overwrite = (bool) $overwrite; + } + + /** + * Sets the current progress. + * + * @param int $step The current progress + * + * @throws LogicException + */ + public function setProgress($step) + { + $step = (int) $step; + if ($step < $this->step) { + throw new LogicException('You can\'t regress the progress bar.'); + } + + if ($this->max && $step > $this->max) { + $this->max = $step; + } + + $prevPeriod = (int) ($this->step / $this->redrawFreq); + $currPeriod = (int) ($step / $this->redrawFreq); + $this->step = $step; + $this->percent = $this->max ? (float) $this->step / $this->max : 0; + if ($prevPeriod !== $currPeriod || $this->max === $step) { + $this->display(); + } + } + + /** + * Finishes the progress output. + */ + public function finish() + { + if (!$this->max) { + $this->max = $this->step; + } + + if ($this->step === $this->max && !$this->overwrite) { + // prevent double 100% output + return; + } + + $this->setProgress($this->max); + } + + /** + * Outputs the current progress string. + */ + public function display() + { + if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { + return; + } + + if (null === $this->format) { + $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); + } + + // these 3 variables can be removed in favor of using $this in the closure when support for PHP 5.3 will be dropped. + $self = $this; + $output = $this->output; + $messages = $this->messages; + $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self, $output, $messages) { + if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) { + $text = call_user_func($formatter, $self, $output); + } elseif (isset($messages[$matches[1]])) { + $text = $messages[$matches[1]]; + } else { + return $matches[0]; + } + + if (isset($matches[2])) { + $text = sprintf('%'.$matches[2], $text); + } + + return $text; + }, $this->format)); + } + + /** + * Removes the progress bar from the current line. + * + * This is useful if you wish to write some output + * while a progress bar is running. + * Call display() to show the progress bar again. + */ + public function clear() + { + if (!$this->overwrite) { + return; + } + + if (null === $this->format) { + $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); + } + + $this->overwrite(''); + } + + /** + * Sets the progress bar format. + * + * @param string $format The format + */ + private function setRealFormat($format) + { + // try to use the _nomax variant if available + if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) { + $this->format = self::getFormatDefinition($format.'_nomax'); + } elseif (null !== self::getFormatDefinition($format)) { + $this->format = self::getFormatDefinition($format); + } else { + $this->format = $format; + } + + $this->formatLineCount = substr_count($this->format, "\n"); + } + + /** + * Sets the progress bar maximal steps. + * + * @param int $max The progress bar max steps + */ + private function setMaxSteps($max) + { + $this->max = max(0, (int) $max); + $this->stepWidth = $this->max ? Helper::strlen($this->max) : 4; + } + + /** + * Overwrites a previous message to the output. + * + * @param string $message The message + */ + private function overwrite($message) + { + if ($this->overwrite) { + // Move the cursor to the beginning of the line + $this->output->write("\x0D"); + + // Erase the line + $this->output->write("\x1B[2K"); + + // Erase previous lines + if ($this->formatLineCount > 0) { + $this->output->write(str_repeat("\x1B[1A\x1B[2K", $this->formatLineCount)); + } + } elseif ($this->step > 0) { + $this->output->writeln(''); + } + + $this->output->write($message); + } + + private function determineBestFormat() + { + switch ($this->output->getVerbosity()) { + // OutputInterface::VERBOSITY_QUIET: display is disabled anyway + case OutputInterface::VERBOSITY_VERBOSE: + return $this->max ? 'verbose' : 'verbose_nomax'; + case OutputInterface::VERBOSITY_VERY_VERBOSE: + return $this->max ? 'very_verbose' : 'very_verbose_nomax'; + case OutputInterface::VERBOSITY_DEBUG: + return $this->max ? 'debug' : 'debug_nomax'; + default: + return $this->max ? 'normal' : 'normal_nomax'; + } + } + + private static function initPlaceholderFormatters() + { + return array( + 'bar' => function (ProgressBar $bar, OutputInterface $output) { + $completeBars = floor($bar->getMaxSteps() > 0 ? $bar->getProgressPercent() * $bar->getBarWidth() : $bar->getProgress() % $bar->getBarWidth()); + $display = str_repeat($bar->getBarCharacter(), $completeBars); + if ($completeBars < $bar->getBarWidth()) { + $emptyBars = $bar->getBarWidth() - $completeBars - Helper::strlenWithoutDecoration($output->getFormatter(), $bar->getProgressCharacter()); + $display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars); + } + + return $display; + }, + 'elapsed' => function (ProgressBar $bar) { + return Helper::formatTime(time() - $bar->getStartTime()); + }, + 'remaining' => function (ProgressBar $bar) { + if (!$bar->getMaxSteps()) { + throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.'); + } + + if (!$bar->getProgress()) { + $remaining = 0; + } else { + $remaining = round((time() - $bar->getStartTime()) / $bar->getProgress() * ($bar->getMaxSteps() - $bar->getProgress())); + } + + return Helper::formatTime($remaining); + }, + 'estimated' => function (ProgressBar $bar) { + if (!$bar->getMaxSteps()) { + throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.'); + } + + if (!$bar->getProgress()) { + $estimated = 0; + } else { + $estimated = round((time() - $bar->getStartTime()) / $bar->getProgress() * $bar->getMaxSteps()); + } + + return Helper::formatTime($estimated); + }, + 'memory' => function (ProgressBar $bar) { + return Helper::formatMemory(memory_get_usage(true)); + }, + 'current' => function (ProgressBar $bar) { + return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', STR_PAD_LEFT); + }, + 'max' => function (ProgressBar $bar) { + return $bar->getMaxSteps(); + }, + 'percent' => function (ProgressBar $bar) { + return floor($bar->getProgressPercent() * 100); + }, + ); + } + + private static function initFormats() + { + return array( + 'normal' => ' %current%/%max% [%bar%] %percent:3s%%', + 'normal_nomax' => ' %current% [%bar%]', + + 'verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%', + 'verbose_nomax' => ' %current% [%bar%] %elapsed:6s%', + + 'very_verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%', + 'very_verbose_nomax' => ' %current% [%bar%] %elapsed:6s%', + + 'debug' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%', + 'debug_nomax' => ' %current% [%bar%] %elapsed:6s% %memory:6s%', + ); + } +} diff --git a/vendor/symfony/console/Helper/ProgressHelper.php b/vendor/symfony/console/Helper/ProgressHelper.php new file mode 100644 index 00000000..96b6202c --- /dev/null +++ b/vendor/symfony/console/Helper/ProgressHelper.php @@ -0,0 +1,471 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\LogicException; + +/** + * The Progress class provides helpers to display progress output. + * + * @author Chris Jones <leeked@gmail.com> + * @author Fabien Potencier <fabien@symfony.com> + * + * @deprecated since version 2.5, to be removed in 3.0 + * Use {@link ProgressBar} instead. + */ +class ProgressHelper extends Helper +{ + const FORMAT_QUIET = ' %percent%%'; + const FORMAT_NORMAL = ' %current%/%max% [%bar%] %percent%%'; + const FORMAT_VERBOSE = ' %current%/%max% [%bar%] %percent%% Elapsed: %elapsed%'; + const FORMAT_QUIET_NOMAX = ' %current%'; + const FORMAT_NORMAL_NOMAX = ' %current% [%bar%]'; + const FORMAT_VERBOSE_NOMAX = ' %current% [%bar%] Elapsed: %elapsed%'; + + // options + private $barWidth = 28; + private $barChar = '='; + private $emptyBarChar = '-'; + private $progressChar = '>'; + private $format = null; + private $redrawFreq = 1; + + private $lastMessagesLength; + private $barCharOriginal; + + /** + * @var OutputInterface + */ + private $output; + + /** + * Current step. + * + * @var int + */ + private $current; + + /** + * Maximum number of steps. + * + * @var int + */ + private $max; + + /** + * Start time of the progress bar. + * + * @var int + */ + private $startTime; + + /** + * List of formatting variables. + * + * @var array + */ + private $defaultFormatVars = array( + 'current', + 'max', + 'bar', + 'percent', + 'elapsed', + ); + + /** + * Available formatting variables. + * + * @var array + */ + private $formatVars; + + /** + * Stored format part widths (used for padding). + * + * @var array + */ + private $widths = array( + 'current' => 4, + 'max' => 4, + 'percent' => 3, + 'elapsed' => 6, + ); + + /** + * Various time formats. + * + * @var array + */ + private $timeFormats = array( + array(0, '???'), + array(2, '1 sec'), + array(59, 'secs', 1), + array(60, '1 min'), + array(3600, 'mins', 60), + array(5400, '1 hr'), + array(86400, 'hrs', 3600), + array(129600, '1 day'), + array(604800, 'days', 86400), + ); + + public function __construct($triggerDeprecationError = true) + { + if ($triggerDeprecationError) { + @trigger_error('The '.__CLASS__.' class is deprecated since version 2.5 and will be removed in 3.0. Use the Symfony\Component\Console\Helper\ProgressBar class instead.', E_USER_DEPRECATED); + } + } + + /** + * Sets the progress bar width. + * + * @param int $size The progress bar size + */ + public function setBarWidth($size) + { + $this->barWidth = (int) $size; + } + + /** + * Sets the bar character. + * + * @param string $char A character + */ + public function setBarCharacter($char) + { + $this->barChar = $char; + } + + /** + * Sets the empty bar character. + * + * @param string $char A character + */ + public function setEmptyBarCharacter($char) + { + $this->emptyBarChar = $char; + } + + /** + * Sets the progress bar character. + * + * @param string $char A character + */ + public function setProgressCharacter($char) + { + $this->progressChar = $char; + } + + /** + * Sets the progress bar format. + * + * @param string $format The format + */ + public function setFormat($format) + { + $this->format = $format; + } + + /** + * Sets the redraw frequency. + * + * @param int $freq The frequency in steps + */ + public function setRedrawFrequency($freq) + { + $this->redrawFreq = (int) $freq; + } + + /** + * Starts the progress output. + * + * @param OutputInterface $output An Output instance + * @param int|null $max Maximum steps + */ + public function start(OutputInterface $output, $max = null) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $this->startTime = time(); + $this->current = 0; + $this->max = (int) $max; + + // Disabling output when it does not support ANSI codes as it would result in a broken display anyway. + $this->output = $output->isDecorated() ? $output : new NullOutput(); + $this->lastMessagesLength = 0; + $this->barCharOriginal = ''; + + if (null === $this->format) { + switch ($output->getVerbosity()) { + case OutputInterface::VERBOSITY_QUIET: + $this->format = self::FORMAT_QUIET_NOMAX; + if ($this->max > 0) { + $this->format = self::FORMAT_QUIET; + } + break; + case OutputInterface::VERBOSITY_VERBOSE: + case OutputInterface::VERBOSITY_VERY_VERBOSE: + case OutputInterface::VERBOSITY_DEBUG: + $this->format = self::FORMAT_VERBOSE_NOMAX; + if ($this->max > 0) { + $this->format = self::FORMAT_VERBOSE; + } + break; + default: + $this->format = self::FORMAT_NORMAL_NOMAX; + if ($this->max > 0) { + $this->format = self::FORMAT_NORMAL; + } + break; + } + } + + $this->initialize(); + } + + /** + * Advances the progress output X steps. + * + * @param int $step Number of steps to advance + * @param bool $redraw Whether to redraw or not + * + * @throws LogicException + */ + public function advance($step = 1, $redraw = false) + { + $this->setCurrent($this->current + $step, $redraw); + } + + /** + * Sets the current progress. + * + * @param int $current The current progress + * @param bool $redraw Whether to redraw or not + * + * @throws LogicException + */ + public function setCurrent($current, $redraw = false) + { + if (null === $this->startTime) { + throw new LogicException('You must start the progress bar before calling setCurrent().'); + } + + $current = (int) $current; + + if ($current < $this->current) { + throw new LogicException('You can\'t regress the progress bar'); + } + + if (0 === $this->current) { + $redraw = true; + } + + $prevPeriod = (int) ($this->current / $this->redrawFreq); + + $this->current = $current; + + $currPeriod = (int) ($this->current / $this->redrawFreq); + if ($redraw || $prevPeriod !== $currPeriod || $this->max === $this->current) { + $this->display(); + } + } + + /** + * Outputs the current progress string. + * + * @param bool $finish Forces the end result + * + * @throws LogicException + */ + public function display($finish = false) + { + if (null === $this->startTime) { + throw new LogicException('You must start the progress bar before calling display().'); + } + + $message = $this->format; + foreach ($this->generate($finish) as $name => $value) { + $message = str_replace("%{$name}%", $value, $message); + } + $this->overwrite($this->output, $message); + } + + /** + * Removes the progress bar from the current line. + * + * This is useful if you wish to write some output + * while a progress bar is running. + * Call display() to show the progress bar again. + */ + public function clear() + { + $this->overwrite($this->output, ''); + } + + /** + * Finishes the progress output. + */ + public function finish() + { + if (null === $this->startTime) { + throw new LogicException('You must start the progress bar before calling finish().'); + } + + if (null !== $this->startTime) { + if (!$this->max) { + $this->barChar = $this->barCharOriginal; + $this->display(true); + } + $this->startTime = null; + $this->output->writeln(''); + $this->output = null; + } + } + + /** + * Initializes the progress helper. + */ + private function initialize() + { + $this->formatVars = array(); + foreach ($this->defaultFormatVars as $var) { + if (false !== strpos($this->format, "%{$var}%")) { + $this->formatVars[$var] = true; + } + } + + if ($this->max > 0) { + $this->widths['max'] = $this->strlen($this->max); + $this->widths['current'] = $this->widths['max']; + } else { + $this->barCharOriginal = $this->barChar; + $this->barChar = $this->emptyBarChar; + } + } + + /** + * Generates the array map of format variables to values. + * + * @param bool $finish Forces the end result + * + * @return array Array of format vars and values + */ + private function generate($finish = false) + { + $vars = array(); + $percent = 0; + if ($this->max > 0) { + $percent = (float) $this->current / $this->max; + } + + if (isset($this->formatVars['bar'])) { + $completeBars = 0; + + if ($this->max > 0) { + $completeBars = floor($percent * $this->barWidth); + } else { + if (!$finish) { + $completeBars = floor($this->current % $this->barWidth); + } else { + $completeBars = $this->barWidth; + } + } + + $emptyBars = $this->barWidth - $completeBars - $this->strlen($this->progressChar); + $bar = str_repeat($this->barChar, $completeBars); + if ($completeBars < $this->barWidth) { + $bar .= $this->progressChar; + $bar .= str_repeat($this->emptyBarChar, $emptyBars); + } + + $vars['bar'] = $bar; + } + + if (isset($this->formatVars['elapsed'])) { + $elapsed = time() - $this->startTime; + $vars['elapsed'] = str_pad($this->humaneTime($elapsed), $this->widths['elapsed'], ' ', STR_PAD_LEFT); + } + + if (isset($this->formatVars['current'])) { + $vars['current'] = str_pad($this->current, $this->widths['current'], ' ', STR_PAD_LEFT); + } + + if (isset($this->formatVars['max'])) { + $vars['max'] = $this->max; + } + + if (isset($this->formatVars['percent'])) { + $vars['percent'] = str_pad(floor($percent * 100), $this->widths['percent'], ' ', STR_PAD_LEFT); + } + + return $vars; + } + + /** + * Converts seconds into human-readable format. + * + * @param int $secs Number of seconds + * + * @return string Time in readable format + */ + private function humaneTime($secs) + { + $text = ''; + foreach ($this->timeFormats as $format) { + if ($secs < $format[0]) { + if (count($format) == 2) { + $text = $format[1]; + break; + } else { + $text = ceil($secs / $format[2]).' '.$format[1]; + break; + } + } + } + + return $text; + } + + /** + * Overwrites a previous message to the output. + * + * @param OutputInterface $output An Output instance + * @param string $message The message + */ + private function overwrite(OutputInterface $output, $message) + { + $length = $this->strlen($message); + + // append whitespace to match the last line's length + if (null !== $this->lastMessagesLength && $this->lastMessagesLength > $length) { + $message = str_pad($message, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT); + } + + // carriage return + $output->write("\x0D"); + $output->write($message); + + $this->lastMessagesLength = $this->strlen($message); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'progress'; + } +} diff --git a/vendor/symfony/console/Helper/ProgressIndicator.php b/vendor/symfony/console/Helper/ProgressIndicator.php new file mode 100644 index 00000000..ccf9771b --- /dev/null +++ b/vendor/symfony/console/Helper/ProgressIndicator.php @@ -0,0 +1,324 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Kevin Bond <kevinbond@gmail.com> + */ +class ProgressIndicator +{ + private $output; + private $startTime; + private $format; + private $message; + private $indicatorValues; + private $indicatorCurrent; + private $indicatorChangeInterval; + private $indicatorUpdateTime; + private $lastMessagesLength; + private $started = false; + + private static $formatters; + private static $formats; + + /** + * @param OutputInterface $output + * @param string|null $format Indicator format + * @param int $indicatorChangeInterval Change interval in milliseconds + * @param array|null $indicatorValues Animated indicator characters + */ + public function __construct(OutputInterface $output, $format = null, $indicatorChangeInterval = 100, $indicatorValues = null) + { + $this->output = $output; + + if (null === $format) { + $format = $this->determineBestFormat(); + } + + if (null === $indicatorValues) { + $indicatorValues = array('-', '\\', '|', '/'); + } + + $indicatorValues = array_values($indicatorValues); + + if (2 > count($indicatorValues)) { + throw new InvalidArgumentException('Must have at least 2 indicator value characters.'); + } + + $this->format = self::getFormatDefinition($format); + $this->indicatorChangeInterval = $indicatorChangeInterval; + $this->indicatorValues = $indicatorValues; + $this->startTime = time(); + } + + /** + * Sets the current indicator message. + * + * @param string|null $message + */ + public function setMessage($message) + { + $this->message = $message; + + $this->display(); + } + + /** + * Gets the current indicator message. + * + * @return string|null + * + * @internal for PHP 5.3 compatibility + */ + public function getMessage() + { + return $this->message; + } + + /** + * Gets the progress bar start time. + * + * @return int The progress bar start time + * + * @internal for PHP 5.3 compatibility + */ + public function getStartTime() + { + return $this->startTime; + } + + /** + * Gets the current animated indicator character. + * + * @return string + * + * @internal for PHP 5.3 compatibility + */ + public function getCurrentValue() + { + return $this->indicatorValues[$this->indicatorCurrent % count($this->indicatorValues)]; + } + + /** + * Starts the indicator output. + * + * @param $message + */ + public function start($message) + { + if ($this->started) { + throw new LogicException('Progress indicator already started.'); + } + + $this->message = $message; + $this->started = true; + $this->lastMessagesLength = 0; + $this->startTime = time(); + $this->indicatorUpdateTime = $this->getCurrentTimeInMilliseconds() + $this->indicatorChangeInterval; + $this->indicatorCurrent = 0; + + $this->display(); + } + + /** + * Advances the indicator. + */ + public function advance() + { + if (!$this->started) { + throw new LogicException('Progress indicator has not yet been started.'); + } + + if (!$this->output->isDecorated()) { + return; + } + + $currentTime = $this->getCurrentTimeInMilliseconds(); + + if ($currentTime < $this->indicatorUpdateTime) { + return; + } + + $this->indicatorUpdateTime = $currentTime + $this->indicatorChangeInterval; + ++$this->indicatorCurrent; + + $this->display(); + } + + /** + * Finish the indicator with message. + * + * @param $message + */ + public function finish($message) + { + if (!$this->started) { + throw new LogicException('Progress indicator has not yet been started.'); + } + + $this->message = $message; + $this->display(); + $this->output->writeln(''); + $this->started = false; + } + + /** + * Gets the format for a given name. + * + * @param string $name The format name + * + * @return string|null A format string + */ + public static function getFormatDefinition($name) + { + if (!self::$formats) { + self::$formats = self::initFormats(); + } + + return isset(self::$formats[$name]) ? self::$formats[$name] : null; + } + + /** + * Sets a placeholder formatter for a given name. + * + * This method also allow you to override an existing placeholder. + * + * @param string $name The placeholder name (including the delimiter char like %) + * @param callable $callable A PHP callable + */ + public static function setPlaceholderFormatterDefinition($name, $callable) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + self::$formatters[$name] = $callable; + } + + /** + * Gets the placeholder formatter for a given name. + * + * @param string $name The placeholder name (including the delimiter char like %) + * + * @return callable|null A PHP callable + */ + public static function getPlaceholderFormatterDefinition($name) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + return isset(self::$formatters[$name]) ? self::$formatters[$name] : null; + } + + private function display() + { + if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { + return; + } + + $self = $this; + + $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self) { + if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) { + return call_user_func($formatter, $self); + } + + return $matches[0]; + }, $this->format)); + } + + private function determineBestFormat() + { + switch ($this->output->getVerbosity()) { + // OutputInterface::VERBOSITY_QUIET: display is disabled anyway + case OutputInterface::VERBOSITY_VERBOSE: + return $this->output->isDecorated() ? 'verbose' : 'verbose_no_ansi'; + case OutputInterface::VERBOSITY_VERY_VERBOSE: + case OutputInterface::VERBOSITY_DEBUG: + return $this->output->isDecorated() ? 'very_verbose' : 'very_verbose_no_ansi'; + default: + return $this->output->isDecorated() ? 'normal' : 'normal_no_ansi'; + } + } + + /** + * Overwrites a previous message to the output. + * + * @param string $message The message + */ + private function overwrite($message) + { + // append whitespace to match the line's length + if (null !== $this->lastMessagesLength) { + if ($this->lastMessagesLength > Helper::strlenWithoutDecoration($this->output->getFormatter(), $message)) { + $message = str_pad($message, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT); + } + } + + if ($this->output->isDecorated()) { + $this->output->write("\x0D"); + $this->output->write($message); + } else { + $this->output->writeln($message); + } + + $this->lastMessagesLength = 0; + + $len = Helper::strlenWithoutDecoration($this->output->getFormatter(), $message); + + if ($len > $this->lastMessagesLength) { + $this->lastMessagesLength = $len; + } + } + + private function getCurrentTimeInMilliseconds() + { + return round(microtime(true) * 1000); + } + + private static function initPlaceholderFormatters() + { + return array( + 'indicator' => function (ProgressIndicator $indicator) { + return $indicator->getCurrentValue(); + }, + 'message' => function (ProgressIndicator $indicator) { + return $indicator->getMessage(); + }, + 'elapsed' => function (ProgressIndicator $indicator) { + return Helper::formatTime(time() - $indicator->getStartTime()); + }, + 'memory' => function () { + return Helper::formatMemory(memory_get_usage(true)); + }, + ); + } + + private static function initFormats() + { + return array( + 'normal' => ' %indicator% %message%', + 'normal_no_ansi' => ' %message%', + + 'verbose' => ' %indicator% %message% (%elapsed:6s%)', + 'verbose_no_ansi' => ' %message% (%elapsed:6s%)', + + 'very_verbose' => ' %indicator% %message% (%elapsed:6s%, %memory:6s%)', + 'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)', + ); + } +} diff --git a/vendor/symfony/console/Helper/QuestionHelper.php b/vendor/symfony/console/Helper/QuestionHelper.php new file mode 100644 index 00000000..5bb30df8 --- /dev/null +++ b/vendor/symfony/console/Helper/QuestionHelper.php @@ -0,0 +1,449 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Question\ChoiceQuestion; + +/** + * The QuestionHelper class provides helpers to interact with the user. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class QuestionHelper extends Helper +{ + private $inputStream; + private static $shell; + private static $stty; + + /** + * Asks a question to the user. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * @param Question $question The question to ask + * + * @return string The user answer + * + * @throws RuntimeException If there is no data to read in the input stream + */ + public function ask(InputInterface $input, OutputInterface $output, Question $question) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + if (!$input->isInteractive()) { + return $question->getDefault(); + } + + if (!$question->getValidator()) { + return $this->doAsk($output, $question); + } + + $that = $this; + + $interviewer = function () use ($output, $question, $that) { + return $that->doAsk($output, $question); + }; + + return $this->validateAttempts($interviewer, $output, $question); + } + + /** + * Sets the input stream to read from when interacting with the user. + * + * This is mainly useful for testing purpose. + * + * @param resource $stream The input stream + * + * @throws InvalidArgumentException In case the stream is not a resource + */ + public function setInputStream($stream) + { + if (!is_resource($stream)) { + throw new InvalidArgumentException('Input stream must be a valid resource.'); + } + + $this->inputStream = $stream; + } + + /** + * Returns the helper's input stream. + * + * @return resource + */ + public function getInputStream() + { + return $this->inputStream; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'question'; + } + + /** + * Asks the question to the user. + * + * This method is public for PHP 5.3 compatibility, it should be private. + * + * @param OutputInterface $output + * @param Question $question + * + * @return bool|mixed|null|string + * + * @throws \Exception + * @throws \RuntimeException + */ + public function doAsk(OutputInterface $output, Question $question) + { + $this->writePrompt($output, $question); + + $inputStream = $this->inputStream ?: STDIN; + $autocomplete = $question->getAutocompleterValues(); + + if (null === $autocomplete || !$this->hasSttyAvailable()) { + $ret = false; + if ($question->isHidden()) { + try { + $ret = trim($this->getHiddenResponse($output, $inputStream)); + } catch (\RuntimeException $e) { + if (!$question->isHiddenFallback()) { + throw $e; + } + } + } + + if (false === $ret) { + $ret = fgets($inputStream, 4096); + if (false === $ret) { + throw new \RuntimeException('Aborted'); + } + $ret = trim($ret); + } + } else { + $ret = trim($this->autocomplete($output, $question, $inputStream)); + } + + $ret = strlen($ret) > 0 ? $ret : $question->getDefault(); + + if ($normalizer = $question->getNormalizer()) { + return $normalizer($ret); + } + + return $ret; + } + + /** + * Outputs the question prompt. + * + * @param OutputInterface $output + * @param Question $question + */ + protected function writePrompt(OutputInterface $output, Question $question) + { + $message = $question->getQuestion(); + + if ($question instanceof ChoiceQuestion) { + $maxWidth = max(array_map(array($this, 'strlen'), array_keys($question->getChoices()))); + + $messages = (array) $question->getQuestion(); + foreach ($question->getChoices() as $key => $value) { + $width = $maxWidth - $this->strlen($key); + $messages[] = ' [<info>'.$key.str_repeat(' ', $width).'</info>] '.$value; + } + + $output->writeln($messages); + + $message = $question->getPrompt(); + } + + $output->write($message); + } + + /** + * Outputs an error message. + * + * @param OutputInterface $output + * @param \Exception $error + */ + protected function writeError(OutputInterface $output, \Exception $error) + { + if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) { + $message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'); + } else { + $message = '<error>'.$error->getMessage().'</error>'; + } + + $output->writeln($message); + } + + /** + * Autocompletes a question. + * + * @param OutputInterface $output + * @param Question $question + * + * @return string + */ + private function autocomplete(OutputInterface $output, Question $question, $inputStream) + { + $autocomplete = $question->getAutocompleterValues(); + $ret = ''; + + $i = 0; + $ofs = -1; + $matches = $autocomplete; + $numMatches = count($matches); + + $sttyMode = shell_exec('stty -g'); + + // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead) + shell_exec('stty -icanon -echo'); + + // Add highlighted text style + $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white')); + + // Read a keypress + while (!feof($inputStream)) { + $c = fread($inputStream, 1); + + // Backspace Character + if ("\177" === $c) { + if (0 === $numMatches && 0 !== $i) { + --$i; + // Move cursor backwards + $output->write("\033[1D"); + } + + if ($i === 0) { + $ofs = -1; + $matches = $autocomplete; + $numMatches = count($matches); + } else { + $numMatches = 0; + } + + // Pop the last character off the end of our string + $ret = substr($ret, 0, $i); + } elseif ("\033" === $c) { + // Did we read an escape sequence? + $c .= fread($inputStream, 2); + + // A = Up Arrow. B = Down Arrow + if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) { + if ('A' === $c[2] && -1 === $ofs) { + $ofs = 0; + } + + if (0 === $numMatches) { + continue; + } + + $ofs += ('A' === $c[2]) ? -1 : 1; + $ofs = ($numMatches + $ofs) % $numMatches; + } + } elseif (ord($c) < 32) { + if ("\t" === $c || "\n" === $c) { + if ($numMatches > 0 && -1 !== $ofs) { + $ret = $matches[$ofs]; + // Echo out remaining chars for current match + $output->write(substr($ret, $i)); + $i = strlen($ret); + } + + if ("\n" === $c) { + $output->write($c); + break; + } + + $numMatches = 0; + } + + continue; + } else { + $output->write($c); + $ret .= $c; + ++$i; + + $numMatches = 0; + $ofs = 0; + + foreach ($autocomplete as $value) { + // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle) + if (0 === strpos($value, $ret) && $i !== strlen($value)) { + $matches[$numMatches++] = $value; + } + } + } + + // Erase characters from cursor to end of line + $output->write("\033[K"); + + if ($numMatches > 0 && -1 !== $ofs) { + // Save cursor position + $output->write("\0337"); + // Write highlighted text + $output->write('<hl>'.substr($matches[$ofs], $i).'</hl>'); + // Restore cursor position + $output->write("\0338"); + } + } + + // Reset stty so it behaves normally again + shell_exec(sprintf('stty %s', $sttyMode)); + + return $ret; + } + + /** + * Gets a hidden response from user. + * + * @param OutputInterface $output An Output instance + * + * @return string The answer + * + * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden + */ + private function getHiddenResponse(OutputInterface $output, $inputStream) + { + if ('\\' === DIRECTORY_SEPARATOR) { + $exe = __DIR__.'/../Resources/bin/hiddeninput.exe'; + + // handle code running from a phar + if ('phar:' === substr(__FILE__, 0, 5)) { + $tmpExe = sys_get_temp_dir().'/hiddeninput.exe'; + copy($exe, $tmpExe); + $exe = $tmpExe; + } + + $value = rtrim(shell_exec($exe)); + $output->writeln(''); + + if (isset($tmpExe)) { + unlink($tmpExe); + } + + return $value; + } + + if ($this->hasSttyAvailable()) { + $sttyMode = shell_exec('stty -g'); + + shell_exec('stty -echo'); + $value = fgets($inputStream, 4096); + shell_exec(sprintf('stty %s', $sttyMode)); + + if (false === $value) { + throw new RuntimeException('Aborted'); + } + + $value = trim($value); + $output->writeln(''); + + return $value; + } + + if (false !== $shell = $this->getShell()) { + $readCmd = $shell === 'csh' ? 'set mypassword = $<' : 'read -r mypassword'; + $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd); + $value = rtrim(shell_exec($command)); + $output->writeln(''); + + return $value; + } + + throw new RuntimeException('Unable to hide the response.'); + } + + /** + * Validates an attempt. + * + * @param callable $interviewer A callable that will ask for a question and return the result + * @param OutputInterface $output An Output instance + * @param Question $question A Question instance + * + * @return string The validated response + * + * @throws \Exception In case the max number of attempts has been reached and no valid response has been given + */ + private function validateAttempts($interviewer, OutputInterface $output, Question $question) + { + $error = null; + $attempts = $question->getMaxAttempts(); + while (null === $attempts || $attempts--) { + if (null !== $error) { + $this->writeError($output, $error); + } + + try { + return call_user_func($question->getValidator(), $interviewer()); + } catch (\Exception $error) { + } + } + + throw $error; + } + + /** + * Returns a valid unix shell. + * + * @return string|bool The valid shell name, false in case no valid shell is found + */ + private function getShell() + { + if (null !== self::$shell) { + return self::$shell; + } + + self::$shell = false; + + if (file_exists('/usr/bin/env')) { + // handle other OSs with bash/zsh/ksh/csh if available to hide the answer + $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null"; + foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) { + if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) { + self::$shell = $sh; + break; + } + } + } + + return self::$shell; + } + + /** + * Returns whether Stty is available or not. + * + * @return bool + */ + private function hasSttyAvailable() + { + if (null !== self::$stty) { + return self::$stty; + } + + exec('stty 2>&1', $output, $exitcode); + + return self::$stty = $exitcode === 0; + } +} diff --git a/vendor/symfony/console/Helper/SymfonyQuestionHelper.php b/vendor/symfony/console/Helper/SymfonyQuestionHelper.php new file mode 100644 index 00000000..942278bd --- /dev/null +++ b/vendor/symfony/console/Helper/SymfonyQuestionHelper.php @@ -0,0 +1,107 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * Symfony Style Guide compliant question helper. + * + * @author Kevin Bond <kevinbond@gmail.com> + */ +class SymfonyQuestionHelper extends QuestionHelper +{ + /** + * {@inheritdoc} + */ + public function ask(InputInterface $input, OutputInterface $output, Question $question) + { + $validator = $question->getValidator(); + $question->setValidator(function ($value) use ($validator) { + if (null !== $validator) { + $value = $validator($value); + } + + // make required + if (!is_array($value) && !is_bool($value) && 0 === strlen($value)) { + throw new LogicException('A value is required.'); + } + + return $value; + }); + + return parent::ask($input, $output, $question); + } + + /** + * {@inheritdoc} + */ + protected function writePrompt(OutputInterface $output, Question $question) + { + $text = $question->getQuestion(); + $default = $question->getDefault(); + + switch (true) { + case null === $default: + $text = sprintf(' <info>%s</info>:', $text); + + break; + + case $question instanceof ConfirmationQuestion: + $text = sprintf(' <info>%s (yes/no)</info> [<comment>%s</comment>]:', $text, $default ? 'yes' : 'no'); + + break; + + case $question instanceof ChoiceQuestion: + $choices = $question->getChoices(); + $text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, $choices[$default]); + + break; + + default: + $text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, $default); + } + + $output->writeln($text); + + if ($question instanceof ChoiceQuestion) { + $width = max(array_map('strlen', array_keys($question->getChoices()))); + + foreach ($question->getChoices() as $key => $value) { + $output->writeln(sprintf(" [<comment>%-${width}s</comment>] %s", $key, $value)); + } + } + + $output->write(' > '); + } + + /** + * {@inheritdoc} + */ + protected function writeError(OutputInterface $output, \Exception $error) + { + if ($output instanceof SymfonyStyle) { + $output->newLine(); + $output->error($error->getMessage()); + + return; + } + + parent::writeError($output, $error); + } +} diff --git a/vendor/symfony/console/Helper/Table.php b/vendor/symfony/console/Helper/Table.php new file mode 100644 index 00000000..13e4c3cf --- /dev/null +++ b/vendor/symfony/console/Helper/Table.php @@ -0,0 +1,663 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Provides helpers to display a table. + * + * @author Fabien Potencier <fabien@symfony.com> + * @author Саша Стаменковић <umpirsky@gmail.com> + * @author Abdellatif Ait boudad <a.aitboudad@gmail.com> + * @author Max Grigorian <maxakawizard@gmail.com> + */ +class Table +{ + /** + * Table headers. + * + * @var array + */ + private $headers = array(); + + /** + * Table rows. + * + * @var array + */ + private $rows = array(); + + /** + * Column widths cache. + * + * @var array + */ + private $columnWidths = array(); + + /** + * Number of columns cache. + * + * @var array + */ + private $numberOfColumns; + + /** + * @var OutputInterface + */ + private $output; + + /** + * @var TableStyle + */ + private $style; + + /** + * @var array + */ + private $columnStyles = array(); + + private static $styles; + + public function __construct(OutputInterface $output) + { + $this->output = $output; + + if (!self::$styles) { + self::$styles = self::initStyles(); + } + + $this->setStyle('default'); + } + + /** + * Sets a style definition. + * + * @param string $name The style name + * @param TableStyle $style A TableStyle instance + */ + public static function setStyleDefinition($name, TableStyle $style) + { + if (!self::$styles) { + self::$styles = self::initStyles(); + } + + self::$styles[$name] = $style; + } + + /** + * Gets a style definition by name. + * + * @param string $name The style name + * + * @return TableStyle A TableStyle instance + */ + public static function getStyleDefinition($name) + { + if (!self::$styles) { + self::$styles = self::initStyles(); + } + + if (!self::$styles[$name]) { + throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + } + + return self::$styles[$name]; + } + + /** + * Sets table style. + * + * @param TableStyle|string $name The style name or a TableStyle instance + * + * @return Table + */ + public function setStyle($name) + { + if ($name instanceof TableStyle) { + $this->style = $name; + } elseif (isset(self::$styles[$name])) { + $this->style = self::$styles[$name]; + } else { + throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + } + + return $this; + } + + /** + * Gets the current table style. + * + * @return TableStyle + */ + public function getStyle() + { + return $this->style; + } + + /** + * Sets table column style. + * + * @param int $columnIndex Column index + * @param TableStyle|string $name The style name or a TableStyle instance + * + * @return Table + */ + public function setColumnStyle($columnIndex, $name) + { + $columnIndex = intval($columnIndex); + + if ($name instanceof TableStyle) { + $this->columnStyles[$columnIndex] = $name; + } elseif (isset(self::$styles[$name])) { + $this->columnStyles[$columnIndex] = self::$styles[$name]; + } else { + throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + } + + return $this; + } + + /** + * Gets the current style for a column. + * + * If style was not set, it returns the global table style. + * + * @param int $columnIndex Column index + * + * @return TableStyle + */ + public function getColumnStyle($columnIndex) + { + if (isset($this->columnStyles[$columnIndex])) { + return $this->columnStyles[$columnIndex]; + } + + return $this->getStyle(); + } + + public function setHeaders(array $headers) + { + $headers = array_values($headers); + if (!empty($headers) && !is_array($headers[0])) { + $headers = array($headers); + } + + $this->headers = $headers; + + return $this; + } + + public function setRows(array $rows) + { + $this->rows = array(); + + return $this->addRows($rows); + } + + public function addRows(array $rows) + { + foreach ($rows as $row) { + $this->addRow($row); + } + + return $this; + } + + public function addRow($row) + { + if ($row instanceof TableSeparator) { + $this->rows[] = $row; + + return $this; + } + + if (!is_array($row)) { + throw new InvalidArgumentException('A row must be an array or a TableSeparator instance.'); + } + + $this->rows[] = array_values($row); + + return $this; + } + + public function setRow($column, array $row) + { + $this->rows[$column] = $row; + + return $this; + } + + /** + * Renders table to output. + * + * Example: + * +---------------+-----------------------+------------------+ + * | ISBN | Title | Author | + * +---------------+-----------------------+------------------+ + * | 99921-58-10-7 | Divine Comedy | Dante Alighieri | + * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | + * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | + * +---------------+-----------------------+------------------+ + */ + public function render() + { + $this->calculateNumberOfColumns(); + $rows = $this->buildTableRows($this->rows); + $headers = $this->buildTableRows($this->headers); + + $this->calculateColumnsWidth(array_merge($headers, $rows)); + + $this->renderRowSeparator(); + if (!empty($headers)) { + foreach ($headers as $header) { + $this->renderRow($header, $this->style->getCellHeaderFormat()); + $this->renderRowSeparator(); + } + } + foreach ($rows as $row) { + if ($row instanceof TableSeparator) { + $this->renderRowSeparator(); + } else { + $this->renderRow($row, $this->style->getCellRowFormat()); + } + } + if (!empty($rows)) { + $this->renderRowSeparator(); + } + + $this->cleanup(); + } + + /** + * Renders horizontal header separator. + * + * Example: +-----+-----------+-------+ + */ + private function renderRowSeparator() + { + if (0 === $count = $this->numberOfColumns) { + return; + } + + if (!$this->style->getHorizontalBorderChar() && !$this->style->getCrossingChar()) { + return; + } + + $markup = $this->style->getCrossingChar(); + for ($column = 0; $column < $count; ++$column) { + $markup .= str_repeat($this->style->getHorizontalBorderChar(), $this->columnWidths[$column]).$this->style->getCrossingChar(); + } + + $this->output->writeln(sprintf($this->style->getBorderFormat(), $markup)); + } + + /** + * Renders vertical column separator. + */ + private function renderColumnSeparator() + { + return sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar()); + } + + /** + * Renders table row. + * + * Example: | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | + * + * @param array $row + * @param string $cellFormat + */ + private function renderRow(array $row, $cellFormat) + { + if (empty($row)) { + return; + } + + $rowContent = $this->renderColumnSeparator(); + foreach ($this->getRowColumns($row) as $column) { + $rowContent .= $this->renderCell($row, $column, $cellFormat); + $rowContent .= $this->renderColumnSeparator(); + } + $this->output->writeln($rowContent); + } + + /** + * Renders table cell with padding. + * + * @param array $row + * @param int $column + * @param string $cellFormat + */ + private function renderCell(array $row, $column, $cellFormat) + { + $cell = isset($row[$column]) ? $row[$column] : ''; + $width = $this->columnWidths[$column]; + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + // add the width of the following columns(numbers of colspan). + foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) { + $width += $this->getColumnSeparatorWidth() + $this->columnWidths[$nextColumn]; + } + } + + // str_pad won't work properly with multi-byte strings, we need to fix the padding + if (false !== $encoding = mb_detect_encoding($cell, null, true)) { + $width += strlen($cell) - mb_strwidth($cell, $encoding); + } + + $style = $this->getColumnStyle($column); + + if ($cell instanceof TableSeparator) { + return sprintf($style->getBorderFormat(), str_repeat($style->getHorizontalBorderChar(), $width)); + } + + $width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); + $content = sprintf($style->getCellRowContentFormat(), $cell); + + return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $style->getPadType())); + } + + /** + * Calculate number of columns for this table. + */ + private function calculateNumberOfColumns() + { + if (null !== $this->numberOfColumns) { + return; + } + + $columns = array(0); + foreach (array_merge($this->headers, $this->rows) as $row) { + if ($row instanceof TableSeparator) { + continue; + } + + $columns[] = $this->getNumberOfColumns($row); + } + + $this->numberOfColumns = max($columns); + } + + private function buildTableRows($rows) + { + $unmergedRows = array(); + for ($rowKey = 0; $rowKey < count($rows); ++$rowKey) { + $rows = $this->fillNextRows($rows, $rowKey); + + // Remove any new line breaks and replace it with a new line + foreach ($rows[$rowKey] as $column => $cell) { + if (!strstr($cell, "\n")) { + continue; + } + $lines = explode("\n", $cell); + foreach ($lines as $lineKey => $line) { + if ($cell instanceof TableCell) { + $line = new TableCell($line, array('colspan' => $cell->getColspan())); + } + if (0 === $lineKey) { + $rows[$rowKey][$column] = $line; + } else { + $unmergedRows[$rowKey][$lineKey][$column] = $line; + } + } + } + } + + $tableRows = array(); + foreach ($rows as $rowKey => $row) { + $tableRows[] = $this->fillCells($row); + if (isset($unmergedRows[$rowKey])) { + $tableRows = array_merge($tableRows, $unmergedRows[$rowKey]); + } + } + + return $tableRows; + } + + /** + * fill rows that contains rowspan > 1. + * + * @param array $rows + * @param int $line + * + * @return array + */ + private function fillNextRows($rows, $line) + { + $unmergedRows = array(); + foreach ($rows[$line] as $column => $cell) { + if ($cell instanceof TableCell && $cell->getRowspan() > 1) { + $nbLines = $cell->getRowspan() - 1; + $lines = array($cell); + if (strstr($cell, "\n")) { + $lines = explode("\n", $cell); + $nbLines = count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines; + + $rows[$line][$column] = new TableCell($lines[0], array('colspan' => $cell->getColspan())); + unset($lines[0]); + } + + // create a two dimensional array (rowspan x colspan) + $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, ''), $unmergedRows); + foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { + $value = isset($lines[$unmergedRowKey - $line]) ? $lines[$unmergedRowKey - $line] : ''; + $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, array('colspan' => $cell->getColspan())); + } + } + } + + foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { + // we need to know if $unmergedRow will be merged or inserted into $rows + if (isset($rows[$unmergedRowKey]) && is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) { + foreach ($unmergedRow as $cellKey => $cell) { + // insert cell into row at cellKey position + array_splice($rows[$unmergedRowKey], $cellKey, 0, array($cell)); + } + } else { + $row = $this->copyRow($rows, $unmergedRowKey - 1); + foreach ($unmergedRow as $column => $cell) { + if (!empty($cell)) { + $row[$column] = $unmergedRow[$column]; + } + } + array_splice($rows, $unmergedRowKey, 0, array($row)); + } + } + + return $rows; + } + + /** + * fill cells for a row that contains colspan > 1. + * + * @param array $row + * + * @return array + */ + private function fillCells($row) + { + $newRow = array(); + foreach ($row as $column => $cell) { + $newRow[] = $cell; + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) { + // insert empty value at column position + $newRow[] = ''; + } + } + } + + return $newRow ?: $row; + } + + /** + * @param array $rows + * @param int $line + * + * @return array + */ + private function copyRow($rows, $line) + { + $row = $rows[$line]; + foreach ($row as $cellKey => $cellValue) { + $row[$cellKey] = ''; + if ($cellValue instanceof TableCell) { + $row[$cellKey] = new TableCell('', array('colspan' => $cellValue->getColspan())); + } + } + + return $row; + } + + /** + * Gets number of columns by row. + * + * @param array $row + * + * @return int + */ + private function getNumberOfColumns(array $row) + { + $columns = count($row); + foreach ($row as $column) { + $columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0; + } + + return $columns; + } + + /** + * Gets list of columns for the given row. + * + * @param array $row + * + * @return array + */ + private function getRowColumns($row) + { + $columns = range(0, $this->numberOfColumns - 1); + foreach ($row as $cellKey => $cell) { + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + // exclude grouped columns. + $columns = array_diff($columns, range($cellKey + 1, $cellKey + $cell->getColspan() - 1)); + } + } + + return $columns; + } + + /** + * Calculates columns widths. + * + * @param array $rows + */ + private function calculateColumnsWidth($rows) + { + for ($column = 0; $column < $this->numberOfColumns; ++$column) { + $lengths = array(); + foreach ($rows as $row) { + if ($row instanceof TableSeparator) { + continue; + } + + foreach ($row as $i => $cell) { + if ($cell instanceof TableCell) { + $textLength = strlen($cell); + if ($textLength > 0) { + $contentColumns = str_split($cell, ceil($textLength / $cell->getColspan())); + foreach ($contentColumns as $position => $content) { + $row[$i + $position] = $content; + } + } + } + } + + $lengths[] = $this->getCellWidth($row, $column); + } + + $this->columnWidths[$column] = max($lengths) + strlen($this->style->getCellRowContentFormat()) - 2; + } + } + + /** + * Gets column width. + * + * @return int + */ + private function getColumnSeparatorWidth() + { + return strlen(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar())); + } + + /** + * Gets cell width. + * + * @param array $row + * @param int $column + * + * @return int + */ + private function getCellWidth(array $row, $column) + { + if (isset($row[$column])) { + $cell = $row[$column]; + $cellWidth = Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); + + return $cellWidth; + } + + return 0; + } + + /** + * Called after rendering to cleanup cache data. + */ + private function cleanup() + { + $this->columnWidths = array(); + $this->numberOfColumns = null; + } + + private static function initStyles() + { + $borderless = new TableStyle(); + $borderless + ->setHorizontalBorderChar('=') + ->setVerticalBorderChar(' ') + ->setCrossingChar(' ') + ; + + $compact = new TableStyle(); + $compact + ->setHorizontalBorderChar('') + ->setVerticalBorderChar(' ') + ->setCrossingChar('') + ->setCellRowContentFormat('%s') + ; + + $styleGuide = new TableStyle(); + $styleGuide + ->setHorizontalBorderChar('-') + ->setVerticalBorderChar(' ') + ->setCrossingChar(' ') + ->setCellHeaderFormat('%s') + ; + + return array( + 'default' => new TableStyle(), + 'borderless' => $borderless, + 'compact' => $compact, + 'symfony-style-guide' => $styleGuide, + ); + } +} diff --git a/vendor/symfony/console/Helper/TableCell.php b/vendor/symfony/console/Helper/TableCell.php new file mode 100644 index 00000000..69442d42 --- /dev/null +++ b/vendor/symfony/console/Helper/TableCell.php @@ -0,0 +1,79 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Abdellatif Ait boudad <a.aitboudad@gmail.com> + */ +class TableCell +{ + /** + * @var string + */ + private $value; + + /** + * @var array + */ + private $options = array( + 'rowspan' => 1, + 'colspan' => 1, + ); + + /** + * @param string $value + * @param array $options + */ + public function __construct($value = '', array $options = array()) + { + $this->value = $value; + + // check option names + if ($diff = array_diff(array_keys($options), array_keys($this->options))) { + throw new InvalidArgumentException(sprintf('The TableCell does not support the following options: \'%s\'.', implode('\', \'', $diff))); + } + + $this->options = array_merge($this->options, $options); + } + + /** + * Returns the cell value. + * + * @return string + */ + public function __toString() + { + return $this->value; + } + + /** + * Gets number of colspan. + * + * @return int + */ + public function getColspan() + { + return (int) $this->options['colspan']; + } + + /** + * Gets number of rowspan. + * + * @return int + */ + public function getRowspan() + { + return (int) $this->options['rowspan']; + } +} diff --git a/vendor/symfony/console/Helper/TableHelper.php b/vendor/symfony/console/Helper/TableHelper.php new file mode 100644 index 00000000..3c7a1a78 --- /dev/null +++ b/vendor/symfony/console/Helper/TableHelper.php @@ -0,0 +1,269 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Provides helpers to display table output. + * + * @author Саша Стаменковић <umpirsky@gmail.com> + * @author Fabien Potencier <fabien@symfony.com> + * + * @deprecated since version 2.5, to be removed in 3.0 + * Use {@link Table} instead. + */ +class TableHelper extends Helper +{ + const LAYOUT_DEFAULT = 0; + const LAYOUT_BORDERLESS = 1; + const LAYOUT_COMPACT = 2; + + /** + * @var Table + */ + private $table; + + public function __construct($triggerDeprecationError = true) + { + if ($triggerDeprecationError) { + @trigger_error('The '.__CLASS__.' class is deprecated since version 2.5 and will be removed in 3.0. Use the Symfony\Component\Console\Helper\Table class instead.', E_USER_DEPRECATED); + } + + $this->table = new Table(new NullOutput()); + } + + /** + * Sets table layout type. + * + * @param int $layout self::LAYOUT_* + * + * @return TableHelper + * + * @throws InvalidArgumentException when the table layout is not known + */ + public function setLayout($layout) + { + switch ($layout) { + case self::LAYOUT_BORDERLESS: + $this->table->setStyle('borderless'); + break; + + case self::LAYOUT_COMPACT: + $this->table->setStyle('compact'); + break; + + case self::LAYOUT_DEFAULT: + $this->table->setStyle('default'); + break; + + default: + throw new InvalidArgumentException(sprintf('Invalid table layout "%s".', $layout)); + } + + return $this; + } + + public function setHeaders(array $headers) + { + $this->table->setHeaders($headers); + + return $this; + } + + public function setRows(array $rows) + { + $this->table->setRows($rows); + + return $this; + } + + public function addRows(array $rows) + { + $this->table->addRows($rows); + + return $this; + } + + public function addRow(array $row) + { + $this->table->addRow($row); + + return $this; + } + + public function setRow($column, array $row) + { + $this->table->setRow($column, $row); + + return $this; + } + + /** + * Sets padding character, used for cell padding. + * + * @param string $paddingChar + * + * @return TableHelper + */ + public function setPaddingChar($paddingChar) + { + $this->table->getStyle()->setPaddingChar($paddingChar); + + return $this; + } + + /** + * Sets horizontal border character. + * + * @param string $horizontalBorderChar + * + * @return TableHelper + */ + public function setHorizontalBorderChar($horizontalBorderChar) + { + $this->table->getStyle()->setHorizontalBorderChar($horizontalBorderChar); + + return $this; + } + + /** + * Sets vertical border character. + * + * @param string $verticalBorderChar + * + * @return TableHelper + */ + public function setVerticalBorderChar($verticalBorderChar) + { + $this->table->getStyle()->setVerticalBorderChar($verticalBorderChar); + + return $this; + } + + /** + * Sets crossing character. + * + * @param string $crossingChar + * + * @return TableHelper + */ + public function setCrossingChar($crossingChar) + { + $this->table->getStyle()->setCrossingChar($crossingChar); + + return $this; + } + + /** + * Sets header cell format. + * + * @param string $cellHeaderFormat + * + * @return TableHelper + */ + public function setCellHeaderFormat($cellHeaderFormat) + { + $this->table->getStyle()->setCellHeaderFormat($cellHeaderFormat); + + return $this; + } + + /** + * Sets row cell format. + * + * @param string $cellRowFormat + * + * @return TableHelper + */ + public function setCellRowFormat($cellRowFormat) + { + $this->table->getStyle()->setCellHeaderFormat($cellRowFormat); + + return $this; + } + + /** + * Sets row cell content format. + * + * @param string $cellRowContentFormat + * + * @return TableHelper + */ + public function setCellRowContentFormat($cellRowContentFormat) + { + $this->table->getStyle()->setCellRowContentFormat($cellRowContentFormat); + + return $this; + } + + /** + * Sets table border format. + * + * @param string $borderFormat + * + * @return TableHelper + */ + public function setBorderFormat($borderFormat) + { + $this->table->getStyle()->setBorderFormat($borderFormat); + + return $this; + } + + /** + * Sets cell padding type. + * + * @param int $padType STR_PAD_* + * + * @return TableHelper + */ + public function setPadType($padType) + { + $this->table->getStyle()->setPadType($padType); + + return $this; + } + + /** + * Renders table to output. + * + * Example: + * +---------------+-----------------------+------------------+ + * | ISBN | Title | Author | + * +---------------+-----------------------+------------------+ + * | 99921-58-10-7 | Divine Comedy | Dante Alighieri | + * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | + * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | + * +---------------+-----------------------+------------------+ + * + * @param OutputInterface $output + */ + public function render(OutputInterface $output) + { + $p = new \ReflectionProperty($this->table, 'output'); + $p->setAccessible(true); + $p->setValue($this->table, $output); + + $this->table->render(); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'table'; + } +} diff --git a/vendor/symfony/console/Helper/TableSeparator.php b/vendor/symfony/console/Helper/TableSeparator.php new file mode 100644 index 00000000..8cbbc661 --- /dev/null +++ b/vendor/symfony/console/Helper/TableSeparator.php @@ -0,0 +1,29 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * Marks a row as being a separator. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class TableSeparator extends TableCell +{ + /** + * @param string $value + * @param array $options + */ + public function __construct(array $options = array()) + { + parent::__construct('', $options); + } +} diff --git a/vendor/symfony/console/Helper/TableStyle.php b/vendor/symfony/console/Helper/TableStyle.php new file mode 100644 index 00000000..d7e28ff2 --- /dev/null +++ b/vendor/symfony/console/Helper/TableStyle.php @@ -0,0 +1,258 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Defines the styles for a Table. + * + * @author Fabien Potencier <fabien@symfony.com> + * @author Саша Стаменковић <umpirsky@gmail.com> + */ +class TableStyle +{ + private $paddingChar = ' '; + private $horizontalBorderChar = '-'; + private $verticalBorderChar = '|'; + private $crossingChar = '+'; + private $cellHeaderFormat = '<info>%s</info>'; + private $cellRowFormat = '%s'; + private $cellRowContentFormat = ' %s '; + private $borderFormat = '%s'; + private $padType = STR_PAD_RIGHT; + + /** + * Sets padding character, used for cell padding. + * + * @param string $paddingChar + * + * @return TableStyle + */ + public function setPaddingChar($paddingChar) + { + if (!$paddingChar) { + throw new LogicException('The padding char must not be empty'); + } + + $this->paddingChar = $paddingChar; + + return $this; + } + + /** + * Gets padding character, used for cell padding. + * + * @return string + */ + public function getPaddingChar() + { + return $this->paddingChar; + } + + /** + * Sets horizontal border character. + * + * @param string $horizontalBorderChar + * + * @return TableStyle + */ + public function setHorizontalBorderChar($horizontalBorderChar) + { + $this->horizontalBorderChar = $horizontalBorderChar; + + return $this; + } + + /** + * Gets horizontal border character. + * + * @return string + */ + public function getHorizontalBorderChar() + { + return $this->horizontalBorderChar; + } + + /** + * Sets vertical border character. + * + * @param string $verticalBorderChar + * + * @return TableStyle + */ + public function setVerticalBorderChar($verticalBorderChar) + { + $this->verticalBorderChar = $verticalBorderChar; + + return $this; + } + + /** + * Gets vertical border character. + * + * @return string + */ + public function getVerticalBorderChar() + { + return $this->verticalBorderChar; + } + + /** + * Sets crossing character. + * + * @param string $crossingChar + * + * @return TableStyle + */ + public function setCrossingChar($crossingChar) + { + $this->crossingChar = $crossingChar; + + return $this; + } + + /** + * Gets crossing character. + * + * @return string $crossingChar + */ + public function getCrossingChar() + { + return $this->crossingChar; + } + + /** + * Sets header cell format. + * + * @param string $cellHeaderFormat + * + * @return TableStyle + */ + public function setCellHeaderFormat($cellHeaderFormat) + { + $this->cellHeaderFormat = $cellHeaderFormat; + + return $this; + } + + /** + * Gets header cell format. + * + * @return string + */ + public function getCellHeaderFormat() + { + return $this->cellHeaderFormat; + } + + /** + * Sets row cell format. + * + * @param string $cellRowFormat + * + * @return TableStyle + */ + public function setCellRowFormat($cellRowFormat) + { + $this->cellRowFormat = $cellRowFormat; + + return $this; + } + + /** + * Gets row cell format. + * + * @return string + */ + public function getCellRowFormat() + { + return $this->cellRowFormat; + } + + /** + * Sets row cell content format. + * + * @param string $cellRowContentFormat + * + * @return TableStyle + */ + public function setCellRowContentFormat($cellRowContentFormat) + { + $this->cellRowContentFormat = $cellRowContentFormat; + + return $this; + } + + /** + * Gets row cell content format. + * + * @return string + */ + public function getCellRowContentFormat() + { + return $this->cellRowContentFormat; + } + + /** + * Sets table border format. + * + * @param string $borderFormat + * + * @return TableStyle + */ + public function setBorderFormat($borderFormat) + { + $this->borderFormat = $borderFormat; + + return $this; + } + + /** + * Gets table border format. + * + * @return string + */ + public function getBorderFormat() + { + return $this->borderFormat; + } + + /** + * Sets cell padding type. + * + * @param int $padType STR_PAD_* + * + * @return TableStyle + */ + public function setPadType($padType) + { + if (!in_array($padType, array(STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH), true)) { + throw new InvalidArgumentException('Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH).'); + } + + $this->padType = $padType; + + return $this; + } + + /** + * Gets cell padding type. + * + * @return int + */ + public function getPadType() + { + return $this->padType; + } +} diff --git a/vendor/symfony/console/Input/ArgvInput.php b/vendor/symfony/console/Input/ArgvInput.php new file mode 100644 index 00000000..02d4cdb3 --- /dev/null +++ b/vendor/symfony/console/Input/ArgvInput.php @@ -0,0 +1,351 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\RuntimeException; + +/** + * ArgvInput represents an input coming from the CLI arguments. + * + * Usage: + * + * $input = new ArgvInput(); + * + * By default, the `$_SERVER['argv']` array is used for the input values. + * + * This can be overridden by explicitly passing the input values in the constructor: + * + * $input = new ArgvInput($_SERVER['argv']); + * + * If you pass it yourself, don't forget that the first element of the array + * is the name of the running application. + * + * When passing an argument to the constructor, be sure that it respects + * the same rules as the argv one. It's almost always better to use the + * `StringInput` when you want to provide your own input. + * + * @author Fabien Potencier <fabien@symfony.com> + * + * @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html + * @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 + */ +class ArgvInput extends Input +{ + private $tokens; + private $parsed; + + /** + * Constructor. + * + * @param array $argv An array of parameters from the CLI (in the argv format) + * @param InputDefinition $definition A InputDefinition instance + */ + public function __construct(array $argv = null, InputDefinition $definition = null) + { + if (null === $argv) { + $argv = $_SERVER['argv']; + } + + // strip the application name + array_shift($argv); + + $this->tokens = $argv; + + parent::__construct($definition); + } + + protected function setTokens(array $tokens) + { + $this->tokens = $tokens; + } + + /** + * Processes command line arguments. + */ + protected function parse() + { + $parseOptions = true; + $this->parsed = $this->tokens; + while (null !== $token = array_shift($this->parsed)) { + if ($parseOptions && '' == $token) { + $this->parseArgument($token); + } elseif ($parseOptions && '--' == $token) { + $parseOptions = false; + } elseif ($parseOptions && 0 === strpos($token, '--')) { + $this->parseLongOption($token); + } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) { + $this->parseShortOption($token); + } else { + $this->parseArgument($token); + } + } + } + + /** + * Parses a short option. + * + * @param string $token The current token. + */ + private function parseShortOption($token) + { + $name = substr($token, 1); + + if (strlen($name) > 1) { + if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) { + // an option with a value (with no space) + $this->addShortOption($name[0], substr($name, 1)); + } else { + $this->parseShortOptionSet($name); + } + } else { + $this->addShortOption($name, null); + } + } + + /** + * Parses a short option set. + * + * @param string $name The current token + * + * @throws RuntimeException When option given doesn't exist + */ + private function parseShortOptionSet($name) + { + $len = strlen($name); + for ($i = 0; $i < $len; ++$i) { + if (!$this->definition->hasShortcut($name[$i])) { + throw new RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i])); + } + + $option = $this->definition->getOptionForShortcut($name[$i]); + if ($option->acceptValue()) { + $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1)); + + break; + } else { + $this->addLongOption($option->getName(), null); + } + } + } + + /** + * Parses a long option. + * + * @param string $token The current token + */ + private function parseLongOption($token) + { + $name = substr($token, 2); + + if (false !== $pos = strpos($name, '=')) { + $this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1)); + } else { + $this->addLongOption($name, null); + } + } + + /** + * Parses an argument. + * + * @param string $token The current token + * + * @throws RuntimeException When too many arguments are given + */ + private function parseArgument($token) + { + $c = count($this->arguments); + + // if input is expecting another argument, add it + if ($this->definition->hasArgument($c)) { + $arg = $this->definition->getArgument($c); + $this->arguments[$arg->getName()] = $arg->isArray() ? array($token) : $token; + + // if last argument isArray(), append token to last argument + } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) { + $arg = $this->definition->getArgument($c - 1); + $this->arguments[$arg->getName()][] = $token; + + // unexpected argument + } else { + throw new RuntimeException('Too many arguments.'); + } + } + + /** + * Adds a short option value. + * + * @param string $shortcut The short option key + * @param mixed $value The value for the option + * + * @throws RuntimeException When option given doesn't exist + */ + private function addShortOption($shortcut, $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @throws RuntimeException When option given doesn't exist + */ + private function addLongOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + // Convert empty values to null + if (!isset($value[0])) { + $value = null; + } + + if (null !== $value && !$option->acceptValue()) { + throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name)); + } + + if (null === $value && $option->acceptValue() && count($this->parsed)) { + // if option accepts an optional or mandatory argument + // let's see if there is one provided + $next = array_shift($this->parsed); + if (isset($next[0]) && '-' !== $next[0]) { + $value = $next; + } elseif (empty($next)) { + $value = ''; + } else { + array_unshift($this->parsed, $next); + } + } + + if (null === $value) { + if ($option->isValueRequired()) { + throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name)); + } + + if (!$option->isArray()) { + $value = $option->isValueOptional() ? $option->getDefault() : true; + } + } + + if ($option->isArray()) { + $this->options[$name][] = $value; + } else { + $this->options[$name] = $value; + } + } + + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument() + { + foreach ($this->tokens as $token) { + if ($token && '-' === $token[0]) { + continue; + } + + return $token; + } + } + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * + * @return bool true if the value is contained in the raw parameters + */ + public function hasParameterOption($values) + { + $values = (array) $values; + + foreach ($this->tokens as $token) { + foreach ($values as $value) { + if ($token === $value || 0 === strpos($token, $value.'=')) { + return true; + } + } + } + + return false; + } + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false) + { + $values = (array) $values; + $tokens = $this->tokens; + + while (0 < count($tokens)) { + $token = array_shift($tokens); + + foreach ($values as $value) { + if ($token === $value || 0 === strpos($token, $value.'=')) { + if (false !== $pos = strpos($token, '=')) { + return substr($token, $pos + 1); + } + + return array_shift($tokens); + } + } + } + + return $default; + } + + /** + * Returns a stringified representation of the args passed to the command. + * + * @return string + */ + public function __toString() + { + $self = $this; + $tokens = array_map(function ($token) use ($self) { + if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) { + return $match[1].$self->escapeToken($match[2]); + } + + if ($token && $token[0] !== '-') { + return $self->escapeToken($token); + } + + return $token; + }, $this->tokens); + + return implode(' ', $tokens); + } +} diff --git a/vendor/symfony/console/Input/ArrayInput.php b/vendor/symfony/console/Input/ArrayInput.php new file mode 100644 index 00000000..8cedbb37 --- /dev/null +++ b/vendor/symfony/console/Input/ArrayInput.php @@ -0,0 +1,210 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\InvalidOptionException; + +/** + * ArrayInput represents an input provided as an array. + * + * Usage: + * + * $input = new ArrayInput(array('name' => 'foo', '--bar' => 'foobar')); + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class ArrayInput extends Input +{ + private $parameters; + + /** + * Constructor. + * + * @param array $parameters An array of parameters + * @param InputDefinition $definition A InputDefinition instance + */ + public function __construct(array $parameters, InputDefinition $definition = null) + { + $this->parameters = $parameters; + + parent::__construct($definition); + } + + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument() + { + foreach ($this->parameters as $key => $value) { + if ($key && '-' === $key[0]) { + continue; + } + + return $value; + } + } + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The values to look for in the raw parameters (can be an array) + * + * @return bool true if the value is contained in the raw parameters + */ + public function hasParameterOption($values) + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if (!is_int($k)) { + $v = $k; + } + + if (in_array($v, $values)) { + return true; + } + } + + return false; + } + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false) + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if (is_int($k)) { + if (in_array($v, $values)) { + return true; + } + } elseif (in_array($k, $values)) { + return $v; + } + } + + return $default; + } + + /** + * Returns a stringified representation of the args passed to the command. + * + * @return string + */ + public function __toString() + { + $params = array(); + foreach ($this->parameters as $param => $val) { + if ($param && '-' === $param[0]) { + $params[] = $param.('' != $val ? '='.$this->escapeToken($val) : ''); + } else { + $params[] = $this->escapeToken($val); + } + } + + return implode(' ', $params); + } + + /** + * Processes command line arguments. + */ + protected function parse() + { + foreach ($this->parameters as $key => $value) { + if (0 === strpos($key, '--')) { + $this->addLongOption(substr($key, 2), $value); + } elseif ('-' === $key[0]) { + $this->addShortOption(substr($key, 1), $value); + } else { + $this->addArgument($key, $value); + } + } + } + + /** + * Adds a short option value. + * + * @param string $shortcut The short option key + * @param mixed $value The value for the option + * + * @throws InvalidOptionException When option given doesn't exist + */ + private function addShortOption($shortcut, $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new InvalidOptionException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @throws InvalidOptionException When option given doesn't exist + * @throws InvalidOptionException When a required value is missing + */ + private function addLongOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new InvalidOptionException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + if (null === $value) { + if ($option->isValueRequired()) { + throw new InvalidOptionException(sprintf('The "--%s" option requires a value.', $name)); + } + + $value = $option->isValueOptional() ? $option->getDefault() : true; + } + + $this->options[$name] = $value; + } + + /** + * Adds an argument value. + * + * @param string $name The argument name + * @param mixed $value The value for the argument + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + private function addArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } +} diff --git a/vendor/symfony/console/Input/Input.php b/vendor/symfony/console/Input/Input.php new file mode 100644 index 00000000..85499fc4 --- /dev/null +++ b/vendor/symfony/console/Input/Input.php @@ -0,0 +1,236 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; + +/** + * Input is the base class for all concrete Input classes. + * + * Three concrete classes are provided by default: + * + * * `ArgvInput`: The input comes from the CLI arguments (argv) + * * `StringInput`: The input is provided as a string + * * `ArrayInput`: The input is provided as an array + * + * @author Fabien Potencier <fabien@symfony.com> + */ +abstract class Input implements InputInterface +{ + /** + * @var InputDefinition + */ + protected $definition; + protected $options = array(); + protected $arguments = array(); + protected $interactive = true; + + /** + * Constructor. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function __construct(InputDefinition $definition = null) + { + if (null === $definition) { + $this->definition = new InputDefinition(); + } else { + $this->bind($definition); + $this->validate(); + } + } + + /** + * Binds the current Input instance with the given arguments and options. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function bind(InputDefinition $definition) + { + $this->arguments = array(); + $this->options = array(); + $this->definition = $definition; + + $this->parse(); + } + + /** + * Processes command line arguments. + */ + abstract protected function parse(); + + /** + * Validates the input. + * + * @throws RuntimeException When not enough arguments are given + */ + public function validate() + { + $definition = $this->definition; + $givenArguments = $this->arguments; + + $missingArguments = array_filter(array_keys($definition->getArguments()), function ($argument) use ($definition, $givenArguments) { + return !array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired(); + }); + + if (count($missingArguments) > 0) { + throw new RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments))); + } + } + + /** + * Checks if the input is interactive. + * + * @return bool Returns true if the input is interactive + */ + public function isInteractive() + { + return $this->interactive; + } + + /** + * Sets the input interactivity. + * + * @param bool $interactive If the input should be interactive + */ + public function setInteractive($interactive) + { + $this->interactive = (bool) $interactive; + } + + /** + * Returns the argument values. + * + * @return array An array of argument values + */ + public function getArguments() + { + return array_merge($this->definition->getArgumentDefaults(), $this->arguments); + } + + /** + * Returns the argument value for a given argument name. + * + * @param string $name The argument name + * + * @return mixed The argument value + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function getArgument($name) + { + if (!$this->definition->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault(); + } + + /** + * Sets an argument value by name. + * + * @param string $name The argument name + * @param string $value The argument value + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function setArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|int $name The InputArgument name or position + * + * @return bool true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name) + { + return $this->definition->hasArgument($name); + } + + /** + * Returns the options values. + * + * @return array An array of option values + */ + public function getOptions() + { + return array_merge($this->definition->getOptionDefaults(), $this->options); + } + + /** + * Returns the option value for a given option name. + * + * @param string $name The option name + * + * @return mixed The option value + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function getOption($name) + { + if (!$this->definition->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault(); + } + + /** + * Sets an option value by name. + * + * @param string $name The option name + * @param string|bool $value The option value + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function setOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + $this->options[$name] = $value; + } + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasOption($name) + { + return $this->definition->hasOption($name); + } + + /** + * Escapes a token through escapeshellarg if it contains unsafe chars. + * + * @param string $token + * + * @return string + */ + public function escapeToken($token) + { + return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token); + } +} diff --git a/vendor/symfony/console/Input/InputArgument.php b/vendor/symfony/console/Input/InputArgument.php new file mode 100644 index 00000000..048ee4ff --- /dev/null +++ b/vendor/symfony/console/Input/InputArgument.php @@ -0,0 +1,131 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a command line argument. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class InputArgument +{ + const REQUIRED = 1; + const OPTIONAL = 2; + const IS_ARRAY = 4; + + private $name; + private $mode; + private $default; + private $description; + + /** + * Constructor. + * + * @param string $name The argument name + * @param int $mode The argument mode: self::REQUIRED or self::OPTIONAL + * @param string $description A description text + * @param mixed $default The default value (for self::OPTIONAL mode only) + * + * @throws InvalidArgumentException When argument mode is not valid + */ + public function __construct($name, $mode = null, $description = '', $default = null) + { + if (null === $mode) { + $mode = self::OPTIONAL; + } elseif (!is_int($mode) || $mode > 7 || $mode < 1) { + throw new InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->mode = $mode; + $this->description = $description; + + $this->setDefault($default); + } + + /** + * Returns the argument name. + * + * @return string The argument name + */ + public function getName() + { + return $this->name; + } + + /** + * Returns true if the argument is required. + * + * @return bool true if parameter mode is self::REQUIRED, false otherwise + */ + public function isRequired() + { + return self::REQUIRED === (self::REQUIRED & $this->mode); + } + + /** + * Returns true if the argument can take multiple values. + * + * @return bool true if mode is self::IS_ARRAY, false otherwise + */ + public function isArray() + { + return self::IS_ARRAY === (self::IS_ARRAY & $this->mode); + } + + /** + * Sets the default value. + * + * @param mixed $default The default value + * + * @throws LogicException When incorrect default value is given + */ + public function setDefault($default = null) + { + if (self::REQUIRED === $this->mode && null !== $default) { + throw new LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.'); + } + + if ($this->isArray()) { + if (null === $default) { + $default = array(); + } elseif (!is_array($default)) { + throw new LogicException('A default value for an array argument must be an array.'); + } + } + + $this->default = $default; + } + + /** + * Returns the default value. + * + * @return mixed The default value + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns the description text. + * + * @return string The description text + */ + public function getDescription() + { + return $this->description; + } +} diff --git a/vendor/symfony/console/Input/InputAwareInterface.php b/vendor/symfony/console/Input/InputAwareInterface.php new file mode 100644 index 00000000..d0f11e98 --- /dev/null +++ b/vendor/symfony/console/Input/InputAwareInterface.php @@ -0,0 +1,28 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * InputAwareInterface should be implemented by classes that depends on the + * Console Input. + * + * @author Wouter J <waldio.webdesign@gmail.com> + */ +interface InputAwareInterface +{ + /** + * Sets the Console Input. + * + * @param InputInterface + */ + public function setInput(InputInterface $input); +} diff --git a/vendor/symfony/console/Input/InputDefinition.php b/vendor/symfony/console/Input/InputDefinition.php new file mode 100644 index 00000000..bd64163b --- /dev/null +++ b/vendor/symfony/console/Input/InputDefinition.php @@ -0,0 +1,457 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Descriptor\TextDescriptor; +use Symfony\Component\Console\Descriptor\XmlDescriptor; +use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * A InputDefinition represents a set of valid command line arguments and options. + * + * Usage: + * + * $definition = new InputDefinition(array( + * new InputArgument('name', InputArgument::REQUIRED), + * new InputOption('foo', 'f', InputOption::VALUE_REQUIRED), + * )); + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class InputDefinition +{ + private $arguments; + private $requiredCount; + private $hasAnArrayArgument = false; + private $hasOptional; + private $options; + private $shortcuts; + + /** + * Constructor. + * + * @param array $definition An array of InputArgument and InputOption instance + */ + public function __construct(array $definition = array()) + { + $this->setDefinition($definition); + } + + /** + * Sets the definition of the input. + * + * @param array $definition The definition array + */ + public function setDefinition(array $definition) + { + $arguments = array(); + $options = array(); + foreach ($definition as $item) { + if ($item instanceof InputOption) { + $options[] = $item; + } else { + $arguments[] = $item; + } + } + + $this->setArguments($arguments); + $this->setOptions($options); + } + + /** + * Sets the InputArgument objects. + * + * @param InputArgument[] $arguments An array of InputArgument objects + */ + public function setArguments($arguments = array()) + { + $this->arguments = array(); + $this->requiredCount = 0; + $this->hasOptional = false; + $this->hasAnArrayArgument = false; + $this->addArguments($arguments); + } + + /** + * Adds an array of InputArgument objects. + * + * @param InputArgument[] $arguments An array of InputArgument objects + */ + public function addArguments($arguments = array()) + { + if (null !== $arguments) { + foreach ($arguments as $argument) { + $this->addArgument($argument); + } + } + } + + /** + * Adds an InputArgument object. + * + * @param InputArgument $argument An InputArgument object + * + * @throws LogicException When incorrect argument is given + */ + public function addArgument(InputArgument $argument) + { + if (isset($this->arguments[$argument->getName()])) { + throw new LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName())); + } + + if ($this->hasAnArrayArgument) { + throw new LogicException('Cannot add an argument after an array argument.'); + } + + if ($argument->isRequired() && $this->hasOptional) { + throw new LogicException('Cannot add a required argument after an optional one.'); + } + + if ($argument->isArray()) { + $this->hasAnArrayArgument = true; + } + + if ($argument->isRequired()) { + ++$this->requiredCount; + } else { + $this->hasOptional = true; + } + + $this->arguments[$argument->getName()] = $argument; + } + + /** + * Returns an InputArgument by name or by position. + * + * @param string|int $name The InputArgument name or position + * + * @return InputArgument An InputArgument object + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function getArgument($name) + { + if (!$this->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + + return $arguments[$name]; + } + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|int $name The InputArgument name or position + * + * @return bool true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name) + { + $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + + return isset($arguments[$name]); + } + + /** + * Gets the array of InputArgument objects. + * + * @return InputArgument[] An array of InputArgument objects + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Returns the number of InputArguments. + * + * @return int The number of InputArguments + */ + public function getArgumentCount() + { + return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments); + } + + /** + * Returns the number of required InputArguments. + * + * @return int The number of required InputArguments + */ + public function getArgumentRequiredCount() + { + return $this->requiredCount; + } + + /** + * Gets the default values. + * + * @return array An array of default values + */ + public function getArgumentDefaults() + { + $values = array(); + foreach ($this->arguments as $argument) { + $values[$argument->getName()] = $argument->getDefault(); + } + + return $values; + } + + /** + * Sets the InputOption objects. + * + * @param InputOption[] $options An array of InputOption objects + */ + public function setOptions($options = array()) + { + $this->options = array(); + $this->shortcuts = array(); + $this->addOptions($options); + } + + /** + * Adds an array of InputOption objects. + * + * @param InputOption[] $options An array of InputOption objects + */ + public function addOptions($options = array()) + { + foreach ($options as $option) { + $this->addOption($option); + } + } + + /** + * Adds an InputOption object. + * + * @param InputOption $option An InputOption object + * + * @throws LogicException When option given already exist + */ + public function addOption(InputOption $option) + { + if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) { + throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName())); + } + + if ($option->getShortcut()) { + foreach (explode('|', $option->getShortcut()) as $shortcut) { + if (isset($this->shortcuts[$shortcut]) && !$option->equals($this->options[$this->shortcuts[$shortcut]])) { + throw new LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut)); + } + } + } + + $this->options[$option->getName()] = $option; + if ($option->getShortcut()) { + foreach (explode('|', $option->getShortcut()) as $shortcut) { + $this->shortcuts[$shortcut] = $option->getName(); + } + } + } + + /** + * Returns an InputOption by name. + * + * @param string $name The InputOption name + * + * @return InputOption A InputOption object + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function getOption($name) + { + if (!$this->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); + } + + return $this->options[$name]; + } + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasOption($name) + { + return isset($this->options[$name]); + } + + /** + * Gets the array of InputOption objects. + * + * @return InputOption[] An array of InputOption objects + */ + public function getOptions() + { + return $this->options; + } + + /** + * Returns true if an InputOption object exists by shortcut. + * + * @param string $name The InputOption shortcut + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasShortcut($name) + { + return isset($this->shortcuts[$name]); + } + + /** + * Gets an InputOption by shortcut. + * + * @param string $shortcut the Shortcut name + * + * @return InputOption An InputOption object + */ + public function getOptionForShortcut($shortcut) + { + return $this->getOption($this->shortcutToName($shortcut)); + } + + /** + * Gets an array of default values. + * + * @return array An array of all default values + */ + public function getOptionDefaults() + { + $values = array(); + foreach ($this->options as $option) { + $values[$option->getName()] = $option->getDefault(); + } + + return $values; + } + + /** + * Returns the InputOption name given a shortcut. + * + * @param string $shortcut The shortcut + * + * @return string The InputOption name + * + * @throws InvalidArgumentException When option given does not exist + */ + private function shortcutToName($shortcut) + { + if (!isset($this->shortcuts[$shortcut])) { + throw new InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + return $this->shortcuts[$shortcut]; + } + + /** + * Gets the synopsis. + * + * @param bool $short Whether to return the short version (with options folded) or not + * + * @return string The synopsis + */ + public function getSynopsis($short = false) + { + $elements = array(); + + if ($short && $this->getOptions()) { + $elements[] = '[options]'; + } elseif (!$short) { + foreach ($this->getOptions() as $option) { + $value = ''; + if ($option->acceptValue()) { + $value = sprintf( + ' %s%s%s', + $option->isValueOptional() ? '[' : '', + strtoupper($option->getName()), + $option->isValueOptional() ? ']' : '' + ); + } + + $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : ''; + $elements[] = sprintf('[%s--%s%s]', $shortcut, $option->getName(), $value); + } + } + + if (count($elements) && $this->getArguments()) { + $elements[] = '[--]'; + } + + foreach ($this->getArguments() as $argument) { + $element = '<'.$argument->getName().'>'; + if (!$argument->isRequired()) { + $element = '['.$element.']'; + } elseif ($argument->isArray()) { + $element = $element.' ('.$element.')'; + } + + if ($argument->isArray()) { + $element .= '...'; + } + + $elements[] = $element; + } + + return implode(' ', $elements); + } + + /** + * Returns a textual representation of the InputDefinition. + * + * @return string A string representing the InputDefinition + * + * @deprecated since version 2.3, to be removed in 3.0. + */ + public function asText() + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.3 and will be removed in 3.0.', E_USER_DEPRECATED); + + $descriptor = new TextDescriptor(); + $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); + $descriptor->describe($output, $this, array('raw_output' => true)); + + return $output->fetch(); + } + + /** + * Returns an XML representation of the InputDefinition. + * + * @param bool $asDom Whether to return a DOM or an XML string + * + * @return string|\DOMDocument An XML string representing the InputDefinition + * + * @deprecated since version 2.3, to be removed in 3.0. + */ + public function asXml($asDom = false) + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.3 and will be removed in 3.0.', E_USER_DEPRECATED); + + $descriptor = new XmlDescriptor(); + + if ($asDom) { + return $descriptor->getInputDefinitionDocument($this); + } + + $output = new BufferedOutput(); + $descriptor->describe($output, $this); + + return $output->fetch(); + } +} diff --git a/vendor/symfony/console/Input/InputInterface.php b/vendor/symfony/console/Input/InputInterface.php new file mode 100644 index 00000000..f83b8856 --- /dev/null +++ b/vendor/symfony/console/Input/InputInterface.php @@ -0,0 +1,152 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * InputInterface is the interface implemented by all input classes. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +interface InputInterface +{ + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument(); + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The values to look for in the raw parameters (can be an array) + * + * @return bool true if the value is contained in the raw parameters + */ + public function hasParameterOption($values); + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false); + + /** + * Binds the current Input instance with the given arguments and options. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function bind(InputDefinition $definition); + + /** + * Validates if arguments given are correct. + * + * Throws an exception when not enough arguments are given. + * + * @throws \RuntimeException + */ + public function validate(); + + /** + * Returns all the given arguments merged with the default values. + * + * @return array + */ + public function getArguments(); + + /** + * Gets argument by name. + * + * @param string $name The name of the argument + * + * @return mixed + */ + public function getArgument($name); + + /** + * Sets an argument value by name. + * + * @param string $name The argument name + * @param string $value The argument value + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function setArgument($name, $value); + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|int $name The InputArgument name or position + * + * @return bool true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name); + + /** + * Returns all the given options merged with the default values. + * + * @return array + */ + public function getOptions(); + + /** + * Gets an option by name. + * + * @param string $name The name of the option + * + * @return mixed + */ + public function getOption($name); + + /** + * Sets an option value by name. + * + * @param string $name The option name + * @param string|bool $value The option value + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function setOption($name, $value); + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasOption($name); + + /** + * Is this input means interactive? + * + * @return bool + */ + public function isInteractive(); + + /** + * Sets the input interactivity. + * + * @param bool $interactive If the input should be interactive + */ + public function setInteractive($interactive); +} diff --git a/vendor/symfony/console/Input/InputOption.php b/vendor/symfony/console/Input/InputOption.php new file mode 100644 index 00000000..f08c5f26 --- /dev/null +++ b/vendor/symfony/console/Input/InputOption.php @@ -0,0 +1,212 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a command line option. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class InputOption +{ + const VALUE_NONE = 1; + const VALUE_REQUIRED = 2; + const VALUE_OPTIONAL = 4; + const VALUE_IS_ARRAY = 8; + + private $name; + private $shortcut; + private $mode; + private $default; + private $description; + + /** + * Constructor. + * + * @param string $name The option name + * @param string|array $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts + * @param int $mode The option mode: One of the VALUE_* constants + * @param string $description A description text + * @param mixed $default The default value (must be null for self::VALUE_NONE) + * + * @throws InvalidArgumentException If option mode is invalid or incompatible + */ + public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + if (0 === strpos($name, '--')) { + $name = substr($name, 2); + } + + if (empty($name)) { + throw new InvalidArgumentException('An option name cannot be empty.'); + } + + if (empty($shortcut)) { + $shortcut = null; + } + + if (null !== $shortcut) { + if (is_array($shortcut)) { + $shortcut = implode('|', $shortcut); + } + $shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-')); + $shortcuts = array_filter($shortcuts); + $shortcut = implode('|', $shortcuts); + + if (empty($shortcut)) { + throw new InvalidArgumentException('An option shortcut cannot be empty.'); + } + } + + if (null === $mode) { + $mode = self::VALUE_NONE; + } elseif (!is_int($mode) || $mode > 15 || $mode < 1) { + throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->shortcut = $shortcut; + $this->mode = $mode; + $this->description = $description; + + if ($this->isArray() && !$this->acceptValue()) { + throw new InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.'); + } + + $this->setDefault($default); + } + + /** + * Returns the option shortcut. + * + * @return string The shortcut + */ + public function getShortcut() + { + return $this->shortcut; + } + + /** + * Returns the option name. + * + * @return string The name + */ + public function getName() + { + return $this->name; + } + + /** + * Returns true if the option accepts a value. + * + * @return bool true if value mode is not self::VALUE_NONE, false otherwise + */ + public function acceptValue() + { + return $this->isValueRequired() || $this->isValueOptional(); + } + + /** + * Returns true if the option requires a value. + * + * @return bool true if value mode is self::VALUE_REQUIRED, false otherwise + */ + public function isValueRequired() + { + return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode); + } + + /** + * Returns true if the option takes an optional value. + * + * @return bool true if value mode is self::VALUE_OPTIONAL, false otherwise + */ + public function isValueOptional() + { + return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode); + } + + /** + * Returns true if the option can take multiple values. + * + * @return bool true if mode is self::VALUE_IS_ARRAY, false otherwise + */ + public function isArray() + { + return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode); + } + + /** + * Sets the default value. + * + * @param mixed $default The default value + * + * @throws LogicException When incorrect default value is given + */ + public function setDefault($default = null) + { + if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) { + throw new LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.'); + } + + if ($this->isArray()) { + if (null === $default) { + $default = array(); + } elseif (!is_array($default)) { + throw new LogicException('A default value for an array option must be an array.'); + } + } + + $this->default = $this->acceptValue() ? $default : false; + } + + /** + * Returns the default value. + * + * @return mixed The default value + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns the description text. + * + * @return string The description text + */ + public function getDescription() + { + return $this->description; + } + + /** + * Checks whether the given option equals this one. + * + * @param InputOption $option option to compare + * + * @return bool + */ + public function equals(InputOption $option) + { + return $option->getName() === $this->getName() + && $option->getShortcut() === $this->getShortcut() + && $option->getDefault() === $this->getDefault() + && $option->isArray() === $this->isArray() + && $option->isValueRequired() === $this->isValueRequired() + && $option->isValueOptional() === $this->isValueOptional() + ; + } +} diff --git a/vendor/symfony/console/Input/StringInput.php b/vendor/symfony/console/Input/StringInput.php new file mode 100644 index 00000000..a40ddba3 --- /dev/null +++ b/vendor/symfony/console/Input/StringInput.php @@ -0,0 +1,85 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * StringInput represents an input provided as a string. + * + * Usage: + * + * $input = new StringInput('foo --bar="foobar"'); + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class StringInput extends ArgvInput +{ + const REGEX_STRING = '([^\s]+?)(?:\s|(?<!\\\\)"|(?<!\\\\)\'|$)'; + const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')'; + + /** + * Constructor. + * + * @param string $input An array of parameters from the CLI (in the argv format) + * @param InputDefinition $definition A InputDefinition instance + * + * @deprecated The second argument is deprecated as it does not work (will be removed in 3.0), use 'bind' method instead + */ + public function __construct($input, InputDefinition $definition = null) + { + if ($definition) { + @trigger_error('The $definition argument of the '.__METHOD__.' method is deprecated and will be removed in 3.0. Set this parameter with the bind() method instead.', E_USER_DEPRECATED); + } + + parent::__construct(array(), null); + + $this->setTokens($this->tokenize($input)); + + if (null !== $definition) { + $this->bind($definition); + } + } + + /** + * Tokenizes a string. + * + * @param string $input The input to tokenize + * + * @return array An array of tokens + * + * @throws InvalidArgumentException When unable to parse input (should never happen) + */ + private function tokenize($input) + { + $tokens = array(); + $length = strlen($input); + $cursor = 0; + while ($cursor < $length) { + if (preg_match('/\s+/A', $input, $match, null, $cursor)) { + } elseif (preg_match('/([^="\'\s]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) { + $tokens[] = $match[1].$match[2].stripcslashes(str_replace(array('"\'', '\'"', '\'\'', '""'), '', substr($match[3], 1, strlen($match[3]) - 2))); + } elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor)) { + $tokens[] = stripcslashes(substr($match[0], 1, strlen($match[0]) - 2)); + } elseif (preg_match('/'.self::REGEX_STRING.'/A', $input, $match, null, $cursor)) { + $tokens[] = stripcslashes($match[1]); + } else { + // should never happen + throw new InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10))); + } + + $cursor += strlen($match[0]); + } + + return $tokens; + } +} diff --git a/vendor/symfony/console/LICENSE b/vendor/symfony/console/LICENSE new file mode 100644 index 00000000..12a74531 --- /dev/null +++ b/vendor/symfony/console/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2016 Fabien Potencier + +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. diff --git a/vendor/symfony/console/Logger/ConsoleLogger.php b/vendor/symfony/console/Logger/ConsoleLogger.php new file mode 100644 index 00000000..1f7417ea --- /dev/null +++ b/vendor/symfony/console/Logger/ConsoleLogger.php @@ -0,0 +1,119 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Logger; + +use Psr\Log\AbstractLogger; +use Psr\Log\InvalidArgumentException; +use Psr\Log\LogLevel; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; + +/** + * PSR-3 compliant console logger. + * + * @author Kévin Dunglas <dunglas@gmail.com> + * + * @link http://www.php-fig.org/psr/psr-3/ + */ +class ConsoleLogger extends AbstractLogger +{ + const INFO = 'info'; + const ERROR = 'error'; + + /** + * @var OutputInterface + */ + private $output; + /** + * @var array + */ + private $verbosityLevelMap = array( + LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL, + LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL, + LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL, + LogLevel::NOTICE => OutputInterface::VERBOSITY_VERBOSE, + LogLevel::INFO => OutputInterface::VERBOSITY_VERY_VERBOSE, + LogLevel::DEBUG => OutputInterface::VERBOSITY_DEBUG, + ); + /** + * @var array + */ + private $formatLevelMap = array( + LogLevel::EMERGENCY => self::ERROR, + LogLevel::ALERT => self::ERROR, + LogLevel::CRITICAL => self::ERROR, + LogLevel::ERROR => self::ERROR, + LogLevel::WARNING => self::INFO, + LogLevel::NOTICE => self::INFO, + LogLevel::INFO => self::INFO, + LogLevel::DEBUG => self::INFO, + ); + + /** + * @param OutputInterface $output + * @param array $verbosityLevelMap + * @param array $formatLevelMap + */ + public function __construct(OutputInterface $output, array $verbosityLevelMap = array(), array $formatLevelMap = array()) + { + $this->output = $output; + $this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap; + $this->formatLevelMap = $formatLevelMap + $this->formatLevelMap; + } + + /** + * {@inheritdoc} + */ + public function log($level, $message, array $context = array()) + { + if (!isset($this->verbosityLevelMap[$level])) { + throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level)); + } + + // Write to the error output if necessary and available + if ($this->formatLevelMap[$level] === self::ERROR && $this->output instanceof ConsoleOutputInterface) { + $output = $this->output->getErrorOutput(); + } else { + $output = $this->output; + } + + if ($output->getVerbosity() >= $this->verbosityLevelMap[$level]) { + $output->writeln(sprintf('<%1$s>[%2$s] %3$s</%1$s>', $this->formatLevelMap[$level], $level, $this->interpolate($message, $context))); + } + } + + /** + * Interpolates context values into the message placeholders. + * + * @author PHP Framework Interoperability Group + * + * @param string $message + * @param array $context + * + * @return string + */ + private function interpolate($message, array $context) + { + // build a replacement array with braces around the context keys + $replace = array(); + foreach ($context as $key => $val) { + if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) { + $replace[sprintf('{%s}', $key)] = $val; + } + } + + // interpolate replacement values into the message and return + return strtr($message, $replace); + } +} diff --git a/vendor/symfony/console/Output/BufferedOutput.php b/vendor/symfony/console/Output/BufferedOutput.php new file mode 100644 index 00000000..5682fc24 --- /dev/null +++ b/vendor/symfony/console/Output/BufferedOutput.php @@ -0,0 +1,48 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +/** + * @author Jean-François Simon <contact@jfsimon.fr> + */ +class BufferedOutput extends Output +{ + /** + * @var string + */ + private $buffer = ''; + + /** + * Empties buffer and returns its content. + * + * @return string + */ + public function fetch() + { + $content = $this->buffer; + $this->buffer = ''; + + return $content; + } + + /** + * {@inheritdoc} + */ + protected function doWrite($message, $newline) + { + $this->buffer .= $message; + + if ($newline) { + $this->buffer .= "\n"; + } + } +} diff --git a/vendor/symfony/console/Output/ConsoleOutput.php b/vendor/symfony/console/Output/ConsoleOutput.php new file mode 100644 index 00000000..f666c793 --- /dev/null +++ b/vendor/symfony/console/Output/ConsoleOutput.php @@ -0,0 +1,156 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * ConsoleOutput is the default class for all CLI output. It uses STDOUT. + * + * This class is a convenient wrapper around `StreamOutput`. + * + * $output = new ConsoleOutput(); + * + * This is equivalent to: + * + * $output = new StreamOutput(fopen('php://stdout', 'w')); + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface +{ + /** + * @var StreamOutput + */ + private $stderr; + + /** + * Constructor. + * + * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool|null $decorated Whether to decorate messages (null for auto-guessing) + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + parent::__construct($this->openOutputStream(), $verbosity, $decorated, $formatter); + + $actualDecorated = $this->isDecorated(); + $this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated, $this->getFormatter()); + + if (null === $decorated) { + $this->setDecorated($actualDecorated && $this->stderr->isDecorated()); + } + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + parent::setDecorated($decorated); + $this->stderr->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + parent::setFormatter($formatter); + $this->stderr->setFormatter($formatter); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + parent::setVerbosity($level); + $this->stderr->setVerbosity($level); + } + + /** + * {@inheritdoc} + */ + public function getErrorOutput() + { + return $this->stderr; + } + + /** + * {@inheritdoc} + */ + public function setErrorOutput(OutputInterface $error) + { + $this->stderr = $error; + } + + /** + * Returns true if current environment supports writing console output to + * STDOUT. + * + * @return bool + */ + protected function hasStdoutSupport() + { + return false === $this->isRunningOS400(); + } + + /** + * Returns true if current environment supports writing console output to + * STDERR. + * + * @return bool + */ + protected function hasStderrSupport() + { + return false === $this->isRunningOS400(); + } + + /** + * Checks if current executing environment is IBM iSeries (OS400), which + * doesn't properly convert character-encodings between ASCII to EBCDIC. + * + * @return bool + */ + private function isRunningOS400() + { + $checks = array( + function_exists('php_uname') ? php_uname('s') : '', + getenv('OSTYPE'), + PHP_OS, + ); + + return false !== stripos(implode(';', $checks), 'OS400'); + } + + /** + * @return resource + */ + private function openOutputStream() + { + $outputStream = $this->hasStdoutSupport() ? 'php://stdout' : 'php://output'; + + return @fopen($outputStream, 'w') ?: fopen('php://output', 'w'); + } + + /** + * @return resource + */ + private function openErrorStream() + { + $errorStream = $this->hasStderrSupport() ? 'php://stderr' : 'php://output'; + + return fopen($errorStream, 'w'); + } +} diff --git a/vendor/symfony/console/Output/ConsoleOutputInterface.php b/vendor/symfony/console/Output/ConsoleOutputInterface.php new file mode 100644 index 00000000..5eb4fc7a --- /dev/null +++ b/vendor/symfony/console/Output/ConsoleOutputInterface.php @@ -0,0 +1,35 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +/** + * ConsoleOutputInterface is the interface implemented by ConsoleOutput class. + * This adds information about stderr output stream. + * + * @author Dariusz Górecki <darek.krk@gmail.com> + */ +interface ConsoleOutputInterface extends OutputInterface +{ + /** + * Gets the OutputInterface for errors. + * + * @return OutputInterface + */ + public function getErrorOutput(); + + /** + * Sets the OutputInterface used for errors. + * + * @param OutputInterface $error + */ + public function setErrorOutput(OutputInterface $error); +} diff --git a/vendor/symfony/console/Output/NullOutput.php b/vendor/symfony/console/Output/NullOutput.php new file mode 100644 index 00000000..682f9a4d --- /dev/null +++ b/vendor/symfony/console/Output/NullOutput.php @@ -0,0 +1,111 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * NullOutput suppresses all output. + * + * $output = new NullOutput(); + * + * @author Fabien Potencier <fabien@symfony.com> + * @author Tobias Schultze <http://tobion.de> + */ +class NullOutput implements OutputInterface +{ + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + // to comply with the interface we must return a OutputFormatterInterface + return new OutputFormatter(); + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function isDecorated() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function getVerbosity() + { + return self::VERBOSITY_QUIET; + } + + public function isQuiet() + { + return true; + } + + public function isVerbose() + { + return false; + } + + public function isVeryVerbose() + { + return false; + } + + public function isDebug() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $options = self::OUTPUT_NORMAL) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL) + { + // do nothing + } +} diff --git a/vendor/symfony/console/Output/Output.php b/vendor/symfony/console/Output/Output.php new file mode 100644 index 00000000..4476ffb5 --- /dev/null +++ b/vendor/symfony/console/Output/Output.php @@ -0,0 +1,165 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Formatter\OutputFormatter; + +/** + * Base class for output classes. + * + * There are five levels of verbosity: + * + * * normal: no option passed (normal output) + * * verbose: -v (more output) + * * very verbose: -vv (highly extended output) + * * debug: -vvv (all debug output) + * * quiet: -q (no output) + * + * @author Fabien Potencier <fabien@symfony.com> + */ +abstract class Output implements OutputInterface +{ + private $verbosity; + private $formatter; + + /** + * Constructor. + * + * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool $decorated Whether to decorate messages + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = false, OutputFormatterInterface $formatter = null) + { + $this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity; + $this->formatter = $formatter ?: new OutputFormatter(); + $this->formatter->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + $this->formatter = $formatter; + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + return $this->formatter; + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + $this->formatter->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function isDecorated() + { + return $this->formatter->isDecorated(); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + $this->verbosity = (int) $level; + } + + /** + * {@inheritdoc} + */ + public function getVerbosity() + { + return $this->verbosity; + } + + public function isQuiet() + { + return self::VERBOSITY_QUIET === $this->verbosity; + } + + public function isVerbose() + { + return self::VERBOSITY_VERBOSE <= $this->verbosity; + } + + public function isVeryVerbose() + { + return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity; + } + + public function isDebug() + { + return self::VERBOSITY_DEBUG <= $this->verbosity; + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $options = self::OUTPUT_NORMAL) + { + $this->write($messages, true, $options); + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL) + { + $messages = (array) $messages; + + $types = self::OUTPUT_NORMAL | self::OUTPUT_RAW | self::OUTPUT_PLAIN; + $type = $types & $options ?: self::OUTPUT_NORMAL; + + $verbosities = self::VERBOSITY_QUIET | self::VERBOSITY_NORMAL | self::VERBOSITY_VERBOSE | self::VERBOSITY_VERY_VERBOSE | self::VERBOSITY_DEBUG; + $verbosity = $verbosities & $options ?: self::VERBOSITY_NORMAL; + + if ($verbosity > $this->getVerbosity()) { + return; + } + + foreach ($messages as $message) { + switch ($type) { + case OutputInterface::OUTPUT_NORMAL: + $message = $this->formatter->format($message); + break; + case OutputInterface::OUTPUT_RAW: + break; + case OutputInterface::OUTPUT_PLAIN: + $message = strip_tags($this->formatter->format($message)); + break; + } + + $this->doWrite($message, $newline); + } + } + + /** + * Writes a message to the output. + * + * @param string $message A message to write to the output + * @param bool $newline Whether to add a newline or not + */ + abstract protected function doWrite($message, $newline); +} diff --git a/vendor/symfony/console/Output/OutputInterface.php b/vendor/symfony/console/Output/OutputInterface.php new file mode 100644 index 00000000..9a8290bd --- /dev/null +++ b/vendor/symfony/console/Output/OutputInterface.php @@ -0,0 +1,91 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * OutputInterface is the interface implemented by all Output classes. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +interface OutputInterface +{ + const VERBOSITY_QUIET = 16; + const VERBOSITY_NORMAL = 32; + const VERBOSITY_VERBOSE = 64; + const VERBOSITY_VERY_VERBOSE = 128; + const VERBOSITY_DEBUG = 256; + + const OUTPUT_NORMAL = 1; + const OUTPUT_RAW = 2; + const OUTPUT_PLAIN = 4; + + /** + * Writes a message to the output. + * + * @param string|array $messages The message as an array of lines or a single string + * @param bool $newline Whether to add a newline + * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL + */ + public function write($messages, $newline = false, $options = 0); + + /** + * Writes a message to the output and adds a newline at the end. + * + * @param string|array $messages The message as an array of lines of a single string + * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL + */ + public function writeln($messages, $options = 0); + + /** + * Sets the verbosity of the output. + * + * @param int $level The level of verbosity (one of the VERBOSITY constants) + */ + public function setVerbosity($level); + + /** + * Gets the current verbosity of the output. + * + * @return int The current level of verbosity (one of the VERBOSITY constants) + */ + public function getVerbosity(); + + /** + * Sets the decorated flag. + * + * @param bool $decorated Whether to decorate the messages + */ + public function setDecorated($decorated); + + /** + * Gets the decorated flag. + * + * @return bool true if the output will decorate messages, false otherwise + */ + public function isDecorated(); + + /** + * Sets output formatter. + * + * @param OutputFormatterInterface $formatter + */ + public function setFormatter(OutputFormatterInterface $formatter); + + /** + * Returns current output formatter instance. + * + * @return OutputFormatterInterface + */ + public function getFormatter(); +} diff --git a/vendor/symfony/console/Output/StreamOutput.php b/vendor/symfony/console/Output/StreamOutput.php new file mode 100644 index 00000000..9e6b7481 --- /dev/null +++ b/vendor/symfony/console/Output/StreamOutput.php @@ -0,0 +1,105 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * StreamOutput writes the output to a given stream. + * + * Usage: + * + * $output = new StreamOutput(fopen('php://stdout', 'w')); + * + * As `StreamOutput` can use any stream, you can also use a file: + * + * $output = new StreamOutput(fopen('/path/to/output.log', 'a', false)); + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class StreamOutput extends Output +{ + private $stream; + + /** + * Constructor. + * + * @param resource $stream A stream resource + * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool|null $decorated Whether to decorate messages (null for auto-guessing) + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + * + * @throws InvalidArgumentException When first argument is not a real stream + */ + public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) { + throw new InvalidArgumentException('The StreamOutput class needs a stream as its first argument.'); + } + + $this->stream = $stream; + + if (null === $decorated) { + $decorated = $this->hasColorSupport(); + } + + parent::__construct($verbosity, $decorated, $formatter); + } + + /** + * Gets the stream attached to this StreamOutput instance. + * + * @return resource A stream resource + */ + public function getStream() + { + return $this->stream; + } + + /** + * {@inheritdoc} + */ + protected function doWrite($message, $newline) + { + if (false === @fwrite($this->stream, $message) || ($newline && (false === @fwrite($this->stream, PHP_EOL)))) { + // should never happen + throw new RuntimeException('Unable to write output.'); + } + + fflush($this->stream); + } + + /** + * Returns true if the stream supports colorization. + * + * Colorization is disabled if not supported by the stream: + * + * - Windows before 10.0.10586 without Ansicon, ConEmu or Mintty + * - non tty consoles + * + * @return bool true if the stream supports colorization, false otherwise + */ + protected function hasColorSupport() + { + if (DIRECTORY_SEPARATOR === '\\') { + return + 0 >= version_compare('10.0.10586', PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD) + || false !== getenv('ANSICON') + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM'); + } + + return function_exists('posix_isatty') && @posix_isatty($this->stream); + } +} diff --git a/vendor/symfony/console/Question/ChoiceQuestion.php b/vendor/symfony/console/Question/ChoiceQuestion.php new file mode 100644 index 00000000..2c40638d --- /dev/null +++ b/vendor/symfony/console/Question/ChoiceQuestion.php @@ -0,0 +1,177 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Represents a choice question. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class ChoiceQuestion extends Question +{ + private $choices; + private $multiselect = false; + private $prompt = ' > '; + private $errorMessage = 'Value "%s" is invalid'; + + /** + * Constructor. + * + * @param string $question The question to ask to the user + * @param array $choices The list of available choices + * @param mixed $default The default answer to return + */ + public function __construct($question, array $choices, $default = null) + { + parent::__construct($question, $default); + + $this->choices = $choices; + $this->setValidator($this->getDefaultValidator()); + $this->setAutocompleterValues($choices); + } + + /** + * Returns available choices. + * + * @return array + */ + public function getChoices() + { + return $this->choices; + } + + /** + * Sets multiselect option. + * + * When multiselect is set to true, multiple choices can be answered. + * + * @param bool $multiselect + * + * @return ChoiceQuestion The current instance + */ + public function setMultiselect($multiselect) + { + $this->multiselect = $multiselect; + $this->setValidator($this->getDefaultValidator()); + + return $this; + } + + /** + * Gets the prompt for choices. + * + * @return string + */ + public function getPrompt() + { + return $this->prompt; + } + + /** + * Sets the prompt for choices. + * + * @param string $prompt + * + * @return ChoiceQuestion The current instance + */ + public function setPrompt($prompt) + { + $this->prompt = $prompt; + + return $this; + } + + /** + * Sets the error message for invalid values. + * + * The error message has a string placeholder (%s) for the invalid value. + * + * @param string $errorMessage + * + * @return ChoiceQuestion The current instance + */ + public function setErrorMessage($errorMessage) + { + $this->errorMessage = $errorMessage; + $this->setValidator($this->getDefaultValidator()); + + return $this; + } + + /** + * Returns the default answer validator. + * + * @return callable + */ + private function getDefaultValidator() + { + $choices = $this->choices; + $errorMessage = $this->errorMessage; + $multiselect = $this->multiselect; + $isAssoc = $this->isAssoc($choices); + + return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) { + // Collapse all spaces. + $selectedChoices = str_replace(' ', '', $selected); + + if ($multiselect) { + // Check for a separated comma values + if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) { + throw new InvalidArgumentException(sprintf($errorMessage, $selected)); + } + $selectedChoices = explode(',', $selectedChoices); + } else { + $selectedChoices = array($selected); + } + + $multiselectChoices = array(); + foreach ($selectedChoices as $value) { + $results = array(); + foreach ($choices as $key => $choice) { + if ($choice === $value) { + $results[] = $key; + } + } + + if (count($results) > 1) { + throw new InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of %s.', implode(' or ', $results))); + } + + $result = array_search($value, $choices); + + if (!$isAssoc) { + if (false !== $result) { + $result = $choices[$result]; + } elseif (isset($choices[$value])) { + $result = $choices[$value]; + } + } elseif (false === $result && isset($choices[$value])) { + $result = $value; + } + + if (false === $result) { + throw new InvalidArgumentException(sprintf($errorMessage, $value)); + } + + $multiselectChoices[] = (string) $result; + } + + if ($multiselect) { + return $multiselectChoices; + } + + return current($multiselectChoices); + }; + } +} diff --git a/vendor/symfony/console/Question/ConfirmationQuestion.php b/vendor/symfony/console/Question/ConfirmationQuestion.php new file mode 100644 index 00000000..29d98879 --- /dev/null +++ b/vendor/symfony/console/Question/ConfirmationQuestion.php @@ -0,0 +1,61 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +/** + * Represents a yes/no question. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class ConfirmationQuestion extends Question +{ + private $trueAnswerRegex; + + /** + * Constructor. + * + * @param string $question The question to ask to the user + * @param bool $default The default answer to return, true or false + * @param string $trueAnswerRegex A regex to match the "yes" answer + */ + public function __construct($question, $default = true, $trueAnswerRegex = '/^y/i') + { + parent::__construct($question, (bool) $default); + + $this->trueAnswerRegex = $trueAnswerRegex; + $this->setNormalizer($this->getDefaultNormalizer()); + } + + /** + * Returns the default answer normalizer. + * + * @return callable + */ + private function getDefaultNormalizer() + { + $default = $this->getDefault(); + $regex = $this->trueAnswerRegex; + + return function ($answer) use ($default, $regex) { + if (is_bool($answer)) { + return $answer; + } + + $answerIsTrue = (bool) preg_match($regex, $answer); + if (false === $default) { + return $answer && $answerIsTrue; + } + + return !$answer || $answerIsTrue; + }; + } +} diff --git a/vendor/symfony/console/Question/Question.php b/vendor/symfony/console/Question/Question.php new file mode 100644 index 00000000..ab415c26 --- /dev/null +++ b/vendor/symfony/console/Question/Question.php @@ -0,0 +1,250 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a Question. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class Question +{ + private $question; + private $attempts; + private $hidden = false; + private $hiddenFallback = true; + private $autocompleterValues; + private $validator; + private $default; + private $normalizer; + + /** + * Constructor. + * + * @param string $question The question to ask to the user + * @param mixed $default The default answer to return if the user enters nothing + */ + public function __construct($question, $default = null) + { + $this->question = $question; + $this->default = $default; + } + + /** + * Returns the question. + * + * @return string + */ + public function getQuestion() + { + return $this->question; + } + + /** + * Returns the default answer. + * + * @return mixed + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns whether the user response must be hidden. + * + * @return bool + */ + public function isHidden() + { + return $this->hidden; + } + + /** + * Sets whether the user response must be hidden or not. + * + * @param bool $hidden + * + * @return Question The current instance + * + * @throws LogicException In case the autocompleter is also used + */ + public function setHidden($hidden) + { + if ($this->autocompleterValues) { + throw new LogicException('A hidden question cannot use the autocompleter.'); + } + + $this->hidden = (bool) $hidden; + + return $this; + } + + /** + * In case the response can not be hidden, whether to fallback on non-hidden question or not. + * + * @return bool + */ + public function isHiddenFallback() + { + return $this->hiddenFallback; + } + + /** + * Sets whether to fallback on non-hidden question if the response can not be hidden. + * + * @param bool $fallback + * + * @return Question The current instance + */ + public function setHiddenFallback($fallback) + { + $this->hiddenFallback = (bool) $fallback; + + return $this; + } + + /** + * Gets values for the autocompleter. + * + * @return null|array|\Traversable + */ + public function getAutocompleterValues() + { + return $this->autocompleterValues; + } + + /** + * Sets values for the autocompleter. + * + * @param null|array|\Traversable $values + * + * @return Question The current instance + * + * @throws InvalidArgumentException + * @throws LogicException + */ + public function setAutocompleterValues($values) + { + if (is_array($values)) { + $values = $this->isAssoc($values) ? array_merge(array_keys($values), array_values($values)) : array_values($values); + } + + if (null !== $values && !is_array($values)) { + if (!$values instanceof \Traversable || !$values instanceof \Countable) { + throw new InvalidArgumentException('Autocompleter values can be either an array, `null` or an object implementing both `Countable` and `Traversable` interfaces.'); + } + } + + if ($this->hidden) { + throw new LogicException('A hidden question cannot use the autocompleter.'); + } + + $this->autocompleterValues = $values; + + return $this; + } + + /** + * Sets a validator for the question. + * + * @param null|callable $validator + * + * @return Question The current instance + */ + public function setValidator($validator) + { + $this->validator = $validator; + + return $this; + } + + /** + * Gets the validator for the question. + * + * @return null|callable + */ + public function getValidator() + { + return $this->validator; + } + + /** + * Sets the maximum number of attempts. + * + * Null means an unlimited number of attempts. + * + * @param null|int $attempts + * + * @return Question The current instance + * + * @throws InvalidArgumentException In case the number of attempts is invalid. + */ + public function setMaxAttempts($attempts) + { + if (null !== $attempts && $attempts < 1) { + throw new InvalidArgumentException('Maximum number of attempts must be a positive value.'); + } + + $this->attempts = $attempts; + + return $this; + } + + /** + * Gets the maximum number of attempts. + * + * Null means an unlimited number of attempts. + * + * @return null|int + */ + public function getMaxAttempts() + { + return $this->attempts; + } + + /** + * Sets a normalizer for the response. + * + * The normalizer can be a callable (a string), a closure or a class implementing __invoke. + * + * @param callable $normalizer + * + * @return Question The current instance + */ + public function setNormalizer($normalizer) + { + $this->normalizer = $normalizer; + + return $this; + } + + /** + * Gets the normalizer for the response. + * + * The normalizer can ba a callable (a string), a closure or a class implementing __invoke. + * + * @return callable + */ + public function getNormalizer() + { + return $this->normalizer; + } + + protected function isAssoc($array) + { + return (bool) count(array_filter(array_keys($array), 'is_string')); + } +} diff --git a/vendor/symfony/console/README.md b/vendor/symfony/console/README.md new file mode 100644 index 00000000..664a37c0 --- /dev/null +++ b/vendor/symfony/console/README.md @@ -0,0 +1,20 @@ +Console Component +================= + +The Console component eases the creation of beautiful and testable command line +interfaces. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/console/index.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) + +Credits +------- + +`Resources/bin/hiddeninput.exe` is a third party binary provided within this +component. Find sources and license at https://github.com/Seldaek/hidden-input. diff --git a/vendor/symfony/console/Resources/bin/hiddeninput.exe b/vendor/symfony/console/Resources/bin/hiddeninput.exe Binary files differnew file mode 100644 index 00000000..c8cf65e8 --- /dev/null +++ b/vendor/symfony/console/Resources/bin/hiddeninput.exe diff --git a/vendor/symfony/console/Shell.php b/vendor/symfony/console/Shell.php new file mode 100644 index 00000000..dacdf223 --- /dev/null +++ b/vendor/symfony/console/Shell.php @@ -0,0 +1,233 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Input\StringInput; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Process\ProcessBuilder; +use Symfony\Component\Process\PhpExecutableFinder; + +/** + * A Shell wraps an Application to add shell capabilities to it. + * + * Support for history and completion only works with a PHP compiled + * with readline support (either --with-readline or --with-libedit) + * + * @deprecated since version 2.8, to be removed in 3.0. + * + * @author Fabien Potencier <fabien@symfony.com> + * @author Martin Hasoň <martin.hason@gmail.com> + */ +class Shell +{ + private $application; + private $history; + private $output; + private $hasReadline; + private $processIsolation = false; + + /** + * Constructor. + * + * If there is no readline support for the current PHP executable + * a \RuntimeException exception is thrown. + * + * @param Application $application An application instance + */ + public function __construct(Application $application) + { + @trigger_error('The '.__CLASS__.' class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + + $this->hasReadline = function_exists('readline'); + $this->application = $application; + $this->history = getenv('HOME').'/.history_'.$application->getName(); + $this->output = new ConsoleOutput(); + } + + /** + * Runs the shell. + */ + public function run() + { + $this->application->setAutoExit(false); + $this->application->setCatchExceptions(true); + + if ($this->hasReadline) { + readline_read_history($this->history); + readline_completion_function(array($this, 'autocompleter')); + } + + $this->output->writeln($this->getHeader()); + $php = null; + if ($this->processIsolation) { + $finder = new PhpExecutableFinder(); + $php = $finder->find(); + $this->output->writeln(<<<'EOF' +<info>Running with process isolation, you should consider this:</info> + * each command is executed as separate process, + * commands don't support interactivity, all params must be passed explicitly, + * commands output is not colorized. + +EOF + ); + } + + while (true) { + $command = $this->readline(); + + if (false === $command) { + $this->output->writeln("\n"); + + break; + } + + if ($this->hasReadline) { + readline_add_history($command); + readline_write_history($this->history); + } + + if ($this->processIsolation) { + $pb = new ProcessBuilder(); + + $process = $pb + ->add($php) + ->add($_SERVER['argv'][0]) + ->add($command) + ->inheritEnvironmentVariables(true) + ->getProcess() + ; + + $output = $this->output; + $process->run(function ($type, $data) use ($output) { + $output->writeln($data); + }); + + $ret = $process->getExitCode(); + } else { + $ret = $this->application->run(new StringInput($command), $this->output); + } + + if (0 !== $ret) { + $this->output->writeln(sprintf('<error>The command terminated with an error status (%s)</error>', $ret)); + } + } + } + + /** + * Returns the shell header. + * + * @return string The header string + */ + protected function getHeader() + { + return <<<EOF + +Welcome to the <info>{$this->application->getName()}</info> shell (<comment>{$this->application->getVersion()}</comment>). + +At the prompt, type <comment>help</comment> for some help, +or <comment>list</comment> to get a list of available commands. + +To exit the shell, type <comment>^D</comment>. + +EOF; + } + + /** + * Renders a prompt. + * + * @return string The prompt + */ + protected function getPrompt() + { + // using the formatter here is required when using readline + return $this->output->getFormatter()->format($this->application->getName().' > '); + } + + protected function getOutput() + { + return $this->output; + } + + protected function getApplication() + { + return $this->application; + } + + /** + * Tries to return autocompletion for the current entered text. + * + * @param string $text The last segment of the entered text + * + * @return bool|array A list of guessed strings or true + */ + private function autocompleter($text) + { + $info = readline_info(); + $text = substr($info['line_buffer'], 0, $info['end']); + + if ($info['point'] !== $info['end']) { + return true; + } + + // task name? + if (false === strpos($text, ' ') || !$text) { + return array_keys($this->application->all()); + } + + // options and arguments? + try { + $command = $this->application->find(substr($text, 0, strpos($text, ' '))); + } catch (\Exception $e) { + return true; + } + + $list = array('--help'); + foreach ($command->getDefinition()->getOptions() as $option) { + $list[] = '--'.$option->getName(); + } + + return $list; + } + + /** + * Reads a single line from standard input. + * + * @return string The single line from standard input + */ + private function readline() + { + if ($this->hasReadline) { + $line = readline($this->getPrompt()); + } else { + $this->output->write($this->getPrompt()); + $line = fgets(STDIN, 1024); + $line = (false === $line || '' === $line) ? false : rtrim($line); + } + + return $line; + } + + public function getProcessIsolation() + { + return $this->processIsolation; + } + + public function setProcessIsolation($processIsolation) + { + $this->processIsolation = (bool) $processIsolation; + + if ($this->processIsolation && !class_exists('Symfony\\Component\\Process\\Process')) { + throw new RuntimeException('Unable to isolate processes as the Symfony Process Component is not installed.'); + } + } +} diff --git a/vendor/symfony/console/Style/OutputStyle.php b/vendor/symfony/console/Style/OutputStyle.php new file mode 100644 index 00000000..8371bb53 --- /dev/null +++ b/vendor/symfony/console/Style/OutputStyle.php @@ -0,0 +1,116 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Decorates output to add console style guide helpers. + * + * @author Kevin Bond <kevinbond@gmail.com> + */ +abstract class OutputStyle implements OutputInterface, StyleInterface +{ + private $output; + + /** + * @param OutputInterface $output + */ + public function __construct(OutputInterface $output) + { + $this->output = $output; + } + + /** + * {@inheritdoc} + */ + public function newLine($count = 1) + { + $this->output->write(str_repeat(PHP_EOL, $count)); + } + + /** + * @param int $max + * + * @return ProgressBar + */ + public function createProgressBar($max = 0) + { + return new ProgressBar($this->output, $max); + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) + { + $this->output->write($messages, $newline, $type); + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $type = self::OUTPUT_NORMAL) + { + $this->output->writeln($messages, $type); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + $this->output->setVerbosity($level); + } + + /** + * {@inheritdoc} + */ + public function getVerbosity() + { + return $this->output->getVerbosity(); + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + $this->output->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function isDecorated() + { + return $this->output->isDecorated(); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + $this->output->setFormatter($formatter); + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + return $this->output->getFormatter(); + } +} diff --git a/vendor/symfony/console/Style/StyleInterface.php b/vendor/symfony/console/Style/StyleInterface.php new file mode 100644 index 00000000..2448547f --- /dev/null +++ b/vendor/symfony/console/Style/StyleInterface.php @@ -0,0 +1,159 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +/** + * Output style helpers. + * + * @author Kevin Bond <kevinbond@gmail.com> + */ +interface StyleInterface +{ + /** + * Formats a command title. + * + * @param string $message + */ + public function title($message); + + /** + * Formats a section title. + * + * @param string $message + */ + public function section($message); + + /** + * Formats a list. + * + * @param array $elements + */ + public function listing(array $elements); + + /** + * Formats informational text. + * + * @param string|array $message + */ + public function text($message); + + /** + * Formats a success result bar. + * + * @param string|array $message + */ + public function success($message); + + /** + * Formats an error result bar. + * + * @param string|array $message + */ + public function error($message); + + /** + * Formats an warning result bar. + * + * @param string|array $message + */ + public function warning($message); + + /** + * Formats a note admonition. + * + * @param string|array $message + */ + public function note($message); + + /** + * Formats a caution admonition. + * + * @param string|array $message + */ + public function caution($message); + + /** + * Formats a table. + * + * @param array $headers + * @param array $rows + */ + public function table(array $headers, array $rows); + + /** + * Asks a question. + * + * @param string $question + * @param string|null $default + * @param callable|null $validator + * + * @return string + */ + public function ask($question, $default = null, $validator = null); + + /** + * Asks a question with the user input hidden. + * + * @param string $question + * @param callable|null $validator + * + * @return string + */ + public function askHidden($question, $validator = null); + + /** + * Asks for confirmation. + * + * @param string $question + * @param bool $default + * + * @return bool + */ + public function confirm($question, $default = true); + + /** + * Asks a choice question. + * + * @param string $question + * @param array $choices + * @param string|int|null $default + * + * @return string + */ + public function choice($question, array $choices, $default = null); + + /** + * Add newline(s). + * + * @param int $count The number of newlines + */ + public function newLine($count = 1); + + /** + * Starts the progress output. + * + * @param int $max Maximum steps (0 if unknown) + */ + public function progressStart($max = 0); + + /** + * Advances the progress output X steps. + * + * @param int $step Number of steps to advance + */ + public function progressAdvance($step = 1); + + /** + * Finishes the progress output. + */ + public function progressFinish(); +} diff --git a/vendor/symfony/console/Style/SymfonyStyle.php b/vendor/symfony/console/Style/SymfonyStyle.php new file mode 100644 index 00000000..53a7951e --- /dev/null +++ b/vendor/symfony/console/Style/SymfonyStyle.php @@ -0,0 +1,440 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Helper\SymfonyQuestionHelper; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Helper\TableCell; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; + +/** + * Output decorator helpers for the Symfony Style Guide. + * + * @author Kevin Bond <kevinbond@gmail.com> + */ +class SymfonyStyle extends OutputStyle +{ + const MAX_LINE_LENGTH = 120; + + private $input; + private $questionHelper; + private $progressBar; + private $lineLength; + private $bufferedOutput; + + /** + * @param InputInterface $input + * @param OutputInterface $output + */ + public function __construct(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->bufferedOutput = new BufferedOutput($output->getVerbosity(), false, clone $output->getFormatter()); + // Windows cmd wraps lines as soon as the terminal width is reached, whether there are following chars or not. + $this->lineLength = min($this->getTerminalWidth() - (int) (DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH); + + parent::__construct($output); + } + + /** + * Formats a message as a block of text. + * + * @param string|array $messages The message to write in the block + * @param string|null $type The block type (added in [] on first line) + * @param string|null $style The style to apply to the whole block + * @param string $prefix The prefix for the block + * @param bool $padding Whether to add vertical padding + */ + public function block($messages, $type = null, $style = null, $prefix = ' ', $padding = false) + { + $this->autoPrependBlock(); + $messages = is_array($messages) ? array_values($messages) : array($messages); + $indentLength = 0; + $lines = array(); + + if (null !== $type) { + $typePrefix = sprintf('[%s] ', $type); + $indentLength = strlen($typePrefix); + $lineIndentation = str_repeat(' ', $indentLength); + } + + // wrap and add newlines for each element + foreach ($messages as $key => $message) { + $message = OutputFormatter::escape($message); + $lines = array_merge($lines, explode(PHP_EOL, wordwrap($message, $this->lineLength - Helper::strlen($prefix) - $indentLength, PHP_EOL, true))); + + // prefix each line with a number of spaces equivalent to the type length + if (null !== $type) { + foreach ($lines as &$line) { + $line = $lineIndentation === substr($line, 0, $indentLength) ? $line : $lineIndentation.$line; + } + } + + if (count($messages) > 1 && $key < count($messages) - 1) { + $lines[] = ''; + } + } + + if (null !== $type) { + $lines[0] = substr_replace($lines[0], $typePrefix, 0, $indentLength); + } + + if ($padding && $this->isDecorated()) { + array_unshift($lines, ''); + $lines[] = ''; + } + + foreach ($lines as &$line) { + $line = sprintf('%s%s', $prefix, $line); + $line .= str_repeat(' ', $this->lineLength - Helper::strlenWithoutDecoration($this->getFormatter(), $line)); + + if ($style) { + $line = sprintf('<%s>%s</>', $style, $line); + } + } + + $this->writeln($lines); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function title($message) + { + $this->autoPrependBlock(); + $this->writeln(array( + sprintf('<comment>%s</>', $message), + sprintf('<comment>%s</>', str_repeat('=', Helper::strlenWithoutDecoration($this->getFormatter(), $message))), + )); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function section($message) + { + $this->autoPrependBlock(); + $this->writeln(array( + sprintf('<comment>%s</>', $message), + sprintf('<comment>%s</>', str_repeat('-', Helper::strlenWithoutDecoration($this->getFormatter(), $message))), + )); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function listing(array $elements) + { + $this->autoPrependText(); + $elements = array_map(function ($element) { + return sprintf(' * %s', $element); + }, $elements); + + $this->writeln($elements); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function text($message) + { + $this->autoPrependText(); + + $messages = is_array($message) ? array_values($message) : array($message); + foreach ($messages as $message) { + $this->writeln(sprintf(' %s', $message)); + } + } + + /** + * Formats a command comment. + * + * @param string|array $message + */ + public function comment($message) + { + $messages = is_array($message) ? array_values($message) : array($message); + foreach ($messages as &$message) { + $message = $this->getFormatter()->format($message); + } + + $this->block($messages, null, null, ' // '); + } + + /** + * {@inheritdoc} + */ + public function success($message) + { + $this->block($message, 'OK', 'fg=black;bg=green', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function error($message) + { + $this->block($message, 'ERROR', 'fg=white;bg=red', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function warning($message) + { + $this->block($message, 'WARNING', 'fg=white;bg=red', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function note($message) + { + $this->block($message, 'NOTE', 'fg=yellow', ' ! '); + } + + /** + * {@inheritdoc} + */ + public function caution($message) + { + $this->block($message, 'CAUTION', 'fg=white;bg=red', ' ! ', true); + } + + /** + * {@inheritdoc} + */ + public function table(array $headers, array $rows) + { + array_walk_recursive($headers, function (&$value) { + if ($value instanceof TableCell) { + $value = new TableCell(sprintf('<info>%s</>', $value), array( + 'colspan' => $value->getColspan(), + 'rowspan' => $value->getRowspan(), + )); + } else { + $value = sprintf('<info>%s</>', $value); + } + }); + + $table = new Table($this); + $table->setHeaders($headers); + $table->setRows($rows); + $table->setStyle('symfony-style-guide'); + + $table->render(); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function ask($question, $default = null, $validator = null) + { + $question = new Question($question, $default); + $question->setValidator($validator); + + return $this->askQuestion($question); + } + + /** + * {@inheritdoc} + */ + public function askHidden($question, $validator = null) + { + $question = new Question($question); + + $question->setHidden(true); + $question->setValidator($validator); + + return $this->askQuestion($question); + } + + /** + * {@inheritdoc} + */ + public function confirm($question, $default = true) + { + return $this->askQuestion(new ConfirmationQuestion($question, $default)); + } + + /** + * {@inheritdoc} + */ + public function choice($question, array $choices, $default = null) + { + if (null !== $default) { + $values = array_flip($choices); + $default = $values[$default]; + } + + return $this->askQuestion(new ChoiceQuestion($question, $choices, $default)); + } + + /** + * {@inheritdoc} + */ + public function progressStart($max = 0) + { + $this->progressBar = $this->createProgressBar($max); + $this->progressBar->start(); + } + + /** + * {@inheritdoc} + */ + public function progressAdvance($step = 1) + { + $this->getProgressBar()->advance($step); + } + + /** + * {@inheritdoc} + */ + public function progressFinish() + { + $this->getProgressBar()->finish(); + $this->newLine(2); + $this->progressBar = null; + } + + /** + * {@inheritdoc} + */ + public function createProgressBar($max = 0) + { + $progressBar = parent::createProgressBar($max); + + if ('\\' !== DIRECTORY_SEPARATOR) { + $progressBar->setEmptyBarCharacter('â–‘'); // light shade character \u2591 + $progressBar->setProgressCharacter(''); + $progressBar->setBarCharacter('â–“'); // dark shade character \u2593 + } + + return $progressBar; + } + + /** + * @param Question $question + * + * @return string + */ + public function askQuestion(Question $question) + { + if ($this->input->isInteractive()) { + $this->autoPrependBlock(); + } + + if (!$this->questionHelper) { + $this->questionHelper = new SymfonyQuestionHelper(); + } + + $answer = $this->questionHelper->ask($this->input, $this, $question); + + if ($this->input->isInteractive()) { + $this->newLine(); + $this->bufferedOutput->write("\n"); + } + + return $answer; + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $type = self::OUTPUT_NORMAL) + { + parent::writeln($messages, $type); + $this->bufferedOutput->writeln($this->reduceBuffer($messages), $type); + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) + { + parent::write($messages, $newline, $type); + $this->bufferedOutput->write($this->reduceBuffer($messages), $newline, $type); + } + + /** + * {@inheritdoc} + */ + public function newLine($count = 1) + { + parent::newLine($count); + $this->bufferedOutput->write(str_repeat("\n", $count)); + } + + /** + * @return ProgressBar + */ + private function getProgressBar() + { + if (!$this->progressBar) { + throw new RuntimeException('The ProgressBar is not started.'); + } + + return $this->progressBar; + } + + private function getTerminalWidth() + { + $application = new Application(); + $dimensions = $application->getTerminalDimensions(); + + return $dimensions[0] ?: self::MAX_LINE_LENGTH; + } + + private function autoPrependBlock() + { + $chars = substr(str_replace(PHP_EOL, "\n", $this->bufferedOutput->fetch()), -2); + + if (!isset($chars[0])) { + return $this->newLine(); //empty history, so we should start with a new line. + } + //Prepend new line for each non LF chars (This means no blank line was output before) + $this->newLine(2 - substr_count($chars, "\n")); + } + + private function autoPrependText() + { + $fetched = $this->bufferedOutput->fetch(); + //Prepend new line if last char isn't EOL: + if ("\n" !== substr($fetched, -1)) { + $this->newLine(); + } + } + + private function reduceBuffer($messages) + { + // We need to know if the two last chars are PHP_EOL + // Preserve the last 4 chars inserted (PHP_EOL on windows is two chars) in the history buffer + return array_map(function ($value) { + return substr($value, -4); + }, array_merge(array($this->bufferedOutput->fetch()), (array) $messages)); + } +} diff --git a/vendor/symfony/console/Tester/ApplicationTester.php b/vendor/symfony/console/Tester/ApplicationTester.php new file mode 100644 index 00000000..da8a19ce --- /dev/null +++ b/vendor/symfony/console/Tester/ApplicationTester.php @@ -0,0 +1,128 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * Eases the testing of console applications. + * + * When testing an application, don't forget to disable the auto exit flag: + * + * $application = new Application(); + * $application->setAutoExit(false); + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class ApplicationTester +{ + private $application; + private $input; + private $output; + private $statusCode; + + /** + * Constructor. + * + * @param Application $application An Application instance to test. + */ + public function __construct(Application $application) + { + $this->application = $application; + } + + /** + * Executes the application. + * + * Available options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * + * @param array $input An array of arguments and options + * @param array $options An array of options + * + * @return int The command exit code + */ + public function run(array $input, $options = array()) + { + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + + return $this->statusCode = $this->application->run($this->input, $this->output); + } + + /** + * Gets the display returned by the last execution of the application. + * + * @param bool $normalize Whether to normalize end of lines to \n or not + * + * @return string The display + */ + public function getDisplay($normalize = false) + { + rewind($this->output->getStream()); + + $display = stream_get_contents($this->output->getStream()); + + if ($normalize) { + $display = str_replace(PHP_EOL, "\n", $display); + } + + return $display; + } + + /** + * Gets the input instance used by the last execution of the application. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the application. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } + + /** + * Gets the status code returned by the last execution of the application. + * + * @return int The status code + */ + public function getStatusCode() + { + return $this->statusCode; + } +} diff --git a/vendor/symfony/console/Tester/CommandTester.php b/vendor/symfony/console/Tester/CommandTester.php new file mode 100644 index 00000000..8d6486e1 --- /dev/null +++ b/vendor/symfony/console/Tester/CommandTester.php @@ -0,0 +1,132 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Eases the testing of console commands. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class CommandTester +{ + private $command; + private $input; + private $output; + private $statusCode; + + /** + * Constructor. + * + * @param Command $command A Command instance to test. + */ + public function __construct(Command $command) + { + $this->command = $command; + } + + /** + * Executes the command. + * + * Available execution options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * + * @param array $input An array of command arguments and options + * @param array $options An array of execution options + * + * @return int The command exit code + */ + public function execute(array $input, array $options = array()) + { + // set the command name automatically if the application requires + // this argument and no command name was passed + if (!isset($input['command']) + && (null !== $application = $this->command->getApplication()) + && $application->getDefinition()->hasArgument('command') + ) { + $input = array_merge(array('command' => $this->command->getName()), $input); + } + + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + + return $this->statusCode = $this->command->run($this->input, $this->output); + } + + /** + * Gets the display returned by the last execution of the command. + * + * @param bool $normalize Whether to normalize end of lines to \n or not + * + * @return string The display + */ + public function getDisplay($normalize = false) + { + rewind($this->output->getStream()); + + $display = stream_get_contents($this->output->getStream()); + + if ($normalize) { + $display = str_replace(PHP_EOL, "\n", $display); + } + + return $display; + } + + /** + * Gets the input instance used by the last execution of the command. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the command. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } + + /** + * Gets the status code returned by the last execution of the application. + * + * @return int The status code + */ + public function getStatusCode() + { + return $this->statusCode; + } +} diff --git a/vendor/symfony/console/Tests/ApplicationTest.php b/vendor/symfony/console/Tests/ApplicationTest.php new file mode 100644 index 00000000..927fa0bd --- /dev/null +++ b/vendor/symfony/console/Tests/ApplicationTest.php @@ -0,0 +1,1146 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Tester\ApplicationTester; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleExceptionEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class ApplicationTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/Fixtures/'); + require_once self::$fixturesPath.'/FooCommand.php'; + require_once self::$fixturesPath.'/Foo1Command.php'; + require_once self::$fixturesPath.'/Foo2Command.php'; + require_once self::$fixturesPath.'/Foo3Command.php'; + require_once self::$fixturesPath.'/Foo4Command.php'; + require_once self::$fixturesPath.'/Foo5Command.php'; + require_once self::$fixturesPath.'/FoobarCommand.php'; + require_once self::$fixturesPath.'/BarBucCommand.php'; + require_once self::$fixturesPath.'/FooSubnamespaced1Command.php'; + require_once self::$fixturesPath.'/FooSubnamespaced2Command.php'; + } + + protected function normalizeLineBreaks($text) + { + return str_replace(PHP_EOL, "\n", $text); + } + + /** + * Replaces the dynamic placeholders of the command help text with a static version. + * The placeholder %command.full_name% includes the script path that is not predictable + * and can not be tested against. + */ + protected function ensureStaticCommandHelp(Application $application) + { + foreach ($application->all() as $command) { + $command->setHelp(str_replace('%command.full_name%', 'app/console %command.name%', $command->getHelp())); + } + } + + public function testConstructor() + { + $application = new Application('foo', 'bar'); + $this->assertEquals('foo', $application->getName(), '__construct() takes the application name as its first argument'); + $this->assertEquals('bar', $application->getVersion(), '__construct() takes the application version as its second argument'); + $this->assertEquals(array('help', 'list'), array_keys($application->all()), '__construct() registered the help and list commands by default'); + } + + public function testSetGetName() + { + $application = new Application(); + $application->setName('foo'); + $this->assertEquals('foo', $application->getName(), '->setName() sets the name of the application'); + } + + public function testSetGetVersion() + { + $application = new Application(); + $application->setVersion('bar'); + $this->assertEquals('bar', $application->getVersion(), '->setVersion() sets the version of the application'); + } + + public function testGetLongVersion() + { + $application = new Application('foo', 'bar'); + $this->assertEquals('<info>foo</info> version <comment>bar</comment>', $application->getLongVersion(), '->getLongVersion() returns the long version of the application'); + } + + public function testHelp() + { + $application = new Application(); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_gethelp.txt', $this->normalizeLineBreaks($application->getHelp()), '->getHelp() returns a help message'); + } + + public function testAll() + { + $application = new Application(); + $commands = $application->all(); + $this->assertInstanceOf('Symfony\\Component\\Console\\Command\\HelpCommand', $commands['help'], '->all() returns the registered commands'); + + $application->add(new \FooCommand()); + $commands = $application->all('foo'); + $this->assertCount(1, $commands, '->all() takes a namespace as its first argument'); + } + + public function testRegister() + { + $application = new Application(); + $command = $application->register('foo'); + $this->assertEquals('foo', $command->getName(), '->register() registers a new command'); + } + + public function testAdd() + { + $application = new Application(); + $application->add($foo = new \FooCommand()); + $commands = $application->all(); + $this->assertEquals($foo, $commands['foo:bar'], '->add() registers a command'); + + $application = new Application(); + $application->addCommands(array($foo = new \FooCommand(), $foo1 = new \Foo1Command())); + $commands = $application->all(); + $this->assertEquals(array($foo, $foo1), array($commands['foo:bar'], $commands['foo:bar1']), '->addCommands() registers an array of commands'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Command class "Foo5Command" is not correctly initialized. You probably forgot to call the parent constructor. + */ + public function testAddCommandWithEmptyConstructor() + { + $application = new Application(); + $application->add(new \Foo5Command()); + } + + public function testHasGet() + { + $application = new Application(); + $this->assertTrue($application->has('list'), '->has() returns true if a named command is registered'); + $this->assertFalse($application->has('afoobar'), '->has() returns false if a named command is not registered'); + + $application->add($foo = new \FooCommand()); + $this->assertTrue($application->has('afoobar'), '->has() returns true if an alias is registered'); + $this->assertEquals($foo, $application->get('foo:bar'), '->get() returns a command by name'); + $this->assertEquals($foo, $application->get('afoobar'), '->get() returns a command by alias'); + + $application = new Application(); + $application->add($foo = new \FooCommand()); + // simulate --help + $r = new \ReflectionObject($application); + $p = $r->getProperty('wantHelps'); + $p->setAccessible(true); + $p->setValue($application, true); + $command = $application->get('foo:bar'); + $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $command, '->get() returns the help command if --help is provided as the input'); + } + + public function testSilentHelp() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $tester = new ApplicationTester($application); + $tester->run(array('-h' => true, '-q' => true), array('decorated' => false)); + + $this->assertEmpty($tester->getDisplay(true)); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage The command "foofoo" does not exist. + */ + public function testGetInvalidCommand() + { + $application = new Application(); + $application->get('foofoo'); + } + + public function testGetNamespaces() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $this->assertEquals(array('foo'), $application->getNamespaces(), '->getNamespaces() returns an array of unique used namespaces'); + } + + public function testFindNamespace() + { + $application = new Application(); + $application->add(new \FooCommand()); + $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns the given namespace if it exists'); + $this->assertEquals('foo', $application->findNamespace('f'), '->findNamespace() finds a namespace given an abbreviation'); + $application->add(new \Foo2Command()); + $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns the given namespace if it exists'); + } + + public function testFindNamespaceWithSubnamespaces() + { + $application = new Application(); + $application->add(new \FooSubnamespaced1Command()); + $application->add(new \FooSubnamespaced2Command()); + $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns commands even if the commands are only contained in subnamespaces'); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage The namespace "f" is ambiguous (foo, foo1). + */ + public function testFindAmbiguousNamespace() + { + $application = new Application(); + $application->add(new \BarBucCommand()); + $application->add(new \FooCommand()); + $application->add(new \Foo2Command()); + $application->findNamespace('f'); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage There are no commands defined in the "bar" namespace. + */ + public function testFindInvalidNamespace() + { + $application = new Application(); + $application->findNamespace('bar'); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage Command "foo1" is not defined + */ + public function testFindUniqueNameButNamespaceName() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + $application->find($commandName = 'foo1'); + } + + public function testFind() + { + $application = new Application(); + $application->add(new \FooCommand()); + + $this->assertInstanceOf('FooCommand', $application->find('foo:bar'), '->find() returns a command if its name exists'); + $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $application->find('h'), '->find() returns a command if its name exists'); + $this->assertInstanceOf('FooCommand', $application->find('f:bar'), '->find() returns a command if the abbreviation for the namespace exists'); + $this->assertInstanceOf('FooCommand', $application->find('f:b'), '->find() returns a command if the abbreviation for the namespace and the command name exist'); + $this->assertInstanceOf('FooCommand', $application->find('a'), '->find() returns a command if the abbreviation exists for an alias'); + } + + /** + * @dataProvider provideAmbiguousAbbreviations + */ + public function testFindWithAmbiguousAbbreviations($abbreviation, $expectedExceptionMessage) + { + $this->setExpectedException('Symfony\Component\Console\Exception\CommandNotFoundException', $expectedExceptionMessage); + + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + $application->find($abbreviation); + } + + public function provideAmbiguousAbbreviations() + { + return array( + array('f', 'Command "f" is not defined.'), + array('a', 'Command "a" is ambiguous (afoobar, afoobar1 and 1 more).'), + array('foo:b', 'Command "foo:b" is ambiguous (foo:bar, foo:bar1 and 1 more).'), + ); + } + + public function testFindCommandEqualNamespace() + { + $application = new Application(); + $application->add(new \Foo3Command()); + $application->add(new \Foo4Command()); + + $this->assertInstanceOf('Foo3Command', $application->find('foo3:bar'), '->find() returns the good command even if a namespace has same name'); + $this->assertInstanceOf('Foo4Command', $application->find('foo3:bar:toh'), '->find() returns a command even if its namespace equals another command name'); + } + + public function testFindCommandWithAmbiguousNamespacesButUniqueName() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \FoobarCommand()); + + $this->assertInstanceOf('FoobarCommand', $application->find('f:f')); + } + + public function testFindCommandWithMissingNamespace() + { + $application = new Application(); + $application->add(new \Foo4Command()); + + $this->assertInstanceOf('Foo4Command', $application->find('f::t')); + } + + /** + * @dataProvider provideInvalidCommandNamesSingle + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage Did you mean this + */ + public function testFindAlternativeExceptionMessageSingle($name) + { + $application = new Application(); + $application->add(new \Foo3Command()); + $application->find($name); + } + + public function provideInvalidCommandNamesSingle() + { + return array( + array('foo3:baR'), + array('foO3:bar'), + ); + } + + public function testFindAlternativeExceptionMessageMultiple() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + // Command + plural + try { + $application->find('foo:baR'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/foo1:bar/', $e->getMessage()); + $this->assertRegExp('/foo:bar/', $e->getMessage()); + } + + // Namespace + plural + try { + $application->find('foo2:bar'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/foo1/', $e->getMessage()); + } + + $application->add(new \Foo3Command()); + $application->add(new \Foo4Command()); + + // Subnamespace + plural + try { + $a = $application->find('foo3:'); + $this->fail('->find() should throw an Symfony\Component\Console\Exception\CommandNotFoundException if a command is ambiguous because of a subnamespace, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e); + $this->assertRegExp('/foo3:bar/', $e->getMessage()); + $this->assertRegExp('/foo3:bar:toh/', $e->getMessage()); + } + } + + public function testFindAlternativeCommands() + { + $application = new Application(); + + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + try { + $application->find($commandName = 'Unknown command'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist'); + $this->assertSame(array(), $e->getAlternatives()); + $this->assertEquals(sprintf('Command "%s" is not defined.', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without alternatives'); + } + + // Test if "bar1" command throw a "CommandNotFoundException" and does not contain + // "foo:bar" as alternative because "bar1" is too far from "foo:bar" + try { + $application->find($commandName = 'bar1'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist'); + $this->assertSame(array('afoobar1', 'foo:bar1'), $e->getAlternatives()); + $this->assertRegExp(sprintf('/Command "%s" is not defined./', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/afoobar1/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternative : "afoobar1"'); + $this->assertRegExp('/foo:bar1/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternative : "foo:bar1"'); + $this->assertNotRegExp('/foo:bar(?>!1)/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without "foo:bar" alternative'); + } + } + + public function testFindAlternativeCommandsWithAnAlias() + { + $fooCommand = new \FooCommand(); + $fooCommand->setAliases(array('foo2')); + + $application = new Application(); + $application->add($fooCommand); + + $result = $application->find('foo'); + + $this->assertSame($fooCommand, $result); + } + + public function testFindAlternativeNamespace() + { + $application = new Application(); + + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + $application->add(new \foo3Command()); + + try { + $application->find('Unknown-namespace:Unknown-command'); + $this->fail('->find() throws a CommandNotFoundException if namespace does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if namespace does not exist'); + $this->assertSame(array(), $e->getAlternatives()); + $this->assertEquals('There are no commands defined in the "Unknown-namespace" namespace.', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, without alternatives'); + } + + try { + $application->find('foo2:command'); + $this->fail('->find() throws a CommandNotFoundException if namespace does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if namespace does not exist'); + $this->assertCount(3, $e->getAlternatives()); + $this->assertContains('foo', $e->getAlternatives()); + $this->assertContains('foo1', $e->getAlternatives()); + $this->assertContains('foo3', $e->getAlternatives()); + $this->assertRegExp('/There are no commands defined in the "foo2" namespace./', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative'); + $this->assertRegExp('/foo/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo"'); + $this->assertRegExp('/foo1/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo1"'); + $this->assertRegExp('/foo3/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo3"'); + } + } + + public function testFindNamespaceDoesNotFailOnDeepSimilarNamespaces() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getNamespaces')); + $application->expects($this->once()) + ->method('getNamespaces') + ->will($this->returnValue(array('foo:sublong', 'bar:sub'))); + + $this->assertEquals('foo:sublong', $application->findNamespace('f:sub')); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage Command "foo::bar" is not defined. + */ + public function testFindWithDoubleColonInNameThrowsException() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo4Command()); + $application->find('foo::bar'); + } + + public function testSetCatchExceptions() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(120)); + $tester = new ApplicationTester($application); + + $application->setCatchExceptions(true); + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->setCatchExceptions() sets the catch exception flag'); + + $application->setCatchExceptions(false); + try { + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->fail('->setCatchExceptions() sets the catch exception flag'); + } catch (\Exception $e) { + $this->assertInstanceOf('\Exception', $e, '->setCatchExceptions() sets the catch exception flag'); + $this->assertEquals('Command "foo" is not defined.', $e->getMessage(), '->setCatchExceptions() sets the catch exception flag'); + } + } + + /** + * @group legacy + */ + public function testLegacyAsText() + { + $application = new Application(); + $application->add(new \FooCommand()); + $this->ensureStaticCommandHelp($application); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_astext1.txt', $this->normalizeLineBreaks($application->asText()), '->asText() returns a text representation of the application'); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_astext2.txt', $this->normalizeLineBreaks($application->asText('foo')), '->asText() returns a text representation of the application'); + } + + /** + * @group legacy + */ + public function testLegacyAsXml() + { + $application = new Application(); + $application->add(new \FooCommand()); + $this->ensureStaticCommandHelp($application); + $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/application_asxml1.txt', $application->asXml(), '->asXml() returns an XML representation of the application'); + $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/application_asxml2.txt', $application->asXml('foo'), '->asXml() returns an XML representation of the application'); + } + + public function testRenderException() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(120)); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->renderException() renders a pretty exception'); + + $tester->run(array('command' => 'foo'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); + $this->assertContains('Exception trace', $tester->getDisplay(), '->renderException() renders a pretty exception with a stack trace when verbosity is verbose'); + + $tester->run(array('command' => 'list', '--foo' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception2.txt', $tester->getDisplay(true), '->renderException() renders the command synopsis when an exception occurs in the context of a command'); + + $application->add(new \Foo3Command()); + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo3:bar'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $tester->run(array('command' => 'foo3:bar'), array('decorated' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3decorated.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(32)); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal'); + } + + public function testRenderExceptionWithDoubleWidthCharacters() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(120)); + $application->register('foo')->setCode(function () { + throw new \Exception('エラーメッセージ'); + }); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $tester->run(array('command' => 'foo'), array('decorated' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1decorated.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(32)); + $application->register('foo')->setCode(function () { + throw new \Exception('コマンドã®å®Ÿè¡Œä¸ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚'); + }); + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth2.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal'); + } + + public function testRun() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add($command = new \Foo1Command()); + $_SERVER['argv'] = array('cli.php', 'foo:bar1'); + + ob_start(); + $application->run(); + ob_end_clean(); + + $this->assertInstanceOf('Symfony\Component\Console\Input\ArgvInput', $command->input, '->run() creates an ArgvInput by default if none is given'); + $this->assertInstanceOf('Symfony\Component\Console\Output\ConsoleOutput', $command->output, '->run() creates a ConsoleOutput by default if none is given'); + + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $this->ensureStaticCommandHelp($application); + $tester = new ApplicationTester($application); + + $tester->run(array(), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run1.txt', $tester->getDisplay(true), '->run() runs the list command if no argument is passed'); + + $tester->run(array('--help' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run2.txt', $tester->getDisplay(true), '->run() runs the help command if --help is passed'); + + $tester->run(array('-h' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run2.txt', $tester->getDisplay(true), '->run() runs the help command if -h is passed'); + + $tester->run(array('command' => 'list', '--help' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run3.txt', $tester->getDisplay(true), '->run() displays the help if --help is passed'); + + $tester->run(array('command' => 'list', '-h' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run3.txt', $tester->getDisplay(true), '->run() displays the help if -h is passed'); + + $tester->run(array('--ansi' => true)); + $this->assertTrue($tester->getOutput()->isDecorated(), '->run() forces color output if --ansi is passed'); + + $tester->run(array('--no-ansi' => true)); + $this->assertFalse($tester->getOutput()->isDecorated(), '->run() forces color output to be disabled if --no-ansi is passed'); + + $tester->run(array('--version' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run4.txt', $tester->getDisplay(true), '->run() displays the program version if --version is passed'); + + $tester->run(array('-V' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run4.txt', $tester->getDisplay(true), '->run() displays the program version if -v is passed'); + + $tester->run(array('command' => 'list', '--quiet' => true)); + $this->assertSame('', $tester->getDisplay(), '->run() removes all output if --quiet is passed'); + + $tester->run(array('command' => 'list', '-q' => true)); + $this->assertSame('', $tester->getDisplay(), '->run() removes all output if -q is passed'); + + $tester->run(array('command' => 'list', '--verbose' => true)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if --verbose is passed'); + + $tester->run(array('command' => 'list', '--verbose' => 1)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if --verbose=1 is passed'); + + $tester->run(array('command' => 'list', '--verbose' => 2)); + $this->assertSame(Output::VERBOSITY_VERY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to very verbose if --verbose=2 is passed'); + + $tester->run(array('command' => 'list', '--verbose' => 3)); + $this->assertSame(Output::VERBOSITY_DEBUG, $tester->getOutput()->getVerbosity(), '->run() sets the output to debug if --verbose=3 is passed'); + + $tester->run(array('command' => 'list', '--verbose' => 4)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if unknown --verbose level is passed'); + + $tester->run(array('command' => 'list', '-v' => true)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); + + $tester->run(array('command' => 'list', '-vv' => true)); + $this->assertSame(Output::VERBOSITY_VERY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); + + $tester->run(array('command' => 'list', '-vvv' => true)); + $this->assertSame(Output::VERBOSITY_DEBUG, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); + + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add(new \FooCommand()); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo:bar', '--no-interaction' => true), array('decorated' => false)); + $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if --no-interaction is passed'); + + $tester->run(array('command' => 'foo:bar', '-n' => true), array('decorated' => false)); + $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if -n is passed'); + } + + /** + * Issue #9285. + * + * If the "verbose" option is just before an argument in ArgvInput, + * an argument value should not be treated as verbosity value. + * This test will fail with "Not enough arguments." if broken + */ + public function testVerboseValueNotBreakArguments() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add(new \FooCommand()); + + $output = new StreamOutput(fopen('php://memory', 'w', false)); + + $input = new ArgvInput(array('cli.php', '-v', 'foo:bar')); + $application->run($input, $output); + + $input = new ArgvInput(array('cli.php', '--verbose', 'foo:bar')); + $application->run($input, $output); + } + + public function testRunReturnsIntegerExitCode() + { + $exception = new \Exception('', 4); + + $application = $this->getMock('Symfony\Component\Console\Application', array('doRun')); + $application->setAutoExit(false); + $application->expects($this->once()) + ->method('doRun') + ->will($this->throwException($exception)); + + $exitCode = $application->run(new ArrayInput(array()), new NullOutput()); + + $this->assertSame(4, $exitCode, '->run() returns integer exit code extracted from raised exception'); + } + + public function testRunReturnsExitCodeOneForExceptionCodeZero() + { + $exception = new \Exception('', 0); + + $application = $this->getMock('Symfony\Component\Console\Application', array('doRun')); + $application->setAutoExit(false); + $application->expects($this->once()) + ->method('doRun') + ->will($this->throwException($exception)); + + $exitCode = $application->run(new ArrayInput(array()), new NullOutput()); + + $this->assertSame(1, $exitCode, '->run() returns exit code 1 when exception code is 0'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage An option with shortcut "e" already exists. + */ + public function testAddingOptionWithDuplicateShortcut() + { + $dispatcher = new EventDispatcher(); + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->setDispatcher($dispatcher); + + $application->getDefinition()->addOption(new InputOption('--env', '-e', InputOption::VALUE_REQUIRED, 'Environment')); + + $application + ->register('foo') + ->setAliases(array('f')) + ->setDefinition(array(new InputOption('survey', 'e', InputOption::VALUE_REQUIRED, 'My option with a shortcut.'))) + ->setCode(function (InputInterface $input, OutputInterface $output) {}) + ; + + $input = new ArrayInput(array('command' => 'foo')); + $output = new NullOutput(); + + $application->run($input, $output); + } + + /** + * @expectedException \LogicException + * @dataProvider getAddingAlreadySetDefinitionElementData + */ + public function testAddingAlreadySetDefinitionElementData($def) + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application + ->register('foo') + ->setDefinition(array($def)) + ->setCode(function (InputInterface $input, OutputInterface $output) {}) + ; + + $input = new ArrayInput(array('command' => 'foo')); + $output = new NullOutput(); + $application->run($input, $output); + } + + public function getAddingAlreadySetDefinitionElementData() + { + return array( + array(new InputArgument('command', InputArgument::REQUIRED)), + array(new InputOption('quiet', '', InputOption::VALUE_NONE)), + array(new InputOption('query', 'q', InputOption::VALUE_NONE)), + ); + } + + public function testGetDefaultHelperSetReturnsDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + $this->assertTrue($helperSet->has('dialog')); + $this->assertTrue($helperSet->has('progress')); + } + + public function testAddingSingleHelperSetOverwritesDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setHelperSet(new HelperSet(array(new FormatterHelper()))); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + + // no other default helper set should be returned + $this->assertFalse($helperSet->has('dialog')); + $this->assertFalse($helperSet->has('progress')); + } + + public function testOverwritingDefaultHelperSetOverwritesDefaultValues() + { + $application = new CustomApplication(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setHelperSet(new HelperSet(array(new FormatterHelper()))); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + + // no other default helper set should be returned + $this->assertFalse($helperSet->has('dialog')); + $this->assertFalse($helperSet->has('progress')); + } + + public function testGetDefaultInputDefinitionReturnsDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $inputDefinition = $application->getDefinition(); + + $this->assertTrue($inputDefinition->hasArgument('command')); + + $this->assertTrue($inputDefinition->hasOption('help')); + $this->assertTrue($inputDefinition->hasOption('quiet')); + $this->assertTrue($inputDefinition->hasOption('verbose')); + $this->assertTrue($inputDefinition->hasOption('version')); + $this->assertTrue($inputDefinition->hasOption('ansi')); + $this->assertTrue($inputDefinition->hasOption('no-ansi')); + $this->assertTrue($inputDefinition->hasOption('no-interaction')); + } + + public function testOverwritingDefaultInputDefinitionOverwritesDefaultValues() + { + $application = new CustomApplication(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $inputDefinition = $application->getDefinition(); + + // check whether the default arguments and options are not returned any more + $this->assertFalse($inputDefinition->hasArgument('command')); + + $this->assertFalse($inputDefinition->hasOption('help')); + $this->assertFalse($inputDefinition->hasOption('quiet')); + $this->assertFalse($inputDefinition->hasOption('verbose')); + $this->assertFalse($inputDefinition->hasOption('version')); + $this->assertFalse($inputDefinition->hasOption('ansi')); + $this->assertFalse($inputDefinition->hasOption('no-ansi')); + $this->assertFalse($inputDefinition->hasOption('no-interaction')); + + $this->assertTrue($inputDefinition->hasOption('custom')); + } + + public function testSettingCustomInputDefinitionOverwritesDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setDefinition(new InputDefinition(array(new InputOption('--custom', '-c', InputOption::VALUE_NONE, 'Set the custom input definition.')))); + + $inputDefinition = $application->getDefinition(); + + // check whether the default arguments and options are not returned any more + $this->assertFalse($inputDefinition->hasArgument('command')); + + $this->assertFalse($inputDefinition->hasOption('help')); + $this->assertFalse($inputDefinition->hasOption('quiet')); + $this->assertFalse($inputDefinition->hasOption('verbose')); + $this->assertFalse($inputDefinition->hasOption('version')); + $this->assertFalse($inputDefinition->hasOption('ansi')); + $this->assertFalse($inputDefinition->hasOption('no-ansi')); + $this->assertFalse($inputDefinition->hasOption('no-interaction')); + + $this->assertTrue($inputDefinition->hasOption('custom')); + } + + public function testRunWithDispatcher() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setDispatcher($this->getDispatcher()); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo')); + $this->assertEquals('before.foo.after.'.PHP_EOL, $tester->getDisplay()); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage caught + */ + public function testRunWithExceptionAndDispatcher() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher()); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + throw new \RuntimeException('foo'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo')); + } + + public function testRunDispatchesAllEventsWithException() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher()); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + + throw new \RuntimeException('foo'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo')); + $this->assertContains('before.foo.caught.after.', $tester->getDisplay()); + } + + public function testRunWithDispatcherSkippingCommand() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher(true)); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $exitCode = $tester->run(array('command' => 'foo')); + $this->assertContains('before.after.', $tester->getDisplay()); + $this->assertEquals(ConsoleCommandEvent::RETURN_CODE_DISABLED, $exitCode); + } + + public function testRunWithDispatcherAccessingInputOptions() + { + $noInteractionValue = null; + $quietValue = null; + + $dispatcher = $this->getDispatcher(); + $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use (&$noInteractionValue, &$quietValue) { + $input = $event->getInput(); + + $noInteractionValue = $input->getOption('no-interaction'); + $quietValue = $input->getOption('quiet'); + }); + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo', '--no-interaction' => true)); + + $this->assertTrue($noInteractionValue); + $this->assertFalse($quietValue); + } + + public function testRunWithDispatcherAddingInputOptions() + { + $extraValue = null; + + $dispatcher = $this->getDispatcher(); + $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use (&$extraValue) { + $definition = $event->getCommand()->getDefinition(); + $input = $event->getInput(); + + $definition->addOption(new InputOption('extra', null, InputOption::VALUE_REQUIRED)); + $input->bind($definition); + + $extraValue = $input->getOption('extra'); + }); + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo', '--extra' => 'some test value')); + + $this->assertEquals('some test value', $extraValue); + } + + public function testTerminalDimensions() + { + $application = new Application(); + $originalDimensions = $application->getTerminalDimensions(); + $this->assertCount(2, $originalDimensions); + + $width = 80; + if ($originalDimensions[0] == $width) { + $width = 100; + } + + $application->setTerminalDimensions($width, 80); + $this->assertSame(array($width, 80), $application->getTerminalDimensions()); + } + + protected function getDispatcher($skipCommand = false) + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use ($skipCommand) { + $event->getOutput()->write('before.'); + + if ($skipCommand) { + $event->disableCommand(); + } + }); + $dispatcher->addListener('console.terminate', function (ConsoleTerminateEvent $event) use ($skipCommand) { + $event->getOutput()->writeln('after.'); + + if (!$skipCommand) { + $event->setExitCode(113); + } + }); + $dispatcher->addListener('console.exception', function (ConsoleExceptionEvent $event) { + $event->getOutput()->write('caught.'); + + $event->setException(new \LogicException('caught.', $event->getExitCode(), $event->getException())); + }); + + return $dispatcher; + } + + public function testSetRunCustomDefaultCommand() + { + $command = new \FooCommand(); + + $application = new Application(); + $application->setAutoExit(false); + $application->add($command); + $application->setDefaultCommand($command->getName()); + + $tester = new ApplicationTester($application); + $tester->run(array()); + $this->assertEquals('interact called'.PHP_EOL.'called'.PHP_EOL, $tester->getDisplay(), 'Application runs the default set command if different from \'list\' command'); + + $application = new CustomDefaultCommandApplication(); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->run(array()); + + $this->assertEquals('interact called'.PHP_EOL.'called'.PHP_EOL, $tester->getDisplay(), 'Application runs the default set command if different from \'list\' command'); + } + + /** + * @requires function posix_isatty + */ + public function testCanCheckIfTerminalIsInteractive() + { + $application = new CustomDefaultCommandApplication(); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'help')); + + $this->assertFalse($tester->getInput()->hasParameterOption(array('--no-interaction', '-n'))); + + $inputStream = $application->getHelperSet()->get('question')->getInputStream(); + $this->assertEquals($tester->getInput()->isInteractive(), @posix_isatty($inputStream)); + } +} + +class CustomApplication extends Application +{ + /** + * Overwrites the default input definition. + * + * @return InputDefinition An InputDefinition instance + */ + protected function getDefaultInputDefinition() + { + return new InputDefinition(array(new InputOption('--custom', '-c', InputOption::VALUE_NONE, 'Set the custom input definition.'))); + } + + /** + * Gets the default helper set with the helpers that should always be available. + * + * @return HelperSet A HelperSet instance + */ + protected function getDefaultHelperSet() + { + return new HelperSet(array(new FormatterHelper())); + } +} + +class CustomDefaultCommandApplication extends Application +{ + /** + * Overwrites the constructor in order to set a different default command. + */ + public function __construct() + { + parent::__construct(); + + $command = new \FooCommand(); + $this->add($command); + $this->setDefaultCommand($command->getName()); + } +} diff --git a/vendor/symfony/console/Tests/Command/CommandTest.php b/vendor/symfony/console/Tests/Command/CommandTest.php new file mode 100644 index 00000000..e8836d8c --- /dev/null +++ b/vendor/symfony/console/Tests/Command/CommandTest.php @@ -0,0 +1,395 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\StringInput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Tester\CommandTester; + +class CommandTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = __DIR__.'/../Fixtures/'; + require_once self::$fixturesPath.'/TestCommand.php'; + } + + public function testConstructor() + { + $command = new Command('foo:bar'); + $this->assertEquals('foo:bar', $command->getName(), '__construct() takes the command name as its first argument'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage The command defined in "Symfony\Component\Console\Command\Command" cannot have an empty name. + */ + public function testCommandNameCannotBeEmpty() + { + new Command(); + } + + public function testSetApplication() + { + $application = new Application(); + $command = new \TestCommand(); + $command->setApplication($application); + $this->assertEquals($application, $command->getApplication(), '->setApplication() sets the current application'); + } + + public function testSetGetDefinition() + { + $command = new \TestCommand(); + $ret = $command->setDefinition($definition = new InputDefinition()); + $this->assertEquals($command, $ret, '->setDefinition() implements a fluent interface'); + $this->assertEquals($definition, $command->getDefinition(), '->setDefinition() sets the current InputDefinition instance'); + $command->setDefinition(array(new InputArgument('foo'), new InputOption('bar'))); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->setDefinition() also takes an array of InputArguments and InputOptions as an argument'); + $this->assertTrue($command->getDefinition()->hasOption('bar'), '->setDefinition() also takes an array of InputArguments and InputOptions as an argument'); + $command->setDefinition(new InputDefinition()); + } + + public function testAddArgument() + { + $command = new \TestCommand(); + $ret = $command->addArgument('foo'); + $this->assertEquals($command, $ret, '->addArgument() implements a fluent interface'); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->addArgument() adds an argument to the command'); + } + + public function testAddOption() + { + $command = new \TestCommand(); + $ret = $command->addOption('foo'); + $this->assertEquals($command, $ret, '->addOption() implements a fluent interface'); + $this->assertTrue($command->getDefinition()->hasOption('foo'), '->addOption() adds an option to the command'); + } + + public function testGetNamespaceGetNameSetName() + { + $command = new \TestCommand(); + $this->assertEquals('namespace:name', $command->getName(), '->getName() returns the command name'); + $command->setName('foo'); + $this->assertEquals('foo', $command->getName(), '->setName() sets the command name'); + + $ret = $command->setName('foobar:bar'); + $this->assertEquals($command, $ret, '->setName() implements a fluent interface'); + $this->assertEquals('foobar:bar', $command->getName(), '->setName() sets the command name'); + } + + /** + * @dataProvider provideInvalidCommandNames + */ + public function testInvalidCommandNames($name) + { + $this->setExpectedException('InvalidArgumentException', sprintf('Command name "%s" is invalid.', $name)); + + $command = new \TestCommand(); + $command->setName($name); + } + + public function provideInvalidCommandNames() + { + return array( + array(''), + array('foo:'), + ); + } + + public function testGetSetDescription() + { + $command = new \TestCommand(); + $this->assertEquals('description', $command->getDescription(), '->getDescription() returns the description'); + $ret = $command->setDescription('description1'); + $this->assertEquals($command, $ret, '->setDescription() implements a fluent interface'); + $this->assertEquals('description1', $command->getDescription(), '->setDescription() sets the description'); + } + + public function testGetSetHelp() + { + $command = new \TestCommand(); + $this->assertEquals('help', $command->getHelp(), '->getHelp() returns the help'); + $ret = $command->setHelp('help1'); + $this->assertEquals($command, $ret, '->setHelp() implements a fluent interface'); + $this->assertEquals('help1', $command->getHelp(), '->setHelp() sets the help'); + $command->setHelp(''); + $this->assertEquals('', $command->getHelp(), '->getHelp() does not fall back to the description'); + } + + public function testGetProcessedHelp() + { + $command = new \TestCommand(); + $command->setHelp('The %command.name% command does... Example: php %command.full_name%.'); + $this->assertContains('The namespace:name command does...', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.name% correctly'); + $this->assertNotContains('%command.full_name%', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.full_name%'); + + $command = new \TestCommand(); + $command->setHelp(''); + $this->assertContains('description', $command->getProcessedHelp(), '->getProcessedHelp() falls back to the description'); + } + + public function testGetSetAliases() + { + $command = new \TestCommand(); + $this->assertEquals(array('name'), $command->getAliases(), '->getAliases() returns the aliases'); + $ret = $command->setAliases(array('name1')); + $this->assertEquals($command, $ret, '->setAliases() implements a fluent interface'); + $this->assertEquals(array('name1'), $command->getAliases(), '->setAliases() sets the aliases'); + } + + public function testGetSynopsis() + { + $command = new \TestCommand(); + $command->addOption('foo'); + $command->addArgument('bar'); + $this->assertEquals('namespace:name [--foo] [--] [<bar>]', $command->getSynopsis(), '->getSynopsis() returns the synopsis'); + } + + public function testGetHelper() + { + $application = new Application(); + $command = new \TestCommand(); + $command->setApplication($application); + $formatterHelper = new FormatterHelper(); + $this->assertEquals($formatterHelper->getName(), $command->getHelper('formatter')->getName(), '->getHelper() returns the correct helper'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot retrieve helper "formatter" because there is no HelperSet defined. + */ + public function testGetHelperWithoutHelperSet() + { + $command = new \TestCommand(); + $command->getHelper('formatter'); + } + + public function testMergeApplicationDefinition() + { + $application1 = new Application(); + $application1->getDefinition()->addArguments(array(new InputArgument('foo'))); + $application1->getDefinition()->addOptions(array(new InputOption('bar'))); + $command = new \TestCommand(); + $command->setApplication($application1); + $command->setDefinition($definition = new InputDefinition(array(new InputArgument('bar'), new InputOption('foo')))); + + $r = new \ReflectionObject($command); + $m = $r->getMethod('mergeApplicationDefinition'); + $m->setAccessible(true); + $m->invoke($command); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition() merges the application arguments and the command arguments'); + $this->assertTrue($command->getDefinition()->hasArgument('bar'), '->mergeApplicationDefinition() merges the application arguments and the command arguments'); + $this->assertTrue($command->getDefinition()->hasOption('foo'), '->mergeApplicationDefinition() merges the application options and the command options'); + $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition() merges the application options and the command options'); + + $m->invoke($command); + $this->assertEquals(3, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments and options'); + } + + public function testMergeApplicationDefinitionWithoutArgsThenWithArgsAddsArgs() + { + $application1 = new Application(); + $application1->getDefinition()->addArguments(array(new InputArgument('foo'))); + $application1->getDefinition()->addOptions(array(new InputOption('bar'))); + $command = new \TestCommand(); + $command->setApplication($application1); + $command->setDefinition($definition = new InputDefinition(array())); + + $r = new \ReflectionObject($command); + $m = $r->getMethod('mergeApplicationDefinition'); + $m->setAccessible(true); + $m->invoke($command, false); + $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition(false) merges the application and the command options'); + $this->assertFalse($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition(false) does not merge the application arguments'); + + $m->invoke($command, true); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition(true) merges the application arguments and the command arguments'); + + $m->invoke($command); + $this->assertEquals(2, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments'); + } + + public function testRunInteractive() + { + $tester = new CommandTester(new \TestCommand()); + + $tester->execute(array(), array('interactive' => true)); + + $this->assertEquals('interact called'.PHP_EOL.'execute called'.PHP_EOL, $tester->getDisplay(), '->run() calls the interact() method if the input is interactive'); + } + + public function testRunNonInteractive() + { + $tester = new CommandTester(new \TestCommand()); + + $tester->execute(array(), array('interactive' => false)); + + $this->assertEquals('execute called'.PHP_EOL, $tester->getDisplay(), '->run() does not call the interact() method if the input is not interactive'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage You must override the execute() method in the concrete command class. + */ + public function testExecuteMethodNeedsToBeOverridden() + { + $command = new Command('foo'); + $command->run(new StringInput(''), new NullOutput()); + } + + /** + * @expectedException Symfony\Component\Console\Exception\InvalidOptionException + * @expectedExceptionMessage The "--bar" option does not exist. + */ + public function testRunWithInvalidOption() + { + $command = new \TestCommand(); + $tester = new CommandTester($command); + $tester->execute(array('--bar' => true)); + } + + public function testRunReturnsIntegerExitCode() + { + $command = new \TestCommand(); + $exitCode = $command->run(new StringInput(''), new NullOutput()); + $this->assertSame(0, $exitCode, '->run() returns integer exit code (treats null as 0)'); + + $command = $this->getMock('TestCommand', array('execute')); + $command->expects($this->once()) + ->method('execute') + ->will($this->returnValue('2.3')); + $exitCode = $command->run(new StringInput(''), new NullOutput()); + $this->assertSame(2, $exitCode, '->run() returns integer exit code (casts numeric to int)'); + } + + public function testRunWithApplication() + { + $command = new \TestCommand(); + $command->setApplication(new Application()); + $exitCode = $command->run(new StringInput(''), new NullOutput()); + + $this->assertSame(0, $exitCode, '->run() returns an integer exit code'); + } + + public function testRunReturnsAlwaysInteger() + { + $command = new \TestCommand(); + + $this->assertSame(0, $command->run(new StringInput(''), new NullOutput())); + } + + public function testSetCode() + { + $command = new \TestCommand(); + $ret = $command->setCode(function (InputInterface $input, OutputInterface $output) { + $output->writeln('from the code...'); + }); + $this->assertEquals($command, $ret, '->setCode() implements a fluent interface'); + $tester = new CommandTester($command); + $tester->execute(array()); + $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay()); + } + + public function getSetCodeBindToClosureTests() + { + return array( + array(true, 'not bound to the command'), + array(false, 'bound to the command'), + ); + } + + /** + * @dataProvider getSetCodeBindToClosureTests + * @requires PHP 5.4 + */ + public function testSetCodeBindToClosure($previouslyBound, $expected) + { + $code = createClosure(); + if ($previouslyBound) { + $code = $code->bindTo($this); + } + + $command = new \TestCommand(); + $command->setCode($code); + $tester = new CommandTester($command); + $tester->execute(array()); + $this->assertEquals('interact called'.PHP_EOL.$expected.PHP_EOL, $tester->getDisplay()); + } + + public function testSetCodeWithNonClosureCallable() + { + $command = new \TestCommand(); + $ret = $command->setCode(array($this, 'callableMethodCommand')); + $this->assertEquals($command, $ret, '->setCode() implements a fluent interface'); + $tester = new CommandTester($command); + $tester->execute(array()); + $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid callable provided to Command::setCode. + */ + public function testSetCodeWithNonCallable() + { + $command = new \TestCommand(); + $command->setCode(array($this, 'nonExistentMethod')); + } + + public function callableMethodCommand(InputInterface $input, OutputInterface $output) + { + $output->writeln('from the code...'); + } + + /** + * @group legacy + */ + public function testLegacyAsText() + { + $command = new \TestCommand(); + $command->setApplication(new Application()); + $tester = new CommandTester($command); + $tester->execute(array('command' => $command->getName())); + $this->assertStringEqualsFile(self::$fixturesPath.'/command_astext.txt', $command->asText(), '->asText() returns a text representation of the command'); + } + + /** + * @group legacy + */ + public function testLegacyAsXml() + { + $command = new \TestCommand(); + $command->setApplication(new Application()); + $tester = new CommandTester($command); + $tester->execute(array('command' => $command->getName())); + $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/command_asxml.txt', $command->asXml(), '->asXml() returns an XML representation of the command'); + } +} + +// In order to get an unbound closure, we should create it outside a class +// scope. +function createClosure() +{ + return function (InputInterface $input, OutputInterface $output) { + $output->writeln($this instanceof Command ? 'bound to the command' : 'not bound to the command'); + }; +} diff --git a/vendor/symfony/console/Tests/Command/HelpCommandTest.php b/vendor/symfony/console/Tests/Command/HelpCommandTest.php new file mode 100644 index 00000000..9e068587 --- /dev/null +++ b/vendor/symfony/console/Tests/Command/HelpCommandTest.php @@ -0,0 +1,70 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Command; + +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Console\Command\HelpCommand; +use Symfony\Component\Console\Command\ListCommand; +use Symfony\Component\Console\Application; + +class HelpCommandTest extends \PHPUnit_Framework_TestCase +{ + public function testExecuteForCommandAlias() + { + $command = new HelpCommand(); + $command->setApplication(new Application()); + $commandTester = new CommandTester($command); + $commandTester->execute(array('command_name' => 'li'), array('decorated' => false)); + $this->assertContains('list [options] [--] [<namespace>]', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); + $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); + $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); + } + + public function testExecuteForCommand() + { + $command = new HelpCommand(); + $commandTester = new CommandTester($command); + $command->setCommand(new ListCommand()); + $commandTester->execute(array(), array('decorated' => false)); + $this->assertContains('list [options] [--] [<namespace>]', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + } + + public function testExecuteForCommandWithXmlOption() + { + $command = new HelpCommand(); + $commandTester = new CommandTester($command); + $command->setCommand(new ListCommand()); + $commandTester->execute(array('--format' => 'xml')); + $this->assertContains('<command', $commandTester->getDisplay(), '->execute() returns an XML help text if --xml is passed'); + } + + public function testExecuteForApplicationCommand() + { + $application = new Application(); + $commandTester = new CommandTester($application->get('help')); + $commandTester->execute(array('command_name' => 'list')); + $this->assertContains('list [options] [--] [<namespace>]', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + } + + public function testExecuteForApplicationCommandWithXmlOption() + { + $application = new Application(); + $commandTester = new CommandTester($application->get('help')); + $commandTester->execute(array('command_name' => 'list', '--format' => 'xml')); + $this->assertContains('list [--xml] [--raw] [--format FORMAT] [--] [<namespace>]', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('<command', $commandTester->getDisplay(), '->execute() returns an XML help text if --format=xml is passed'); + } +} diff --git a/vendor/symfony/console/Tests/Command/ListCommandTest.php b/vendor/symfony/console/Tests/Command/ListCommandTest.php new file mode 100644 index 00000000..a166a040 --- /dev/null +++ b/vendor/symfony/console/Tests/Command/ListCommandTest.php @@ -0,0 +1,112 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Command; + +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Console\Application; + +class ListCommandTest extends \PHPUnit_Framework_TestCase +{ + public function testExecuteListsCommands() + { + $application = new Application(); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName()), array('decorated' => false)); + + $this->assertRegExp('/help\s{2,}Displays help for a command/', $commandTester->getDisplay(), '->execute() returns a list of available commands'); + } + + public function testExecuteListsCommandsWithXmlOption() + { + $application = new Application(); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName(), '--format' => 'xml')); + $this->assertRegExp('/<command id="list" name="list">/', $commandTester->getDisplay(), '->execute() returns a list of available commands in XML if --xml is passed'); + } + + public function testExecuteListsCommandsWithRawOption() + { + $application = new Application(); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName(), '--raw' => true)); + $output = <<<'EOF' +help Displays help for a command +list Lists commands + +EOF; + + $this->assertEquals($output, $commandTester->getDisplay(true)); + } + + public function testExecuteListsCommandsWithNamespaceArgument() + { + require_once realpath(__DIR__.'/../Fixtures/FooCommand.php'); + $application = new Application(); + $application->add(new \FooCommand()); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName(), 'namespace' => 'foo', '--raw' => true)); + $output = <<<'EOF' +foo:bar The foo:bar command + +EOF; + + $this->assertEquals($output, $commandTester->getDisplay(true)); + } + + public function testExecuteListsCommandsOrder() + { + require_once realpath(__DIR__.'/../Fixtures/Foo6Command.php'); + $application = new Application(); + $application->add(new \Foo6Command()); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName()), array('decorated' => false)); + $output = <<<'EOF' +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + help Displays help for a command + list Lists commands + 0foo + 0foo:bar 0foo:bar command +EOF; + + $this->assertEquals($output, trim($commandTester->getDisplay(true))); + } + + public function testExecuteListsCommandsOrderRaw() + { + require_once realpath(__DIR__.'/../Fixtures/Foo6Command.php'); + $application = new Application(); + $application->add(new \Foo6Command()); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName(), '--raw' => true)); + $output = <<<'EOF' +help Displays help for a command +list Lists commands +0foo:bar 0foo:bar command +EOF; + + $this->assertEquals($output, trim($commandTester->getDisplay(true))); + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/AbstractDescriptorTest.php b/vendor/symfony/console/Tests/Descriptor/AbstractDescriptorTest.php new file mode 100644 index 00000000..c36c4a8e --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/AbstractDescriptorTest.php @@ -0,0 +1,106 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\BufferedOutput; + +abstract class AbstractDescriptorTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getDescribeInputArgumentTestData */ + public function testDescribeInputArgument(InputArgument $argument, $expectedDescription) + { + $this->assertDescription($expectedDescription, $argument); + } + + /** @dataProvider getDescribeInputOptionTestData */ + public function testDescribeInputOption(InputOption $option, $expectedDescription) + { + $this->assertDescription($expectedDescription, $option); + } + + /** @dataProvider getDescribeInputDefinitionTestData */ + public function testDescribeInputDefinition(InputDefinition $definition, $expectedDescription) + { + $this->assertDescription($expectedDescription, $definition); + } + + /** @dataProvider getDescribeCommandTestData */ + public function testDescribeCommand(Command $command, $expectedDescription) + { + $this->assertDescription($expectedDescription, $command); + } + + /** @dataProvider getDescribeApplicationTestData */ + public function testDescribeApplication(Application $application, $expectedDescription) + { + // Replaces the dynamic placeholders of the command help text with a static version. + // The placeholder %command.full_name% includes the script path that is not predictable + // and can not be tested against. + foreach ($application->all() as $command) { + $command->setHelp(str_replace('%command.full_name%', 'app/console %command.name%', $command->getHelp())); + } + + $this->assertDescription($expectedDescription, $application); + } + + public function getDescribeInputArgumentTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getInputArguments()); + } + + public function getDescribeInputOptionTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getInputOptions()); + } + + public function getDescribeInputDefinitionTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getInputDefinitions()); + } + + public function getDescribeCommandTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getCommands()); + } + + public function getDescribeApplicationTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getApplications()); + } + + abstract protected function getDescriptor(); + + abstract protected function getFormat(); + + private function getDescriptionTestData(array $objects) + { + $data = array(); + foreach ($objects as $name => $object) { + $description = file_get_contents(sprintf('%s/../Fixtures/%s.%s', __DIR__, $name, $this->getFormat())); + $data[] = array($object, $description); + } + + return $data; + } + + protected function assertDescription($expectedDescription, $describedObject) + { + $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); + $this->getDescriptor()->describe($output, $describedObject, array('raw_output' => true)); + $this->assertEquals(trim($expectedDescription), trim(str_replace(PHP_EOL, "\n", $output->fetch()))); + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/JsonDescriptorTest.php b/vendor/symfony/console/Tests/Descriptor/JsonDescriptorTest.php new file mode 100644 index 00000000..f9a15612 --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/JsonDescriptorTest.php @@ -0,0 +1,35 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Descriptor\JsonDescriptor; +use Symfony\Component\Console\Output\BufferedOutput; + +class JsonDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new JsonDescriptor(); + } + + protected function getFormat() + { + return 'json'; + } + + protected function assertDescription($expectedDescription, $describedObject) + { + $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); + $this->getDescriptor()->describe($output, $describedObject, array('raw_output' => true)); + $this->assertEquals(json_decode(trim($expectedDescription), true), json_decode(trim(str_replace(PHP_EOL, "\n", $output->fetch())), true)); + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/MarkdownDescriptorTest.php b/vendor/symfony/console/Tests/Descriptor/MarkdownDescriptorTest.php new file mode 100644 index 00000000..c85e8a59 --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/MarkdownDescriptorTest.php @@ -0,0 +1,27 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Descriptor\MarkdownDescriptor; + +class MarkdownDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new MarkdownDescriptor(); + } + + protected function getFormat() + { + return 'md'; + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/ObjectsProvider.php b/vendor/symfony/console/Tests/Descriptor/ObjectsProvider.php new file mode 100644 index 00000000..45b3b2ff --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/ObjectsProvider.php @@ -0,0 +1,77 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Tests\Fixtures\DescriptorApplication1; +use Symfony\Component\Console\Tests\Fixtures\DescriptorApplication2; +use Symfony\Component\Console\Tests\Fixtures\DescriptorCommand1; +use Symfony\Component\Console\Tests\Fixtures\DescriptorCommand2; + +/** + * @author Jean-François Simon <contact@jfsimon.fr> + */ +class ObjectsProvider +{ + public static function getInputArguments() + { + return array( + 'input_argument_1' => new InputArgument('argument_name', InputArgument::REQUIRED), + 'input_argument_2' => new InputArgument('argument_name', InputArgument::IS_ARRAY, 'argument description'), + 'input_argument_3' => new InputArgument('argument_name', InputArgument::OPTIONAL, 'argument description', 'default_value'), + 'input_argument_4' => new InputArgument('argument_name', InputArgument::REQUIRED, "multiline\nargument description"), + ); + } + + public static function getInputOptions() + { + return array( + 'input_option_1' => new InputOption('option_name', 'o', InputOption::VALUE_NONE), + 'input_option_2' => new InputOption('option_name', 'o', InputOption::VALUE_OPTIONAL, 'option description', 'default_value'), + 'input_option_3' => new InputOption('option_name', 'o', InputOption::VALUE_REQUIRED, 'option description'), + 'input_option_4' => new InputOption('option_name', 'o', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'option description', array()), + 'input_option_5' => new InputOption('option_name', 'o', InputOption::VALUE_REQUIRED, "multiline\noption description"), + 'input_option_6' => new InputOption('option_name', array('o', 'O'), InputOption::VALUE_REQUIRED, 'option with multiple shortcuts'), + ); + } + + public static function getInputDefinitions() + { + return array( + 'input_definition_1' => new InputDefinition(), + 'input_definition_2' => new InputDefinition(array(new InputArgument('argument_name', InputArgument::REQUIRED))), + 'input_definition_3' => new InputDefinition(array(new InputOption('option_name', 'o', InputOption::VALUE_NONE))), + 'input_definition_4' => new InputDefinition(array( + new InputArgument('argument_name', InputArgument::REQUIRED), + new InputOption('option_name', 'o', InputOption::VALUE_NONE), + )), + ); + } + + public static function getCommands() + { + return array( + 'command_1' => new DescriptorCommand1(), + 'command_2' => new DescriptorCommand2(), + ); + } + + public static function getApplications() + { + return array( + 'application_1' => new DescriptorApplication1(), + 'application_2' => new DescriptorApplication2(), + ); + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/TextDescriptorTest.php b/vendor/symfony/console/Tests/Descriptor/TextDescriptorTest.php new file mode 100644 index 00000000..350b6795 --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/TextDescriptorTest.php @@ -0,0 +1,27 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Descriptor\TextDescriptor; + +class TextDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new TextDescriptor(); + } + + protected function getFormat() + { + return 'txt'; + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/XmlDescriptorTest.php b/vendor/symfony/console/Tests/Descriptor/XmlDescriptorTest.php new file mode 100644 index 00000000..59a5d1ed --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/XmlDescriptorTest.php @@ -0,0 +1,27 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Descriptor\XmlDescriptor; + +class XmlDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new XmlDescriptor(); + } + + protected function getFormat() + { + return 'xml'; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/BarBucCommand.php b/vendor/symfony/console/Tests/Fixtures/BarBucCommand.php new file mode 100644 index 00000000..52b619e8 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/BarBucCommand.php @@ -0,0 +1,11 @@ +<?php + +use Symfony\Component\Console\Command\Command; + +class BarBucCommand extends Command +{ + protected function configure() + { + $this->setName('bar:buc'); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/DescriptorApplication1.php b/vendor/symfony/console/Tests/Fixtures/DescriptorApplication1.php new file mode 100644 index 00000000..132b6d57 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/DescriptorApplication1.php @@ -0,0 +1,18 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Application; + +class DescriptorApplication1 extends Application +{ +} diff --git a/vendor/symfony/console/Tests/Fixtures/DescriptorApplication2.php b/vendor/symfony/console/Tests/Fixtures/DescriptorApplication2.php new file mode 100644 index 00000000..ff551358 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/DescriptorApplication2.php @@ -0,0 +1,24 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Application; + +class DescriptorApplication2 extends Application +{ + public function __construct() + { + parent::__construct('My Symfony application', 'v1.0'); + $this->add(new DescriptorCommand1()); + $this->add(new DescriptorCommand2()); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/DescriptorCommand1.php b/vendor/symfony/console/Tests/Fixtures/DescriptorCommand1.php new file mode 100644 index 00000000..ede05d7a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/DescriptorCommand1.php @@ -0,0 +1,27 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Command\Command; + +class DescriptorCommand1 extends Command +{ + protected function configure() + { + $this + ->setName('descriptor:command1') + ->setAliases(array('alias1', 'alias2')) + ->setDescription('command 1 description') + ->setHelp('command 1 help') + ; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/DescriptorCommand2.php b/vendor/symfony/console/Tests/Fixtures/DescriptorCommand2.php new file mode 100644 index 00000000..51106b96 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/DescriptorCommand2.php @@ -0,0 +1,32 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class DescriptorCommand2 extends Command +{ + protected function configure() + { + $this + ->setName('descriptor:command2') + ->setDescription('command 2 description') + ->setHelp('command 2 help') + ->addUsage('-o|--option_name <argument_name>') + ->addUsage('<argument_name>') + ->addArgument('argument_name', InputArgument::REQUIRED) + ->addOption('option_name', 'o', InputOption::VALUE_NONE) + ; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/DummyOutput.php b/vendor/symfony/console/Tests/Fixtures/DummyOutput.php new file mode 100644 index 00000000..0070c0a4 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/DummyOutput.php @@ -0,0 +1,36 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Output\BufferedOutput; + +/** + * Dummy output. + * + * @author Kévin Dunglas <dunglas@gmail.com> + */ +class DummyOutput extends BufferedOutput +{ + /** + * @return array + */ + public function getLogs() + { + $logs = array(); + foreach (explode("\n", trim($this->fetch())) as $message) { + preg_match('/^\[(.*)\] (.*)/', $message, $matches); + $logs[] = sprintf('%s %s', $matches[1], $matches[2]); + } + + return $logs; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo1Command.php b/vendor/symfony/console/Tests/Fixtures/Foo1Command.php new file mode 100644 index 00000000..254162f3 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo1Command.php @@ -0,0 +1,26 @@ +<?php + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class Foo1Command extends Command +{ + public $input; + public $output; + + protected function configure() + { + $this + ->setName('foo:bar1') + ->setDescription('The foo:bar1 command') + ->setAliases(array('afoobar1')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo2Command.php b/vendor/symfony/console/Tests/Fixtures/Foo2Command.php new file mode 100644 index 00000000..8071dc8f --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo2Command.php @@ -0,0 +1,21 @@ +<?php + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class Foo2Command extends Command +{ + protected function configure() + { + $this + ->setName('foo1:bar') + ->setDescription('The foo1:bar command') + ->setAliases(array('afoobar2')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo3Command.php b/vendor/symfony/console/Tests/Fixtures/Foo3Command.php new file mode 100644 index 00000000..6c890faf --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo3Command.php @@ -0,0 +1,29 @@ +<?php + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class Foo3Command extends Command +{ + protected function configure() + { + $this + ->setName('foo3:bar') + ->setDescription('The foo3:bar command') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + try { + try { + throw new \Exception('First exception <p>this is html</p>'); + } catch (\Exception $e) { + throw new \Exception('Second exception <comment>comment</comment>', 0, $e); + } + } catch (\Exception $e) { + throw new \Exception('Third exception <fg=blue;bg=red>comment</>', 0, $e); + } + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo4Command.php b/vendor/symfony/console/Tests/Fixtures/Foo4Command.php new file mode 100644 index 00000000..1c546399 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo4Command.php @@ -0,0 +1,11 @@ +<?php + +use Symfony\Component\Console\Command\Command; + +class Foo4Command extends Command +{ + protected function configure() + { + $this->setName('foo3:bar:toh'); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo5Command.php b/vendor/symfony/console/Tests/Fixtures/Foo5Command.php new file mode 100644 index 00000000..a1c60827 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo5Command.php @@ -0,0 +1,10 @@ +<?php + +use Symfony\Component\Console\Command\Command; + +class Foo5Command extends Command +{ + public function __construct() + { + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo6Command.php b/vendor/symfony/console/Tests/Fixtures/Foo6Command.php new file mode 100644 index 00000000..6ae987e4 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo6Command.php @@ -0,0 +1,12 @@ +<?php + + +use Symfony\Component\Console\Command\Command; + +class Foo6Command extends Command +{ + protected function configure() + { + $this->setName('0foo:bar')->setDescription('0foo:bar command'); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/FooCommand.php b/vendor/symfony/console/Tests/Fixtures/FooCommand.php new file mode 100644 index 00000000..355e0ad6 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/FooCommand.php @@ -0,0 +1,33 @@ +<?php + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class FooCommand extends Command +{ + public $input; + public $output; + + protected function configure() + { + $this + ->setName('foo:bar') + ->setDescription('The foo:bar command') + ->setAliases(array('afoobar')) + ; + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $output->writeln('interact called'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + + $output->writeln('called'); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced1Command.php b/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced1Command.php new file mode 100644 index 00000000..fc50c72b --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced1Command.php @@ -0,0 +1,26 @@ +<?php + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class FooSubnamespaced1Command extends Command +{ + public $input; + public $output; + + protected function configure() + { + $this + ->setName('foo:bar:baz') + ->setDescription('The foo:bar:baz command') + ->setAliases(array('foobarbaz')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced2Command.php b/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced2Command.php new file mode 100644 index 00000000..1cf31ff1 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced2Command.php @@ -0,0 +1,26 @@ +<?php + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class FooSubnamespaced2Command extends Command +{ + public $input; + public $output; + + protected function configure() + { + $this + ->setName('foo:go:bret') + ->setDescription('The foo:bar:go command') + ->setAliases(array('foobargo')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/FoobarCommand.php b/vendor/symfony/console/Tests/Fixtures/FoobarCommand.php new file mode 100644 index 00000000..96816280 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/FoobarCommand.php @@ -0,0 +1,25 @@ +<?php + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class FoobarCommand extends Command +{ + public $input; + public $output; + + protected function configure() + { + $this + ->setName('foobar:foo') + ->setDescription('The foobar:foo command') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php new file mode 100644 index 00000000..996fafb9 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php @@ -0,0 +1,11 @@ +<?php + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tests\Style\SymfonyStyleWithForcedLineLength; + +//Ensure has single blank line at start when using block element +return function (InputInterface $input, OutputInterface $output) { + $output = new SymfonyStyleWithForcedLineLength($input, $output); + $output->caution('Lorem ipsum dolor sit amet'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php new file mode 100644 index 00000000..6634cd56 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php @@ -0,0 +1,13 @@ +<?php + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tests\Style\SymfonyStyleWithForcedLineLength; + +//Ensure has single blank line between titles and blocks +return function (InputInterface $input, OutputInterface $output) { + $output = new SymfonyStyleWithForcedLineLength($input, $output); + $output->title('Title'); + $output->warning('Lorem ipsum dolor sit amet'); + $output->title('Title'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_10.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_10.php new file mode 100644 index 00000000..4120df9c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_10.php @@ -0,0 +1,17 @@ +<?php + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tests\Style\SymfonyStyleWithForcedLineLength; + +//Ensure that all lines are aligned to the begin of the first line in a very long line block +return function (InputInterface $input, OutputInterface $output) { + $output = new SymfonyStyleWithForcedLineLength($input, $output); + $output->block( + 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum', + 'CUSTOM', + 'fg=white;bg=green', + 'X ', + true + ); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_11.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_11.php new file mode 100644 index 00000000..a2781ddb --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_11.php @@ -0,0 +1,13 @@ +<?php + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tests\Style\SymfonyStyleWithForcedLineLength; + +//Ensure that all lines are aligned to the begin of the first one and start with '//' in a very long line comment +return function (InputInterface $input, OutputInterface $output) { + $output = new SymfonyStyleWithForcedLineLength($input, $output); + $output->comment( + 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum' + ); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php new file mode 100644 index 00000000..6004e3d6 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php @@ -0,0 +1,16 @@ +<?php + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tests\Style\SymfonyStyleWithForcedLineLength; + +//Ensure has single blank line between blocks +return function (InputInterface $input, OutputInterface $output) { + $output = new SymfonyStyleWithForcedLineLength($input, $output); + $output->warning('Warning'); + $output->caution('Caution'); + $output->error('Error'); + $output->success('Success'); + $output->note('Note'); + $output->block('Custom block', 'CUSTOM', 'fg=white;bg=green', 'X ', true); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php new file mode 100644 index 00000000..c7a08f13 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php @@ -0,0 +1,12 @@ +<?php + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tests\Style\SymfonyStyleWithForcedLineLength; + +//Ensure has single blank line between two titles +return function (InputInterface $input, OutputInterface $output) { + $output = new SymfonyStyleWithForcedLineLength($input, $output); + $output->title('First title'); + $output->title('Second title'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php new file mode 100644 index 00000000..afea70c7 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php @@ -0,0 +1,34 @@ +<?php + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tests\Style\SymfonyStyleWithForcedLineLength; + +//Ensure has single blank line after any text and a title +return function (InputInterface $input, OutputInterface $output) { + $output = new SymfonyStyleWithForcedLineLength($input, $output); + + $output->write('Lorem ipsum dolor sit amet'); + $output->title('First title'); + + $output->writeln('Lorem ipsum dolor sit amet'); + $output->title('Second title'); + + $output->write('Lorem ipsum dolor sit amet'); + $output->write(''); + $output->title('Third title'); + + //Ensure edge case by appending empty strings to history: + $output->write('Lorem ipsum dolor sit amet'); + $output->write(array('', '', '')); + $output->title('Fourth title'); + + //Ensure have manual control over number of blank lines: + $output->writeln('Lorem ipsum dolor sit amet'); + $output->writeln(array('', '')); //Should append an extra blank line + $output->title('Fifth title'); + + $output->writeln('Lorem ipsum dolor sit amet'); + $output->newLine(2); //Should append an extra blank line + $output->title('Fifth title'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php new file mode 100644 index 00000000..d2c68a9e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php @@ -0,0 +1,37 @@ +<?php + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tests\Style\SymfonyStyleWithForcedLineLength; + +//Ensure has proper line ending before outputing a text block like with SymfonyStyle::listing() or SymfonyStyle::text() +return function (InputInterface $input, OutputInterface $output) { + $output = new SymfonyStyleWithForcedLineLength($input, $output); + + $output->writeln('Lorem ipsum dolor sit amet'); + $output->listing(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); + + //Even using write: + $output->write('Lorem ipsum dolor sit amet'); + $output->listing(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); + + $output->write('Lorem ipsum dolor sit amet'); + $output->text(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); + + $output->newLine(); + + $output->write('Lorem ipsum dolor sit amet'); + $output->comment(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php new file mode 100644 index 00000000..f1d79905 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php @@ -0,0 +1,16 @@ +<?php + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tests\Style\SymfonyStyleWithForcedLineLength; + +//Ensure has proper blank line after text block when using a block like with SymfonyStyle::success +return function (InputInterface $input, OutputInterface $output) { + $output = new SymfonyStyleWithForcedLineLength($input, $output); + + $output->listing(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); + $output->success('Lorem ipsum dolor sit amet'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php new file mode 100644 index 00000000..cbfea734 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php @@ -0,0 +1,15 @@ +<?php + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tests\Style\SymfonyStyleWithForcedLineLength; + +//Ensure questions do not output anything when input is non-interactive +return function (InputInterface $input, OutputInterface $output) { + $output = new SymfonyStyleWithForcedLineLength($input, $output); + $output->title('Title'); + $output->askHidden('Hidden question'); + $output->choice('Choice question with default', array('choice1', 'choice2'), 'choice1'); + $output->confirm('Confirmation with yes default', true); + $output->text('Duis aute irure dolor in reprehenderit in voluptate velit esse'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_8.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_8.php new file mode 100644 index 00000000..0244fd27 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_8.php @@ -0,0 +1,26 @@ +<?php + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tests\Style\SymfonyStyleWithForcedLineLength; +use Symfony\Component\Console\Helper\TableCell; + +//Ensure formatting tables when using multiple headers with TableCell +return function (InputInterface $input, OutputInterface $output) { + $headers = array( + array(new TableCell('Main table title', array('colspan' => 3))), + array('ISBN', 'Title', 'Author'), + ); + + $rows = array( + array( + '978-0521567817', + 'De Monarchia', + new TableCell("Dante Alighieri\nspans multiple rows", array('rowspan' => 2)), + ), + array('978-0804169127', 'Divine Comedy'), + ); + + $output = new SymfonyStyleWithForcedLineLength($input, $output); + $output->table($headers, $rows); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_9.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_9.php new file mode 100644 index 00000000..6420730f --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_9.php @@ -0,0 +1,11 @@ +<?php + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tests\Style\SymfonyStyleWithForcedLineLength; + +//Ensure that all lines are aligned to the begin of the first line in a multi-line block +return function (InputInterface $input, OutputInterface $output) { + $output = new SymfonyStyleWithForcedLineLength($input, $output); + $output->block(array('Custom block', 'Second custom block line'), 'CUSTOM', 'fg=white;bg=green', 'X ', true); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt new file mode 100644 index 00000000..a42e0f79 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt @@ -0,0 +1,3 @@ + + ! [CAUTION] Lorem ipsum dolor sit amet + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt new file mode 100644 index 00000000..334875f7 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt @@ -0,0 +1,9 @@ + +Title +===== + + [WARNING] Lorem ipsum dolor sit amet + +Title +===== + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_10.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_10.txt new file mode 100644 index 00000000..385c6a28 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_10.txt @@ -0,0 +1,7 @@ + +X [CUSTOM] Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et +X dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea +X commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat +X nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit +X anim id est laborum + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_11.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_11.txt new file mode 100644 index 00000000..9983af83 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_11.txt @@ -0,0 +1,6 @@ + + // Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna + // aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + // Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + // sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt new file mode 100644 index 00000000..ca609760 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt @@ -0,0 +1,13 @@ + + [WARNING] Warning + + ! [CAUTION] Caution + + [ERROR] Error + + [OK] Success + + ! [NOTE] Note + +X [CUSTOM] Custom block + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt new file mode 100644 index 00000000..f4b6d582 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt @@ -0,0 +1,7 @@ + +First title +=========== + +Second title +============ + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt new file mode 100644 index 00000000..2646d858 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt @@ -0,0 +1,32 @@ +Lorem ipsum dolor sit amet + +First title +=========== + +Lorem ipsum dolor sit amet + +Second title +============ + +Lorem ipsum dolor sit amet + +Third title +=========== + +Lorem ipsum dolor sit amet + +Fourth title +============ + +Lorem ipsum dolor sit amet + + +Fifth title +=========== + +Lorem ipsum dolor sit amet + + +Fifth title +=========== + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt new file mode 100644 index 00000000..be4a2db6 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt @@ -0,0 +1,18 @@ +Lorem ipsum dolor sit amet + * Lorem ipsum dolor sit amet + * consectetur adipiscing elit + +Lorem ipsum dolor sit amet + * Lorem ipsum dolor sit amet + * consectetur adipiscing elit + +Lorem ipsum dolor sit amet + Lorem ipsum dolor sit amet + consectetur adipiscing elit + +Lorem ipsum dolor sit amet + + // Lorem ipsum dolor sit amet + // + // consectetur adipiscing elit + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt new file mode 100644 index 00000000..5f2d33c1 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt @@ -0,0 +1,6 @@ + + * Lorem ipsum dolor sit amet + * consectetur adipiscing elit + + [OK] Lorem ipsum dolor sit amet + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt new file mode 100644 index 00000000..ecea9778 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt @@ -0,0 +1,5 @@ + +Title +===== + + Duis aute irure dolor in reprehenderit in voluptate velit esse diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_8.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_8.txt new file mode 100644 index 00000000..005b846e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_8.txt @@ -0,0 +1,9 @@ + ---------------- --------------- --------------------- + Main table title + ---------------- --------------- --------------------- + ISBN Title Author + ---------------- --------------- --------------------- + 978-0521567817 De Monarchia Dante Alighieri + 978-0804169127 Divine Comedy spans multiple rows + ---------------- --------------- --------------------- + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_9.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_9.txt new file mode 100644 index 00000000..069c0d51 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_9.txt @@ -0,0 +1,5 @@ + +X [CUSTOM] Custom block +X +X Second custom block line + diff --git a/vendor/symfony/console/Tests/Fixtures/TestCommand.php b/vendor/symfony/console/Tests/Fixtures/TestCommand.php new file mode 100644 index 00000000..dcd32739 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/TestCommand.php @@ -0,0 +1,28 @@ +<?php + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class TestCommand extends Command +{ + protected function configure() + { + $this + ->setName('namespace:name') + ->setAliases(array('name')) + ->setDescription('description') + ->setHelp('help') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $output->writeln('execute called'); + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $output->writeln('interact called'); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/application_1.json b/vendor/symfony/console/Tests/Fixtures/application_1.json new file mode 100644 index 00000000..b17b38d8 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_1.json @@ -0,0 +1 @@ +{"commands":[{"name":"help","usage":["help [--xml] [--format FORMAT] [--raw] [--] [<command_name>]"],"description":"Displays help for a command","help":"The <info>help<\/info> command displays help for a given command:\n\n <info>php app\/console help list<\/info>\n\nYou can also output the help in other formats by using the <comment>--format<\/comment> option:\n\n <info>php app\/console help --format=xml list<\/info>\n\nTo display the list of available commands, please use the <info>list<\/info> command.","definition":{"arguments":{"command_name":{"name":"command_name","is_required":false,"is_array":false,"description":"The command name","default":"help"}},"options":{"xml":{"name":"--xml","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output help as XML","default":false},"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"The output format (txt, xml, json, or md)","default":"txt"},"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command help","default":false},"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question","default":false}}}},{"name":"list","usage":["list [--xml] [--raw] [--format FORMAT] [--] [<namespace>]"],"description":"Lists commands","help":"The <info>list<\/info> command lists all commands:\n\n <info>php app\/console list<\/info>\n\nYou can also display the commands for a specific namespace:\n\n <info>php app\/console list test<\/info>\n\nYou can also output the information in other formats by using the <comment>--format<\/comment> option:\n\n <info>php app\/console list --format=xml<\/info>\n\nIt's also possible to get raw list of commands (useful for embedding command runner):\n\n <info>php app\/console list --raw<\/info>","definition":{"arguments":{"namespace":{"name":"namespace","is_required":false,"is_array":false,"description":"The namespace name","default":null}},"options":{"xml":{"name":"--xml","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output list as XML","default":false},"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command list","default":false},"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"The output format (txt, xml, json, or md)","default":"txt"}}}}],"namespaces":[{"id":"_global","commands":["help","list"]}]} diff --git a/vendor/symfony/console/Tests/Fixtures/application_1.md b/vendor/symfony/console/Tests/Fixtures/application_1.md new file mode 100644 index 00000000..82a605da --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_1.md @@ -0,0 +1,201 @@ +UNKNOWN +======= + +* help +* list + +help +---- + +* Description: Displays help for a command +* Usage: + + * `help [--xml] [--format FORMAT] [--raw] [--] [<command_name>]` + +The <info>help</info> command displays help for a given command: + + <info>php app/console help list</info> + +You can also output the help in other formats by using the <comment>--format</comment> option: + + <info>php app/console help --format=xml list</info> + +To display the list of available commands, please use the <info>list</info> command. + +### Arguments: + +**command_name:** + +* Name: command_name +* Is required: no +* Is array: no +* Description: The command name +* Default: `'help'` + +### Options: + +**xml:** + +* Name: `--xml` +* Shortcut: <none> +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output help as XML +* Default: `false` + +**format:** + +* Name: `--format` +* Shortcut: <none> +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: The output format (txt, xml, json, or md) +* Default: `'txt'` + +**raw:** + +* Name: `--raw` +* Shortcut: <none> +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output raw command help +* Default: `false` + +**help:** + +* Name: `--help` +* Shortcut: `-h` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this help message +* Default: `false` + +**quiet:** + +* Name: `--quiet` +* Shortcut: `-q` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not output any message +* Default: `false` + +**verbose:** + +* Name: `--verbose` +* Shortcut: `-v|-vv|-vvv` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +* Default: `false` + +**version:** + +* Name: `--version` +* Shortcut: `-V` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this application version +* Default: `false` + +**ansi:** + +* Name: `--ansi` +* Shortcut: <none> +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Force ANSI output +* Default: `false` + +**no-ansi:** + +* Name: `--no-ansi` +* Shortcut: <none> +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Disable ANSI output +* Default: `false` + +**no-interaction:** + +* Name: `--no-interaction` +* Shortcut: `-n` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not ask any interactive question +* Default: `false` + +list +---- + +* Description: Lists commands +* Usage: + + * `list [--xml] [--raw] [--format FORMAT] [--] [<namespace>]` + +The <info>list</info> command lists all commands: + + <info>php app/console list</info> + +You can also display the commands for a specific namespace: + + <info>php app/console list test</info> + +You can also output the information in other formats by using the <comment>--format</comment> option: + + <info>php app/console list --format=xml</info> + +It's also possible to get raw list of commands (useful for embedding command runner): + + <info>php app/console list --raw</info> + +### Arguments: + +**namespace:** + +* Name: namespace +* Is required: no +* Is array: no +* Description: The namespace name +* Default: `NULL` + +### Options: + +**xml:** + +* Name: `--xml` +* Shortcut: <none> +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output list as XML +* Default: `false` + +**raw:** + +* Name: `--raw` +* Shortcut: <none> +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output raw command list +* Default: `false` + +**format:** + +* Name: `--format` +* Shortcut: <none> +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: The output format (txt, xml, json, or md) +* Default: `'txt'` diff --git a/vendor/symfony/console/Tests/Fixtures/application_1.txt b/vendor/symfony/console/Tests/Fixtures/application_1.txt new file mode 100644 index 00000000..c4cf8f21 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_1.txt @@ -0,0 +1,17 @@ +<info>Console Tool</info> + +<comment>Usage:</comment> + command [options] [arguments] + +<comment>Options:</comment> + <info>-h, --help</info> Display this help message + <info>-q, --quiet</info> Do not output any message + <info>-V, --version</info> Display this application version + <info> --ansi</info> Force ANSI output + <info> --no-ansi</info> Disable ANSI output + <info>-n, --no-interaction</info> Do not ask any interactive question + <info>-v|vv|vvv, --verbose</info> Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +<comment>Available commands:</comment> + <info>help</info> Displays help for a command + <info>list</info> Lists commands diff --git a/vendor/symfony/console/Tests/Fixtures/application_1.xml b/vendor/symfony/console/Tests/Fixtures/application_1.xml new file mode 100644 index 00000000..35d1db4d --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_1.xml @@ -0,0 +1,110 @@ +<?xml version="1.0" encoding="UTF-8"?> +<symfony> + <commands> + <command id="help" name="help"> + <usages> + <usage>help [--xml] [--format FORMAT] [--raw] [--] [<command_name>]</usage> + </usages> + <description>Displays help for a command</description> + <help>The <info>help</info> command displays help for a given command: + + <info>php app/console help list</info> + + You can also output the help in other formats by using the <comment>--format</comment> option: + + <info>php app/console help --format=xml list</info> + + To display the list of available commands, please use the <info>list</info> command.</help> + <arguments> + <argument name="command_name" is_required="0" is_array="0"> + <description>The command name</description> + <defaults> + <default>help</default> + </defaults> + </argument> + </arguments> + <options> + <option name="--xml" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>To output help as XML</description> + </option> + <option name="--format" shortcut="" accept_value="1" is_value_required="1" is_multiple="0"> + <description>The output format (txt, xml, json, or md)</description> + <defaults> + <default>txt</default> + </defaults> + </option> + <option name="--raw" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>To output raw command help</description> + </option> + <option name="--help" shortcut="-h" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Display this help message</description> + </option> + <option name="--quiet" shortcut="-q" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Do not output any message</description> + </option> + <option name="--verbose" shortcut="-v" shortcuts="-v|-vv|-vvv" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug</description> + </option> + <option name="--version" shortcut="-V" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Display this application version</description> + </option> + <option name="--ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Force ANSI output</description> + </option> + <option name="--no-ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Disable ANSI output</description> + </option> + <option name="--no-interaction" shortcut="-n" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Do not ask any interactive question</description> + </option> + </options> + </command> + <command id="list" name="list"> + <usages> + <usage>list [--xml] [--raw] [--format FORMAT] [--] [<namespace>]</usage> + </usages> + <description>Lists commands</description> + <help>The <info>list</info> command lists all commands: + + <info>php app/console list</info> + + You can also display the commands for a specific namespace: + + <info>php app/console list test</info> + + You can also output the information in other formats by using the <comment>--format</comment> option: + + <info>php app/console list --format=xml</info> + + It's also possible to get raw list of commands (useful for embedding command runner): + + <info>php app/console list --raw</info></help> + <arguments> + <argument name="namespace" is_required="0" is_array="0"> + <description>The namespace name</description> + <defaults/> + </argument> + </arguments> + <options> + <option name="--xml" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>To output list as XML</description> + </option> + <option name="--raw" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>To output raw command list</description> + </option> + <option name="--format" shortcut="" accept_value="1" is_value_required="1" is_multiple="0"> + <description>The output format (txt, xml, json, or md)</description> + <defaults> + <default>txt</default> + </defaults> + </option> + </options> + </command> + </commands> + <namespaces> + <namespace id="_global"> + <command>help</command> + <command>list</command> + </namespace> + </namespaces> +</symfony> diff --git a/vendor/symfony/console/Tests/Fixtures/application_2.json b/vendor/symfony/console/Tests/Fixtures/application_2.json new file mode 100644 index 00000000..e47a7a96 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_2.json @@ -0,0 +1 @@ +{"commands":[{"name":"help","usage":["help [--xml] [--format FORMAT] [--raw] [--] [<command_name>]"],"description":"Displays help for a command","help":"The <info>help<\/info> command displays help for a given command:\n\n <info>php app\/console help list<\/info>\n\nYou can also output the help in other formats by using the <comment>--format<\/comment> option:\n\n <info>php app\/console help --format=xml list<\/info>\n\nTo display the list of available commands, please use the <info>list<\/info> command.","definition":{"arguments":{"command_name":{"name":"command_name","is_required":false,"is_array":false,"description":"The command name","default":"help"}},"options":{"xml":{"name":"--xml","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output help as XML","default":false},"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"The output format (txt, xml, json, or md)","default":"txt"},"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command help","default":false},"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question","default":false}}}},{"name":"list","usage":["list [--xml] [--raw] [--format FORMAT] [--] [<namespace>]"],"description":"Lists commands","help":"The <info>list<\/info> command lists all commands:\n\n <info>php app\/console list<\/info>\n\nYou can also display the commands for a specific namespace:\n\n <info>php app\/console list test<\/info>\n\nYou can also output the information in other formats by using the <comment>--format<\/comment> option:\n\n <info>php app\/console list --format=xml<\/info>\n\nIt's also possible to get raw list of commands (useful for embedding command runner):\n\n <info>php app\/console list --raw<\/info>","definition":{"arguments":{"namespace":{"name":"namespace","is_required":false,"is_array":false,"description":"The namespace name","default":null}},"options":{"xml":{"name":"--xml","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output list as XML","default":false},"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command list","default":false},"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"The output format (txt, xml, json, or md)","default":"txt"}}}},{"name":"descriptor:command1","usage":["descriptor:command1", "alias1", "alias2"],"description":"command 1 description","help":"command 1 help","definition":{"arguments":[],"options":{"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question","default":false}}}},{"name":"descriptor:command2","usage":["descriptor:command2 [-o|--option_name] [--] <argument_name>", "descriptor:command2 -o|--option_name <argument_name>", "descriptor:command2 <argument_name>"],"description":"command 2 description","help":"command 2 help","definition":{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false},"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question","default":false}}}}],"namespaces":[{"id":"_global","commands":["alias1","alias2","help","list"]},{"id":"descriptor","commands":["descriptor:command1","descriptor:command2"]}]}
\ No newline at end of file diff --git a/vendor/symfony/console/Tests/Fixtures/application_2.md b/vendor/symfony/console/Tests/Fixtures/application_2.md new file mode 100644 index 00000000..f031c9e5 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_2.md @@ -0,0 +1,396 @@ +My Symfony application +====================== + +* alias1 +* alias2 +* help +* list + +**descriptor:** + +* descriptor:command1 +* descriptor:command2 + +help +---- + +* Description: Displays help for a command +* Usage: + + * `help [--xml] [--format FORMAT] [--raw] [--] [<command_name>]` + +The <info>help</info> command displays help for a given command: + + <info>php app/console help list</info> + +You can also output the help in other formats by using the <comment>--format</comment> option: + + <info>php app/console help --format=xml list</info> + +To display the list of available commands, please use the <info>list</info> command. + +### Arguments: + +**command_name:** + +* Name: command_name +* Is required: no +* Is array: no +* Description: The command name +* Default: `'help'` + +### Options: + +**xml:** + +* Name: `--xml` +* Shortcut: <none> +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output help as XML +* Default: `false` + +**format:** + +* Name: `--format` +* Shortcut: <none> +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: The output format (txt, xml, json, or md) +* Default: `'txt'` + +**raw:** + +* Name: `--raw` +* Shortcut: <none> +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output raw command help +* Default: `false` + +**help:** + +* Name: `--help` +* Shortcut: `-h` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this help message +* Default: `false` + +**quiet:** + +* Name: `--quiet` +* Shortcut: `-q` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not output any message +* Default: `false` + +**verbose:** + +* Name: `--verbose` +* Shortcut: `-v|-vv|-vvv` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +* Default: `false` + +**version:** + +* Name: `--version` +* Shortcut: `-V` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this application version +* Default: `false` + +**ansi:** + +* Name: `--ansi` +* Shortcut: <none> +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Force ANSI output +* Default: `false` + +**no-ansi:** + +* Name: `--no-ansi` +* Shortcut: <none> +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Disable ANSI output +* Default: `false` + +**no-interaction:** + +* Name: `--no-interaction` +* Shortcut: `-n` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not ask any interactive question +* Default: `false` + +list +---- + +* Description: Lists commands +* Usage: + + * `list [--xml] [--raw] [--format FORMAT] [--] [<namespace>]` + +The <info>list</info> command lists all commands: + + <info>php app/console list</info> + +You can also display the commands for a specific namespace: + + <info>php app/console list test</info> + +You can also output the information in other formats by using the <comment>--format</comment> option: + + <info>php app/console list --format=xml</info> + +It's also possible to get raw list of commands (useful for embedding command runner): + + <info>php app/console list --raw</info> + +### Arguments: + +**namespace:** + +* Name: namespace +* Is required: no +* Is array: no +* Description: The namespace name +* Default: `NULL` + +### Options: + +**xml:** + +* Name: `--xml` +* Shortcut: <none> +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output list as XML +* Default: `false` + +**raw:** + +* Name: `--raw` +* Shortcut: <none> +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output raw command list +* Default: `false` + +**format:** + +* Name: `--format` +* Shortcut: <none> +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: The output format (txt, xml, json, or md) +* Default: `'txt'` + +descriptor:command1 +------------------- + +* Description: command 1 description +* Usage: + + * `descriptor:command1` + * `alias1` + * `alias2` + +command 1 help + +### Options: + +**help:** + +* Name: `--help` +* Shortcut: `-h` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this help message +* Default: `false` + +**quiet:** + +* Name: `--quiet` +* Shortcut: `-q` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not output any message +* Default: `false` + +**verbose:** + +* Name: `--verbose` +* Shortcut: `-v|-vv|-vvv` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +* Default: `false` + +**version:** + +* Name: `--version` +* Shortcut: `-V` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this application version +* Default: `false` + +**ansi:** + +* Name: `--ansi` +* Shortcut: <none> +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Force ANSI output +* Default: `false` + +**no-ansi:** + +* Name: `--no-ansi` +* Shortcut: <none> +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Disable ANSI output +* Default: `false` + +**no-interaction:** + +* Name: `--no-interaction` +* Shortcut: `-n` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not ask any interactive question +* Default: `false` + +descriptor:command2 +------------------- + +* Description: command 2 description +* Usage: + + * `descriptor:command2 [-o|--option_name] [--] <argument_name>` + * `descriptor:command2 -o|--option_name <argument_name>` + * `descriptor:command2 <argument_name>` + +command 2 help + +### Arguments: + +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: <none> +* Default: `NULL` + +### Options: + +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: <none> +* Default: `false` + +**help:** + +* Name: `--help` +* Shortcut: `-h` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this help message +* Default: `false` + +**quiet:** + +* Name: `--quiet` +* Shortcut: `-q` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not output any message +* Default: `false` + +**verbose:** + +* Name: `--verbose` +* Shortcut: `-v|-vv|-vvv` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +* Default: `false` + +**version:** + +* Name: `--version` +* Shortcut: `-V` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this application version +* Default: `false` + +**ansi:** + +* Name: `--ansi` +* Shortcut: <none> +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Force ANSI output +* Default: `false` + +**no-ansi:** + +* Name: `--no-ansi` +* Shortcut: <none> +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Disable ANSI output +* Default: `false` + +**no-interaction:** + +* Name: `--no-interaction` +* Shortcut: `-n` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not ask any interactive question +* Default: `false` diff --git a/vendor/symfony/console/Tests/Fixtures/application_2.txt b/vendor/symfony/console/Tests/Fixtures/application_2.txt new file mode 100644 index 00000000..292aa829 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_2.txt @@ -0,0 +1,22 @@ +<info>My Symfony application</info> version <comment>v1.0</comment> + +<comment>Usage:</comment> + command [options] [arguments] + +<comment>Options:</comment> + <info>-h, --help</info> Display this help message + <info>-q, --quiet</info> Do not output any message + <info>-V, --version</info> Display this application version + <info> --ansi</info> Force ANSI output + <info> --no-ansi</info> Disable ANSI output + <info>-n, --no-interaction</info> Do not ask any interactive question + <info>-v|vv|vvv, --verbose</info> Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +<comment>Available commands:</comment> + <info>alias1</info> command 1 description + <info>alias2</info> command 1 description + <info>help</info> Displays help for a command + <info>list</info> Lists commands + <comment>descriptor</comment> + <info>descriptor:command1</info> command 1 description + <info>descriptor:command2</info> command 2 description diff --git a/vendor/symfony/console/Tests/Fixtures/application_2.xml b/vendor/symfony/console/Tests/Fixtures/application_2.xml new file mode 100644 index 00000000..bc8ab219 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_2.xml @@ -0,0 +1,190 @@ +<?xml version="1.0" encoding="UTF-8"?> +<symfony name="My Symfony application" version="v1.0"> + <commands> + <command id="help" name="help"> + <usages> + <usage>help [--xml] [--format FORMAT] [--raw] [--] [<command_name>]</usage> + </usages> + <description>Displays help for a command</description> + <help>The <info>help</info> command displays help for a given command: + + <info>php app/console help list</info> + + You can also output the help in other formats by using the <comment>--format</comment> option: + + <info>php app/console help --format=xml list</info> + + To display the list of available commands, please use the <info>list</info> command.</help> + <arguments> + <argument name="command_name" is_required="0" is_array="0"> + <description>The command name</description> + <defaults> + <default>help</default> + </defaults> + </argument> + </arguments> + <options> + <option name="--xml" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>To output help as XML</description> + </option> + <option name="--format" shortcut="" accept_value="1" is_value_required="1" is_multiple="0"> + <description>The output format (txt, xml, json, or md)</description> + <defaults> + <default>txt</default> + </defaults> + </option> + <option name="--raw" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>To output raw command help</description> + </option> + <option name="--help" shortcut="-h" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Display this help message</description> + </option> + <option name="--quiet" shortcut="-q" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Do not output any message</description> + </option> + <option name="--verbose" shortcut="-v" shortcuts="-v|-vv|-vvv" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug</description> + </option> + <option name="--version" shortcut="-V" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Display this application version</description> + </option> + <option name="--ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Force ANSI output</description> + </option> + <option name="--no-ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Disable ANSI output</description> + </option> + <option name="--no-interaction" shortcut="-n" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Do not ask any interactive question</description> + </option> + </options> + </command> + <command id="list" name="list"> + <usages> + <usage>list [--xml] [--raw] [--format FORMAT] [--] [<namespace>]</usage> + </usages> + <description>Lists commands</description> + <help>The <info>list</info> command lists all commands: + + <info>php app/console list</info> + + You can also display the commands for a specific namespace: + + <info>php app/console list test</info> + + You can also output the information in other formats by using the <comment>--format</comment> option: + + <info>php app/console list --format=xml</info> + + It's also possible to get raw list of commands (useful for embedding command runner): + + <info>php app/console list --raw</info></help> + <arguments> + <argument name="namespace" is_required="0" is_array="0"> + <description>The namespace name</description> + <defaults/> + </argument> + </arguments> + <options> + <option name="--xml" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>To output list as XML</description> + </option> + <option name="--raw" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>To output raw command list</description> + </option> + <option name="--format" shortcut="" accept_value="1" is_value_required="1" is_multiple="0"> + <description>The output format (txt, xml, json, or md)</description> + <defaults> + <default>txt</default> + </defaults> + </option> + </options> + </command> + <command id="descriptor:command1" name="descriptor:command1"> + <usages> + <usage>descriptor:command1</usage> + <usage>alias1</usage> + <usage>alias2</usage> + </usages> + <description>command 1 description</description> + <help>command 1 help</help> + <arguments/> + <options> + <option name="--help" shortcut="-h" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Display this help message</description> + </option> + <option name="--quiet" shortcut="-q" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Do not output any message</description> + </option> + <option name="--verbose" shortcut="-v" shortcuts="-v|-vv|-vvv" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug</description> + </option> + <option name="--version" shortcut="-V" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Display this application version</description> + </option> + <option name="--ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Force ANSI output</description> + </option> + <option name="--no-ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Disable ANSI output</description> + </option> + <option name="--no-interaction" shortcut="-n" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Do not ask any interactive question</description> + </option> + </options> + </command> + <command id="descriptor:command2" name="descriptor:command2"> + <usages> + <usage>descriptor:command2 [-o|--option_name] [--] <argument_name></usage> + <usage>descriptor:command2 -o|--option_name <argument_name></usage> + <usage>descriptor:command2 <argument_name></usage> + </usages> + <description>command 2 description</description> + <help>command 2 help</help> + <arguments> + <argument name="argument_name" is_required="1" is_array="0"> + <description></description> + <defaults/> + </argument> + </arguments> + <options> + <option name="--option_name" shortcut="-o" accept_value="0" is_value_required="0" is_multiple="0"> + <description></description> + </option> + <option name="--help" shortcut="-h" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Display this help message</description> + </option> + <option name="--quiet" shortcut="-q" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Do not output any message</description> + </option> + <option name="--verbose" shortcut="-v" shortcuts="-v|-vv|-vvv" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug</description> + </option> + <option name="--version" shortcut="-V" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Display this application version</description> + </option> + <option name="--ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Force ANSI output</description> + </option> + <option name="--no-ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Disable ANSI output</description> + </option> + <option name="--no-interaction" shortcut="-n" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Do not ask any interactive question</description> + </option> + </options> + </command> + </commands> + <namespaces> + <namespace id="_global"> + <command>alias1</command> + <command>alias2</command> + <command>help</command> + <command>list</command> + </namespace> + <namespace id="descriptor"> + <command>descriptor:command1</command> + <command>descriptor:command2</command> + </namespace> + </namespaces> +</symfony> diff --git a/vendor/symfony/console/Tests/Fixtures/application_astext1.txt b/vendor/symfony/console/Tests/Fixtures/application_astext1.txt new file mode 100644 index 00000000..19dacb23 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_astext1.txt @@ -0,0 +1,20 @@ +<info>Console Tool</info> + +<comment>Usage:</comment> + command [options] [arguments] + +<comment>Options:</comment> + <info>-h, --help</info> Display this help message + <info>-q, --quiet</info> Do not output any message + <info>-V, --version</info> Display this application version + <info> --ansi</info> Force ANSI output + <info> --no-ansi</info> Disable ANSI output + <info>-n, --no-interaction</info> Do not ask any interactive question + <info>-v|vv|vvv, --verbose</info> Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +<comment>Available commands:</comment> + <info>afoobar</info> The foo:bar command + <info>help</info> Displays help for a command + <info>list</info> Lists commands + <comment>foo</comment> + <info>foo:bar</info> The foo:bar command diff --git a/vendor/symfony/console/Tests/Fixtures/application_astext2.txt b/vendor/symfony/console/Tests/Fixtures/application_astext2.txt new file mode 100644 index 00000000..c99ccdda --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_astext2.txt @@ -0,0 +1,16 @@ +<info>Console Tool</info> + +<comment>Usage:</comment> + command [options] [arguments] + +<comment>Options:</comment> + <info>-h, --help</info> Display this help message + <info>-q, --quiet</info> Do not output any message + <info>-V, --version</info> Display this application version + <info> --ansi</info> Force ANSI output + <info> --no-ansi</info> Disable ANSI output + <info>-n, --no-interaction</info> Do not ask any interactive question + <info>-v|vv|vvv, --verbose</info> Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +<comment>Available commands for the "foo" namespace:</comment> + <info>foo:bar</info> The foo:bar command diff --git a/vendor/symfony/console/Tests/Fixtures/application_asxml1.txt b/vendor/symfony/console/Tests/Fixtures/application_asxml1.txt new file mode 100644 index 00000000..8277d9e6 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_asxml1.txt @@ -0,0 +1,146 @@ +<?xml version="1.0" encoding="UTF-8"?> +<symfony> + <commands> + <command id="help" name="help"> + <usages> + <usage>help [--xml] [--format FORMAT] [--raw] [--] [<command_name>]</usage> + </usages> + <description>Displays help for a command</description> + <help>The <info>help</info> command displays help for a given command: + + <info>php app/console help list</info> + + You can also output the help in other formats by using the <comment>--format</comment> option: + + <info>php app/console help --format=xml list</info> + + To display the list of available commands, please use the <info>list</info> command.</help> + <arguments> + <argument name="command_name" is_required="0" is_array="0"> + <description>The command name</description> + <defaults> + <default>help</default> + </defaults> + </argument> + </arguments> + <options> + <option name="--xml" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>To output help as XML</description> + </option> + <option name="--format" shortcut="" accept_value="1" is_value_required="1" is_multiple="0"> + <description>The output format (txt, xml, json, or md)</description> + <defaults> + <default>txt</default> + </defaults> + </option> + <option name="--raw" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>To output raw command help</description> + </option> + <option name="--help" shortcut="-h" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Display this help message</description> + </option> + <option name="--quiet" shortcut="-q" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Do not output any message</description> + </option> + <option name="--verbose" shortcut="-v" shortcuts="-v|-vv|-vvv" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug</description> + </option> + <option name="--version" shortcut="-V" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Display this application version</description> + </option> + <option name="--ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Force ANSI output</description> + </option> + <option name="--no-ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Disable ANSI output</description> + </option> + <option name="--no-interaction" shortcut="-n" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Do not ask any interactive question</description> + </option> + </options> + </command> + <command id="list" name="list"> + <usages> + <usage>list [--xml] [--raw] [--format FORMAT] [--] [<namespace>]</usage> + </usages> + <description>Lists commands</description> + <help>The <info>list</info> command lists all commands: + + <info>php app/console list</info> + + You can also display the commands for a specific namespace: + + <info>php app/console list test</info> + + You can also output the information in other formats by using the <comment>--format</comment> option: + + <info>php app/console list --format=xml</info> + + It's also possible to get raw list of commands (useful for embedding command runner): + + <info>php app/console list --raw</info></help> + <arguments> + <argument name="namespace" is_required="0" is_array="0"> + <description>The namespace name</description> + <defaults/> + </argument> + </arguments> + <options> + <option name="--xml" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>To output list as XML</description> + </option> + <option name="--raw" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>To output raw command list</description> + </option> + <option name="--format" shortcut="" accept_value="1" is_value_required="1" is_multiple="0"> + <description>The output format (txt, xml, json, or md)</description> + <defaults> + <default>txt</default> + </defaults> + </option> + </options> + </command> + <command id="foo:bar" name="foo:bar"> + <usages> + <usage>foo:bar</usage> + <usage>afoobar</usage> + </usages> + <description>The foo:bar command</description> + <help>The foo:bar command</help> + <arguments/> + <options> + <option name="--help" shortcut="-h" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Display this help message</description> + </option> + <option name="--quiet" shortcut="-q" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Do not output any message</description> + </option> + <option name="--verbose" shortcut="-v" shortcuts="-v|-vv|-vvv" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug</description> + </option> + <option name="--version" shortcut="-V" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Display this application version</description> + </option> + <option name="--ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Force ANSI output</description> + </option> + <option name="--no-ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Disable ANSI output</description> + </option> + <option name="--no-interaction" shortcut="-n" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Do not ask any interactive question</description> + </option> + </options> + </command> + </commands> + <namespaces> + <namespace id="_global"> + <command>afoobar</command> + <command>help</command> + <command>list</command> + </namespace> + <namespace id="foo"> + <command>foo:bar</command> + </namespace> + </namespaces> +</symfony> diff --git a/vendor/symfony/console/Tests/Fixtures/application_asxml2.txt b/vendor/symfony/console/Tests/Fixtures/application_asxml2.txt new file mode 100644 index 00000000..93d6d4e9 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_asxml2.txt @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<symfony> + <commands namespace="foo"> + <command id="foo:bar" name="foo:bar"> + <usages> + <usage>foo:bar</usage> + <usage>afoobar</usage> + </usages> + <description>The foo:bar command</description> + <help>The foo:bar command</help> + <arguments/> + <options> + <option name="--help" shortcut="-h" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Display this help message</description> + </option> + <option name="--quiet" shortcut="-q" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Do not output any message</description> + </option> + <option name="--verbose" shortcut="-v" shortcuts="-v|-vv|-vvv" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug</description> + </option> + <option name="--version" shortcut="-V" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Display this application version</description> + </option> + <option name="--ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Force ANSI output</description> + </option> + <option name="--no-ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Disable ANSI output</description> + </option> + <option name="--no-interaction" shortcut="-n" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Do not ask any interactive question</description> + </option> + </options> + </command> + </commands> +</symfony> diff --git a/vendor/symfony/console/Tests/Fixtures/application_gethelp.txt b/vendor/symfony/console/Tests/Fixtures/application_gethelp.txt new file mode 100644 index 00000000..0c16e3c8 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_gethelp.txt @@ -0,0 +1 @@ +<info>Console Tool</info>
\ No newline at end of file diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception1.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception1.txt new file mode 100644 index 00000000..919cec42 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception1.txt @@ -0,0 +1,6 @@ + + + [Symfony\Component\Console\Exception\CommandNotFoundException] + Command "foo" is not defined. + + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception2.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception2.txt new file mode 100644 index 00000000..d9e93da4 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception2.txt @@ -0,0 +1,8 @@ + + + [Symfony\Component\Console\Exception\InvalidOptionException] + The "--foo" option does not exist. + + +list [--xml] [--raw] [--format FORMAT] [--] [<namespace>] + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception3.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception3.txt new file mode 100644 index 00000000..8276137b --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception3.txt @@ -0,0 +1,18 @@ + + + [Exception] + Third exception comment + + + + [Exception] + Second exception comment + + + + [Exception] + First exception <p>this is html</p> + + +foo3:bar + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception3decorated.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception3decorated.txt new file mode 100644 index 00000000..b4a7b018 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception3decorated.txt @@ -0,0 +1,18 @@ + +[37;41m [39;49m +[37;41m [Exception] [39;49m +[37;41m Third exception [39;49m[34;41mcomment[39;49m[37;41m [39;49m +[37;41m [39;49m + +[37;41m [39;49m +[37;41m [Exception] [39;49m +[37;41m Second exception [39;49m[33mcomment[39m[37;41m [39;49m +[37;41m [39;49m + +[37;41m [39;49m +[37;41m [Exception] [39;49m +[37;41m First exception [39;49m[37;41m<p>[39;49m[37;41mthis is html[39;49m[37;41m</p>[39;49m[37;41m [39;49m +[37;41m [39;49m + +[32mfoo3:bar[39m + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception4.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception4.txt new file mode 100644 index 00000000..cb080e9c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception4.txt @@ -0,0 +1,7 @@ + + + [Symfony\Component\Console\Exception\CommandNotFoundException] + Command "foo" is not define + d. + + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1.txt new file mode 100644 index 00000000..1ba5f8fd --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1.txt @@ -0,0 +1,8 @@ + + + [Exception] + エラーメッセージ + + +foo + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt new file mode 100644 index 00000000..20644251 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt @@ -0,0 +1,8 @@ + +[37;41m [39;49m +[37;41m [Exception] [39;49m +[37;41m エラーメッセージ [39;49m +[37;41m [39;49m + +[32mfoo[39m + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth2.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth2.txt new file mode 100644 index 00000000..e41fcfcf --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth2.txt @@ -0,0 +1,9 @@ + + + [Exception] + コマンドã®å®Ÿè¡Œä¸ã«ã‚¨ãƒ©ãƒ¼ãŒ + 発生ã—ã¾ã—ãŸã€‚ + + +foo + diff --git a/vendor/symfony/console/Tests/Fixtures/application_run1.txt b/vendor/symfony/console/Tests/Fixtures/application_run1.txt new file mode 100644 index 00000000..0dc27309 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_run1.txt @@ -0,0 +1,17 @@ +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + help Displays help for a command + list Lists commands diff --git a/vendor/symfony/console/Tests/Fixtures/application_run2.txt b/vendor/symfony/console/Tests/Fixtures/application_run2.txt new file mode 100644 index 00000000..d28b928e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_run2.txt @@ -0,0 +1,29 @@ +Usage: + help [options] [--] [<command_name>] + +Arguments: + command The command to execute + command_name The command name [default: "help"] + +Options: + --xml To output help as XML + --format=FORMAT The output format (txt, xml, json, or md) [default: "txt"] + --raw To output raw command help + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Help: + The help command displays help for a given command: + + php app/console help list + + You can also output the help in other formats by using the --format option: + + php app/console help --format=xml list + + To display the list of available commands, please use the list command. diff --git a/vendor/symfony/console/Tests/Fixtures/application_run3.txt b/vendor/symfony/console/Tests/Fixtures/application_run3.txt new file mode 100644 index 00000000..bc51995f --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_run3.txt @@ -0,0 +1,27 @@ +Usage: + list [options] [--] [<namespace>] + +Arguments: + namespace The namespace name + +Options: + --xml To output list as XML + --raw To output raw command list + --format=FORMAT The output format (txt, xml, json, or md) [default: "txt"] + +Help: + The list command lists all commands: + + php app/console list + + You can also display the commands for a specific namespace: + + php app/console list test + + You can also output the information in other formats by using the --format option: + + php app/console list --format=xml + + It's also possible to get raw list of commands (useful for embedding command runner): + + php app/console list --raw diff --git a/vendor/symfony/console/Tests/Fixtures/application_run4.txt b/vendor/symfony/console/Tests/Fixtures/application_run4.txt new file mode 100644 index 00000000..47187fc2 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_run4.txt @@ -0,0 +1 @@ +Console Tool diff --git a/vendor/symfony/console/Tests/Fixtures/command_1.json b/vendor/symfony/console/Tests/Fixtures/command_1.json new file mode 100644 index 00000000..20f310b4 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_1.json @@ -0,0 +1 @@ +{"name":"descriptor:command1","usage":["descriptor:command1", "alias1", "alias2"],"description":"command 1 description","help":"command 1 help","definition":{"arguments":[],"options":[]}} diff --git a/vendor/symfony/console/Tests/Fixtures/command_1.md b/vendor/symfony/console/Tests/Fixtures/command_1.md new file mode 100644 index 00000000..34ed3ea7 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_1.md @@ -0,0 +1,11 @@ +descriptor:command1 +------------------- + +* Description: command 1 description +* Usage: + + * `descriptor:command1` + * `alias1` + * `alias2` + +command 1 help diff --git a/vendor/symfony/console/Tests/Fixtures/command_1.txt b/vendor/symfony/console/Tests/Fixtures/command_1.txt new file mode 100644 index 00000000..28e14a05 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_1.txt @@ -0,0 +1,7 @@ +<comment>Usage:</comment> + descriptor:command1 + alias1 + alias2 + +<comment>Help:</comment> + command 1 help diff --git a/vendor/symfony/console/Tests/Fixtures/command_1.xml b/vendor/symfony/console/Tests/Fixtures/command_1.xml new file mode 100644 index 00000000..838b9bd9 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_1.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<command id="descriptor:command1" name="descriptor:command1"> + <usages> + <usage>descriptor:command1</usage> + <usage>alias1</usage> + <usage>alias2</usage> + </usages> + <description>command 1 description</description> + <help>command 1 help</help> + <arguments/> + <options/> +</command> diff --git a/vendor/symfony/console/Tests/Fixtures/command_2.json b/vendor/symfony/console/Tests/Fixtures/command_2.json new file mode 100644 index 00000000..38edd1e2 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_2.json @@ -0,0 +1 @@ +{"name":"descriptor:command2","usage":["descriptor:command2 [-o|--option_name] [--] <argument_name>", "descriptor:command2 -o|--option_name <argument_name>", "descriptor:command2 <argument_name>"],"description":"command 2 description","help":"command 2 help","definition":{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false}}}} diff --git a/vendor/symfony/console/Tests/Fixtures/command_2.md b/vendor/symfony/console/Tests/Fixtures/command_2.md new file mode 100644 index 00000000..6f538b64 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_2.md @@ -0,0 +1,33 @@ +descriptor:command2 +------------------- + +* Description: command 2 description +* Usage: + + * `descriptor:command2 [-o|--option_name] [--] <argument_name>` + * `descriptor:command2 -o|--option_name <argument_name>` + * `descriptor:command2 <argument_name>` + +command 2 help + +### Arguments: + +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: <none> +* Default: `NULL` + +### Options: + +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: <none> +* Default: `false` diff --git a/vendor/symfony/console/Tests/Fixtures/command_2.txt b/vendor/symfony/console/Tests/Fixtures/command_2.txt new file mode 100644 index 00000000..72f7ce05 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_2.txt @@ -0,0 +1,13 @@ +<comment>Usage:</comment> + descriptor:command2 [options] [--] <argument_name> + descriptor:command2 -o|--option_name <argument_name> + descriptor:command2 <argument_name> + +<comment>Arguments:</comment> + <info>argument_name</info> + +<comment>Options:</comment> + <info>-o, --option_name</info> + +<comment>Help:</comment> + command 2 help diff --git a/vendor/symfony/console/Tests/Fixtures/command_2.xml b/vendor/symfony/console/Tests/Fixtures/command_2.xml new file mode 100644 index 00000000..67364caa --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_2.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<command id="descriptor:command2" name="descriptor:command2"> + <usages> + <usage>descriptor:command2 [-o|--option_name] [--] <argument_name></usage> + <usage>descriptor:command2 -o|--option_name <argument_name></usage> + <usage>descriptor:command2 <argument_name></usage> + </usages> + <description>command 2 description</description> + <help>command 2 help</help> + <arguments> + <argument name="argument_name" is_required="1" is_array="0"> + <description></description> + <defaults/> + </argument> + </arguments> + <options> + <option name="--option_name" shortcut="-o" accept_value="0" is_value_required="0" is_multiple="0"> + <description></description> + </option> + </options> +</command> diff --git a/vendor/symfony/console/Tests/Fixtures/command_astext.txt b/vendor/symfony/console/Tests/Fixtures/command_astext.txt new file mode 100644 index 00000000..7e206388 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_astext.txt @@ -0,0 +1,18 @@ +<comment>Usage:</comment> + namespace:name + name + +<comment>Arguments:</comment> + <info>command</info> The command to execute + +<comment>Options:</comment> + <info>-h, --help</info> Display this help message + <info>-q, --quiet</info> Do not output any message + <info>-V, --version</info> Display this application version + <info> --ansi</info> Force ANSI output + <info> --no-ansi</info> Disable ANSI output + <info>-n, --no-interaction</info> Do not ask any interactive question + <info>-v|vv|vvv, --verbose</info> Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +<comment>Help:</comment> + help diff --git a/vendor/symfony/console/Tests/Fixtures/command_asxml.txt b/vendor/symfony/console/Tests/Fixtures/command_asxml.txt new file mode 100644 index 00000000..5e776238 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_asxml.txt @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<command id="namespace:name" name="namespace:name"> + <usages> + <usage>namespace:name</usage> + <usage>name</usage> + </usages> + <description>description</description> + <help>help</help> + <arguments> + <argument name="command" is_required="1" is_array="0"> + <description>The command to execute</description> + <defaults/> + </argument> + </arguments> + <options> + <option name="--help" shortcut="-h" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Display this help message</description> + </option> + <option name="--quiet" shortcut="-q" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Do not output any message</description> + </option> + <option name="--verbose" shortcut="-v" shortcuts="-v|-vv|-vvv" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug</description> + </option> + <option name="--version" shortcut="-V" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Display this application version</description> + </option> + <option name="--ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Force ANSI output</description> + </option> + <option name="--no-ansi" shortcut="" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Disable ANSI output</description> + </option> + <option name="--no-interaction" shortcut="-n" accept_value="0" is_value_required="0" is_multiple="0"> + <description>Do not ask any interactive question</description> + </option> + </options> +</command> diff --git a/vendor/symfony/console/Tests/Fixtures/definition_astext.txt b/vendor/symfony/console/Tests/Fixtures/definition_astext.txt new file mode 100644 index 00000000..0431c072 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/definition_astext.txt @@ -0,0 +1,11 @@ +<comment>Arguments:</comment> + <info>foo</info> The foo argument + <info>baz</info> The baz argument<comment> [default: true]</comment> + <info>bar</info> The bar argument<comment> [default: ["http://foo.com/"]]</comment> + +<comment>Options:</comment> + <info>-f, --foo=FOO</info> The foo option + <info> --baz[=BAZ]</info> The baz option<comment> [default: false]</comment> + <info>-b, --bar[=BAR]</info> The bar option<comment> [default: "bar"]</comment> + <info> --qux[=QUX]</info> The qux option<comment> [default: ["http://foo.com/","bar"]]</comment><comment> (multiple values allowed)</comment> + <info> --qux2[=QUX2]</info> The qux2 option<comment> [default: {"foo":"bar"}]</comment><comment> (multiple values allowed)</comment>
\ No newline at end of file diff --git a/vendor/symfony/console/Tests/Fixtures/definition_asxml.txt b/vendor/symfony/console/Tests/Fixtures/definition_asxml.txt new file mode 100644 index 00000000..eec8c079 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/definition_asxml.txt @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<definition> + <arguments> + <argument name="foo" is_required="0" is_array="0"> + <description>The foo argument</description> + <defaults/> + </argument> + <argument name="baz" is_required="0" is_array="0"> + <description>The baz argument</description> + <defaults> + <default>true</default> + </defaults> + </argument> + <argument name="bar" is_required="0" is_array="1"> + <description>The bar argument</description> + <defaults> + <default>bar</default> + </defaults> + </argument> + </arguments> + <options> + <option name="--foo" shortcut="-f" accept_value="1" is_value_required="1" is_multiple="0"> + <description>The foo option</description> + <defaults/> + </option> + <option name="--baz" shortcut="" accept_value="1" is_value_required="0" is_multiple="0"> + <description>The baz option</description> + <defaults> + <default>false</default> + </defaults> + </option> + <option name="--bar" shortcut="-b" accept_value="1" is_value_required="0" is_multiple="0"> + <description>The bar option</description> + <defaults> + <default>bar</default> + </defaults> + </option> + </options> +</definition> diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_1.json b/vendor/symfony/console/Tests/Fixtures/input_argument_1.json new file mode 100644 index 00000000..b8173b6b --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_1.json @@ -0,0 +1 @@ +{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null} diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_1.md b/vendor/symfony/console/Tests/Fixtures/input_argument_1.md new file mode 100644 index 00000000..88f311ab --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_1.md @@ -0,0 +1,7 @@ +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: <none> +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_1.txt b/vendor/symfony/console/Tests/Fixtures/input_argument_1.txt new file mode 100644 index 00000000..55035183 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_1.txt @@ -0,0 +1 @@ + <info>argument_name</info> diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_1.xml b/vendor/symfony/console/Tests/Fixtures/input_argument_1.xml new file mode 100644 index 00000000..cb37f812 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_1.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<argument name="argument_name" is_required="1" is_array="0"> + <description></description> + <defaults/> +</argument> diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_2.json b/vendor/symfony/console/Tests/Fixtures/input_argument_2.json new file mode 100644 index 00000000..ef06b09a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_2.json @@ -0,0 +1 @@ +{"name":"argument_name","is_required":false,"is_array":true,"description":"argument description","default":[]} diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_2.md b/vendor/symfony/console/Tests/Fixtures/input_argument_2.md new file mode 100644 index 00000000..3cdb00cc --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_2.md @@ -0,0 +1,7 @@ +**argument_name:** + +* Name: argument_name +* Is required: no +* Is array: yes +* Description: argument description +* Default: `array ()` diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_2.txt b/vendor/symfony/console/Tests/Fixtures/input_argument_2.txt new file mode 100644 index 00000000..e7136607 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_2.txt @@ -0,0 +1 @@ + <info>argument_name</info> argument description diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_2.xml b/vendor/symfony/console/Tests/Fixtures/input_argument_2.xml new file mode 100644 index 00000000..629da5a9 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_2.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<argument name="argument_name" is_required="0" is_array="1"> + <description>argument description</description> + <defaults/> +</argument> diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_3.json b/vendor/symfony/console/Tests/Fixtures/input_argument_3.json new file mode 100644 index 00000000..de8484e6 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_3.json @@ -0,0 +1 @@ +{"name":"argument_name","is_required":false,"is_array":false,"description":"argument description","default":"default_value"} diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_3.md b/vendor/symfony/console/Tests/Fixtures/input_argument_3.md new file mode 100644 index 00000000..be1c443a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_3.md @@ -0,0 +1,7 @@ +**argument_name:** + +* Name: argument_name +* Is required: no +* Is array: no +* Description: argument description +* Default: `'default_value'` diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_3.txt b/vendor/symfony/console/Tests/Fixtures/input_argument_3.txt new file mode 100644 index 00000000..6b76639e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_3.txt @@ -0,0 +1 @@ + <info>argument_name</info> argument description<comment> [default: "default_value"]</comment> diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_3.xml b/vendor/symfony/console/Tests/Fixtures/input_argument_3.xml new file mode 100644 index 00000000..399a5c86 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_3.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<argument name="argument_name" is_required="0" is_array="0"> + <description>argument description</description> + <defaults> + <default>default_value</default> + </defaults> +</argument> diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_4.json b/vendor/symfony/console/Tests/Fixtures/input_argument_4.json new file mode 100644 index 00000000..8067a4d1 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_4.json @@ -0,0 +1 @@ +{"name":"argument_name","is_required":true,"is_array":false,"description":"multiline argument description","default":null} diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_4.md b/vendor/symfony/console/Tests/Fixtures/input_argument_4.md new file mode 100644 index 00000000..f026ab37 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_4.md @@ -0,0 +1,8 @@ +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: multiline + argument description +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_4.txt b/vendor/symfony/console/Tests/Fixtures/input_argument_4.txt new file mode 100644 index 00000000..aa74e8ce --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_4.txt @@ -0,0 +1,2 @@ + <info>argument_name</info> multiline + argument description diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_4.xml b/vendor/symfony/console/Tests/Fixtures/input_argument_4.xml new file mode 100644 index 00000000..5ca135ec --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_4.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<argument name="argument_name" is_required="1" is_array="0"> + <description>multiline +argument description</description> + <defaults/> +</argument> diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_1.json b/vendor/symfony/console/Tests/Fixtures/input_definition_1.json new file mode 100644 index 00000000..c7a7d838 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_1.json @@ -0,0 +1 @@ +{"arguments":[],"options":[]} diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_1.md b/vendor/symfony/console/Tests/Fixtures/input_definition_1.md new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_1.md diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_1.txt b/vendor/symfony/console/Tests/Fixtures/input_definition_1.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_1.txt diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_1.xml b/vendor/symfony/console/Tests/Fixtures/input_definition_1.xml new file mode 100644 index 00000000..b5481ce1 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_1.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<definition> + <arguments/> + <options/> +</definition> diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_2.json b/vendor/symfony/console/Tests/Fixtures/input_definition_2.json new file mode 100644 index 00000000..9964a55a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_2.json @@ -0,0 +1 @@ +{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":[]} diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_2.md b/vendor/symfony/console/Tests/Fixtures/input_definition_2.md new file mode 100644 index 00000000..923191cd --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_2.md @@ -0,0 +1,9 @@ +### Arguments: + +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: <none> +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_2.txt b/vendor/symfony/console/Tests/Fixtures/input_definition_2.txt new file mode 100644 index 00000000..73b0f308 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_2.txt @@ -0,0 +1,2 @@ +<comment>Arguments:</comment> + <info>argument_name</info> diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_2.xml b/vendor/symfony/console/Tests/Fixtures/input_definition_2.xml new file mode 100644 index 00000000..102efc14 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_2.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<definition> + <arguments> + <argument name="argument_name" is_required="1" is_array="0"> + <description></description> + <defaults/> + </argument> + </arguments> + <options/> +</definition> diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_3.json b/vendor/symfony/console/Tests/Fixtures/input_definition_3.json new file mode 100644 index 00000000..6a860560 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_3.json @@ -0,0 +1 @@ +{"arguments":[],"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false}}} diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_3.md b/vendor/symfony/console/Tests/Fixtures/input_definition_3.md new file mode 100644 index 00000000..40fd7b0a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_3.md @@ -0,0 +1,11 @@ +### Options: + +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: <none> +* Default: `false` diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_3.txt b/vendor/symfony/console/Tests/Fixtures/input_definition_3.txt new file mode 100644 index 00000000..c02766fd --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_3.txt @@ -0,0 +1,2 @@ +<comment>Options:</comment> + <info>-o, --option_name</info> diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_3.xml b/vendor/symfony/console/Tests/Fixtures/input_definition_3.xml new file mode 100644 index 00000000..bc951515 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_3.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<definition> + <arguments/> + <options> + <option name="--option_name" shortcut="-o" accept_value="0" is_value_required="0" is_multiple="0"> + <description></description> + </option> + </options> +</definition> diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_4.json b/vendor/symfony/console/Tests/Fixtures/input_definition_4.json new file mode 100644 index 00000000..c5a0019f --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_4.json @@ -0,0 +1 @@ +{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false}}} diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_4.md b/vendor/symfony/console/Tests/Fixtures/input_definition_4.md new file mode 100644 index 00000000..a31feea4 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_4.md @@ -0,0 +1,21 @@ +### Arguments: + +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: <none> +* Default: `NULL` + +### Options: + +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: <none> +* Default: `false` diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_4.txt b/vendor/symfony/console/Tests/Fixtures/input_definition_4.txt new file mode 100644 index 00000000..63aa81d2 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_4.txt @@ -0,0 +1,5 @@ +<comment>Arguments:</comment> + <info>argument_name</info> + +<comment>Options:</comment> + <info>-o, --option_name</info> diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_4.xml b/vendor/symfony/console/Tests/Fixtures/input_definition_4.xml new file mode 100644 index 00000000..cffceece --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_4.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<definition> + <arguments> + <argument name="argument_name" is_required="1" is_array="0"> + <description></description> + <defaults/> + </argument> + </arguments> + <options> + <option name="--option_name" shortcut="-o" accept_value="0" is_value_required="0" is_multiple="0"> + <description></description> + </option> + </options> +</definition> diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_1.json b/vendor/symfony/console/Tests/Fixtures/input_option_1.json new file mode 100644 index 00000000..60c5b56c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_1.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_1.md b/vendor/symfony/console/Tests/Fixtures/input_option_1.md new file mode 100644 index 00000000..6f9e9a7e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_1.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: <none> +* Default: `false` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_1.txt b/vendor/symfony/console/Tests/Fixtures/input_option_1.txt new file mode 100644 index 00000000..3a5e4eed --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_1.txt @@ -0,0 +1 @@ + <info>-o, --option_name</info> diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_1.xml b/vendor/symfony/console/Tests/Fixtures/input_option_1.xml new file mode 100644 index 00000000..8a64ea65 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_1.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<option name="--option_name" shortcut="-o" accept_value="0" is_value_required="0" is_multiple="0"> + <description></description> +</option> diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_2.json b/vendor/symfony/console/Tests/Fixtures/input_option_2.json new file mode 100644 index 00000000..04e4228e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_2.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":false,"is_multiple":false,"description":"option description","default":"default_value"} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_2.md b/vendor/symfony/console/Tests/Fixtures/input_option_2.md new file mode 100644 index 00000000..634ac0b0 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_2.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: yes +* Is value required: no +* Is multiple: no +* Description: option description +* Default: `'default_value'` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_2.txt b/vendor/symfony/console/Tests/Fixtures/input_option_2.txt new file mode 100644 index 00000000..1009eff1 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_2.txt @@ -0,0 +1 @@ + <info>-o, --option_name[=OPTION_NAME]</info> option description<comment> [default: "default_value"]</comment> diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_2.xml b/vendor/symfony/console/Tests/Fixtures/input_option_2.xml new file mode 100644 index 00000000..4afac5b0 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_2.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<option name="--option_name" shortcut="-o" accept_value="1" is_value_required="0" is_multiple="0"> + <description>option description</description> + <defaults> + <default>default_value</default> + </defaults> +</option> diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_3.json b/vendor/symfony/console/Tests/Fixtures/input_option_3.json new file mode 100644 index 00000000..c1ea120c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_3.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"option description","default":null} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_3.md b/vendor/symfony/console/Tests/Fixtures/input_option_3.md new file mode 100644 index 00000000..34282896 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_3.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: option description +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_3.txt b/vendor/symfony/console/Tests/Fixtures/input_option_3.txt new file mode 100644 index 00000000..947bb652 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_3.txt @@ -0,0 +1 @@ + <info>-o, --option_name=OPTION_NAME</info> option description diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_3.xml b/vendor/symfony/console/Tests/Fixtures/input_option_3.xml new file mode 100644 index 00000000..dcc0631c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_3.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<option name="--option_name" shortcut="-o" accept_value="1" is_value_required="1" is_multiple="0"> + <description>option description</description> + <defaults/> +</option> diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_4.json b/vendor/symfony/console/Tests/Fixtures/input_option_4.json new file mode 100644 index 00000000..1b671d80 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_4.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":false,"is_multiple":true,"description":"option description","default":[]} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_4.md b/vendor/symfony/console/Tests/Fixtures/input_option_4.md new file mode 100644 index 00000000..8ffba56e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_4.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: yes +* Is value required: no +* Is multiple: yes +* Description: option description +* Default: `array ()` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_4.txt b/vendor/symfony/console/Tests/Fixtures/input_option_4.txt new file mode 100644 index 00000000..27edf77b --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_4.txt @@ -0,0 +1 @@ + <info>-o, --option_name[=OPTION_NAME]</info> option description<comment> (multiple values allowed)</comment> diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_4.xml b/vendor/symfony/console/Tests/Fixtures/input_option_4.xml new file mode 100644 index 00000000..5e2418b1 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_4.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<option name="--option_name" shortcut="-o" accept_value="1" is_value_required="0" is_multiple="1"> + <description>option description</description> + <defaults/> +</option> diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_5.json b/vendor/symfony/console/Tests/Fixtures/input_option_5.json new file mode 100644 index 00000000..35a1405f --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_5.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"multiline option description","default":null} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_5.md b/vendor/symfony/console/Tests/Fixtures/input_option_5.md new file mode 100644 index 00000000..82f51cad --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_5.md @@ -0,0 +1,10 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: multiline + option description +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_5.txt b/vendor/symfony/console/Tests/Fixtures/input_option_5.txt new file mode 100644 index 00000000..4368883c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_5.txt @@ -0,0 +1,2 @@ + <info>-o, --option_name=OPTION_NAME</info> multiline + option description diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_5.xml b/vendor/symfony/console/Tests/Fixtures/input_option_5.xml new file mode 100644 index 00000000..90040ccd --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_5.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<option name="--option_name" shortcut="-o" accept_value="1" is_value_required="1" is_multiple="0"> + <description>multiline +option description</description> + <defaults/> +</option> diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_6.json b/vendor/symfony/console/Tests/Fixtures/input_option_6.json new file mode 100644 index 00000000..d84e8721 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_6.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o|-O","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"option with multiple shortcuts","default":null} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_6.md b/vendor/symfony/console/Tests/Fixtures/input_option_6.md new file mode 100644 index 00000000..ed1ea1c8 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_6.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o|-O` +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: option with multiple shortcuts +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_6.txt b/vendor/symfony/console/Tests/Fixtures/input_option_6.txt new file mode 100644 index 00000000..0e6c9759 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_6.txt @@ -0,0 +1 @@ + <info>-o|O, --option_name=OPTION_NAME</info> option with multiple shortcuts diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_6.xml b/vendor/symfony/console/Tests/Fixtures/input_option_6.xml new file mode 100644 index 00000000..06126a2f --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_6.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<option name="--option_name" shortcut="-o" shortcuts="-o|-O" accept_value="1" is_value_required="1" is_multiple="0"> + <description>option with multiple shortcuts</description> + <defaults/> +</option> diff --git a/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleStackTest.php b/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleStackTest.php new file mode 100644 index 00000000..774df268 --- /dev/null +++ b/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleStackTest.php @@ -0,0 +1,70 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Formatter; + +use Symfony\Component\Console\Formatter\OutputFormatterStyleStack; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputFormatterStyleStackTest extends \PHPUnit_Framework_TestCase +{ + public function testPush() + { + $stack = new OutputFormatterStyleStack(); + $stack->push($s1 = new OutputFormatterStyle('white', 'black')); + $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); + + $this->assertEquals($s2, $stack->getCurrent()); + + $stack->push($s3 = new OutputFormatterStyle('green', 'red')); + + $this->assertEquals($s3, $stack->getCurrent()); + } + + public function testPop() + { + $stack = new OutputFormatterStyleStack(); + $stack->push($s1 = new OutputFormatterStyle('white', 'black')); + $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); + + $this->assertEquals($s2, $stack->pop()); + $this->assertEquals($s1, $stack->pop()); + } + + public function testPopEmpty() + { + $stack = new OutputFormatterStyleStack(); + $style = new OutputFormatterStyle(); + + $this->assertEquals($style, $stack->pop()); + } + + public function testPopNotLast() + { + $stack = new OutputFormatterStyleStack(); + $stack->push($s1 = new OutputFormatterStyle('white', 'black')); + $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); + $stack->push($s3 = new OutputFormatterStyle('green', 'red')); + + $this->assertEquals($s2, $stack->pop($s2)); + $this->assertEquals($s1, $stack->pop()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testInvalidPop() + { + $stack = new OutputFormatterStyleStack(); + $stack->push(new OutputFormatterStyle('white', 'black')); + $stack->pop(new OutputFormatterStyle('yellow', 'blue')); + } +} diff --git a/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php b/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php new file mode 100644 index 00000000..0abfb3ce --- /dev/null +++ b/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php @@ -0,0 +1,99 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Formatter; + +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputFormatterStyleTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $style = new OutputFormatterStyle('green', 'black', array('bold', 'underscore')); + $this->assertEquals("\033[32;40;1;4mfoo\033[39;49;22;24m", $style->apply('foo')); + + $style = new OutputFormatterStyle('red', null, array('blink')); + $this->assertEquals("\033[31;5mfoo\033[39;25m", $style->apply('foo')); + + $style = new OutputFormatterStyle(null, 'white'); + $this->assertEquals("\033[47mfoo\033[49m", $style->apply('foo')); + } + + public function testForeground() + { + $style = new OutputFormatterStyle(); + + $style->setForeground('black'); + $this->assertEquals("\033[30mfoo\033[39m", $style->apply('foo')); + + $style->setForeground('blue'); + $this->assertEquals("\033[34mfoo\033[39m", $style->apply('foo')); + + $style->setForeground('default'); + $this->assertEquals("\033[39mfoo\033[39m", $style->apply('foo')); + + $this->setExpectedException('InvalidArgumentException'); + $style->setForeground('undefined-color'); + } + + public function testBackground() + { + $style = new OutputFormatterStyle(); + + $style->setBackground('black'); + $this->assertEquals("\033[40mfoo\033[49m", $style->apply('foo')); + + $style->setBackground('yellow'); + $this->assertEquals("\033[43mfoo\033[49m", $style->apply('foo')); + + $style->setBackground('default'); + $this->assertEquals("\033[49mfoo\033[49m", $style->apply('foo')); + + $this->setExpectedException('InvalidArgumentException'); + $style->setBackground('undefined-color'); + } + + public function testOptions() + { + $style = new OutputFormatterStyle(); + + $style->setOptions(array('reverse', 'conceal')); + $this->assertEquals("\033[7;8mfoo\033[27;28m", $style->apply('foo')); + + $style->setOption('bold'); + $this->assertEquals("\033[7;8;1mfoo\033[27;28;22m", $style->apply('foo')); + + $style->unsetOption('reverse'); + $this->assertEquals("\033[8;1mfoo\033[28;22m", $style->apply('foo')); + + $style->setOption('bold'); + $this->assertEquals("\033[8;1mfoo\033[28;22m", $style->apply('foo')); + + $style->setOptions(array('bold')); + $this->assertEquals("\033[1mfoo\033[22m", $style->apply('foo')); + + try { + $style->setOption('foo'); + $this->fail('->setOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->setOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + $this->assertContains('Invalid option specified: "foo"', $e->getMessage(), '->setOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + } + + try { + $style->unsetOption('foo'); + $this->fail('->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + $this->assertContains('Invalid option specified: "foo"', $e->getMessage(), '->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + } + } +} diff --git a/vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php b/vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php new file mode 100644 index 00000000..b8d5ca6d --- /dev/null +++ b/vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php @@ -0,0 +1,273 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Formatter; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputFormatterTest extends \PHPUnit_Framework_TestCase +{ + public function testEmptyTag() + { + $formatter = new OutputFormatter(true); + $this->assertEquals('foo<>bar', $formatter->format('foo<>bar')); + } + + public function testLGCharEscaping() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals('foo<bar', $formatter->format('foo\\<bar')); + $this->assertEquals('<info>some info</info>', $formatter->format('\\<info>some info\\</info>')); + $this->assertEquals('\\<info>some info\\</info>', OutputFormatter::escape('<info>some info</info>')); + + $this->assertEquals( + "\033[33mSymfony\\Component\\Console does work very well!\033[39m", + $formatter->format('<comment>Symfony\Component\Console does work very well!</comment>') + ); + } + + public function testBundledStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertTrue($formatter->hasStyle('error')); + $this->assertTrue($formatter->hasStyle('info')); + $this->assertTrue($formatter->hasStyle('comment')); + $this->assertTrue($formatter->hasStyle('question')); + + $this->assertEquals( + "\033[37;41msome error\033[39;49m", + $formatter->format('<error>some error</error>') + ); + $this->assertEquals( + "\033[32msome info\033[39m", + $formatter->format('<info>some info</info>') + ); + $this->assertEquals( + "\033[33msome comment\033[39m", + $formatter->format('<comment>some comment</comment>') + ); + $this->assertEquals( + "\033[30;46msome question\033[39;49m", + $formatter->format('<question>some question</question>') + ); + } + + public function testNestedStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "\033[37;41msome \033[39;49m\033[32msome info\033[39m\033[37;41m error\033[39;49m", + $formatter->format('<error>some <info>some info</info> error</error>') + ); + } + + public function testAdjacentStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "\033[37;41msome error\033[39;49m\033[32msome info\033[39m", + $formatter->format('<error>some error</error><info>some info</info>') + ); + } + + public function testStyleMatchingNotGreedy() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "(\033[32m>=2.0,<2.3\033[39m)", + $formatter->format('(<info>>=2.0,<2.3</info>)') + ); + } + + public function testStyleEscaping() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "(\033[32mz>=2.0,<<<a2.3\\\033[39m)", + $formatter->format('(<info>'.$formatter->escape('z>=2.0,<\\<<a2.3\\').'</info>)') + ); + + $this->assertEquals( + "\033[32m<error>some error</error>\033[39m", + $formatter->format('<info>'.$formatter->escape('<error>some error</error>').'</info>') + ); + } + + public function testDeepNestedStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "\033[37;41merror\033[39;49m\033[32minfo\033[39m\033[33mcomment\033[39m\033[37;41merror\033[39;49m", + $formatter->format('<error>error<info>info<comment>comment</info>error</error>') + ); + } + + public function testNewStyle() + { + $formatter = new OutputFormatter(true); + + $style = new OutputFormatterStyle('blue', 'white'); + $formatter->setStyle('test', $style); + + $this->assertEquals($style, $formatter->getStyle('test')); + $this->assertNotEquals($style, $formatter->getStyle('info')); + + $style = new OutputFormatterStyle('blue', 'white'); + $formatter->setStyle('b', $style); + + $this->assertEquals("\033[34;47msome \033[39;49m\033[34;47mcustom\033[39;49m\033[34;47m msg\033[39;49m", $formatter->format('<test>some <b>custom</b> msg</test>')); + } + + public function testRedefineStyle() + { + $formatter = new OutputFormatter(true); + + $style = new OutputFormatterStyle('blue', 'white'); + $formatter->setStyle('info', $style); + + $this->assertEquals("\033[34;47msome custom msg\033[39;49m", $formatter->format('<info>some custom msg</info>')); + } + + public function testInlineStyle() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals("\033[34;41msome text\033[39;49m", $formatter->format('<fg=blue;bg=red>some text</>')); + $this->assertEquals("\033[34;41msome text\033[39;49m", $formatter->format('<fg=blue;bg=red>some text</fg=blue;bg=red>')); + } + + public function testNonStyleTag() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals("\033[32msome \033[39m\033[32m<tag>\033[39m\033[32m \033[39m\033[32m<setting=value>\033[39m\033[32m styled \033[39m\033[32m<p>\033[39m\033[32msingle-char tag\033[39m\033[32m</p>\033[39m", $formatter->format('<info>some <tag> <setting=value> styled <p>single-char tag</p></info>')); + } + + public function testFormatLongString() + { + $formatter = new OutputFormatter(true); + $long = str_repeat('\\', 14000); + $this->assertEquals("\033[37;41msome error\033[39;49m".$long, $formatter->format('<error>some error</error>'.$long)); + } + + public function testFormatToStringObject() + { + $formatter = new OutputFormatter(false); + $this->assertEquals( + 'some info', $formatter->format(new TableCell()) + ); + } + + public function testNotDecoratedFormatter() + { + $formatter = new OutputFormatter(false); + + $this->assertTrue($formatter->hasStyle('error')); + $this->assertTrue($formatter->hasStyle('info')); + $this->assertTrue($formatter->hasStyle('comment')); + $this->assertTrue($formatter->hasStyle('question')); + + $this->assertEquals( + 'some error', $formatter->format('<error>some error</error>') + ); + $this->assertEquals( + 'some info', $formatter->format('<info>some info</info>') + ); + $this->assertEquals( + 'some comment', $formatter->format('<comment>some comment</comment>') + ); + $this->assertEquals( + 'some question', $formatter->format('<question>some question</question>') + ); + + $formatter->setDecorated(true); + + $this->assertEquals( + "\033[37;41msome error\033[39;49m", $formatter->format('<error>some error</error>') + ); + $this->assertEquals( + "\033[32msome info\033[39m", $formatter->format('<info>some info</info>') + ); + $this->assertEquals( + "\033[33msome comment\033[39m", $formatter->format('<comment>some comment</comment>') + ); + $this->assertEquals( + "\033[30;46msome question\033[39;49m", $formatter->format('<question>some question</question>') + ); + } + + public function testContentWithLineBreaks() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals(<<<EOF +\033[32m +some text\033[39m +EOF + , $formatter->format(<<<'EOF' +<info> +some text</info> +EOF + )); + + $this->assertEquals(<<<EOF +\033[32msome text +\033[39m +EOF + , $formatter->format(<<<'EOF' +<info>some text +</info> +EOF + )); + + $this->assertEquals(<<<EOF +\033[32m +some text +\033[39m +EOF + , $formatter->format(<<<'EOF' +<info> +some text +</info> +EOF + )); + + $this->assertEquals(<<<EOF +\033[32m +some text +more text +\033[39m +EOF + , $formatter->format(<<<'EOF' +<info> +some text +more text +</info> +EOF + )); + } +} + +class TableCell +{ + public function __toString() + { + return '<info>some info</info>'; + } +} diff --git a/vendor/symfony/console/Tests/Helper/FormatterHelperTest.php b/vendor/symfony/console/Tests/Helper/FormatterHelperTest.php new file mode 100644 index 00000000..e0aa9211 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/FormatterHelperTest.php @@ -0,0 +1,92 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\FormatterHelper; + +class FormatterHelperTest extends \PHPUnit_Framework_TestCase +{ + public function testFormatSection() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + '<info>[cli]</info> Some text to display', + $formatter->formatSection('cli', 'Some text to display'), + '::formatSection() formats a message in a section' + ); + } + + public function testFormatBlock() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + '<error> Some text to display </error>', + $formatter->formatBlock('Some text to display', 'error'), + '::formatBlock() formats a message in a block' + ); + + $this->assertEquals( + '<error> Some text to display </error>'."\n". + '<error> foo bar </error>', + $formatter->formatBlock(array('Some text to display', 'foo bar'), 'error'), + '::formatBlock() formats a message in a block' + ); + + $this->assertEquals( + '<error> </error>'."\n". + '<error> Some text to display </error>'."\n". + '<error> </error>', + $formatter->formatBlock('Some text to display', 'error', true), + '::formatBlock() formats a message in a block' + ); + } + + public function testFormatBlockWithDiacriticLetters() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + '<error> </error>'."\n". + '<error> Du texte à afficher </error>'."\n". + '<error> </error>', + $formatter->formatBlock('Du texte à afficher', 'error', true), + '::formatBlock() formats a message in a block' + ); + } + + public function testFormatBlockWithDoubleWidthDiacriticLetters() + { + $formatter = new FormatterHelper(); + $this->assertEquals( + '<error> </error>'."\n". + '<error> 表示ã™ã‚‹ãƒ†ã‚スト </error>'."\n". + '<error> </error>', + $formatter->formatBlock('表示ã™ã‚‹ãƒ†ã‚スト', 'error', true), + '::formatBlock() formats a message in a block' + ); + } + + public function testFormatBlockLGEscaping() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + '<error> </error>'."\n". + '<error> \<info>some info\</info> </error>'."\n". + '<error> </error>', + $formatter->formatBlock('<info>some info</info>', 'error', true), + '::formatBlock() escapes \'<\' chars' + ); + } +} diff --git a/vendor/symfony/console/Tests/Helper/HelperSetTest.php b/vendor/symfony/console/Tests/Helper/HelperSetTest.php new file mode 100644 index 00000000..04edd304 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/HelperSetTest.php @@ -0,0 +1,133 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Command\Command; + +class HelperSetTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $mock_helper = $this->getGenericMockHelper('fake_helper'); + $helperset = new HelperSet(array('fake_helper_alias' => $mock_helper)); + + $this->assertEquals($mock_helper, $helperset->get('fake_helper_alias'), '__construct sets given helper to helpers'); + $this->assertTrue($helperset->has('fake_helper_alias'), '__construct sets helper alias for given helper'); + } + + public function testSet() + { + $helperset = new HelperSet(); + $helperset->set($this->getGenericMockHelper('fake_helper', $helperset)); + $this->assertTrue($helperset->has('fake_helper'), '->set() adds helper to helpers'); + + $helperset = new HelperSet(); + $helperset->set($this->getGenericMockHelper('fake_helper_01', $helperset)); + $helperset->set($this->getGenericMockHelper('fake_helper_02', $helperset)); + $this->assertTrue($helperset->has('fake_helper_01'), '->set() will set multiple helpers on consecutive calls'); + $this->assertTrue($helperset->has('fake_helper_02'), '->set() will set multiple helpers on consecutive calls'); + + $helperset = new HelperSet(); + $helperset->set($this->getGenericMockHelper('fake_helper', $helperset), 'fake_helper_alias'); + $this->assertTrue($helperset->has('fake_helper'), '->set() adds helper alias when set'); + $this->assertTrue($helperset->has('fake_helper_alias'), '->set() adds helper alias when set'); + } + + public function testHas() + { + $helperset = new HelperSet(array('fake_helper_alias' => $this->getGenericMockHelper('fake_helper'))); + $this->assertTrue($helperset->has('fake_helper'), '->has() finds set helper'); + $this->assertTrue($helperset->has('fake_helper_alias'), '->has() finds set helper by alias'); + } + + public function testGet() + { + $helper_01 = $this->getGenericMockHelper('fake_helper_01'); + $helper_02 = $this->getGenericMockHelper('fake_helper_02'); + $helperset = new HelperSet(array('fake_helper_01_alias' => $helper_01, 'fake_helper_02_alias' => $helper_02)); + $this->assertEquals($helper_01, $helperset->get('fake_helper_01'), '->get() returns correct helper by name'); + $this->assertEquals($helper_01, $helperset->get('fake_helper_01_alias'), '->get() returns correct helper by alias'); + $this->assertEquals($helper_02, $helperset->get('fake_helper_02'), '->get() returns correct helper by name'); + $this->assertEquals($helper_02, $helperset->get('fake_helper_02_alias'), '->get() returns correct helper by alias'); + + $helperset = new HelperSet(); + try { + $helperset->get('foo'); + $this->fail('->get() throws InvalidArgumentException when helper not found'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->get() throws InvalidArgumentException when helper not found'); + $this->assertInstanceOf('Symfony\Component\Console\Exception\ExceptionInterface', $e, '->get() throws domain specific exception when helper not found'); + $this->assertContains('The helper "foo" is not defined.', $e->getMessage(), '->get() throws InvalidArgumentException when helper not found'); + } + } + + public function testSetCommand() + { + $cmd_01 = new Command('foo'); + $cmd_02 = new Command('bar'); + + $helperset = new HelperSet(); + $helperset->setCommand($cmd_01); + $this->assertEquals($cmd_01, $helperset->getCommand(), '->setCommand() stores given command'); + + $helperset = new HelperSet(); + $helperset->setCommand($cmd_01); + $helperset->setCommand($cmd_02); + $this->assertEquals($cmd_02, $helperset->getCommand(), '->setCommand() overwrites stored command with consecutive calls'); + } + + public function testGetCommand() + { + $cmd = new Command('foo'); + $helperset = new HelperSet(); + $helperset->setCommand($cmd); + $this->assertEquals($cmd, $helperset->getCommand(), '->getCommand() retrieves stored command'); + } + + public function testIteration() + { + $helperset = new HelperSet(); + $helperset->set($this->getGenericMockHelper('fake_helper_01', $helperset)); + $helperset->set($this->getGenericMockHelper('fake_helper_02', $helperset)); + + $helpers = array('fake_helper_01', 'fake_helper_02'); + $i = 0; + + foreach ($helperset as $helper) { + $this->assertEquals($helpers[$i++], $helper->getName()); + } + } + + /** + * Create a generic mock for the helper interface. Optionally check for a call to setHelperSet with a specific + * helperset instance. + * + * @param string $name + * @param HelperSet $helperset allows a mock to verify a particular helperset set is being added to the Helper + */ + private function getGenericMockHelper($name, HelperSet $helperset = null) + { + $mock_helper = $this->getMock('\Symfony\Component\Console\Helper\HelperInterface'); + $mock_helper->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)); + + if ($helperset) { + $mock_helper->expects($this->any()) + ->method('setHelperSet') + ->with($this->equalTo($helperset)); + } + + return $mock_helper; + } +} diff --git a/vendor/symfony/console/Tests/Helper/HelperTest.php b/vendor/symfony/console/Tests/Helper/HelperTest.php new file mode 100644 index 00000000..33fa2205 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/HelperTest.php @@ -0,0 +1,54 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\Helper; + +class HelperTest extends \PHPUnit_Framework_TestCase +{ + public function formatTimeProvider() + { + return array( + array(0, '< 1 sec'), + array(1, '1 sec'), + array(2, '2 secs'), + array(59, '59 secs'), + array(60, '1 min'), + array(61, '1 min'), + array(119, '1 min'), + array(120, '2 mins'), + array(121, '2 mins'), + array(3599, '59 mins'), + array(3600, '1 hr'), + array(7199, '1 hr'), + array(7200, '2 hrs'), + array(7201, '2 hrs'), + array(86399, '23 hrs'), + array(86400, '1 day'), + array(86401, '1 day'), + array(172799, '1 day'), + array(172800, '2 days'), + array(172801, '2 days'), + ); + } + + /** + * @dataProvider formatTimeProvider + * + * @param int $secs + * @param string $expectedFormat + */ + public function testFormatTime($secs, $expectedFormat) + { + $this->assertEquals($expectedFormat, Helper::formatTime($secs)); + } +} diff --git a/vendor/symfony/console/Tests/Helper/LegacyDialogHelperTest.php b/vendor/symfony/console/Tests/Helper/LegacyDialogHelperTest.php new file mode 100644 index 00000000..97bf7756 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/LegacyDialogHelperTest.php @@ -0,0 +1,263 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Helper\DialogHelper; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @group legacy + */ +class LegacyDialogHelperTest extends \PHPUnit_Framework_TestCase +{ + public function testSelect() + { + $dialog = new DialogHelper(); + + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $heroes = array('Superman', 'Batman', 'Spiderman'); + + $dialog->setInputStream($this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n")); + $this->assertEquals('2', $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, '2')); + $this->assertEquals('1', $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes)); + $this->assertEquals('1', $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes)); + $this->assertEquals('1', $dialog->select($output = $this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', false)); + + rewind($output->getStream()); + $this->assertContains('Input "Fabien" is not a superhero!', stream_get_contents($output->getStream())); + + try { + $this->assertEquals('1', $dialog->select($output = $this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, 1)); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertEquals('Value "Fabien" is invalid', $e->getMessage()); + } + + $this->assertEquals(array('1'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', true)); + $this->assertEquals(array('0', '2'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', true)); + $this->assertEquals(array('0', '2'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', true)); + $this->assertEquals(array('0', '1'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, '0,1', false, 'Input "%s" is not a superhero!', true)); + $this->assertEquals(array('0', '1'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, ' 0 , 1 ', false, 'Input "%s" is not a superhero!', true)); + } + + public function testSelectOnErrorOutput() + { + $dialog = new DialogHelper(); + + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $heroes = array('Superman', 'Batman', 'Spiderman'); + + $dialog->setInputStream($this->getInputStream("Stdout\n1\n")); + $this->assertEquals('1', $dialog->select($output = $this->getConsoleOutput($this->getOutputStream()), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', false)); + + rewind($output->getErrorOutput()->getStream()); + $this->assertContains('Input "Stdout" is not a superhero!', stream_get_contents($output->getErrorOutput()->getStream())); + } + + public function testAsk() + { + $dialog = new DialogHelper(); + + $dialog->setInputStream($this->getInputStream("\n8AM\n")); + + $this->assertEquals('2PM', $dialog->ask($this->getOutputStream(), 'What time is it?', '2PM')); + $this->assertEquals('8AM', $dialog->ask($output = $this->getOutputStream(), 'What time is it?', '2PM')); + + rewind($output->getStream()); + $this->assertEquals('What time is it?', stream_get_contents($output->getStream())); + } + + public function testAskOnErrorOutput() + { + if (!$this->hasSttyAvailable()) { + $this->markTestSkipped('`stderr` is required to test stderr output functionality'); + } + + $dialog = new DialogHelper(); + + $dialog->setInputStream($this->getInputStream("not stdout\n")); + + $this->assertEquals('not stdout', $dialog->ask($output = $this->getConsoleOutput($this->getOutputStream()), 'Where should output go?', 'stderr')); + + rewind($output->getErrorOutput()->getStream()); + $this->assertEquals('Where should output go?', stream_get_contents($output->getErrorOutput()->getStream())); + } + + public function testAskWithAutocomplete() + { + if (!$this->hasSttyAvailable()) { + $this->markTestSkipped('`stty` is required to test autocomplete functionality'); + } + + // Acm<NEWLINE> + // Ac<BACKSPACE><BACKSPACE>s<TAB>Test<NEWLINE> + // <NEWLINE> + // <UP ARROW><UP ARROW><NEWLINE> + // <UP ARROW><UP ARROW><UP ARROW><UP ARROW><UP ARROW><TAB>Test<NEWLINE> + // <DOWN ARROW><NEWLINE> + // S<BACKSPACE><BACKSPACE><DOWN ARROW><DOWN ARROW><NEWLINE> + // F00<BACKSPACE><BACKSPACE>oo<TAB><NEWLINE> + $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\n"); + + $dialog = new DialogHelper(); + $dialog->setInputStream($inputStream); + + $bundles = array('AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle'); + + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('AsseticBundleTest', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('FrameworkBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('SecurityBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('FooBundleTest', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('AsseticBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('FooBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + } + + /** + * @group tty + */ + public function testAskHiddenResponse() + { + if ('\\' === DIRECTORY_SEPARATOR) { + $this->markTestSkipped('This test is not supported on Windows'); + } + + $dialog = new DialogHelper(); + + $dialog->setInputStream($this->getInputStream("8AM\n")); + + $this->assertEquals('8AM', $dialog->askHiddenResponse($this->getOutputStream(), 'What time is it?')); + } + + /** + * @group tty + */ + public function testAskHiddenResponseOnErrorOutput() + { + if ('\\' === DIRECTORY_SEPARATOR) { + $this->markTestSkipped('This test is not supported on Windows'); + } + + $dialog = new DialogHelper(); + + $dialog->setInputStream($this->getInputStream("8AM\n")); + + $this->assertEquals('8AM', $dialog->askHiddenResponse($output = $this->getConsoleOutput($this->getOutputStream()), 'What time is it?')); + + rewind($output->getErrorOutput()->getStream()); + $this->assertContains('What time is it?', stream_get_contents($output->getErrorOutput()->getStream())); + } + + public function testAskConfirmation() + { + $dialog = new DialogHelper(); + + $dialog->setInputStream($this->getInputStream("\n\n")); + $this->assertTrue($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?')); + $this->assertFalse($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', false)); + + $dialog->setInputStream($this->getInputStream("y\nyes\n")); + $this->assertTrue($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', false)); + $this->assertTrue($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', false)); + + $dialog->setInputStream($this->getInputStream("n\nno\n")); + $this->assertFalse($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', true)); + $this->assertFalse($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', true)); + } + + public function testAskAndValidate() + { + $dialog = new DialogHelper(); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = 'What color was the white horse of Henry IV?'; + $error = 'This is not a color!'; + $validator = function ($color) use ($error) { + if (!in_array($color, array('white', 'black'))) { + throw new InvalidArgumentException($error); + } + + return $color; + }; + + $dialog->setInputStream($this->getInputStream("\nblack\n")); + $this->assertEquals('white', $dialog->askAndValidate($this->getOutputStream(), $question, $validator, 2, 'white')); + $this->assertEquals('black', $dialog->askAndValidate($this->getOutputStream(), $question, $validator, 2, 'white')); + + $dialog->setInputStream($this->getInputStream("green\nyellow\norange\n")); + try { + $this->assertEquals('white', $dialog->askAndValidate($output = $this->getConsoleOutput($this->getOutputStream()), $question, $validator, 2, 'white')); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertEquals($error, $e->getMessage()); + rewind($output->getErrorOutput()->getStream()); + $this->assertContains('What color was the white horse of Henry IV?', stream_get_contents($output->getErrorOutput()->getStream())); + } + } + + public function testNoInteraction() + { + $dialog = new DialogHelper(); + + $input = new ArrayInput(array()); + $input->setInteractive(false); + + $dialog->setInput($input); + + $this->assertEquals('not yet', $dialog->ask($this->getOutputStream(), 'Do you have a job?', 'not yet')); + } + + protected function getInputStream($input) + { + $stream = fopen('php://memory', 'r+', false); + fwrite($stream, $input); + rewind($stream); + + return $stream; + } + + protected function getOutputStream() + { + return new StreamOutput(fopen('php://memory', 'r+', false)); + } + + protected function getConsoleOutput($stderr) + { + $output = new ConsoleOutput(); + $output->setErrorOutput($stderr); + + return $output; + } + + private function hasStderrSupport() + { + return false === $this->isRunningOS400(); + } + + private function hasSttyAvailable() + { + exec('stty 2>&1', $output, $exitcode); + + return $exitcode === 0; + } +} diff --git a/vendor/symfony/console/Tests/Helper/LegacyProgressHelperTest.php b/vendor/symfony/console/Tests/Helper/LegacyProgressHelperTest.php new file mode 100644 index 00000000..f835a71e --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/LegacyProgressHelperTest.php @@ -0,0 +1,224 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\ProgressHelper; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * @group legacy + * @group time-sensitive + */ +class LegacyProgressHelperTest extends \PHPUnit_Framework_TestCase +{ + public function testAdvance() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream()); + $progress->advance(); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 1 [->--------------------------]'), stream_get_contents($output->getStream())); + } + + public function testAdvanceWithStep() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream()); + $progress->advance(5); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 5 [----->----------------------]'), stream_get_contents($output->getStream())); + } + + public function testAdvanceMultipleTimes() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream()); + $progress->advance(3); + $progress->advance(2); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 3 [--->------------------------]').$this->generateOutput(' 5 [----->----------------------]'), stream_get_contents($output->getStream())); + } + + public function testCustomizations() + { + $progress = new ProgressHelper(); + $progress->setBarWidth(10); + $progress->setBarCharacter('_'); + $progress->setEmptyBarCharacter(' '); + $progress->setProgressCharacter('/'); + $progress->setFormat(' %current%/%max% [%bar%] %percent%%'); + $progress->start($output = $this->getOutputStream(), 10); + $progress->advance(); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 1/10 [_/ ] 10%'), stream_get_contents($output->getStream())); + } + + public function testPercent() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream(), 50); + $progress->display(); + $progress->advance(); + $progress->advance(); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 0/50 [>---------------------------] 0%').$this->generateOutput(' 1/50 [>---------------------------] 2%').$this->generateOutput(' 2/50 [=>--------------------------] 4%'), stream_get_contents($output->getStream())); + } + + public function testOverwriteWithShorterLine() + { + $progress = new ProgressHelper(); + $progress->setFormat(' %current%/%max% [%bar%] %percent%%'); + $progress->start($output = $this->getOutputStream(), 50); + $progress->display(); + $progress->advance(); + + // set shorter format + $progress->setFormat(' %current%/%max% [%bar%]'); + $progress->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 1/50 [>---------------------------] 2%'). + $this->generateOutput(' 2/50 [=>--------------------------] '), + stream_get_contents($output->getStream()) + ); + } + + public function testSetCurrentProgress() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream(), 50); + $progress->display(); + $progress->advance(); + $progress->setCurrent(15); + $progress->setCurrent(25); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 1/50 [>---------------------------] 2%'). + $this->generateOutput(' 15/50 [========>-------------------] 30%'). + $this->generateOutput(' 25/50 [==============>-------------] 50%'), + stream_get_contents($output->getStream()) + ); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage You must start the progress bar + */ + public function testSetCurrentBeforeStarting() + { + $progress = new ProgressHelper(); + $progress->setCurrent(15); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage You can't regress the progress bar + */ + public function testRegressProgress() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream(), 50); + $progress->setCurrent(15); + $progress->setCurrent(10); + } + + public function testRedrawFrequency() + { + $progress = $this->getMock('Symfony\Component\Console\Helper\ProgressHelper', array('display')); + $progress->expects($this->exactly(4)) + ->method('display'); + + $progress->setRedrawFrequency(2); + + $progress->start($output = $this->getOutputStream(), 6); + $progress->setCurrent(1); + $progress->advance(2); + $progress->advance(2); + $progress->advance(1); + } + + public function testMultiByteSupport() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream()); + $progress->setBarCharacter('â– '); + $progress->advance(3); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 3 [â– â– â– >------------------------]'), stream_get_contents($output->getStream())); + } + + public function testClear() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream(), 50); + $progress->setCurrent(25); + $progress->clear(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 25/50 [==============>-------------] 50%').$this->generateOutput(''), + stream_get_contents($output->getStream()) + ); + } + + public function testPercentNotHundredBeforeComplete() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream(), 200); + $progress->display(); + $progress->advance(199); + $progress->advance(); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 0/200 [>---------------------------] 0%').$this->generateOutput(' 199/200 [===========================>] 99%').$this->generateOutput(' 200/200 [============================] 100%'), stream_get_contents($output->getStream())); + } + + public function testNonDecoratedOutput() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream(false)); + $progress->advance(); + + rewind($output->getStream()); + $this->assertEquals('', stream_get_contents($output->getStream())); + } + + protected function getOutputStream($decorated = true) + { + return new StreamOutput(fopen('php://memory', 'r+', false), StreamOutput::VERBOSITY_NORMAL, $decorated); + } + + protected $lastMessagesLength; + + protected function generateOutput($expected) + { + $expectedout = $expected; + + if ($this->lastMessagesLength !== null) { + $expectedout = str_pad($expected, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT); + } + + $this->lastMessagesLength = strlen($expectedout); + + return "\x0D".$expectedout; + } +} diff --git a/vendor/symfony/console/Tests/Helper/LegacyTableHelperTest.php b/vendor/symfony/console/Tests/Helper/LegacyTableHelperTest.php new file mode 100644 index 00000000..557dc14f --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/LegacyTableHelperTest.php @@ -0,0 +1,316 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\TableHelper; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * @group legacy + */ +class LegacyTableHelperTest extends \PHPUnit_Framework_TestCase +{ + protected $stream; + + protected function setUp() + { + $this->stream = fopen('php://memory', 'r+'); + } + + protected function tearDown() + { + fclose($this->stream); + $this->stream = null; + } + + /** + * @dataProvider testRenderProvider + */ + public function testRender($headers, $rows, $layout, $expected) + { + $table = new TableHelper(); + $table + ->setHeaders($headers) + ->setRows($rows) + ->setLayout($layout) + ; + $table->render($output = $this->getOutputStream()); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + /** + * @dataProvider testRenderProvider + */ + public function testRenderAddRows($headers, $rows, $layout, $expected) + { + $table = new TableHelper(); + $table + ->setHeaders($headers) + ->addRows($rows) + ->setLayout($layout) + ; + $table->render($output = $this->getOutputStream()); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + /** + * @dataProvider testRenderProvider + */ + public function testRenderAddRowsOneByOne($headers, $rows, $layout, $expected) + { + $table = new TableHelper(); + $table + ->setHeaders($headers) + ->setLayout($layout) + ; + foreach ($rows as $row) { + $table->addRow($row); + } + $table->render($output = $this->getOutputStream()); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + public function testRenderProvider() + { + $books = array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), + array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), + ); + + return array( + array( + array('ISBN', 'Title', 'Author'), + $books, + TableHelper::LAYOUT_DEFAULT, +<<<'TABLE' ++---------------+--------------------------+------------------+ +| ISBN | Title | Author | ++---------------+--------------------------+------------------+ +| 99921-58-10-7 | Divine Comedy | Dante Alighieri | +| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | +| 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | +| 80-902734-1-6 | And Then There Were None | Agatha Christie | ++---------------+--------------------------+------------------+ + +TABLE + ), + array( + array('ISBN', 'Title', 'Author'), + $books, + TableHelper::LAYOUT_COMPACT, +<<<TABLE + ISBN Title Author + 99921-58-10-7 Divine Comedy Dante Alighieri + 9971-5-0210-0 A Tale of Two Cities Charles Dickens + 960-425-059-0 The Lord of the Rings J. R. R. Tolkien + 80-902734-1-6 And Then There Were None Agatha Christie + +TABLE + ), + array( + array('ISBN', 'Title', 'Author'), + $books, + TableHelper::LAYOUT_BORDERLESS, +<<<TABLE + =============== ========================== ================== + ISBN Title Author + =============== ========================== ================== + 99921-58-10-7 Divine Comedy Dante Alighieri + 9971-5-0210-0 A Tale of Two Cities Charles Dickens + 960-425-059-0 The Lord of the Rings J. R. R. Tolkien + 80-902734-1-6 And Then There Were None Agatha Christie + =============== ========================== ================== + +TABLE + ), + array( + array('ISBN', 'Title'), + array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0'), + array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), + array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), + ), + TableHelper::LAYOUT_DEFAULT, +<<<'TABLE' ++---------------+--------------------------+------------------+ +| ISBN | Title | | ++---------------+--------------------------+------------------+ +| 99921-58-10-7 | Divine Comedy | Dante Alighieri | +| 9971-5-0210-0 | | | +| 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | +| 80-902734-1-6 | And Then There Were None | Agatha Christie | ++---------------+--------------------------+------------------+ + +TABLE + ), + array( + array(), + array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0'), + array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), + array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), + ), + TableHelper::LAYOUT_DEFAULT, +<<<'TABLE' ++---------------+--------------------------+------------------+ +| 99921-58-10-7 | Divine Comedy | Dante Alighieri | +| 9971-5-0210-0 | | | +| 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | +| 80-902734-1-6 | And Then There Were None | Agatha Christie | ++---------------+--------------------------+------------------+ + +TABLE + ), + array( + array('ISBN', 'Title', 'Author'), + array( + array('99921-58-10-7', "Divine\nComedy", 'Dante Alighieri'), + array('9971-5-0210-2', "Harry Potter\nand the Chamber of Secrets", "Rowling\nJoanne K."), + array('9971-5-0210-2', "Harry Potter\nand the Chamber of Secrets", "Rowling\nJoanne K."), + array('960-425-059-0', 'The Lord of the Rings', "J. R. R.\nTolkien"), + ), + TableHelper::LAYOUT_DEFAULT, +<<<'TABLE' ++---------------+----------------------------+-----------------+ +| ISBN | Title | Author | ++---------------+----------------------------+-----------------+ +| 99921-58-10-7 | Divine | Dante Alighieri | +| | Comedy | | +| 9971-5-0210-2 | Harry Potter | Rowling | +| | and the Chamber of Secrets | Joanne K. | +| 9971-5-0210-2 | Harry Potter | Rowling | +| | and the Chamber of Secrets | Joanne K. | +| 960-425-059-0 | The Lord of the Rings | J. R. R. | +| | | Tolkien | ++---------------+----------------------------+-----------------+ + +TABLE + ), + array( + array('ISBN', 'Title'), + array(), + TableHelper::LAYOUT_DEFAULT, +<<<'TABLE' ++------+-------+ +| ISBN | Title | ++------+-------+ + +TABLE + ), + array( + array(), + array(), + TableHelper::LAYOUT_DEFAULT, + '', + ), + 'Cell text with tags used for Output styling' => array( + array('ISBN', 'Title', 'Author'), + array( + array('<info>99921-58-10-7</info>', '<error>Divine Comedy</error>', '<fg=blue;bg=white>Dante Alighieri</fg=blue;bg=white>'), + array('9971-5-0210-0', 'A Tale of Two Cities', '<info>Charles Dickens</>'), + ), + TableHelper::LAYOUT_DEFAULT, +<<<'TABLE' ++---------------+----------------------+-----------------+ +| ISBN | Title | Author | ++---------------+----------------------+-----------------+ +| 99921-58-10-7 | Divine Comedy | Dante Alighieri | +| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | ++---------------+----------------------+-----------------+ + +TABLE + ), + 'Cell text with tags not used for Output styling' => array( + array('ISBN', 'Title', 'Author'), + array( + array('<strong>99921-58-10-700</strong>', '<f>Divine Com</f>', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + ), + TableHelper::LAYOUT_DEFAULT, +<<<'TABLE' ++----------------------------------+----------------------+-----------------+ +| ISBN | Title | Author | ++----------------------------------+----------------------+-----------------+ +| <strong>99921-58-10-700</strong> | <f>Divine Com</f> | Dante Alighieri | +| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | ++----------------------------------+----------------------+-----------------+ + +TABLE + ), + ); + } + + public function testRenderMultiByte() + { + $table = new TableHelper(); + $table + ->setHeaders(array('â– â– ')) + ->setRows(array(array(1234))) + ->setLayout(TableHelper::LAYOUT_DEFAULT) + ; + $table->render($output = $this->getOutputStream()); + + $expected = +<<<'TABLE' ++------+ +| â– â– | ++------+ +| 1234 | ++------+ + +TABLE; + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + public function testRenderFullWidthCharacters() + { + $table = new TableHelper(); + $table + ->setHeaders(array('ã‚ã„ã†ãˆãŠ')) + ->setRows(array(array(1234567890))) + ->setLayout(TableHelper::LAYOUT_DEFAULT) + ; + $table->render($output = $this->getOutputStream()); + + $expected = + <<<'TABLE' ++------------+ +| ã‚ã„ã†ãˆãŠ | ++------------+ +| 1234567890 | ++------------+ + +TABLE; + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + protected function getOutputStream() + { + return new StreamOutput($this->stream, StreamOutput::VERBOSITY_NORMAL, false); + } + + protected function getOutputContent(StreamOutput $output) + { + rewind($output->getStream()); + + return str_replace(PHP_EOL, "\n", stream_get_contents($output->getStream())); + } +} diff --git a/vendor/symfony/console/Tests/Helper/ProcessHelperTest.php b/vendor/symfony/console/Tests/Helper/ProcessHelperTest.php new file mode 100644 index 00000000..a51fb435 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/ProcessHelperTest.php @@ -0,0 +1,117 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\DebugFormatterHelper; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Helper\ProcessHelper; +use Symfony\Component\Process\Process; + +class ProcessHelperTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider provideCommandsAndOutput + */ + public function testVariousProcessRuns($expected, $cmd, $verbosity, $error) + { + $helper = new ProcessHelper(); + $helper->setHelperSet(new HelperSet(array(new DebugFormatterHelper()))); + $output = $this->getOutputStream($verbosity); + $helper->run($output, $cmd, $error); + $this->assertEquals($expected, $this->getOutput($output)); + } + + public function testPassedCallbackIsExecuted() + { + $helper = new ProcessHelper(); + $helper->setHelperSet(new HelperSet(array(new DebugFormatterHelper()))); + $output = $this->getOutputStream(StreamOutput::VERBOSITY_NORMAL); + + $executed = false; + $callback = function () use (&$executed) { $executed = true; }; + + $helper->run($output, 'php -r "echo 42;"', null, $callback); + $this->assertTrue($executed); + } + + public function provideCommandsAndOutput() + { + $successOutputVerbose = <<<EOT + RUN php -r "echo 42;" + RES Command ran successfully + +EOT; + $successOutputDebug = <<<EOT + RUN php -r "echo 42;" + OUT 42 + RES Command ran successfully + +EOT; + $successOutputDebugWithTags = <<<EOT + RUN php -r "echo '<info>42</info>';" + OUT <info>42</info> + RES Command ran successfully + +EOT; + $successOutputProcessDebug = <<<EOT + RUN 'php' '-r' 'echo 42;' + OUT 42 + RES Command ran successfully + +EOT; + $syntaxErrorOutputVerbose = <<<EOT + RUN php -r "fwrite(STDERR, 'error message');usleep(50000);fwrite(STDOUT, 'out message');exit(252);" + RES 252 Command did not run successfully + +EOT; + $syntaxErrorOutputDebug = <<<EOT + RUN php -r "fwrite(STDERR, 'error message');usleep(500000);fwrite(STDOUT, 'out message');exit(252);" + ERR error message + OUT out message + RES 252 Command did not run successfully + +EOT; + + $errorMessage = 'An error occurred'; + if ('\\' === DIRECTORY_SEPARATOR) { + $successOutputProcessDebug = str_replace("'", '"', $successOutputProcessDebug); + } + + return array( + array('', 'php -r "echo 42;"', StreamOutput::VERBOSITY_VERBOSE, null), + array($successOutputVerbose, 'php -r "echo 42;"', StreamOutput::VERBOSITY_VERY_VERBOSE, null), + array($successOutputDebug, 'php -r "echo 42;"', StreamOutput::VERBOSITY_DEBUG, null), + array($successOutputDebugWithTags, 'php -r "echo \'<info>42</info>\';"', StreamOutput::VERBOSITY_DEBUG, null), + array('', 'php -r "syntax error"', StreamOutput::VERBOSITY_VERBOSE, null), + array($syntaxErrorOutputVerbose, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERY_VERBOSE, null), + array($syntaxErrorOutputDebug, 'php -r "fwrite(STDERR, \'error message\');usleep(500000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_DEBUG, null), + array($errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERBOSE, $errorMessage), + array($syntaxErrorOutputVerbose.$errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERY_VERBOSE, $errorMessage), + array($syntaxErrorOutputDebug.$errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(500000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_DEBUG, $errorMessage), + array($successOutputProcessDebug, array('php', '-r', 'echo 42;'), StreamOutput::VERBOSITY_DEBUG, null), + array($successOutputDebug, new Process('php -r "echo 42;"'), StreamOutput::VERBOSITY_DEBUG, null), + ); + } + + private function getOutputStream($verbosity) + { + return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, false); + } + + private function getOutput(StreamOutput $output) + { + rewind($output->getStream()); + + return stream_get_contents($output->getStream()); + } +} diff --git a/vendor/symfony/console/Tests/Helper/ProgressBarTest.php b/vendor/symfony/console/Tests/Helper/ProgressBarTest.php new file mode 100644 index 00000000..261908b5 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/ProgressBarTest.php @@ -0,0 +1,664 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * @group time-sensitive + */ +class ProgressBarTest extends \PHPUnit_Framework_TestCase +{ + public function testMultipleStart() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->advance(); + $bar->start(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'). + $this->generateOutput(' 1 [->--------------------------]'). + $this->generateOutput(' 0 [>---------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testAdvance() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'). + $this->generateOutput(' 1 [->--------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testAdvanceWithStep() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->advance(5); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'). + $this->generateOutput(' 5 [----->----------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testAdvanceMultipleTimes() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->advance(3); + $bar->advance(2); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'). + $this->generateOutput(' 3 [--->------------------------]'). + $this->generateOutput(' 5 [----->----------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testAdvanceOverMax() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->setProgress(9); + $bar->advance(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 9/10 [=========================>--] 90%'). + $this->generateOutput(' 10/10 [============================] 100%'). + $this->generateOutput(' 11/11 [============================] 100%'), + stream_get_contents($output->getStream()) + ); + } + + public function testFormat() + { + $expected = + $this->generateOutput(' 0/10 [>---------------------------] 0%'). + $this->generateOutput(' 10/10 [============================] 100%'). + $this->generateOutput(' 10/10 [============================] 100%') + ; + + // max in construct, no format + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->start(); + $bar->advance(10); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals($expected, stream_get_contents($output->getStream())); + + // max in start, no format + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(10); + $bar->advance(10); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals($expected, stream_get_contents($output->getStream())); + + // max in construct, explicit format before + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->setFormat('normal'); + $bar->start(); + $bar->advance(10); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals($expected, stream_get_contents($output->getStream())); + + // max in start, explicit format before + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat('normal'); + $bar->start(10); + $bar->advance(10); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals($expected, stream_get_contents($output->getStream())); + } + + public function testCustomizations() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->setBarWidth(10); + $bar->setBarCharacter('_'); + $bar->setEmptyBarCharacter(' '); + $bar->setProgressCharacter('/'); + $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%'); + $bar->start(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/10 [/ ] 0%'). + $this->generateOutput(' 1/10 [_/ ] 10%'), + stream_get_contents($output->getStream()) + ); + } + + public function testDisplayWithoutStart() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->display(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'), + stream_get_contents($output->getStream()) + ); + } + + public function testDisplayWithQuietVerbosity() + { + $bar = new ProgressBar($output = $this->getOutputStream(true, StreamOutput::VERBOSITY_QUIET), 50); + $bar->display(); + + rewind($output->getStream()); + $this->assertEquals( + '', + stream_get_contents($output->getStream()) + ); + } + + public function testFinishWithoutStart() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 50/50 [============================] 100%'), + stream_get_contents($output->getStream()) + ); + } + + public function testPercent() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->start(); + $bar->display(); + $bar->advance(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 1/50 [>---------------------------] 2%'). + $this->generateOutput(' 2/50 [=>--------------------------] 4%'), + stream_get_contents($output->getStream()) + ); + } + + public function testOverwriteWithShorterLine() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%'); + $bar->start(); + $bar->display(); + $bar->advance(); + + // set shorter format + $bar->setFormat(' %current%/%max% [%bar%]'); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 1/50 [>---------------------------] 2%'). + $this->generateOutput(' 2/50 [=>--------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testStartWithMax() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat('%current%/%max% [%bar%]'); + $bar->start(50); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------]'). + $this->generateOutput(' 1/50 [>---------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testSetCurrentProgress() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->start(); + $bar->display(); + $bar->advance(); + $bar->setProgress(15); + $bar->setProgress(25); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 1/50 [>---------------------------] 2%'). + $this->generateOutput(' 15/50 [========>-------------------] 30%'). + $this->generateOutput(' 25/50 [==============>-------------] 50%'), + stream_get_contents($output->getStream()) + ); + } + + /** + */ + public function testSetCurrentBeforeStarting() + { + $bar = new ProgressBar($this->getOutputStream()); + $bar->setProgress(15); + $this->assertNotNull($bar->getStartTime()); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage You can't regress the progress bar + */ + public function testRegressProgress() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->start(); + $bar->setProgress(15); + $bar->setProgress(10); + } + + public function testRedrawFrequency() + { + $bar = $this->getMock('Symfony\Component\Console\Helper\ProgressBar', array('display'), array($this->getOutputStream(), 6)); + $bar->expects($this->exactly(4))->method('display'); + + $bar->setRedrawFrequency(2); + $bar->start(); + $bar->setProgress(1); + $bar->advance(2); + $bar->advance(2); + $bar->advance(1); + } + + public function testRedrawFrequencyIsAtLeastOneIfZeroGiven() + { + $bar = $this->getMock('Symfony\Component\Console\Helper\ProgressBar', array('display'), array($this->getOutputStream())); + + $bar->expects($this->exactly(2))->method('display'); + $bar->setRedrawFrequency(0); + $bar->start(); + $bar->advance(); + } + + public function testRedrawFrequencyIsAtLeastOneIfSmallerOneGiven() + { + $bar = $this->getMock('Symfony\Component\Console\Helper\ProgressBar', array('display'), array($this->getOutputStream())); + + $bar->expects($this->exactly(2))->method('display'); + $bar->setRedrawFrequency(0.9); + $bar->start(); + $bar->advance(); + } + + public function testMultiByteSupport() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->setBarCharacter('â– '); + $bar->advance(3); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'). + $this->generateOutput(' 3 [â– â– â– >------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testClear() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->start(); + $bar->setProgress(25); + $bar->clear(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 25/50 [==============>-------------] 50%'). + $this->generateOutput(''), + stream_get_contents($output->getStream()) + ); + } + + public function testPercentNotHundredBeforeComplete() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 200); + $bar->start(); + $bar->display(); + $bar->advance(199); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/200 [>---------------------------] 0%'). + $this->generateOutput(' 0/200 [>---------------------------] 0%'). + $this->generateOutput(' 199/200 [===========================>] 99%'). + $this->generateOutput(' 200/200 [============================] 100%'), + stream_get_contents($output->getStream()) + ); + } + + public function testNonDecoratedOutput() + { + $bar = new ProgressBar($output = $this->getOutputStream(false), 200); + $bar->start(); + + for ($i = 0; $i < 200; ++$i) { + $bar->advance(); + } + + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/200 [>---------------------------] 0%'.PHP_EOL. + ' 20/200 [==>-------------------------] 10%'.PHP_EOL. + ' 40/200 [=====>----------------------] 20%'.PHP_EOL. + ' 60/200 [========>-------------------] 30%'.PHP_EOL. + ' 80/200 [===========>----------------] 40%'.PHP_EOL. + ' 100/200 [==============>-------------] 50%'.PHP_EOL. + ' 120/200 [================>-----------] 60%'.PHP_EOL. + ' 140/200 [===================>--------] 70%'.PHP_EOL. + ' 160/200 [======================>-----] 80%'.PHP_EOL. + ' 180/200 [=========================>--] 90%'.PHP_EOL. + ' 200/200 [============================] 100%', + stream_get_contents($output->getStream()) + ); + } + + public function testNonDecoratedOutputWithClear() + { + $bar = new ProgressBar($output = $this->getOutputStream(false), 50); + $bar->start(); + $bar->setProgress(25); + $bar->clear(); + $bar->setProgress(50); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/50 [>---------------------------] 0%'.PHP_EOL. + ' 25/50 [==============>-------------] 50%'.PHP_EOL. + ' 50/50 [============================] 100%', + stream_get_contents($output->getStream()) + ); + } + + public function testNonDecoratedOutputWithoutMax() + { + $bar = new ProgressBar($output = $this->getOutputStream(false)); + $bar->start(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0 [>---------------------------]'.PHP_EOL. + ' 1 [->--------------------------]', + stream_get_contents($output->getStream()) + ); + } + + public function testParallelBars() + { + $output = $this->getOutputStream(); + $bar1 = new ProgressBar($output, 2); + $bar2 = new ProgressBar($output, 3); + $bar2->setProgressCharacter('#'); + $bar3 = new ProgressBar($output); + + $bar1->start(); + $output->write("\n"); + $bar2->start(); + $output->write("\n"); + $bar3->start(); + + for ($i = 1; $i <= 3; ++$i) { + // up two lines + $output->write("\033[2A"); + if ($i <= 2) { + $bar1->advance(); + } + $output->write("\n"); + $bar2->advance(); + $output->write("\n"); + $bar3->advance(); + } + $output->write("\033[2A"); + $output->write("\n"); + $output->write("\n"); + $bar3->finish(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/2 [>---------------------------] 0%')."\n". + $this->generateOutput(' 0/3 [#---------------------------] 0%')."\n". + rtrim($this->generateOutput(' 0 [>---------------------------]')). + + "\033[2A". + $this->generateOutput(' 1/2 [==============>-------------] 50%')."\n". + $this->generateOutput(' 1/3 [=========#------------------] 33%')."\n". + rtrim($this->generateOutput(' 1 [->--------------------------]')). + + "\033[2A". + $this->generateOutput(' 2/2 [============================] 100%')."\n". + $this->generateOutput(' 2/3 [==================#---------] 66%')."\n". + rtrim($this->generateOutput(' 2 [-->-------------------------]')). + + "\033[2A". + "\n". + $this->generateOutput(' 3/3 [============================] 100%')."\n". + rtrim($this->generateOutput(' 3 [--->------------------------]')). + + "\033[2A". + "\n". + "\n". + rtrim($this->generateOutput(' 3 [============================]')), + stream_get_contents($output->getStream()) + ); + } + + public function testWithoutMax() + { + $output = $this->getOutputStream(); + + $bar = new ProgressBar($output); + $bar->start(); + $bar->advance(); + $bar->advance(); + $bar->advance(); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + rtrim($this->generateOutput(' 0 [>---------------------------]')). + rtrim($this->generateOutput(' 1 [->--------------------------]')). + rtrim($this->generateOutput(' 2 [-->-------------------------]')). + rtrim($this->generateOutput(' 3 [--->------------------------]')). + rtrim($this->generateOutput(' 3 [============================]')), + stream_get_contents($output->getStream()) + ); + } + + public function testAddingPlaceholderFormatter() + { + ProgressBar::setPlaceholderFormatterDefinition('remaining_steps', function (ProgressBar $bar) { + return $bar->getMaxSteps() - $bar->getProgress(); + }); + $bar = new ProgressBar($output = $this->getOutputStream(), 3); + $bar->setFormat(' %remaining_steps% [%bar%]'); + + $bar->start(); + $bar->advance(); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 3 [>---------------------------]'). + $this->generateOutput(' 2 [=========>------------------]'). + $this->generateOutput(' 0 [============================]'), + stream_get_contents($output->getStream()) + ); + } + + public function testMultilineFormat() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 3); + $bar->setFormat("%bar%\nfoobar"); + + $bar->start(); + $bar->advance(); + $bar->clear(); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(">---------------------------\nfoobar"). + $this->generateOutput("=========>------------------\nfoobar"). + "\x0D\x1B[2K\x1B[1A\x1B[2K". + $this->generateOutput("============================\nfoobar"), + stream_get_contents($output->getStream()) + ); + } + + public function testAnsiColorsAndEmojis() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 15); + ProgressBar::setPlaceholderFormatterDefinition('memory', function (ProgressBar $bar) { + static $i = 0; + $mem = 100000 * $i; + $colors = $i++ ? '41;37' : '44;37'; + + return "\033[".$colors.'m '.Helper::formatMemory($mem)." \033[0m"; + }); + $bar->setFormat(" \033[44;37m %title:-37s% \033[0m\n %current%/%max% %bar% %percent:3s%%\n ðŸ %remaining:-10s% %memory:37s%"); + $bar->setBarCharacter($done = "\033[32mâ—\033[0m"); + $bar->setEmptyBarCharacter($empty = "\033[31mâ—\033[0m"); + $bar->setProgressCharacter($progress = "\033[32m➤ \033[0m"); + + $bar->setMessage('Starting the demo... fingers crossed', 'title'); + $bar->start(); + $bar->setMessage('Looks good to me...', 'title'); + $bar->advance(4); + $bar->setMessage('Thanks, bye', 'title'); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput( + " \033[44;37m Starting the demo... fingers crossed \033[0m\n". + ' 0/15 '.$progress.str_repeat($empty, 26)." 0%\n". + " \xf0\x9f\x8f\x81 < 1 sec \033[44;37m 0 B \033[0m" + ). + $this->generateOutput( + " \033[44;37m Looks good to me... \033[0m\n". + ' 4/15 '.str_repeat($done, 7).$progress.str_repeat($empty, 19)." 26%\n". + " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 97 KiB \033[0m" + ). + $this->generateOutput( + " \033[44;37m Thanks, bye \033[0m\n". + ' 15/15 '.str_repeat($done, 28)." 100%\n". + " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 195 KiB \033[0m" + ), + stream_get_contents($output->getStream()) + ); + } + + public function testSetFormat() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat('normal'); + $bar->start(); + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'), + stream_get_contents($output->getStream()) + ); + + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->setFormat('normal'); + $bar->start(); + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/10 [>---------------------------] 0%'), + stream_get_contents($output->getStream()) + ); + } + + /** + * @dataProvider provideFormat + */ + public function testFormatsWithoutMax($format) + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat($format); + $bar->start(); + + rewind($output->getStream()); + $this->assertNotEmpty(stream_get_contents($output->getStream())); + } + + /** + * Provides each defined format. + * + * @return array + */ + public function provideFormat() + { + return array( + array('normal'), + array('verbose'), + array('very_verbose'), + array('debug'), + ); + } + + protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL) + { + return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated); + } + + protected function generateOutput($expected) + { + $count = substr_count($expected, "\n"); + + return "\x0D\x1B[2K".($count ? str_repeat("\x1B[1A\x1B[2K", $count) : '').$expected; + } +} diff --git a/vendor/symfony/console/Tests/Helper/ProgressIndicatorTest.php b/vendor/symfony/console/Tests/Helper/ProgressIndicatorTest.php new file mode 100644 index 00000000..19262526 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/ProgressIndicatorTest.php @@ -0,0 +1,182 @@ +<?php + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\ProgressIndicator; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * @group time-sensitive + */ +class ProgressIndicatorTest extends \PHPUnit_Framework_TestCase +{ + public function testDefaultIndicator() + { + $bar = new ProgressIndicator($output = $this->getOutputStream()); + $bar->start('Starting...'); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->setMessage('Advancing...'); + $bar->advance(); + $bar->finish('Done...'); + $bar->start('Starting Again...'); + usleep(101000); + $bar->advance(); + $bar->finish('Done Again...'); + + rewind($output->getStream()); + + $this->assertEquals( + $this->generateOutput(' - Starting...'). + $this->generateOutput(' \\ Starting...'). + $this->generateOutput(' | Starting...'). + $this->generateOutput(' / Starting...'). + $this->generateOutput(' - Starting...'). + $this->generateOutput(' \\ Starting...'). + $this->generateOutput(' \\ Advancing...'). + $this->generateOutput(' | Advancing...'). + $this->generateOutput(' | Done... '). + PHP_EOL. + $this->generateOutput(' - Starting Again...'). + $this->generateOutput(' \\ Starting Again...'). + $this->generateOutput(' \\ Done Again... '). + PHP_EOL, + stream_get_contents($output->getStream()) + ); + } + + public function testNonDecoratedOutput() + { + $bar = new ProgressIndicator($output = $this->getOutputStream(false)); + + $bar->start('Starting...'); + $bar->advance(); + $bar->advance(); + $bar->setMessage('Midway...'); + $bar->advance(); + $bar->advance(); + $bar->finish('Done...'); + + rewind($output->getStream()); + + $this->assertEquals( + ' Starting...'.PHP_EOL. + ' Midway... '.PHP_EOL. + ' Done... '.PHP_EOL.PHP_EOL, + stream_get_contents($output->getStream()) + ); + } + + public function testCustomIndicatorValues() + { + $bar = new ProgressIndicator($output = $this->getOutputStream(), null, 100, array('a', 'b', 'c')); + + $bar->start('Starting...'); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + + rewind($output->getStream()); + + $this->assertEquals( + $this->generateOutput(' a Starting...'). + $this->generateOutput(' b Starting...'). + $this->generateOutput(' c Starting...'). + $this->generateOutput(' a Starting...'), + stream_get_contents($output->getStream()) + ); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Must have at least 2 indicator value characters. + */ + public function testCannotSetInvalidIndicatorCharacters() + { + $bar = new ProgressIndicator($this->getOutputStream(), null, 100, array('1')); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Progress indicator already started. + */ + public function testCannotStartAlreadyStartedIndicator() + { + $bar = new ProgressIndicator($this->getOutputStream()); + $bar->start('Starting...'); + $bar->start('Starting Again.'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Progress indicator has not yet been started. + */ + public function testCannotAdvanceUnstartedIndicator() + { + $bar = new ProgressIndicator($this->getOutputStream()); + $bar->advance(); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Progress indicator has not yet been started. + */ + public function testCannotFinishUnstartedIndicator() + { + $bar = new ProgressIndicator($this->getOutputStream()); + $bar->finish('Finished'); + } + + /** + * @dataProvider provideFormat + */ + public function testFormats($format) + { + $bar = new ProgressIndicator($output = $this->getOutputStream(), $format); + $bar->start('Starting...'); + $bar->advance(); + + rewind($output->getStream()); + + $this->assertNotEmpty(stream_get_contents($output->getStream())); + } + + /** + * Provides each defined format. + * + * @return array + */ + public function provideFormat() + { + return array( + array('normal'), + array('verbose'), + array('very_verbose'), + array('debug'), + ); + } + + protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL) + { + return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated); + } + + protected function generateOutput($expected) + { + $count = substr_count($expected, "\n"); + + return "\x0D".($count ? sprintf("\033[%dA", $count) : '').$expected; + } +} diff --git a/vendor/symfony/console/Tests/Helper/QuestionHelperTest.php b/vendor/symfony/console/Tests/Helper/QuestionHelperTest.php new file mode 100644 index 00000000..6a4f8ace --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/QuestionHelperTest.php @@ -0,0 +1,435 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; + +/** + * @group tty + */ +class QuestionHelperTest extends \PHPUnit_Framework_TestCase +{ + public function testAskChoice() + { + $questionHelper = new QuestionHelper(); + + $helperSet = new HelperSet(array(new FormatterHelper())); + $questionHelper->setHelperSet($helperSet); + + $heroes = array('Superman', 'Batman', 'Spiderman'); + + $questionHelper->setInputStream($this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n")); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2'); + $question->setMaxAttempts(1); + // first answer is an empty answer, we're supposed to receive the default value + $this->assertEquals('Spiderman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); + $question->setMaxAttempts(1); + $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); + $question->setErrorMessage('Input "%s" is not a superhero!'); + $question->setMaxAttempts(2); + $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question)); + + rewind($output->getStream()); + $stream = stream_get_contents($output->getStream()); + $this->assertContains('Input "Fabien" is not a superhero!', $stream); + + try { + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1'); + $question->setMaxAttempts(1); + $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertEquals('Value "Fabien" is invalid', $e->getMessage()); + } + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null); + $question->setMaxAttempts(1); + $question->setMultiselect(true); + + $this->assertEquals(array('Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1'); + $question->setMaxAttempts(1); + $question->setMultiselect(true); + + $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 '); + $question->setMaxAttempts(1); + $question->setMultiselect(true); + + $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + public function testAsk() + { + $dialog = new QuestionHelper(); + + $dialog->setInputStream($this->getInputStream("\n8AM\n")); + + $question = new Question('What time is it?', '2PM'); + $this->assertEquals('2PM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new Question('What time is it?', '2PM'); + $this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question)); + + rewind($output->getStream()); + $this->assertEquals('What time is it?', stream_get_contents($output->getStream())); + } + + public function testAskWithAutocomplete() + { + if (!$this->hasSttyAvailable()) { + $this->markTestSkipped('`stty` is required to test autocomplete functionality'); + } + + // Acm<NEWLINE> + // Ac<BACKSPACE><BACKSPACE>s<TAB>Test<NEWLINE> + // <NEWLINE> + // <UP ARROW><UP ARROW><NEWLINE> + // <UP ARROW><UP ARROW><UP ARROW><UP ARROW><UP ARROW><TAB>Test<NEWLINE> + // <DOWN ARROW><NEWLINE> + // S<BACKSPACE><BACKSPACE><DOWN ARROW><DOWN ARROW><NEWLINE> + // F00<BACKSPACE><BACKSPACE>oo<TAB><NEWLINE> + $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\n"); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($inputStream); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new Question('Please select a bundle', 'FrameworkBundle'); + $question->setAutocompleterValues(array('AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle')); + + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AsseticBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('FrameworkBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('SecurityBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('FooBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('FooBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + public function testAskWithAutocompleteWithNonSequentialKeys() + { + if (!$this->hasSttyAvailable()) { + $this->markTestSkipped('`stty` is required to test autocomplete functionality'); + } + + // <UP ARROW><UP ARROW><NEWLINE><DOWN ARROW><DOWN ARROW><NEWLINE> + $inputStream = $this->getInputStream("\033[A\033[A\n\033[B\033[B\n"); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($inputStream); + $dialog->setHelperSet(new HelperSet(array(new FormatterHelper()))); + + $question = new ChoiceQuestion('Please select a bundle', array(1 => 'AcmeDemoBundle', 4 => 'AsseticBundle')); + $question->setMaxAttempts(1); + + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + public function testAskHiddenResponse() + { + if ('\\' === DIRECTORY_SEPARATOR) { + $this->markTestSkipped('This test is not supported on Windows'); + } + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream("8AM\n")); + + $question = new Question('What time is it?'); + $question->setHidden(true); + + $this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + /** + * @dataProvider getAskConfirmationData + */ + public function testAskConfirmation($question, $expected, $default = true) + { + $dialog = new QuestionHelper(); + + $dialog->setInputStream($this->getInputStream($question."\n")); + $question = new ConfirmationQuestion('Do you like French fries?', $default); + $this->assertEquals($expected, $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question), 'confirmation question should '.($expected ? 'pass' : 'cancel')); + } + + public function getAskConfirmationData() + { + return array( + array('', true), + array('', false, false), + array('y', true), + array('yes', true), + array('n', false), + array('no', false), + ); + } + + public function testAskConfirmationWithCustomTrueAnswer() + { + $dialog = new QuestionHelper(); + + $dialog->setInputStream($this->getInputStream("j\ny\n")); + $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i'); + $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i'); + $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + public function testAskAndValidate() + { + $dialog = new QuestionHelper(); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $error = 'This is not a color!'; + $validator = function ($color) use ($error) { + if (!in_array($color, array('white', 'black'))) { + throw new \InvalidArgumentException($error); + } + + return $color; + }; + + $question = new Question('What color was the white horse of Henry IV?', 'white'); + $question->setValidator($validator); + $question->setMaxAttempts(2); + + $dialog->setInputStream($this->getInputStream("\nblack\n")); + $this->assertEquals('white', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('black', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $dialog->setInputStream($this->getInputStream("green\nyellow\norange\n")); + try { + $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertEquals($error, $e->getMessage()); + } + } + + /** + * @dataProvider simpleAnswerProvider + */ + public function testSelectChoiceFromSimpleChoices($providedAnswer, $expectedValue) + { + $possibleChoices = array( + 'My environment 1', + 'My environment 2', + 'My environment 3', + ); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); + $question->setMaxAttempts(1); + $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + + $this->assertSame($expectedValue, $answer); + } + + public function simpleAnswerProvider() + { + return array( + array(0, 'My environment 1'), + array(1, 'My environment 2'), + array(2, 'My environment 3'), + array('My environment 1', 'My environment 1'), + array('My environment 2', 'My environment 2'), + array('My environment 3', 'My environment 3'), + ); + } + + /** + * @dataProvider mixedKeysChoiceListAnswerProvider + */ + public function testChoiceFromChoicelistWithMixedKeys($providedAnswer, $expectedValue) + { + $possibleChoices = array( + '0' => 'No environment', + '1' => 'My environment 1', + 'env_2' => 'My environment 2', + 3 => 'My environment 3', + ); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); + $question->setMaxAttempts(1); + $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + + $this->assertSame($expectedValue, $answer); + } + + public function mixedKeysChoiceListAnswerProvider() + { + return array( + array('0', '0'), + array('No environment', '0'), + array('1', '1'), + array('env_2', 'env_2'), + array(3, '3'), + array('My environment 1', '1'), + ); + } + + /** + * @dataProvider answerProvider + */ + public function testSelectChoiceFromChoiceList($providedAnswer, $expectedValue) + { + $possibleChoices = array( + 'env_1' => 'My environment 1', + 'env_2' => 'My environment', + 'env_3' => 'My environment', + ); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); + $question->setMaxAttempts(1); + $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + + $this->assertSame($expectedValue, $answer); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The provided answer is ambiguous. Value should be one of env_2 or env_3. + */ + public function testAmbiguousChoiceFromChoicelist() + { + $possibleChoices = array( + 'env_1' => 'My first environment', + 'env_2' => 'My environment', + 'env_3' => 'My environment', + ); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream("My environment\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); + $question->setMaxAttempts(1); + + $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + } + + public function answerProvider() + { + return array( + array('env_1', 'env_1'), + array('env_2', 'env_2'), + array('env_3', 'env_3'), + array('My environment 1', 'env_1'), + ); + } + + public function testNoInteraction() + { + $dialog = new QuestionHelper(); + $question = new Question('Do you have a job?', 'not yet'); + $this->assertEquals('not yet', $dialog->ask($this->createInputInterfaceMock(false), $this->createOutputInterface(), $question)); + } + + /** + * @requires function mb_strwidth + */ + public function testChoiceOutputFormattingQuestionForUtf8Keys() + { + $question = 'Lorem ipsum?'; + $possibleChoices = array( + 'foo' => 'foo', + 'żółw' => 'bar', + 'Å‚abÄ…dź' => 'baz', + ); + $outputShown = array( + $question, + ' [<info>foo </info>] foo', + ' [<info>żółw </info>] bar', + ' [<info>Å‚abÄ…dź</info>] baz', + ); + $output = $this->getMock('\Symfony\Component\Console\Output\OutputInterface'); + $output->method('getFormatter')->willReturn(new OutputFormatter()); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream("\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $output->expects($this->once())->method('writeln')->with($this->equalTo($outputShown)); + + $question = new ChoiceQuestion($question, $possibleChoices, 'foo'); + $dialog->ask($this->createInputInterfaceMock(), $output, $question); + } + + protected function getInputStream($input) + { + $stream = fopen('php://memory', 'r+', false); + fwrite($stream, $input); + rewind($stream); + + return $stream; + } + + protected function createOutputInterface() + { + return new StreamOutput(fopen('php://memory', 'r+', false)); + } + + protected function createInputInterfaceMock($interactive = true) + { + $mock = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $mock->expects($this->any()) + ->method('isInteractive') + ->will($this->returnValue($interactive)); + + return $mock; + } + + private function hasSttyAvailable() + { + exec('stty 2>&1', $output, $exitcode); + + return $exitcode === 0; + } +} diff --git a/vendor/symfony/console/Tests/Helper/TableStyleTest.php b/vendor/symfony/console/Tests/Helper/TableStyleTest.php new file mode 100644 index 00000000..587d8414 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/TableStyleTest.php @@ -0,0 +1,27 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\TableStyle; + +class TableStyleTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH). + */ + public function testSetPadTypeWithInvalidType() + { + $style = new TableStyle(); + $style->setPadType('TEST'); + } +} diff --git a/vendor/symfony/console/Tests/Helper/TableTest.php b/vendor/symfony/console/Tests/Helper/TableTest.php new file mode 100644 index 00000000..9ecb381a --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/TableTest.php @@ -0,0 +1,645 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Helper\TableStyle; +use Symfony\Component\Console\Helper\TableSeparator; +use Symfony\Component\Console\Helper\TableCell; +use Symfony\Component\Console\Output\StreamOutput; + +class TableTest extends \PHPUnit_Framework_TestCase +{ + protected $stream; + + protected function setUp() + { + $this->stream = fopen('php://memory', 'r+'); + } + + protected function tearDown() + { + fclose($this->stream); + $this->stream = null; + } + + /** + * @dataProvider testRenderProvider + */ + public function testRender($headers, $rows, $style, $expected) + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders($headers) + ->setRows($rows) + ->setStyle($style) + ; + $table->render(); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + /** + * @dataProvider testRenderProvider + */ + public function testRenderAddRows($headers, $rows, $style, $expected) + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders($headers) + ->addRows($rows) + ->setStyle($style) + ; + $table->render(); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + /** + * @dataProvider testRenderProvider + */ + public function testRenderAddRowsOneByOne($headers, $rows, $style, $expected) + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders($headers) + ->setStyle($style) + ; + foreach ($rows as $row) { + $table->addRow($row); + } + $table->render(); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + public function testRenderProvider() + { + $books = array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), + array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), + ); + + return array( + array( + array('ISBN', 'Title', 'Author'), + $books, + 'default', +<<<TABLE ++---------------+--------------------------+------------------+ +| ISBN | Title | Author | ++---------------+--------------------------+------------------+ +| 99921-58-10-7 | Divine Comedy | Dante Alighieri | +| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | +| 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | +| 80-902734-1-6 | And Then There Were None | Agatha Christie | ++---------------+--------------------------+------------------+ + +TABLE + ), + array( + array('ISBN', 'Title', 'Author'), + $books, + 'compact', +<<<TABLE + ISBN Title Author + 99921-58-10-7 Divine Comedy Dante Alighieri + 9971-5-0210-0 A Tale of Two Cities Charles Dickens + 960-425-059-0 The Lord of the Rings J. R. R. Tolkien + 80-902734-1-6 And Then There Were None Agatha Christie + +TABLE + ), + array( + array('ISBN', 'Title', 'Author'), + $books, + 'borderless', +<<<TABLE + =============== ========================== ================== + ISBN Title Author + =============== ========================== ================== + 99921-58-10-7 Divine Comedy Dante Alighieri + 9971-5-0210-0 A Tale of Two Cities Charles Dickens + 960-425-059-0 The Lord of the Rings J. R. R. Tolkien + 80-902734-1-6 And Then There Were None Agatha Christie + =============== ========================== ================== + +TABLE + ), + array( + array('ISBN', 'Title'), + array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0'), + array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), + array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), + ), + 'default', +<<<TABLE ++---------------+--------------------------+------------------+ +| ISBN | Title | | ++---------------+--------------------------+------------------+ +| 99921-58-10-7 | Divine Comedy | Dante Alighieri | +| 9971-5-0210-0 | | | +| 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | +| 80-902734-1-6 | And Then There Were None | Agatha Christie | ++---------------+--------------------------+------------------+ + +TABLE + ), + array( + array(), + array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0'), + array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), + array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), + ), + 'default', +<<<TABLE ++---------------+--------------------------+------------------+ +| 99921-58-10-7 | Divine Comedy | Dante Alighieri | +| 9971-5-0210-0 | | | +| 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | +| 80-902734-1-6 | And Then There Were None | Agatha Christie | ++---------------+--------------------------+------------------+ + +TABLE + ), + array( + array('ISBN', 'Title', 'Author'), + array( + array('99921-58-10-7', "Divine\nComedy", 'Dante Alighieri'), + array('9971-5-0210-2', "Harry Potter\nand the Chamber of Secrets", "Rowling\nJoanne K."), + array('9971-5-0210-2', "Harry Potter\nand the Chamber of Secrets", "Rowling\nJoanne K."), + array('960-425-059-0', 'The Lord of the Rings', "J. R. R.\nTolkien"), + ), + 'default', +<<<TABLE ++---------------+----------------------------+-----------------+ +| ISBN | Title | Author | ++---------------+----------------------------+-----------------+ +| 99921-58-10-7 | Divine | Dante Alighieri | +| | Comedy | | +| 9971-5-0210-2 | Harry Potter | Rowling | +| | and the Chamber of Secrets | Joanne K. | +| 9971-5-0210-2 | Harry Potter | Rowling | +| | and the Chamber of Secrets | Joanne K. | +| 960-425-059-0 | The Lord of the Rings | J. R. R. | +| | | Tolkien | ++---------------+----------------------------+-----------------+ + +TABLE + ), + array( + array('ISBN', 'Title'), + array(), + 'default', +<<<TABLE ++------+-------+ +| ISBN | Title | ++------+-------+ + +TABLE + ), + array( + array(), + array(), + 'default', + '', + ), + 'Cell text with tags used for Output styling' => array( + array('ISBN', 'Title', 'Author'), + array( + array('<info>99921-58-10-7</info>', '<error>Divine Comedy</error>', '<fg=blue;bg=white>Dante Alighieri</fg=blue;bg=white>'), + array('9971-5-0210-0', 'A Tale of Two Cities', '<info>Charles Dickens</>'), + ), + 'default', +<<<TABLE ++---------------+----------------------+-----------------+ +| ISBN | Title | Author | ++---------------+----------------------+-----------------+ +| 99921-58-10-7 | Divine Comedy | Dante Alighieri | +| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | ++---------------+----------------------+-----------------+ + +TABLE + ), + 'Cell text with tags not used for Output styling' => array( + array('ISBN', 'Title', 'Author'), + array( + array('<strong>99921-58-10-700</strong>', '<f>Divine Com</f>', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + ), + 'default', +<<<TABLE ++----------------------------------+----------------------+-----------------+ +| ISBN | Title | Author | ++----------------------------------+----------------------+-----------------+ +| <strong>99921-58-10-700</strong> | <f>Divine Com</f> | Dante Alighieri | +| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | ++----------------------------------+----------------------+-----------------+ + +TABLE + ), + 'Cell with colspan' => array( + array('ISBN', 'Title', 'Author'), + array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + new TableSeparator(), + array(new TableCell('Divine Comedy(Dante Alighieri)', array('colspan' => 3))), + new TableSeparator(), + array( + new TableCell('Arduino: A Quick-Start Guide', array('colspan' => 2)), + 'Mark Schmidt', + ), + new TableSeparator(), + array( + '9971-5-0210-0', + new TableCell("A Tale of \nTwo Cities", array('colspan' => 2)), + ), + new TableSeparator(), + array( + new TableCell('Cupiditate dicta atque porro, tempora exercitationem modi animi nulla nemo vel nihil!', array('colspan' => 3)), + ), + ), + 'default', +<<<TABLE ++-------------------------------+-------------------------------+-----------------------------+ +| ISBN | Title | Author | ++-------------------------------+-------------------------------+-----------------------------+ +| 99921-58-10-7 | Divine Comedy | Dante Alighieri | ++-------------------------------+-------------------------------+-----------------------------+ +| Divine Comedy(Dante Alighieri) | ++-------------------------------+-------------------------------+-----------------------------+ +| Arduino: A Quick-Start Guide | Mark Schmidt | ++-------------------------------+-------------------------------+-----------------------------+ +| 9971-5-0210-0 | A Tale of | +| | Two Cities | ++-------------------------------+-------------------------------+-----------------------------+ +| Cupiditate dicta atque porro, tempora exercitationem modi animi nulla nemo vel nihil! | ++-------------------------------+-------------------------------+-----------------------------+ + +TABLE + ), + 'Cell with rowspan' => array( + array('ISBN', 'Title', 'Author'), + array( + array( + new TableCell('9971-5-0210-0', array('rowspan' => 3)), + 'Divine Comedy', + 'Dante Alighieri', + ), + array('A Tale of Two Cities', 'Charles Dickens'), + array("The Lord of \nthe Rings", "J. R. \nR. Tolkien"), + new TableSeparator(), + array('80-902734-1-6', new TableCell("And Then \nThere \nWere None", array('rowspan' => 3)), 'Agatha Christie'), + array('80-902734-1-7', 'Test'), + ), + 'default', +<<<TABLE ++---------------+----------------------+-----------------+ +| ISBN | Title | Author | ++---------------+----------------------+-----------------+ +| 9971-5-0210-0 | Divine Comedy | Dante Alighieri | +| | A Tale of Two Cities | Charles Dickens | +| | The Lord of | J. R. | +| | the Rings | R. Tolkien | ++---------------+----------------------+-----------------+ +| 80-902734-1-6 | And Then | Agatha Christie | +| 80-902734-1-7 | There | Test | +| | Were None | | ++---------------+----------------------+-----------------+ + +TABLE + ), + 'Cell with rowspan and colspan' => array( + array('ISBN', 'Title', 'Author'), + array( + array( + new TableCell('9971-5-0210-0', array('rowspan' => 2, 'colspan' => 2)), + 'Dante Alighieri', + ), + array('Charles Dickens'), + new TableSeparator(), + array( + 'Dante Alighieri', + new TableCell('9971-5-0210-0', array('rowspan' => 3, 'colspan' => 2)), + ), + array('J. R. R. Tolkien'), + array('J. R. R'), + ), + 'default', +<<<TABLE ++------------------+---------+-----------------+ +| ISBN | Title | Author | ++------------------+---------+-----------------+ +| 9971-5-0210-0 | Dante Alighieri | +| | Charles Dickens | ++------------------+---------+-----------------+ +| Dante Alighieri | 9971-5-0210-0 | +| J. R. R. Tolkien | | +| J. R. R | | ++------------------+---------+-----------------+ + +TABLE + ), + 'Cell with rowspan and colspan contains new line break' => array( + array('ISBN', 'Title', 'Author'), + array( + array( + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + 'Dante Alighieri', + ), + array('Charles Dickens'), + new TableSeparator(), + array( + 'Dante Alighieri', + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + ), + array('Charles Dickens'), + new TableSeparator(), + array( + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + new TableCell("Dante \nAlighieri", array('rowspan' => 2, 'colspan' => 1)), + ), + ), + 'default', +<<<TABLE ++-----------------+-------+-----------------+ +| ISBN | Title | Author | ++-----------------+-------+-----------------+ +| 9971 | Dante Alighieri | +| -5- | Charles Dickens | +| 021 | | +| 0-0 | | ++-----------------+-------+-----------------+ +| Dante Alighieri | 9971 | +| Charles Dickens | -5- | +| | 021 | +| | 0-0 | ++-----------------+-------+-----------------+ +| 9971 | Dante | +| -5- | Alighieri | +| 021 | | +| 0-0 | | ++-----------------+-------+-----------------+ + +TABLE + ), + 'Cell with rowspan and colspan without using TableSeparator' => array( + array('ISBN', 'Title', 'Author'), + array( + array( + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + 'Dante Alighieri', + ), + array('Charles Dickens'), + array( + 'Dante Alighieri', + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + ), + array('Charles Dickens'), + ), + 'default', +<<<TABLE ++-----------------+-------+-----------------+ +| ISBN | Title | Author | ++-----------------+-------+-----------------+ +| 9971 | Dante Alighieri | +| -5- | Charles Dickens | +| 021 | | +| 0-0 | | +| Dante Alighieri | 9971 | +| Charles Dickens | -5- | +| | 021 | +| | 0-0 | ++-----------------+-------+-----------------+ + +TABLE + ), + 'Cell with rowspan and colspan with separator inside a rowspan' => array( + array('ISBN', 'Author'), + array( + array( + new TableCell('9971-5-0210-0', array('rowspan' => 3, 'colspan' => 1)), + 'Dante Alighieri', + ), + array(new TableSeparator()), + array('Charles Dickens'), + ), + 'default', +<<<TABLE ++---------------+-----------------+ +| ISBN | Author | ++---------------+-----------------+ +| 9971-5-0210-0 | Dante Alighieri | +| |-----------------| +| | Charles Dickens | ++---------------+-----------------+ + +TABLE + ), + 'Multiple header lines' => array( + array( + array(new TableCell('Main title', array('colspan' => 3))), + array('ISBN', 'Title', 'Author'), + ), + array(), + 'default', +<<<TABLE ++------+-------+--------+ +| Main title | ++------+-------+--------+ +| ISBN | Title | Author | ++------+-------+--------+ + +TABLE + ), + 'Row with multiple cells' => array( + array(), + array( + array( + new TableCell('1', array('colspan' => 3)), + new TableCell('2', array('colspan' => 2)), + new TableCell('3', array('colspan' => 2)), + new TableCell('4', array('colspan' => 2)), + ), + ), + 'default', +<<<TABLE ++---+--+--+---+--+---+--+---+--+ +| 1 | 2 | 3 | 4 | ++---+--+--+---+--+---+--+---+--+ + +TABLE + ), + ); + } + + public function testRenderMultiByte() + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders(array('â– â– ')) + ->setRows(array(array(1234))) + ->setStyle('default') + ; + $table->render(); + + $expected = +<<<TABLE ++------+ +| â– â– | ++------+ +| 1234 | ++------+ + +TABLE; + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + public function testStyle() + { + $style = new TableStyle(); + $style + ->setHorizontalBorderChar('.') + ->setVerticalBorderChar('.') + ->setCrossingChar('.') + ; + + Table::setStyleDefinition('dotfull', $style); + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders(array('Foo')) + ->setRows(array(array('Bar'))) + ->setStyle('dotfull'); + $table->render(); + + $expected = +<<<TABLE +....... +. Foo . +....... +. Bar . +....... + +TABLE; + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + public function testRowSeparator() + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders(array('Foo')) + ->setRows(array( + array('Bar1'), + new TableSeparator(), + array('Bar2'), + new TableSeparator(), + array('Bar3'), + )); + $table->render(); + + $expected = +<<<TABLE ++------+ +| Foo | ++------+ +| Bar1 | ++------+ +| Bar2 | ++------+ +| Bar3 | ++------+ + +TABLE; + + $this->assertEquals($expected, $this->getOutputContent($output)); + + $this->assertEquals($table, $table->addRow(new TableSeparator()), 'fluent interface on addRow() with a single TableSeparator() works'); + } + + public function testRenderMultiCalls() + { + $table = new Table($output = $this->getOutputStream()); + $table->setRows(array( + array(new TableCell('foo', array('colspan' => 2))), + )); + $table->render(); + $table->render(); + $table->render(); + + $expected = +<<<TABLE ++----+---+ +| foo | ++----+---+ ++----+---+ +| foo | ++----+---+ ++----+---+ +| foo | ++----+---+ + +TABLE; + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + public function testColumnStyle() + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders(array('ISBN', 'Title', 'Author', 'Price')) + ->setRows(array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'), + )); + + $style = new TableStyle(); + $style->setPadType(STR_PAD_LEFT); + $table->setColumnStyle(3, $style); + + $table->render(); + + $expected = + <<<TABLE ++---------------+----------------------+-----------------+--------+ +| ISBN | Title | Author | Price | ++---------------+----------------------+-----------------+--------+ +| 99921-58-10-7 | Divine Comedy | Dante Alighieri | 9.95 | +| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | 139.25 | ++---------------+----------------------+-----------------+--------+ + +TABLE; + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + protected function getOutputStream() + { + return new StreamOutput($this->stream, StreamOutput::VERBOSITY_NORMAL, false); + } + + protected function getOutputContent(StreamOutput $output) + { + rewind($output->getStream()); + + return str_replace(PHP_EOL, "\n", stream_get_contents($output->getStream())); + } +} diff --git a/vendor/symfony/console/Tests/Input/ArgvInputTest.php b/vendor/symfony/console/Tests/Input/ArgvInputTest.php new file mode 100644 index 00000000..d2c540e6 --- /dev/null +++ b/vendor/symfony/console/Tests/Input/ArgvInputTest.php @@ -0,0 +1,317 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class ArgvInputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $_SERVER['argv'] = array('cli.php', 'foo'); + $input = new ArgvInput(); + $r = new \ReflectionObject($input); + $p = $r->getProperty('tokens'); + $p->setAccessible(true); + + $this->assertEquals(array('foo'), $p->getValue($input), '__construct() automatically get its input from the argv server variable'); + } + + public function testParseArguments() + { + $input = new ArgvInput(array('cli.php', 'foo')); + $input->bind(new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() parses required arguments'); + + $input->bind(new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() is stateless'); + } + + /** + * @dataProvider provideOptions + */ + public function testParseOptions($input, $options, $expectedOptions, $message) + { + $input = new ArgvInput($input); + $input->bind(new InputDefinition($options)); + + $this->assertEquals($expectedOptions, $input->getOptions(), $message); + } + + public function provideOptions() + { + return array( + array( + array('cli.php', '--foo'), + array(new InputOption('foo')), + array('foo' => true), + '->parse() parses long options without a value', + ), + array( + array('cli.php', '--foo=bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), + array('foo' => 'bar'), + '->parse() parses long options with a required value (with a = separator)', + ), + array( + array('cli.php', '--foo', 'bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), + array('foo' => 'bar'), + '->parse() parses long options with a required value (with a space separator)', + ), + array( + array('cli.php', '-f'), + array(new InputOption('foo', 'f')), + array('foo' => true), + '->parse() parses short options without a value', + ), + array( + array('cli.php', '-fbar'), + array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), + array('foo' => 'bar'), + '->parse() parses short options with a required value (with no separator)', + ), + array( + array('cli.php', '-f', 'bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), + array('foo' => 'bar'), + '->parse() parses short options with a required value (with a space separator)', + ), + array( + array('cli.php', '-f', ''), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)), + array('foo' => ''), + '->parse() parses short options with an optional empty value', + ), + array( + array('cli.php', '-f', '', 'foo'), + array(new InputArgument('name'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)), + array('foo' => ''), + '->parse() parses short options with an optional empty value followed by an argument', + ), + array( + array('cli.php', '-f', '', '-b'), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b')), + array('foo' => '', 'bar' => true), + '->parse() parses short options with an optional empty value followed by an option', + ), + array( + array('cli.php', '-f', '-b', 'foo'), + array(new InputArgument('name'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b')), + array('foo' => null, 'bar' => true), + '->parse() parses short options with an optional value which is not present', + ), + array( + array('cli.php', '-fb'), + array(new InputOption('foo', 'f'), new InputOption('bar', 'b')), + array('foo' => true, 'bar' => true), + '->parse() parses short options when they are aggregated as a single one', + ), + array( + array('cli.php', '-fb', 'bar'), + array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_REQUIRED)), + array('foo' => true, 'bar' => 'bar'), + '->parse() parses short options when they are aggregated as a single one and the last one has a required value', + ), + array( + array('cli.php', '-fb', 'bar'), + array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)), + array('foo' => true, 'bar' => 'bar'), + '->parse() parses short options when they are aggregated as a single one and the last one has an optional value', + ), + array( + array('cli.php', '-fbbar'), + array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)), + array('foo' => true, 'bar' => 'bar'), + '->parse() parses short options when they are aggregated as a single one and the last one has an optional value with no separator', + ), + array( + array('cli.php', '-fbbar'), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)), + array('foo' => 'bbar', 'bar' => null), + '->parse() parses short options when they are aggregated as a single one and one of them takes a value', + ), + ); + } + + /** + * @dataProvider provideInvalidInput + */ + public function testInvalidInput($argv, $definition, $expectedExceptionMessage) + { + $this->setExpectedException('RuntimeException', $expectedExceptionMessage); + + $input = new ArgvInput($argv); + $input->bind($definition); + } + + public function provideInvalidInput() + { + return array( + array( + array('cli.php', '--foo'), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), + 'The "--foo" option requires a value.', + ), + array( + array('cli.php', '-f'), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), + 'The "--foo" option requires a value.', + ), + array( + array('cli.php', '-ffoo'), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_NONE))), + 'The "-o" option does not exist.', + ), + array( + array('cli.php', '--foo=bar'), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_NONE))), + 'The "--foo" option does not accept a value.', + ), + array( + array('cli.php', 'foo', 'bar'), + new InputDefinition(), + 'Too many arguments.', + ), + array( + array('cli.php', '--foo'), + new InputDefinition(), + 'The "--foo" option does not exist.', + ), + array( + array('cli.php', '-f'), + new InputDefinition(), + 'The "-f" option does not exist.', + ), + array( + array('cli.php', '-1'), + new InputDefinition(array(new InputArgument('number'))), + 'The "-1" option does not exist.', + ), + ); + } + + public function testParseArrayArgument() + { + $input = new ArgvInput(array('cli.php', 'foo', 'bar', 'baz', 'bat')); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::IS_ARRAY)))); + + $this->assertEquals(array('name' => array('foo', 'bar', 'baz', 'bat')), $input->getArguments(), '->parse() parses array arguments'); + } + + public function testParseArrayOption() + { + $input = new ArgvInput(array('cli.php', '--name=foo', '--name=bar', '--name=baz')); + $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)))); + + $this->assertEquals(array('name' => array('foo', 'bar', 'baz')), $input->getOptions(), '->parse() parses array options ("--option=value" syntax)'); + + $input = new ArgvInput(array('cli.php', '--name', 'foo', '--name', 'bar', '--name', 'baz')); + $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)))); + $this->assertEquals(array('name' => array('foo', 'bar', 'baz')), $input->getOptions(), '->parse() parses array options ("--option value" syntax)'); + + $input = new ArgvInput(array('cli.php', '--name=foo', '--name=bar', '--name=')); + $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)))); + $this->assertSame(array('name' => array('foo', 'bar', null)), $input->getOptions(), '->parse() parses empty array options as null ("--option=value" syntax)'); + + $input = new ArgvInput(array('cli.php', '--name', 'foo', '--name', 'bar', '--name', '--anotherOption')); + $input->bind(new InputDefinition(array( + new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY), + new InputOption('anotherOption', null, InputOption::VALUE_NONE), + ))); + $this->assertSame(array('name' => array('foo', 'bar', null), 'anotherOption' => true), $input->getOptions(), '->parse() parses empty array options as null ("--option value" syntax)'); + } + + public function testParseNegativeNumberAfterDoubleDash() + { + $input = new ArgvInput(array('cli.php', '--', '-1')); + $input->bind(new InputDefinition(array(new InputArgument('number')))); + $this->assertEquals(array('number' => '-1'), $input->getArguments(), '->parse() parses arguments with leading dashes as arguments after having encountered a double-dash sequence'); + + $input = new ArgvInput(array('cli.php', '-f', 'bar', '--', '-1')); + $input->bind(new InputDefinition(array(new InputArgument('number'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)))); + $this->assertEquals(array('foo' => 'bar'), $input->getOptions(), '->parse() parses arguments with leading dashes as options before having encountered a double-dash sequence'); + $this->assertEquals(array('number' => '-1'), $input->getArguments(), '->parse() parses arguments with leading dashes as arguments after having encountered a double-dash sequence'); + } + + public function testParseEmptyStringArgument() + { + $input = new ArgvInput(array('cli.php', '-f', 'bar', '')); + $input->bind(new InputDefinition(array(new InputArgument('empty'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)))); + + $this->assertEquals(array('empty' => ''), $input->getArguments(), '->parse() parses empty string arguments'); + } + + public function testGetFirstArgument() + { + $input = new ArgvInput(array('cli.php', '-fbbar')); + $this->assertNull($input->getFirstArgument(), '->getFirstArgument() returns null when there is no arguments'); + + $input = new ArgvInput(array('cli.php', '-fbbar', 'foo')); + $this->assertEquals('foo', $input->getFirstArgument(), '->getFirstArgument() returns the first argument from the raw input'); + } + + public function testHasParameterOption() + { + $input = new ArgvInput(array('cli.php', '-f', 'foo')); + $this->assertTrue($input->hasParameterOption('-f'), '->hasParameterOption() returns true if the given short option is in the raw input'); + + $input = new ArgvInput(array('cli.php', '--foo', 'foo')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if the given short option is in the raw input'); + + $input = new ArgvInput(array('cli.php', 'foo')); + $this->assertFalse($input->hasParameterOption('--foo'), '->hasParameterOption() returns false if the given short option is not in the raw input'); + + $input = new ArgvInput(array('cli.php', '--foo=bar')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if the given option with provided value is in the raw input'); + } + + public function testToString() + { + $input = new ArgvInput(array('cli.php', '-f', 'foo')); + $this->assertEquals('-f foo', (string) $input); + + $input = new ArgvInput(array('cli.php', '-f', '--bar=foo', 'a b c d', "A\nB'C")); + $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d').' '.escapeshellarg("A\nB'C"), (string) $input); + } + + /** + * @dataProvider provideGetParameterOptionValues + */ + public function testGetParameterOptionEqualSign($argv, $key, $expected) + { + $input = new ArgvInput($argv); + $this->assertEquals($expected, $input->getParameterOption($key), '->getParameterOption() returns the expected value'); + } + + public function provideGetParameterOptionValues() + { + return array( + array(array('app/console', 'foo:bar', '-e', 'dev'), '-e', 'dev'), + array(array('app/console', 'foo:bar', '--env=dev'), '--env', 'dev'), + array(array('app/console', 'foo:bar', '-e', 'dev'), array('-e', '--env'), 'dev'), + array(array('app/console', 'foo:bar', '--env=dev'), array('-e', '--env'), 'dev'), + array(array('app/console', 'foo:bar', '--env=dev', '--en=1'), array('--en'), '1'), + array(array('app/console', 'foo:bar', '--env=dev', '', '--en=1'), array('--en'), '1'), + ); + } + + public function testParseSingleDashAsArgument() + { + $input = new ArgvInput(array('cli.php', '-')); + $input->bind(new InputDefinition(array(new InputArgument('file')))); + $this->assertEquals(array('file' => '-'), $input->getArguments(), '->parse() parses single dash as an argument'); + } +} diff --git a/vendor/symfony/console/Tests/Input/ArrayInputTest.php b/vendor/symfony/console/Tests/Input/ArrayInputTest.php new file mode 100644 index 00000000..cc89083c --- /dev/null +++ b/vendor/symfony/console/Tests/Input/ArrayInputTest.php @@ -0,0 +1,138 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class ArrayInputTest extends \PHPUnit_Framework_TestCase +{ + public function testGetFirstArgument() + { + $input = new ArrayInput(array()); + $this->assertNull($input->getFirstArgument(), '->getFirstArgument() returns null if no argument were passed'); + $input = new ArrayInput(array('name' => 'Fabien')); + $this->assertEquals('Fabien', $input->getFirstArgument(), '->getFirstArgument() returns the first passed argument'); + $input = new ArrayInput(array('--foo' => 'bar', 'name' => 'Fabien')); + $this->assertEquals('Fabien', $input->getFirstArgument(), '->getFirstArgument() returns the first passed argument'); + } + + public function testHasParameterOption() + { + $input = new ArrayInput(array('name' => 'Fabien', '--foo' => 'bar')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if an option is present in the passed parameters'); + $this->assertFalse($input->hasParameterOption('--bar'), '->hasParameterOption() returns false if an option is not present in the passed parameters'); + + $input = new ArrayInput(array('--foo')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if an option is present in the passed parameters'); + } + + public function testGetParameterOption() + { + $input = new ArrayInput(array('name' => 'Fabien', '--foo' => 'bar')); + $this->assertEquals('bar', $input->getParameterOption('--foo'), '->getParameterOption() returns the option of specified name'); + + $input = new ArrayInput(array('Fabien', '--foo' => 'bar')); + $this->assertEquals('bar', $input->getParameterOption('--foo'), '->getParameterOption() returns the option of specified name'); + } + + public function testParseArguments() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); + + $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() parses required arguments'); + } + + /** + * @dataProvider provideOptions + */ + public function testParseOptions($input, $options, $expectedOptions, $message) + { + $input = new ArrayInput($input, new InputDefinition($options)); + + $this->assertEquals($expectedOptions, $input->getOptions(), $message); + } + + public function provideOptions() + { + return array( + array( + array('--foo' => 'bar'), + array(new InputOption('foo')), + array('foo' => 'bar'), + '->parse() parses long options', + ), + array( + array('--foo' => 'bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')), + array('foo' => 'bar'), + '->parse() parses long options with a default value', + ), + array( + array('--foo' => null), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')), + array('foo' => 'default'), + '->parse() parses long options with a default value', + ), + array( + array('-f' => 'bar'), + array(new InputOption('foo', 'f')), + array('foo' => 'bar'), + '->parse() parses short options', + ), + ); + } + + /** + * @dataProvider provideInvalidInput + */ + public function testParseInvalidInput($parameters, $definition, $expectedExceptionMessage) + { + $this->setExpectedException('InvalidArgumentException', $expectedExceptionMessage); + + new ArrayInput($parameters, $definition); + } + + public function provideInvalidInput() + { + return array( + array( + array('foo' => 'foo'), + new InputDefinition(array(new InputArgument('name'))), + 'The "foo" argument does not exist.', + ), + array( + array('--foo' => null), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), + 'The "--foo" option requires a value.', + ), + array( + array('--foo' => 'foo'), + new InputDefinition(), + 'The "--foo" option does not exist.', + ), + array( + array('-o' => 'foo'), + new InputDefinition(), + 'The "-o" option does not exist.', + ), + ); + } + + public function testToString() + { + $input = new ArrayInput(array('-f' => null, '-b' => 'bar', '--foo' => 'b a z', '--lala' => null, 'test' => 'Foo', 'test2' => "A\nB'C")); + $this->assertEquals('-f -b=bar --foo='.escapeshellarg('b a z').' --lala Foo '.escapeshellarg("A\nB'C"), (string) $input); + } +} diff --git a/vendor/symfony/console/Tests/Input/InputArgumentTest.php b/vendor/symfony/console/Tests/Input/InputArgumentTest.php new file mode 100644 index 00000000..cfb37cd4 --- /dev/null +++ b/vendor/symfony/console/Tests/Input/InputArgumentTest.php @@ -0,0 +1,111 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputArgument; + +class InputArgumentTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $argument = new InputArgument('foo'); + $this->assertEquals('foo', $argument->getName(), '__construct() takes a name as its first argument'); + } + + public function testModes() + { + $argument = new InputArgument('foo'); + $this->assertFalse($argument->isRequired(), '__construct() gives a "InputArgument::OPTIONAL" mode by default'); + + $argument = new InputArgument('foo', null); + $this->assertFalse($argument->isRequired(), '__construct() can take "InputArgument::OPTIONAL" as its mode'); + + $argument = new InputArgument('foo', InputArgument::OPTIONAL); + $this->assertFalse($argument->isRequired(), '__construct() can take "InputArgument::OPTIONAL" as its mode'); + + $argument = new InputArgument('foo', InputArgument::REQUIRED); + $this->assertTrue($argument->isRequired(), '__construct() can take "InputArgument::REQUIRED" as its mode'); + } + + /** + * @dataProvider provideInvalidModes + */ + public function testInvalidModes($mode) + { + $this->setExpectedException('InvalidArgumentException', sprintf('Argument mode "%s" is not valid.', $mode)); + + new InputArgument('foo', $mode); + } + + public function provideInvalidModes() + { + return array( + array('ANOTHER_ONE'), + array(-1), + ); + } + + public function testIsArray() + { + $argument = new InputArgument('foo', InputArgument::IS_ARRAY); + $this->assertTrue($argument->isArray(), '->isArray() returns true if the argument can be an array'); + $argument = new InputArgument('foo', InputArgument::OPTIONAL | InputArgument::IS_ARRAY); + $this->assertTrue($argument->isArray(), '->isArray() returns true if the argument can be an array'); + $argument = new InputArgument('foo', InputArgument::OPTIONAL); + $this->assertFalse($argument->isArray(), '->isArray() returns false if the argument can not be an array'); + } + + public function testGetDescription() + { + $argument = new InputArgument('foo', null, 'Some description'); + $this->assertEquals('Some description', $argument->getDescription(), '->getDescription() return the message description'); + } + + public function testGetDefault() + { + $argument = new InputArgument('foo', InputArgument::OPTIONAL, '', 'default'); + $this->assertEquals('default', $argument->getDefault(), '->getDefault() return the default value'); + } + + public function testSetDefault() + { + $argument = new InputArgument('foo', InputArgument::OPTIONAL, '', 'default'); + $argument->setDefault(null); + $this->assertNull($argument->getDefault(), '->setDefault() can reset the default value by passing null'); + $argument->setDefault('another'); + $this->assertEquals('another', $argument->getDefault(), '->setDefault() changes the default value'); + + $argument = new InputArgument('foo', InputArgument::OPTIONAL | InputArgument::IS_ARRAY); + $argument->setDefault(array(1, 2)); + $this->assertEquals(array(1, 2), $argument->getDefault(), '->setDefault() changes the default value'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot set a default value except for InputArgument::OPTIONAL mode. + */ + public function testSetDefaultWithRequiredArgument() + { + $argument = new InputArgument('foo', InputArgument::REQUIRED); + $argument->setDefault('default'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage A default value for an array argument must be an array. + */ + public function testSetDefaultWithArrayArgument() + { + $argument = new InputArgument('foo', InputArgument::IS_ARRAY); + $argument->setDefault('default'); + } +} diff --git a/vendor/symfony/console/Tests/Input/InputDefinitionTest.php b/vendor/symfony/console/Tests/Input/InputDefinitionTest.php new file mode 100644 index 00000000..7e0a2425 --- /dev/null +++ b/vendor/symfony/console/Tests/Input/InputDefinitionTest.php @@ -0,0 +1,437 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class InputDefinitionTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixtures; + + protected $foo, $bar, $foo1, $foo2; + + public static function setUpBeforeClass() + { + self::$fixtures = __DIR__.'/../Fixtures/'; + } + + public function testConstructorArguments() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $this->assertEquals(array(), $definition->getArguments(), '__construct() creates a new InputDefinition object'); + + $definition = new InputDefinition(array($this->foo, $this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '__construct() takes an array of InputArgument objects as its first argument'); + } + + public function testConstructorOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $this->assertEquals(array(), $definition->getOptions(), '__construct() creates a new InputDefinition object'); + + $definition = new InputDefinition(array($this->foo, $this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '__construct() takes an array of InputOption objects as its first argument'); + } + + public function testSetArguments() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->setArguments(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->setArguments() sets the array of InputArgument objects'); + $definition->setArguments(array($this->bar)); + + $this->assertEquals(array('bar' => $this->bar), $definition->getArguments(), '->setArguments() clears all InputArgument objects'); + } + + public function testAddArguments() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->addArguments() adds an array of InputArgument objects'); + $definition->addArguments(array($this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '->addArguments() does not clear existing InputArgument objects'); + } + + public function testAddArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo); + $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->addArgument() adds a InputArgument object'); + $definition->addArgument($this->bar); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '->addArgument() adds a InputArgument object'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage An argument with name "foo" already exists. + */ + public function testArgumentsMustHaveDifferentNames() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo); + $definition->addArgument($this->foo1); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot add an argument after an array argument. + */ + public function testArrayArgumentHasToBeLast() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument(new InputArgument('fooarray', InputArgument::IS_ARRAY)); + $definition->addArgument(new InputArgument('anotherbar')); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot add a required argument after an optional one. + */ + public function testRequiredArgumentCannotFollowAnOptionalOne() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo); + $definition->addArgument($this->foo2); + } + + public function testGetArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + $this->assertEquals($this->foo, $definition->getArgument('foo'), '->getArgument() returns a InputArgument by its name'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "bar" argument does not exist. + */ + public function testGetInvalidArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + $definition->getArgument('bar'); + } + + public function testHasArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + + $this->assertTrue($definition->hasArgument('foo'), '->hasArgument() returns true if a InputArgument exists for the given name'); + $this->assertFalse($definition->hasArgument('bar'), '->hasArgument() returns false if a InputArgument exists for the given name'); + } + + public function testGetArgumentRequiredCount() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo2); + $this->assertEquals(1, $definition->getArgumentRequiredCount(), '->getArgumentRequiredCount() returns the number of required arguments'); + $definition->addArgument($this->foo); + $this->assertEquals(1, $definition->getArgumentRequiredCount(), '->getArgumentRequiredCount() returns the number of required arguments'); + } + + public function testGetArgumentCount() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo2); + $this->assertEquals(1, $definition->getArgumentCount(), '->getArgumentCount() returns the number of arguments'); + $definition->addArgument($this->foo); + $this->assertEquals(2, $definition->getArgumentCount(), '->getArgumentCount() returns the number of arguments'); + } + + public function testGetArgumentDefaults() + { + $definition = new InputDefinition(array( + new InputArgument('foo1', InputArgument::OPTIONAL), + new InputArgument('foo2', InputArgument::OPTIONAL, '', 'default'), + new InputArgument('foo3', InputArgument::OPTIONAL | InputArgument::IS_ARRAY), + // new InputArgument('foo4', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, '', array(1, 2)), + )); + $this->assertEquals(array('foo1' => null, 'foo2' => 'default', 'foo3' => array()), $definition->getArgumentDefaults(), '->getArgumentDefaults() return the default values for each argument'); + + $definition = new InputDefinition(array( + new InputArgument('foo4', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, '', array(1, 2)), + )); + $this->assertEquals(array('foo4' => array(1, 2)), $definition->getArgumentDefaults(), '->getArgumentDefaults() return the default values for each argument'); + } + + public function testSetOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->setOptions() sets the array of InputOption objects'); + $definition->setOptions(array($this->bar)); + $this->assertEquals(array('bar' => $this->bar), $definition->getOptions(), '->setOptions() clears all InputOption objects'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "-f" option does not exist. + */ + public function testSetOptionsClearsOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $definition->setOptions(array($this->bar)); + $definition->getOptionForShortcut('f'); + } + + public function testAddOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->addOptions() adds an array of InputOption objects'); + $definition->addOptions(array($this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '->addOptions() does not clear existing InputOption objects'); + } + + public function testAddOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $definition->addOption($this->foo); + $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->addOption() adds a InputOption object'); + $definition->addOption($this->bar); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '->addOption() adds a InputOption object'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage An option named "foo" already exists. + */ + public function testAddDuplicateOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $definition->addOption($this->foo); + $definition->addOption($this->foo2); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage An option with shortcut "f" already exists. + */ + public function testAddDuplicateShortcutOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $definition->addOption($this->foo); + $definition->addOption($this->foo1); + } + + public function testGetOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals($this->foo, $definition->getOption('foo'), '->getOption() returns a InputOption by its name'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "--bar" option does not exist. + */ + public function testGetInvalidOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $definition->getOption('bar'); + } + + public function testHasOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertTrue($definition->hasOption('foo'), '->hasOption() returns true if a InputOption exists for the given name'); + $this->assertFalse($definition->hasOption('bar'), '->hasOption() returns false if a InputOption exists for the given name'); + } + + public function testHasShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertTrue($definition->hasShortcut('f'), '->hasShortcut() returns true if a InputOption exists for the given shortcut'); + $this->assertFalse($definition->hasShortcut('b'), '->hasShortcut() returns false if a InputOption exists for the given shortcut'); + } + + public function testGetOptionForShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals($this->foo, $definition->getOptionForShortcut('f'), '->getOptionForShortcut() returns a InputOption by its shortcut'); + } + + public function testGetOptionForMultiShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->multi)); + $this->assertEquals($this->multi, $definition->getOptionForShortcut('m'), '->getOptionForShortcut() returns a InputOption by its shortcut'); + $this->assertEquals($this->multi, $definition->getOptionForShortcut('mmm'), '->getOptionForShortcut() returns a InputOption by its shortcut'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "-l" option does not exist. + */ + public function testGetOptionForInvalidShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $definition->getOptionForShortcut('l'); + } + + public function testGetOptionDefaults() + { + $definition = new InputDefinition(array( + new InputOption('foo1', null, InputOption::VALUE_NONE), + new InputOption('foo2', null, InputOption::VALUE_REQUIRED), + new InputOption('foo3', null, InputOption::VALUE_REQUIRED, '', 'default'), + new InputOption('foo4', null, InputOption::VALUE_OPTIONAL), + new InputOption('foo5', null, InputOption::VALUE_OPTIONAL, '', 'default'), + new InputOption('foo6', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY), + new InputOption('foo7', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, '', array(1, 2)), + )); + $defaults = array( + 'foo1' => false, + 'foo2' => null, + 'foo3' => 'default', + 'foo4' => null, + 'foo5' => 'default', + 'foo6' => array(), + 'foo7' => array(1, 2), + ); + $this->assertSame($defaults, $definition->getOptionDefaults(), '->getOptionDefaults() returns the default values for all options'); + } + + /** + * @dataProvider getGetSynopsisData + */ + public function testGetSynopsis(InputDefinition $definition, $expectedSynopsis, $message = null) + { + $this->assertEquals($expectedSynopsis, $definition->getSynopsis(), $message ? '->getSynopsis() '.$message : ''); + } + + public function getGetSynopsisData() + { + return array( + array(new InputDefinition(array(new InputOption('foo'))), '[--foo]', 'puts optional options in square brackets'), + array(new InputDefinition(array(new InputOption('foo', 'f'))), '[-f|--foo]', 'separates shortcut with a pipe'), + array(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), '[-f|--foo FOO]', 'uses shortcut as value placeholder'), + array(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL))), '[-f|--foo [FOO]]', 'puts optional values in square brackets'), + + array(new InputDefinition(array(new InputArgument('foo', InputArgument::REQUIRED))), '<foo>', 'puts arguments in angle brackets'), + array(new InputDefinition(array(new InputArgument('foo'))), '[<foo>]', 'puts optional arguments in square brackets'), + array(new InputDefinition(array(new InputArgument('foo', InputArgument::IS_ARRAY))), '[<foo>]...', 'uses an ellipsis for array arguments'), + array(new InputDefinition(array(new InputArgument('foo', InputArgument::REQUIRED | InputArgument::IS_ARRAY))), '<foo> (<foo>)...', 'uses parenthesis and ellipsis for required array arguments'), + + array(new InputDefinition(array(new InputOption('foo'), new InputArgument('foo', InputArgument::REQUIRED))), '[--foo] [--] <foo>', 'puts [--] between options and arguments'), + ); + } + + public function testGetShortSynopsis() + { + $definition = new InputDefinition(array(new InputOption('foo'), new InputOption('bar'), new InputArgument('cat'))); + $this->assertEquals('[options] [--] [<cat>]', $definition->getSynopsis(true), '->getSynopsis(true) groups options in [options]'); + } + + /** + * @group legacy + */ + public function testLegacyAsText() + { + $definition = new InputDefinition(array( + new InputArgument('foo', InputArgument::OPTIONAL, 'The foo argument'), + new InputArgument('baz', InputArgument::OPTIONAL, 'The baz argument', true), + new InputArgument('bar', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'The bar argument', array('http://foo.com/')), + new InputOption('foo', 'f', InputOption::VALUE_REQUIRED, 'The foo option'), + new InputOption('baz', null, InputOption::VALUE_OPTIONAL, 'The baz option', false), + new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL, 'The bar option', 'bar'), + new InputOption('qux', '', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The qux option', array('http://foo.com/', 'bar')), + new InputOption('qux2', '', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The qux2 option', array('foo' => 'bar')), + )); + + $this->assertStringEqualsFile(self::$fixtures.'/definition_astext.txt', $definition->asText(), '->asText() returns a textual representation of the InputDefinition'); + } + + /** + * @group legacy + */ + public function testLegacyAsXml() + { + $definition = new InputDefinition(array( + new InputArgument('foo', InputArgument::OPTIONAL, 'The foo argument'), + new InputArgument('baz', InputArgument::OPTIONAL, 'The baz argument', true), + new InputArgument('bar', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'The bar argument', array('bar')), + new InputOption('foo', 'f', InputOption::VALUE_REQUIRED, 'The foo option'), + new InputOption('baz', null, InputOption::VALUE_OPTIONAL, 'The baz option', false), + new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL, 'The bar option', 'bar'), + )); + $this->assertXmlStringEqualsXmlFile(self::$fixtures.'/definition_asxml.txt', $definition->asXml(), '->asXml() returns an XML representation of the InputDefinition'); + } + + protected function initializeArguments() + { + $this->foo = new InputArgument('foo'); + $this->bar = new InputArgument('bar'); + $this->foo1 = new InputArgument('foo'); + $this->foo2 = new InputArgument('foo2', InputArgument::REQUIRED); + } + + protected function initializeOptions() + { + $this->foo = new InputOption('foo', 'f'); + $this->bar = new InputOption('bar', 'b'); + $this->foo1 = new InputOption('fooBis', 'f'); + $this->foo2 = new InputOption('foo', 'p'); + $this->multi = new InputOption('multi', 'm|mm|mmm'); + } +} diff --git a/vendor/symfony/console/Tests/Input/InputOptionTest.php b/vendor/symfony/console/Tests/Input/InputOptionTest.php new file mode 100644 index 00000000..53ce1df8 --- /dev/null +++ b/vendor/symfony/console/Tests/Input/InputOptionTest.php @@ -0,0 +1,204 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputOption; + +class InputOptionTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $option = new InputOption('foo'); + $this->assertEquals('foo', $option->getName(), '__construct() takes a name as its first argument'); + $option = new InputOption('--foo'); + $this->assertEquals('foo', $option->getName(), '__construct() removes the leading -- of the option name'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value. + */ + public function testArrayModeWithoutValue() + { + new InputOption('foo', 'f', InputOption::VALUE_IS_ARRAY); + } + + public function testShortcut() + { + $option = new InputOption('foo', 'f'); + $this->assertEquals('f', $option->getShortcut(), '__construct() can take a shortcut as its second argument'); + $option = new InputOption('foo', '-f|-ff|fff'); + $this->assertEquals('f|ff|fff', $option->getShortcut(), '__construct() removes the leading - of the shortcuts'); + $option = new InputOption('foo', array('f', 'ff', '-fff')); + $this->assertEquals('f|ff|fff', $option->getShortcut(), '__construct() removes the leading - of the shortcuts'); + $option = new InputOption('foo'); + $this->assertNull($option->getShortcut(), '__construct() makes the shortcut null by default'); + } + + public function testModes() + { + $option = new InputOption('foo', 'f'); + $this->assertFalse($option->acceptValue(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); + $this->assertFalse($option->isValueRequired(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); + $this->assertFalse($option->isValueOptional(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); + + $option = new InputOption('foo', 'f', null); + $this->assertFalse($option->acceptValue(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + + $option = new InputOption('foo', 'f', InputOption::VALUE_NONE); + $this->assertFalse($option->acceptValue(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + + $option = new InputOption('foo', 'f', InputOption::VALUE_REQUIRED); + $this->assertTrue($option->acceptValue(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); + $this->assertTrue($option->isValueRequired(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); + $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); + + $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL); + $this->assertTrue($option->acceptValue(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); + $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); + $this->assertTrue($option->isValueOptional(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); + } + + /** + * @dataProvider provideInvalidModes + */ + public function testInvalidModes($mode) + { + $this->setExpectedException('InvalidArgumentException', sprintf('Option mode "%s" is not valid.', $mode)); + + new InputOption('foo', 'f', $mode); + } + + public function provideInvalidModes() + { + return array( + array('ANOTHER_ONE'), + array(-1), + ); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testEmptyNameIsInvalid() + { + new InputOption(''); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testDoubleDashNameIsInvalid() + { + new InputOption('--'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testSingleDashOptionIsInvalid() + { + new InputOption('foo', '-'); + } + + public function testIsArray() + { + $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); + $this->assertTrue($option->isArray(), '->isArray() returns true if the option can be an array'); + $option = new InputOption('foo', null, InputOption::VALUE_NONE); + $this->assertFalse($option->isArray(), '->isArray() returns false if the option can not be an array'); + } + + public function testGetDescription() + { + $option = new InputOption('foo', 'f', null, 'Some description'); + $this->assertEquals('Some description', $option->getDescription(), '->getDescription() returns the description message'); + } + + public function testGetDefault() + { + $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL, '', 'default'); + $this->assertEquals('default', $option->getDefault(), '->getDefault() returns the default value'); + + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED, '', 'default'); + $this->assertEquals('default', $option->getDefault(), '->getDefault() returns the default value'); + + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED); + $this->assertNull($option->getDefault(), '->getDefault() returns null if no default value is configured'); + + $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); + $this->assertEquals(array(), $option->getDefault(), '->getDefault() returns an empty array if option is an array'); + + $option = new InputOption('foo', null, InputOption::VALUE_NONE); + $this->assertFalse($option->getDefault(), '->getDefault() returns false if the option does not take a value'); + } + + public function testSetDefault() + { + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED, '', 'default'); + $option->setDefault(null); + $this->assertNull($option->getDefault(), '->setDefault() can reset the default value by passing null'); + $option->setDefault('another'); + $this->assertEquals('another', $option->getDefault(), '->setDefault() changes the default value'); + + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY); + $option->setDefault(array(1, 2)); + $this->assertEquals(array(1, 2), $option->getDefault(), '->setDefault() changes the default value'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot set a default value when using InputOption::VALUE_NONE mode. + */ + public function testDefaultValueWithValueNoneMode() + { + $option = new InputOption('foo', 'f', InputOption::VALUE_NONE); + $option->setDefault('default'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage A default value for an array option must be an array. + */ + public function testDefaultValueWithIsArrayMode() + { + $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); + $option->setDefault('default'); + } + + public function testEquals() + { + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('foo', 'f', null, 'Alternative description'); + $this->assertTrue($option->equals($option2)); + + $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description'); + $option2 = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description', true); + $this->assertFalse($option->equals($option2)); + + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('bar', 'f', null, 'Some description'); + $this->assertFalse($option->equals($option2)); + + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('foo', '', null, 'Some description'); + $this->assertFalse($option->equals($option2)); + + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description'); + $this->assertFalse($option->equals($option2)); + } +} diff --git a/vendor/symfony/console/Tests/Input/InputTest.php b/vendor/symfony/console/Tests/Input/InputTest.php new file mode 100644 index 00000000..eb1c6617 --- /dev/null +++ b/vendor/symfony/console/Tests/Input/InputTest.php @@ -0,0 +1,132 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class InputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals('foo', $input->getArgument('name'), '->__construct() takes a InputDefinition as an argument'); + } + + public function testOptions() + { + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name')))); + $this->assertEquals('foo', $input->getOption('name'), '->getOption() returns the value for the given option'); + + $input->setOption('name', 'bar'); + $this->assertEquals('bar', $input->getOption('name'), '->setOption() sets the value for a given option'); + $this->assertEquals(array('name' => 'bar'), $input->getOptions(), '->getOptions() returns all option values'); + + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')))); + $this->assertEquals('default', $input->getOption('bar'), '->getOption() returns the default value for optional options'); + $this->assertEquals(array('name' => 'foo', 'bar' => 'default'), $input->getOptions(), '->getOptions() returns all option values, even optional ones'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "foo" option does not exist. + */ + public function testSetInvalidOption() + { + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')))); + $input->setOption('foo', 'bar'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "foo" option does not exist. + */ + public function testGetInvalidOption() + { + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')))); + $input->getOption('foo'); + } + + public function testArguments() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals('foo', $input->getArgument('name'), '->getArgument() returns the value for the given argument'); + + $input->setArgument('name', 'bar'); + $this->assertEquals('bar', $input->getArgument('name'), '->setArgument() sets the value for a given argument'); + $this->assertEquals(array('name' => 'bar'), $input->getArguments(), '->getArguments() returns all argument values'); + + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default')))); + $this->assertEquals('default', $input->getArgument('bar'), '->getArgument() returns the default value for optional arguments'); + $this->assertEquals(array('name' => 'foo', 'bar' => 'default'), $input->getArguments(), '->getArguments() returns all argument values, even optional ones'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "foo" argument does not exist. + */ + public function testSetInvalidArgument() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default')))); + $input->setArgument('foo', 'bar'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "foo" argument does not exist. + */ + public function testGetInvalidArgument() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default')))); + $input->getArgument('foo'); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Not enough arguments (missing: "name"). + */ + public function testValidateWithMissingArguments() + { + $input = new ArrayInput(array()); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED)))); + $input->validate(); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Not enough arguments (missing: "name"). + */ + public function testValidateWithMissingRequiredArguments() + { + $input = new ArrayInput(array('bar' => 'baz')); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED), new InputArgument('bar', InputArgument::OPTIONAL)))); + $input->validate(); + } + + public function testValidate() + { + $input = new ArrayInput(array('name' => 'foo')); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED)))); + + $this->assertNull($input->validate()); + } + + public function testSetGetInteractive() + { + $input = new ArrayInput(array()); + $this->assertTrue($input->isInteractive(), '->isInteractive() returns whether the input should be interactive or not'); + $input->setInteractive(false); + $this->assertFalse($input->isInteractive(), '->setInteractive() changes the interactive flag'); + } +} diff --git a/vendor/symfony/console/Tests/Input/StringInputTest.php b/vendor/symfony/console/Tests/Input/StringInputTest.php new file mode 100644 index 00000000..c8a560f6 --- /dev/null +++ b/vendor/symfony/console/Tests/Input/StringInputTest.php @@ -0,0 +1,99 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\StringInput; + +class StringInputTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getTokenizeData + */ + public function testTokenize($input, $tokens, $message) + { + $input = new StringInput($input); + $r = new \ReflectionClass('Symfony\Component\Console\Input\ArgvInput'); + $p = $r->getProperty('tokens'); + $p->setAccessible(true); + $this->assertEquals($tokens, $p->getValue($input), $message); + } + + public function testInputOptionWithGivenString() + { + $definition = new InputDefinition( + array(new InputOption('foo', null, InputOption::VALUE_REQUIRED)) + ); + + // call to bind + $input = new StringInput('--foo=bar'); + $input->bind($definition); + $this->assertEquals('bar', $input->getOption('foo')); + } + + /** + * @group legacy + */ + public function testLegacyInputOptionDefinitionInConstructor() + { + $definition = new InputDefinition( + array(new InputOption('foo', null, InputOption::VALUE_REQUIRED)) + ); + + $input = new StringInput('--foo=bar', $definition); + $this->assertEquals('bar', $input->getOption('foo')); + } + + public function getTokenizeData() + { + return array( + array('', array(), '->tokenize() parses an empty string'), + array('foo', array('foo'), '->tokenize() parses arguments'), + array(' foo bar ', array('foo', 'bar'), '->tokenize() ignores whitespaces between arguments'), + array('"quoted"', array('quoted'), '->tokenize() parses quoted arguments'), + array("'quoted'", array('quoted'), '->tokenize() parses quoted arguments'), + array("'a\rb\nc\td'", array("a\rb\nc\td"), '->tokenize() parses whitespace chars in strings'), + array("'a'\r'b'\n'c'\t'd'", array('a', 'b', 'c', 'd'), '->tokenize() parses whitespace chars between args as spaces'), + array('\"quoted\"', array('"quoted"'), '->tokenize() parses escaped-quoted arguments'), + array("\'quoted\'", array('\'quoted\''), '->tokenize() parses escaped-quoted arguments'), + array('-a', array('-a'), '->tokenize() parses short options'), + array('-azc', array('-azc'), '->tokenize() parses aggregated short options'), + array('-awithavalue', array('-awithavalue'), '->tokenize() parses short options with a value'), + array('-a"foo bar"', array('-afoo bar'), '->tokenize() parses short options with a value'), + array('-a"foo bar""foo bar"', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), + array('-a\'foo bar\'', array('-afoo bar'), '->tokenize() parses short options with a value'), + array('-a\'foo bar\'\'foo bar\'', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), + array('-a\'foo bar\'"foo bar"', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), + array('--long-option', array('--long-option'), '->tokenize() parses long options'), + array('--long-option=foo', array('--long-option=foo'), '->tokenize() parses long options with a value'), + array('--long-option="foo bar"', array('--long-option=foo bar'), '->tokenize() parses long options with a value'), + array('--long-option="foo bar""another"', array('--long-option=foo baranother'), '->tokenize() parses long options with a value'), + array('--long-option=\'foo bar\'', array('--long-option=foo bar'), '->tokenize() parses long options with a value'), + array("--long-option='foo bar''another'", array('--long-option=foo baranother'), '->tokenize() parses long options with a value'), + array("--long-option='foo bar'\"another\"", array('--long-option=foo baranother'), '->tokenize() parses long options with a value'), + array('foo -a -ffoo --long bar', array('foo', '-a', '-ffoo', '--long', 'bar'), '->tokenize() parses when several arguments and options'), + ); + } + + public function testToString() + { + $input = new StringInput('-f foo'); + $this->assertEquals('-f foo', (string) $input); + + $input = new StringInput('-f --bar=foo "a b c d"'); + $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d'), (string) $input); + + $input = new StringInput('-f --bar=foo \'a b c d\' '."'A\nB\\'C'"); + $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d').' '.escapeshellarg("A\nB'C"), (string) $input); + } +} diff --git a/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php b/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php new file mode 100644 index 00000000..c5eca2ca --- /dev/null +++ b/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php @@ -0,0 +1,58 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Logger; + +use Psr\Log\Test\LoggerInterfaceTest; +use Psr\Log\LogLevel; +use Symfony\Component\Console\Logger\ConsoleLogger; +use Symfony\Component\Console\Tests\Fixtures\DummyOutput; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Console logger test. + * + * @author Kévin Dunglas <dunglas@gmail.com> + */ +class ConsoleLoggerTest extends LoggerInterfaceTest +{ + /** + * @var DummyOutput + */ + protected $output; + + /** + * {@inheritdoc} + */ + public function getLogger() + { + $this->output = new DummyOutput(OutputInterface::VERBOSITY_VERBOSE); + + return new ConsoleLogger($this->output, array( + LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL, + LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL, + LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL, + LogLevel::NOTICE => OutputInterface::VERBOSITY_NORMAL, + LogLevel::INFO => OutputInterface::VERBOSITY_NORMAL, + LogLevel::DEBUG => OutputInterface::VERBOSITY_NORMAL, + )); + } + + /** + * {@inheritdoc} + */ + public function getLogs() + { + return $this->output->getLogs(); + } +} diff --git a/vendor/symfony/console/Tests/Output/ConsoleOutputTest.php b/vendor/symfony/console/Tests/Output/ConsoleOutputTest.php new file mode 100644 index 00000000..1afbbb6e --- /dev/null +++ b/vendor/symfony/console/Tests/Output/ConsoleOutputTest.php @@ -0,0 +1,25 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Output; + +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\Output; + +class ConsoleOutputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $output = new ConsoleOutput(Output::VERBOSITY_QUIET, true); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); + $this->assertSame($output->getFormatter(), $output->getErrorOutput()->getFormatter(), '__construct() takes a formatter or null as the third argument'); + } +} diff --git a/vendor/symfony/console/Tests/Output/NullOutputTest.php b/vendor/symfony/console/Tests/Output/NullOutputTest.php new file mode 100644 index 00000000..b20ae4e8 --- /dev/null +++ b/vendor/symfony/console/Tests/Output/NullOutputTest.php @@ -0,0 +1,39 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Output; + +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Output\OutputInterface; + +class NullOutputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $output = new NullOutput(); + + ob_start(); + $output->write('foo'); + $buffer = ob_get_clean(); + + $this->assertSame('', $buffer, '->write() does nothing (at least nothing is printed)'); + $this->assertFalse($output->isDecorated(), '->isDecorated() returns false'); + } + + public function testVerbosity() + { + $output = new NullOutput(); + $this->assertSame(OutputInterface::VERBOSITY_QUIET, $output->getVerbosity(), '->getVerbosity() returns VERBOSITY_QUIET for NullOutput by default'); + + $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + $this->assertSame(OutputInterface::VERBOSITY_QUIET, $output->getVerbosity(), '->getVerbosity() always returns VERBOSITY_QUIET for NullOutput'); + } +} diff --git a/vendor/symfony/console/Tests/Output/OutputTest.php b/vendor/symfony/console/Tests/Output/OutputTest.php new file mode 100644 index 00000000..45e6ddc7 --- /dev/null +++ b/vendor/symfony/console/Tests/Output/OutputTest.php @@ -0,0 +1,175 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Output; + +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $output = new TestOutput(Output::VERBOSITY_QUIET, true); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); + $this->assertTrue($output->isDecorated(), '__construct() takes the decorated flag as its second argument'); + } + + public function testSetIsDecorated() + { + $output = new TestOutput(); + $output->setDecorated(true); + $this->assertTrue($output->isDecorated(), 'setDecorated() sets the decorated flag'); + } + + public function testSetGetVerbosity() + { + $output = new TestOutput(); + $output->setVerbosity(Output::VERBOSITY_QUIET); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '->setVerbosity() sets the verbosity'); + + $this->assertTrue($output->isQuiet()); + $this->assertFalse($output->isVerbose()); + $this->assertFalse($output->isVeryVerbose()); + $this->assertFalse($output->isDebug()); + + $output->setVerbosity(Output::VERBOSITY_NORMAL); + $this->assertFalse($output->isQuiet()); + $this->assertFalse($output->isVerbose()); + $this->assertFalse($output->isVeryVerbose()); + $this->assertFalse($output->isDebug()); + + $output->setVerbosity(Output::VERBOSITY_VERBOSE); + $this->assertFalse($output->isQuiet()); + $this->assertTrue($output->isVerbose()); + $this->assertFalse($output->isVeryVerbose()); + $this->assertFalse($output->isDebug()); + + $output->setVerbosity(Output::VERBOSITY_VERY_VERBOSE); + $this->assertFalse($output->isQuiet()); + $this->assertTrue($output->isVerbose()); + $this->assertTrue($output->isVeryVerbose()); + $this->assertFalse($output->isDebug()); + + $output->setVerbosity(Output::VERBOSITY_DEBUG); + $this->assertFalse($output->isQuiet()); + $this->assertTrue($output->isVerbose()); + $this->assertTrue($output->isVeryVerbose()); + $this->assertTrue($output->isDebug()); + } + + public function testWriteWithVerbosityQuiet() + { + $output = new TestOutput(Output::VERBOSITY_QUIET); + $output->writeln('foo'); + $this->assertEquals('', $output->output, '->writeln() outputs nothing if verbosity is set to VERBOSITY_QUIET'); + } + + public function testWriteAnArrayOfMessages() + { + $output = new TestOutput(); + $output->writeln(array('foo', 'bar')); + $this->assertEquals("foo\nbar\n", $output->output, '->writeln() can take an array of messages to output'); + } + + /** + * @dataProvider provideWriteArguments + */ + public function testWriteRawMessage($message, $type, $expectedOutput) + { + $output = new TestOutput(); + $output->writeln($message, $type); + $this->assertEquals($expectedOutput, $output->output); + } + + public function provideWriteArguments() + { + return array( + array('<info>foo</info>', Output::OUTPUT_RAW, "<info>foo</info>\n"), + array('<info>foo</info>', Output::OUTPUT_PLAIN, "foo\n"), + ); + } + + public function testWriteWithDecorationTurnedOff() + { + $output = new TestOutput(); + $output->setDecorated(false); + $output->writeln('<info>foo</info>'); + $this->assertEquals("foo\n", $output->output, '->writeln() strips decoration tags if decoration is set to false'); + } + + public function testWriteDecoratedMessage() + { + $fooStyle = new OutputFormatterStyle('yellow', 'red', array('blink')); + $output = new TestOutput(); + $output->getFormatter()->setStyle('FOO', $fooStyle); + $output->setDecorated(true); + $output->writeln('<foo>foo</foo>'); + $this->assertEquals("\033[33;41;5mfoo\033[39;49;25m\n", $output->output, '->writeln() decorates the output'); + } + + public function testWriteWithInvalidStyle() + { + $output = new TestOutput(); + + $output->clear(); + $output->write('<bar>foo</bar>'); + $this->assertEquals('<bar>foo</bar>', $output->output, '->write() do nothing when a style does not exist'); + + $output->clear(); + $output->writeln('<bar>foo</bar>'); + $this->assertEquals("<bar>foo</bar>\n", $output->output, '->writeln() do nothing when a style does not exist'); + } + + /** + * @dataProvider verbosityProvider + */ + public function testWriteWithVerbosityOption($verbosity, $expected, $msg) + { + $output = new TestOutput(); + + $output->setVerbosity($verbosity); + $output->clear(); + $output->write('1', false); + $output->write('2', false, Output::VERBOSITY_QUIET); + $output->write('3', false, Output::VERBOSITY_NORMAL); + $output->write('4', false, Output::VERBOSITY_VERBOSE); + $output->write('5', false, Output::VERBOSITY_VERY_VERBOSE); + $output->write('6', false, Output::VERBOSITY_DEBUG); + $this->assertEquals($expected, $output->output, $msg); + } + + public function verbosityProvider() + { + return array( + array(Output::VERBOSITY_QUIET, '2', '->write() in QUIET mode only outputs when an explicit QUIET verbosity is passed'), + array(Output::VERBOSITY_NORMAL, '123', '->write() in NORMAL mode outputs anything below an explicit VERBOSE verbosity'), + array(Output::VERBOSITY_VERBOSE, '1234', '->write() in VERBOSE mode outputs anything below an explicit VERY_VERBOSE verbosity'), + array(Output::VERBOSITY_VERY_VERBOSE, '12345', '->write() in VERY_VERBOSE mode outputs anything below an explicit DEBUG verbosity'), + array(Output::VERBOSITY_DEBUG, '123456', '->write() in DEBUG mode outputs everything'), + ); + } +} + +class TestOutput extends Output +{ + public $output = ''; + + public function clear() + { + $this->output = ''; + } + + protected function doWrite($message, $newline) + { + $this->output .= $message.($newline ? "\n" : ''); + } +} diff --git a/vendor/symfony/console/Tests/Output/StreamOutputTest.php b/vendor/symfony/console/Tests/Output/StreamOutputTest.php new file mode 100644 index 00000000..2fd4f612 --- /dev/null +++ b/vendor/symfony/console/Tests/Output/StreamOutputTest.php @@ -0,0 +1,60 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Output; + +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Output\StreamOutput; + +class StreamOutputTest extends \PHPUnit_Framework_TestCase +{ + protected $stream; + + protected function setUp() + { + $this->stream = fopen('php://memory', 'a', false); + } + + protected function tearDown() + { + $this->stream = null; + } + + public function testConstructor() + { + $output = new StreamOutput($this->stream, Output::VERBOSITY_QUIET, true); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); + $this->assertTrue($output->isDecorated(), '__construct() takes the decorated flag as its second argument'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The StreamOutput class needs a stream as its first argument. + */ + public function testStreamIsRequired() + { + new StreamOutput('foo'); + } + + public function testGetStream() + { + $output = new StreamOutput($this->stream); + $this->assertEquals($this->stream, $output->getStream(), '->getStream() returns the current stream'); + } + + public function testDoWrite() + { + $output = new StreamOutput($this->stream); + $output->writeln('foo'); + rewind($output->getStream()); + $this->assertEquals('foo'.PHP_EOL, stream_get_contents($output->getStream()), '->doWrite() writes to the stream'); + } +} diff --git a/vendor/symfony/console/Tests/Style/SymfonyStyleTest.php b/vendor/symfony/console/Tests/Style/SymfonyStyleTest.php new file mode 100644 index 00000000..889a9c82 --- /dev/null +++ b/vendor/symfony/console/Tests/Style/SymfonyStyleTest.php @@ -0,0 +1,89 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Style; + +use PHPUnit_Framework_TestCase; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Console\Tester\CommandTester; + +class SymfonyStyleTest extends PHPUnit_Framework_TestCase +{ + /** @var Command */ + protected $command; + /** @var CommandTester */ + protected $tester; + + protected function setUp() + { + $this->command = new Command('sfstyle'); + $this->tester = new CommandTester($this->command); + } + + protected function tearDown() + { + $this->command = null; + $this->tester = null; + } + + /** + * @dataProvider inputCommandToOutputFilesProvider + */ + public function testOutputs($inputCommandFilepath, $outputFilepath) + { + $code = require $inputCommandFilepath; + $this->command->setCode($code); + $this->tester->execute(array(), array('interactive' => false, 'decorated' => false)); + $this->assertStringEqualsFile($outputFilepath, $this->tester->getDisplay(true)); + } + + public function inputCommandToOutputFilesProvider() + { + $baseDir = __DIR__.'/../Fixtures/Style/SymfonyStyle'; + + return array_map(null, glob($baseDir.'/command/command_*.php'), glob($baseDir.'/output/output_*.txt')); + } + + public function testLongWordsBlockWrapping() + { + $word = 'Lopadotemachoselachogaleokranioleipsanodrimhypotrimmatosilphioparaomelitokatakechymenokichlepikossyphophattoperisteralektryonoptekephalliokigklopeleiolagoiosiraiobaphetraganopterygovgollhjvhvljfezefeqifzeiqgiqzhrsdgihqzridghqridghqirshdghdghieridgheirhsdgehrsdvhqrsidhqshdgihrsidvqhneriqsdvjzergetsrfhgrstsfhsetsfhesrhdgtesfhbzrtfbrztvetbsdfbrsdfbrn'; + $wordLength = strlen($word); + $maxLineLength = SymfonyStyle::MAX_LINE_LENGTH - 3; + + $this->command->setCode(function (InputInterface $input, OutputInterface $output) use ($word) { + $sfStyle = new SymfonyStyleWithForcedLineLength($input, $output); + $sfStyle->block($word, 'CUSTOM', 'fg=white;bg=blue', ' § ', false); + }); + + $this->tester->execute(array(), array('interactive' => false, 'decorated' => false)); + $expectedCount = (int) ceil($wordLength / ($maxLineLength)) + (int) ($wordLength > $maxLineLength - 5); + $this->assertSame($expectedCount, substr_count($this->tester->getDisplay(true), ' § ')); + } +} + +/** + * Use this class in tests to force the line length + * and ensure a consistent output for expectations. + */ +class SymfonyStyleWithForcedLineLength extends SymfonyStyle +{ + public function __construct(InputInterface $input, OutputInterface $output) + { + parent::__construct($input, $output); + + $ref = new \ReflectionProperty(get_parent_class($this), 'lineLength'); + $ref->setAccessible(true); + $ref->setValue($this, 120); + } +} diff --git a/vendor/symfony/console/Tests/Tester/ApplicationTesterTest.php b/vendor/symfony/console/Tests/Tester/ApplicationTesterTest.php new file mode 100644 index 00000000..a8389dd1 --- /dev/null +++ b/vendor/symfony/console/Tests/Tester/ApplicationTesterTest.php @@ -0,0 +1,69 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Tester\ApplicationTester; + +class ApplicationTesterTest extends \PHPUnit_Framework_TestCase +{ + protected $application; + protected $tester; + + protected function setUp() + { + $this->application = new Application(); + $this->application->setAutoExit(false); + $this->application->register('foo') + ->addArgument('foo') + ->setCode(function ($input, $output) { $output->writeln('foo'); }) + ; + + $this->tester = new ApplicationTester($this->application); + $this->tester->run(array('command' => 'foo', 'foo' => 'bar'), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); + } + + protected function tearDown() + { + $this->application = null; + $this->tester = null; + } + + public function testRun() + { + $this->assertFalse($this->tester->getInput()->isInteractive(), '->execute() takes an interactive option'); + $this->assertFalse($this->tester->getOutput()->isDecorated(), '->execute() takes a decorated option'); + $this->assertEquals(Output::VERBOSITY_VERBOSE, $this->tester->getOutput()->getVerbosity(), '->execute() takes a verbosity option'); + } + + public function testGetInput() + { + $this->assertEquals('bar', $this->tester->getInput()->getArgument('foo'), '->getInput() returns the current input instance'); + } + + public function testGetOutput() + { + rewind($this->tester->getOutput()->getStream()); + $this->assertEquals('foo'.PHP_EOL, stream_get_contents($this->tester->getOutput()->getStream()), '->getOutput() returns the current output instance'); + } + + public function testGetDisplay() + { + $this->assertEquals('foo'.PHP_EOL, $this->tester->getDisplay(), '->getDisplay() returns the display of the last execution'); + } + + public function testGetStatusCode() + { + $this->assertSame(0, $this->tester->getStatusCode(), '->getStatusCode() returns the status code'); + } +} diff --git a/vendor/symfony/console/Tests/Tester/CommandTesterTest.php b/vendor/symfony/console/Tests/Tester/CommandTesterTest.php new file mode 100644 index 00000000..b54c00e8 --- /dev/null +++ b/vendor/symfony/console/Tests/Tester/CommandTesterTest.php @@ -0,0 +1,84 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Tester\CommandTester; + +class CommandTesterTest extends \PHPUnit_Framework_TestCase +{ + protected $command; + protected $tester; + + protected function setUp() + { + $this->command = new Command('foo'); + $this->command->addArgument('command'); + $this->command->addArgument('foo'); + $this->command->setCode(function ($input, $output) { $output->writeln('foo'); }); + + $this->tester = new CommandTester($this->command); + $this->tester->execute(array('foo' => 'bar'), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); + } + + protected function tearDown() + { + $this->command = null; + $this->tester = null; + } + + public function testExecute() + { + $this->assertFalse($this->tester->getInput()->isInteractive(), '->execute() takes an interactive option'); + $this->assertFalse($this->tester->getOutput()->isDecorated(), '->execute() takes a decorated option'); + $this->assertEquals(Output::VERBOSITY_VERBOSE, $this->tester->getOutput()->getVerbosity(), '->execute() takes a verbosity option'); + } + + public function testGetInput() + { + $this->assertEquals('bar', $this->tester->getInput()->getArgument('foo'), '->getInput() returns the current input instance'); + } + + public function testGetOutput() + { + rewind($this->tester->getOutput()->getStream()); + $this->assertEquals('foo'.PHP_EOL, stream_get_contents($this->tester->getOutput()->getStream()), '->getOutput() returns the current output instance'); + } + + public function testGetDisplay() + { + $this->assertEquals('foo'.PHP_EOL, $this->tester->getDisplay(), '->getDisplay() returns the display of the last execution'); + } + + public function testGetStatusCode() + { + $this->assertSame(0, $this->tester->getStatusCode(), '->getStatusCode() returns the status code'); + } + + public function testCommandFromApplication() + { + $application = new Application(); + $application->setAutoExit(false); + + $command = new Command('foo'); + $command->setCode(function ($input, $output) { $output->writeln('foo'); }); + + $application->add($command); + + $tester = new CommandTester($application->find('foo')); + + // check that there is no need to pass the command name here + $this->assertEquals(0, $tester->execute(array())); + } +} diff --git a/vendor/symfony/console/composer.json b/vendor/symfony/console/composer.json new file mode 100644 index 00000000..ab247045 --- /dev/null +++ b/vendor/symfony/console/composer.json @@ -0,0 +1,44 @@ +{ + "name": "symfony/console", + "type": "library", + "description": "Symfony Console Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/event-dispatcher": "~2.1|~3.0.0", + "symfony/process": "~2.1|~3.0.0", + "psr/log": "~1.0" + }, + "suggest": { + "symfony/event-dispatcher": "", + "symfony/process": "", + "psr/log": "For using the console logger" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Console\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + } +} diff --git a/vendor/symfony/console/phpunit.xml.dist b/vendor/symfony/console/phpunit.xml.dist new file mode 100644 index 00000000..8c09554f --- /dev/null +++ b/vendor/symfony/console/phpunit.xml.dist @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd" + backupGlobals="false" + colors="true" + bootstrap="vendor/autoload.php" +> + <php> + <ini name="error_reporting" value="-1" /> + </php> + + <testsuites> + <testsuite name="Symfony Console Component Test Suite"> + <directory>./Tests/</directory> + </testsuite> + </testsuites> + + <filter> + <whitelist> + <directory>./</directory> + <exclude> + <directory>./Resources</directory> + <directory>./Tests</directory> + <directory>./vendor</directory> + </exclude> + </whitelist> + </filter> + + <listeners> + <listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener"> + <arguments> + <array> + <element key="time-sensitive"><string>Symfony\Component\Console</string></element> + </array> + </arguments> + </listener> + </listeners> +</phpunit> diff --git a/vendor/symfony/event-dispatcher/.gitignore b/vendor/symfony/event-dispatcher/.gitignore new file mode 100644 index 00000000..c49a5d8d --- /dev/null +++ b/vendor/symfony/event-dispatcher/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/event-dispatcher/CHANGELOG.md b/vendor/symfony/event-dispatcher/CHANGELOG.md new file mode 100644 index 00000000..bb42ee19 --- /dev/null +++ b/vendor/symfony/event-dispatcher/CHANGELOG.md @@ -0,0 +1,23 @@ +CHANGELOG +========= + +2.5.0 +----- + + * added Debug\TraceableEventDispatcher (originally in HttpKernel) + * changed Debug\TraceableEventDispatcherInterface to extend EventDispatcherInterface + * added RegisterListenersPass (originally in HttpKernel) + +2.1.0 +----- + + * added TraceableEventDispatcherInterface + * added ContainerAwareEventDispatcher + * added a reference to the EventDispatcher on the Event + * added a reference to the Event name on the event + * added fluid interface to the dispatch() method which now returns the Event + object + * added GenericEvent event class + * added the possibility for subscribers to subscribe several times for the + same event + * added ImmutableEventDispatcher diff --git a/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php b/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php new file mode 100644 index 00000000..b92defe6 --- /dev/null +++ b/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php @@ -0,0 +1,187 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Lazily loads listeners and subscribers from the dependency injection + * container. + * + * @author Fabien Potencier <fabien@symfony.com> + * @author Bernhard Schussek <bschussek@gmail.com> + * @author Jordan Alliot <jordan.alliot@gmail.com> + */ +class ContainerAwareEventDispatcher extends EventDispatcher +{ + /** + * The container from where services are loaded. + * + * @var ContainerInterface + */ + private $container; + + /** + * The service IDs of the event listeners and subscribers. + * + * @var array + */ + private $listenerIds = array(); + + /** + * The services registered as listeners. + * + * @var array + */ + private $listeners = array(); + + /** + * Constructor. + * + * @param ContainerInterface $container A ContainerInterface instance + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * Adds a service as event listener. + * + * @param string $eventName Event for which the listener is added + * @param array $callback The service ID of the listener service & the method + * name that has to be called + * @param int $priority The higher this value, the earlier an event listener + * will be triggered in the chain. + * Defaults to 0. + * + * @throws \InvalidArgumentException + */ + public function addListenerService($eventName, $callback, $priority = 0) + { + if (!is_array($callback) || 2 !== count($callback)) { + throw new \InvalidArgumentException('Expected an array("service", "method") argument'); + } + + $this->listenerIds[$eventName][] = array($callback[0], $callback[1], $priority); + } + + public function removeListener($eventName, $listener) + { + $this->lazyLoad($eventName); + + if (isset($this->listenerIds[$eventName])) { + foreach ($this->listenerIds[$eventName] as $i => $args) { + list($serviceId, $method, $priority) = $args; + $key = $serviceId.'.'.$method; + if (isset($this->listeners[$eventName][$key]) && $listener === array($this->listeners[$eventName][$key], $method)) { + unset($this->listeners[$eventName][$key]); + if (empty($this->listeners[$eventName])) { + unset($this->listeners[$eventName]); + } + unset($this->listenerIds[$eventName][$i]); + if (empty($this->listenerIds[$eventName])) { + unset($this->listenerIds[$eventName]); + } + } + } + } + + parent::removeListener($eventName, $listener); + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + if (null === $eventName) { + return (bool) count($this->listenerIds) || (bool) count($this->listeners); + } + + if (isset($this->listenerIds[$eventName])) { + return true; + } + + return parent::hasListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + if (null === $eventName) { + foreach ($this->listenerIds as $serviceEventName => $args) { + $this->lazyLoad($serviceEventName); + } + } else { + $this->lazyLoad($eventName); + } + + return parent::getListeners($eventName); + } + + /** + * Adds a service as event subscriber. + * + * @param string $serviceId The service ID of the subscriber service + * @param string $class The service's class name (which must implement EventSubscriberInterface) + */ + public function addSubscriberService($serviceId, $class) + { + foreach ($class::getSubscribedEvents() as $eventName => $params) { + if (is_string($params)) { + $this->listenerIds[$eventName][] = array($serviceId, $params, 0); + } elseif (is_string($params[0])) { + $this->listenerIds[$eventName][] = array($serviceId, $params[0], isset($params[1]) ? $params[1] : 0); + } else { + foreach ($params as $listener) { + $this->listenerIds[$eventName][] = array($serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0); + } + } + } + } + + public function getContainer() + { + return $this->container; + } + + /** + * Lazily loads listeners for this event from the dependency injection + * container. + * + * @param string $eventName The name of the event to dispatch. The name of + * the event is the name of the method that is + * invoked on listeners. + */ + protected function lazyLoad($eventName) + { + if (isset($this->listenerIds[$eventName])) { + foreach ($this->listenerIds[$eventName] as $args) { + list($serviceId, $method, $priority) = $args; + $listener = $this->container->get($serviceId); + + $key = $serviceId.'.'.$method; + if (!isset($this->listeners[$eventName][$key])) { + $this->addListener($eventName, array($listener, $method), $priority); + } elseif ($listener !== $this->listeners[$eventName][$key]) { + parent::removeListener($eventName, array($this->listeners[$eventName][$key], $method)); + $this->addListener($eventName, array($listener, $method), $priority); + } + + $this->listeners[$eventName][$key] = $listener; + } + } + } +} diff --git a/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php b/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php new file mode 100644 index 00000000..12e2b1c6 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php @@ -0,0 +1,339 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Debug; + +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\Stopwatch\Stopwatch; +use Psr\Log\LoggerInterface; + +/** + * Collects some data about event listeners. + * + * This event dispatcher delegates the dispatching to another one. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class TraceableEventDispatcher implements TraceableEventDispatcherInterface +{ + protected $logger; + protected $stopwatch; + + private $called; + private $dispatcher; + private $wrappedListeners; + + /** + * Constructor. + * + * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance + * @param Stopwatch $stopwatch A Stopwatch instance + * @param LoggerInterface $logger A LoggerInterface instance + */ + public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null) + { + $this->dispatcher = $dispatcher; + $this->stopwatch = $stopwatch; + $this->logger = $logger; + $this->called = array(); + $this->wrappedListeners = array(); + } + + /** + * {@inheritdoc} + */ + public function addListener($eventName, $listener, $priority = 0) + { + $this->dispatcher->addListener($eventName, $listener, $priority); + } + + /** + * {@inheritdoc} + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->dispatcher->addSubscriber($subscriber); + } + + /** + * {@inheritdoc} + */ + public function removeListener($eventName, $listener) + { + if (isset($this->wrappedListeners[$eventName])) { + foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) { + if ($wrappedListener->getWrappedListener() === $listener) { + $listener = $wrappedListener; + unset($this->wrappedListeners[$eventName][$index]); + break; + } + } + } + + return $this->dispatcher->removeListener($eventName, $listener); + } + + /** + * {@inheritdoc} + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + return $this->dispatcher->removeSubscriber($subscriber); + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + return $this->dispatcher->getListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + return $this->dispatcher->hasListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, Event $event = null) + { + if (null === $event) { + $event = new Event(); + } + + if (null !== $this->logger && $event->isPropagationStopped()) { + $this->logger->debug(sprintf('The "%s" event is already stopped. No listeners have been called.', $eventName)); + } + + $this->preProcess($eventName); + $this->preDispatch($eventName, $event); + + $e = $this->stopwatch->start($eventName, 'section'); + + $this->dispatcher->dispatch($eventName, $event); + + if ($e->isStarted()) { + $e->stop(); + } + + $this->postDispatch($eventName, $event); + $this->postProcess($eventName); + + return $event; + } + + /** + * {@inheritdoc} + */ + public function getCalledListeners() + { + $called = array(); + foreach ($this->called as $eventName => $listeners) { + foreach ($listeners as $listener) { + $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName); + $called[$eventName.'.'.$info['pretty']] = $info; + } + } + + return $called; + } + + /** + * {@inheritdoc} + */ + public function getNotCalledListeners() + { + try { + $allListeners = $this->getListeners(); + } catch (\Exception $e) { + if (null !== $this->logger) { + $this->logger->info('An exception was thrown while getting the uncalled listeners.', array('exception' => $e)); + } + + // unable to retrieve the uncalled listeners + return array(); + } + + $notCalled = array(); + foreach ($allListeners as $eventName => $listeners) { + foreach ($listeners as $listener) { + $called = false; + if (isset($this->called[$eventName])) { + foreach ($this->called[$eventName] as $l) { + if ($l->getWrappedListener() === $listener) { + $called = true; + + break; + } + } + } + + if (!$called) { + $info = $this->getListenerInfo($listener, $eventName); + $notCalled[$eventName.'.'.$info['pretty']] = $info; + } + } + } + + return $notCalled; + } + + /** + * Proxies all method calls to the original event dispatcher. + * + * @param string $method The method name + * @param array $arguments The method arguments + * + * @return mixed + */ + public function __call($method, $arguments) + { + return call_user_func_array(array($this->dispatcher, $method), $arguments); + } + + /** + * Called before dispatching the event. + * + * @param string $eventName The event name + * @param Event $event The event + */ + protected function preDispatch($eventName, Event $event) + { + } + + /** + * Called after dispatching the event. + * + * @param string $eventName The event name + * @param Event $event The event + */ + protected function postDispatch($eventName, Event $event) + { + } + + private function preProcess($eventName) + { + foreach ($this->dispatcher->getListeners($eventName) as $listener) { + $this->dispatcher->removeListener($eventName, $listener); + $info = $this->getListenerInfo($listener, $eventName); + $name = isset($info['class']) ? $info['class'] : $info['type']; + $wrappedListener = new WrappedListener($listener, $name, $this->stopwatch, $this); + $this->wrappedListeners[$eventName][] = $wrappedListener; + $this->dispatcher->addListener($eventName, $wrappedListener); + } + } + + private function postProcess($eventName) + { + unset($this->wrappedListeners[$eventName]); + $skipped = false; + foreach ($this->dispatcher->getListeners($eventName) as $listener) { + if (!$listener instanceof WrappedListener) { // #12845: a new listener was added during dispatch. + continue; + } + // Unwrap listener + $this->dispatcher->removeListener($eventName, $listener); + $this->dispatcher->addListener($eventName, $listener->getWrappedListener()); + + $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName); + if ($listener->wasCalled()) { + if (null !== $this->logger) { + $this->logger->debug(sprintf('Notified event "%s" to listener "%s".', $eventName, $info['pretty'])); + } + + if (!isset($this->called[$eventName])) { + $this->called[$eventName] = new \SplObjectStorage(); + } + + $this->called[$eventName]->attach($listener); + } + + if (null !== $this->logger && $skipped) { + $this->logger->debug(sprintf('Listener "%s" was not called for event "%s".', $info['pretty'], $eventName)); + } + + if ($listener->stoppedPropagation()) { + if (null !== $this->logger) { + $this->logger->debug(sprintf('Listener "%s" stopped propagation of the event "%s".', $info['pretty'], $eventName)); + } + + $skipped = true; + } + } + } + + /** + * Returns information about the listener. + * + * @param object $listener The listener + * @param string $eventName The event name + * + * @return array Information about the listener + */ + private function getListenerInfo($listener, $eventName) + { + $info = array( + 'event' => $eventName, + ); + if ($listener instanceof \Closure) { + $info += array( + 'type' => 'Closure', + 'pretty' => 'closure', + ); + } elseif (is_string($listener)) { + try { + $r = new \ReflectionFunction($listener); + $file = $r->getFileName(); + $line = $r->getStartLine(); + } catch (\ReflectionException $e) { + $file = null; + $line = null; + } + $info += array( + 'type' => 'Function', + 'function' => $listener, + 'file' => $file, + 'line' => $line, + 'pretty' => $listener, + ); + } elseif (is_array($listener) || (is_object($listener) && is_callable($listener))) { + if (!is_array($listener)) { + $listener = array($listener, '__invoke'); + } + $class = is_object($listener[0]) ? get_class($listener[0]) : $listener[0]; + try { + $r = new \ReflectionMethod($class, $listener[1]); + $file = $r->getFileName(); + $line = $r->getStartLine(); + } catch (\ReflectionException $e) { + $file = null; + $line = null; + } + $info += array( + 'type' => 'Method', + 'class' => $class, + 'method' => $listener[1], + 'file' => $file, + 'line' => $line, + 'pretty' => $class.'::'.$listener[1], + ); + } + + return $info; + } +} diff --git a/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php b/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php new file mode 100644 index 00000000..5483e815 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php @@ -0,0 +1,34 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Debug; + +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * @author Fabien Potencier <fabien@symfony.com> + */ +interface TraceableEventDispatcherInterface extends EventDispatcherInterface +{ + /** + * Gets the called listeners. + * + * @return array An array of called listeners + */ + public function getCalledListeners(); + + /** + * Gets the not called listeners. + * + * @return array An array of not called listeners + */ + public function getNotCalledListeners(); +} diff --git a/vendor/symfony/event-dispatcher/Debug/WrappedListener.php b/vendor/symfony/event-dispatcher/Debug/WrappedListener.php new file mode 100644 index 00000000..e16627d6 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Debug/WrappedListener.php @@ -0,0 +1,71 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Debug; + +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * @author Fabien Potencier <fabien@symfony.com> + */ +class WrappedListener +{ + private $listener; + private $name; + private $called; + private $stoppedPropagation; + private $stopwatch; + private $dispatcher; + + public function __construct($listener, $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null) + { + $this->listener = $listener; + $this->name = $name; + $this->stopwatch = $stopwatch; + $this->dispatcher = $dispatcher; + $this->called = false; + $this->stoppedPropagation = false; + } + + public function getWrappedListener() + { + return $this->listener; + } + + public function wasCalled() + { + return $this->called; + } + + public function stoppedPropagation() + { + return $this->stoppedPropagation; + } + + public function __invoke(Event $event, $eventName, EventDispatcherInterface $dispatcher) + { + $this->called = true; + + $e = $this->stopwatch->start($this->name, 'event_listener'); + + call_user_func($this->listener, $event, $eventName, $this->dispatcher ?: $dispatcher); + + if ($e->isStarted()) { + $e->stop(); + } + + if ($event->isPropagationStopped()) { + $this->stoppedPropagation = true; + } + } +} diff --git a/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php b/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php new file mode 100644 index 00000000..ebfe435f --- /dev/null +++ b/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php @@ -0,0 +1,109 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * Compiler pass to register tagged services for an event dispatcher. + */ +class RegisterListenersPass implements CompilerPassInterface +{ + /** + * @var string + */ + protected $dispatcherService; + + /** + * @var string + */ + protected $listenerTag; + + /** + * @var string + */ + protected $subscriberTag; + + /** + * Constructor. + * + * @param string $dispatcherService Service name of the event dispatcher in processed container + * @param string $listenerTag Tag name used for listener + * @param string $subscriberTag Tag name used for subscribers + */ + public function __construct($dispatcherService = 'event_dispatcher', $listenerTag = 'kernel.event_listener', $subscriberTag = 'kernel.event_subscriber') + { + $this->dispatcherService = $dispatcherService; + $this->listenerTag = $listenerTag; + $this->subscriberTag = $subscriberTag; + } + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->dispatcherService) && !$container->hasAlias($this->dispatcherService)) { + return; + } + + $definition = $container->findDefinition($this->dispatcherService); + + foreach ($container->findTaggedServiceIds($this->listenerTag) as $id => $events) { + $def = $container->getDefinition($id); + if (!$def->isPublic()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event listeners are lazy-loaded.', $id)); + } + + if ($def->isAbstract()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event listeners are lazy-loaded.', $id)); + } + + foreach ($events as $event) { + $priority = isset($event['priority']) ? $event['priority'] : 0; + + if (!isset($event['event'])) { + throw new \InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag)); + } + + if (!isset($event['method'])) { + $event['method'] = 'on'.preg_replace_callback(array( + '/(?<=\b)[a-z]/i', + '/[^a-z0-9]/i', + ), function ($matches) { return strtoupper($matches[0]); }, $event['event']); + $event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']); + } + + $definition->addMethodCall('addListenerService', array($event['event'], array($id, $event['method']), $priority)); + } + } + + foreach ($container->findTaggedServiceIds($this->subscriberTag) as $id => $attributes) { + $def = $container->getDefinition($id); + if (!$def->isPublic()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event subscribers are lazy-loaded.', $id)); + } + + if ($def->isAbstract()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event subscribers are lazy-loaded.', $id)); + } + + // We must assume that the class value has been correctly filled, even if the service is created by a factory + $class = $container->getParameterBag()->resolveValue($def->getClass()); + + $interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface'; + if (!is_subclass_of($class, $interface)) { + throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface)); + } + + $definition->addMethodCall('addSubscriberService', array($id, $class)); + } + } +} diff --git a/vendor/symfony/event-dispatcher/Event.php b/vendor/symfony/event-dispatcher/Event.php new file mode 100644 index 00000000..4a563495 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Event.php @@ -0,0 +1,120 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * Event is the base class for classes containing event data. + * + * This class contains no event data. It is used by events that do not pass + * state information to an event handler when an event is raised. + * + * You can call the method stopPropagation() to abort the execution of + * further listeners in your event listener. + * + * @author Guilherme Blanco <guilhermeblanco@hotmail.com> + * @author Jonathan Wage <jonwage@gmail.com> + * @author Roman Borschel <roman@code-factory.org> + * @author Bernhard Schussek <bschussek@gmail.com> + */ +class Event +{ + /** + * @var bool Whether no further event listeners should be triggered + */ + private $propagationStopped = false; + + /** + * @var EventDispatcher Dispatcher that dispatched this event + */ + private $dispatcher; + + /** + * @var string This event's name + */ + private $name; + + /** + * Returns whether further event listeners should be triggered. + * + * @see Event::stopPropagation() + * + * @return bool Whether propagation was already stopped for this event. + */ + public function isPropagationStopped() + { + return $this->propagationStopped; + } + + /** + * Stops the propagation of the event to further event listeners. + * + * If multiple event listeners are connected to the same event, no + * further event listener will be triggered once any trigger calls + * stopPropagation(). + */ + public function stopPropagation() + { + $this->propagationStopped = true; + } + + /** + * Stores the EventDispatcher that dispatches this Event. + * + * @param EventDispatcherInterface $dispatcher + * + * @deprecated since version 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call. + */ + public function setDispatcher(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * Returns the EventDispatcher that dispatches this Event. + * + * @return EventDispatcherInterface + * + * @deprecated since version 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call. + */ + public function getDispatcher() + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.4 and will be removed in 3.0. The event dispatcher instance can be received in the listener call instead.', E_USER_DEPRECATED); + + return $this->dispatcher; + } + + /** + * Gets the event's name. + * + * @return string + * + * @deprecated since version 2.4, to be removed in 3.0. The event name is passed to the listener call. + */ + public function getName() + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.4 and will be removed in 3.0. The event name can be received in the listener call instead.', E_USER_DEPRECATED); + + return $this->name; + } + + /** + * Sets the event's name property. + * + * @param string $name The event name. + * + * @deprecated since version 2.4, to be removed in 3.0. The event name is passed to the listener call. + */ + public function setName($name) + { + $this->name = $name; + } +} diff --git a/vendor/symfony/event-dispatcher/EventDispatcher.php b/vendor/symfony/event-dispatcher/EventDispatcher.php new file mode 100644 index 00000000..f1b63f70 --- /dev/null +++ b/vendor/symfony/event-dispatcher/EventDispatcher.php @@ -0,0 +1,177 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * The EventDispatcherInterface is the central point of Symfony's event listener system. + * + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @author Guilherme Blanco <guilhermeblanco@hotmail.com> + * @author Jonathan Wage <jonwage@gmail.com> + * @author Roman Borschel <roman@code-factory.org> + * @author Bernhard Schussek <bschussek@gmail.com> + * @author Fabien Potencier <fabien@symfony.com> + * @author Jordi Boggiano <j.boggiano@seld.be> + * @author Jordan Alliot <jordan.alliot@gmail.com> + */ +class EventDispatcher implements EventDispatcherInterface +{ + private $listeners = array(); + private $sorted = array(); + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, Event $event = null) + { + if (null === $event) { + $event = new Event(); + } + + $event->setDispatcher($this); + $event->setName($eventName); + + if ($listeners = $this->getListeners($eventName)) { + $this->doDispatch($listeners, $eventName, $event); + } + + return $event; + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + if (null !== $eventName) { + if (!isset($this->listeners[$eventName])) { + return array(); + } + + if (!isset($this->sorted[$eventName])) { + $this->sortListeners($eventName); + } + + return $this->sorted[$eventName]; + } + + foreach ($this->listeners as $eventName => $eventListeners) { + if (!isset($this->sorted[$eventName])) { + $this->sortListeners($eventName); + } + } + + return array_filter($this->sorted); + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + return (bool) count($this->getListeners($eventName)); + } + + /** + * {@inheritdoc} + */ + public function addListener($eventName, $listener, $priority = 0) + { + $this->listeners[$eventName][$priority][] = $listener; + unset($this->sorted[$eventName]); + } + + /** + * {@inheritdoc} + */ + public function removeListener($eventName, $listener) + { + if (!isset($this->listeners[$eventName])) { + return; + } + + foreach ($this->listeners[$eventName] as $priority => $listeners) { + if (false !== ($key = array_search($listener, $listeners, true))) { + unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]); + } + } + } + + /** + * {@inheritdoc} + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { + if (is_string($params)) { + $this->addListener($eventName, array($subscriber, $params)); + } elseif (is_string($params[0])) { + $this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0); + } else { + foreach ($params as $listener) { + $this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0); + } + } + } + } + + /** + * {@inheritdoc} + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { + if (is_array($params) && is_array($params[0])) { + foreach ($params as $listener) { + $this->removeListener($eventName, array($subscriber, $listener[0])); + } + } else { + $this->removeListener($eventName, array($subscriber, is_string($params) ? $params : $params[0])); + } + } + } + + /** + * Triggers the listeners of an event. + * + * This method can be overridden to add functionality that is executed + * for each listener. + * + * @param callable[] $listeners The event listeners. + * @param string $eventName The name of the event to dispatch. + * @param Event $event The event object to pass to the event handlers/listeners. + */ + protected function doDispatch($listeners, $eventName, Event $event) + { + foreach ($listeners as $listener) { + if ($event->isPropagationStopped()) { + break; + } + call_user_func($listener, $event, $eventName, $this); + } + } + + /** + * Sorts the internal list of listeners for the given event by priority. + * + * @param string $eventName The name of the event. + */ + private function sortListeners($eventName) + { + $this->sorted[$eventName] = array(); + + krsort($this->listeners[$eventName]); + $this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]); + } +} diff --git a/vendor/symfony/event-dispatcher/EventDispatcherInterface.php b/vendor/symfony/event-dispatcher/EventDispatcherInterface.php new file mode 100644 index 00000000..a9bdd2c8 --- /dev/null +++ b/vendor/symfony/event-dispatcher/EventDispatcherInterface.php @@ -0,0 +1,88 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * The EventDispatcherInterface is the central point of Symfony's event listener system. + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @author Bernhard Schussek <bschussek@gmail.com> + */ +interface EventDispatcherInterface +{ + /** + * Dispatches an event to all registered listeners. + * + * @param string $eventName The name of the event to dispatch. The name of + * the event is the name of the method that is + * invoked on listeners. + * @param Event $event The event to pass to the event handlers/listeners. + * If not supplied, an empty Event instance is created. + * + * @return Event + */ + public function dispatch($eventName, Event $event = null); + + /** + * Adds an event listener that listens on the specified events. + * + * @param string $eventName The event to listen on + * @param callable $listener The listener + * @param int $priority The higher this value, the earlier an event + * listener will be triggered in the chain (defaults to 0) + */ + public function addListener($eventName, $listener, $priority = 0); + + /** + * Adds an event subscriber. + * + * The subscriber is asked for all the events he is + * interested in and added as a listener for these events. + * + * @param EventSubscriberInterface $subscriber The subscriber. + */ + public function addSubscriber(EventSubscriberInterface $subscriber); + + /** + * Removes an event listener from the specified events. + * + * @param string $eventName The event to remove a listener from + * @param callable $listener The listener to remove + */ + public function removeListener($eventName, $listener); + + /** + * Removes an event subscriber. + * + * @param EventSubscriberInterface $subscriber The subscriber + */ + public function removeSubscriber(EventSubscriberInterface $subscriber); + + /** + * Gets the listeners of a specific event or all listeners sorted by descending priority. + * + * @param string $eventName The name of the event + * + * @return array The event listeners for the specified event, or all event listeners by event name + */ + public function getListeners($eventName = null); + + /** + * Checks whether an event has any registered listeners. + * + * @param string $eventName The name of the event + * + * @return bool true if the specified event has any listeners, false otherwise + */ + public function hasListeners($eventName = null); +} diff --git a/vendor/symfony/event-dispatcher/EventSubscriberInterface.php b/vendor/symfony/event-dispatcher/EventSubscriberInterface.php new file mode 100644 index 00000000..8af77891 --- /dev/null +++ b/vendor/symfony/event-dispatcher/EventSubscriberInterface.php @@ -0,0 +1,46 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * An EventSubscriber knows himself what events he is interested in. + * If an EventSubscriber is added to an EventDispatcherInterface, the manager invokes + * {@link getSubscribedEvents} and registers the subscriber as a listener for all + * returned events. + * + * @author Guilherme Blanco <guilhermeblanco@hotmail.com> + * @author Jonathan Wage <jonwage@gmail.com> + * @author Roman Borschel <roman@code-factory.org> + * @author Bernhard Schussek <bschussek@gmail.com> + */ +interface EventSubscriberInterface +{ + /** + * Returns an array of event names this subscriber wants to listen to. + * + * The array keys are event names and the value can be: + * + * * The method name to call (priority defaults to 0) + * * An array composed of the method name to call and the priority + * * An array of arrays composed of the method names to call and respective + * priorities, or 0 if unset + * + * For instance: + * + * * array('eventName' => 'methodName') + * * array('eventName' => array('methodName', $priority)) + * * array('eventName' => array(array('methodName1', $priority), array('methodName2'))) + * + * @return array The event names to listen to + */ + public static function getSubscribedEvents(); +} diff --git a/vendor/symfony/event-dispatcher/GenericEvent.php b/vendor/symfony/event-dispatcher/GenericEvent.php new file mode 100644 index 00000000..03cbcfe3 --- /dev/null +++ b/vendor/symfony/event-dispatcher/GenericEvent.php @@ -0,0 +1,186 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * Event encapsulation class. + * + * Encapsulates events thus decoupling the observer from the subject they encapsulate. + * + * @author Drak <drak@zikula.org> + */ +class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate +{ + /** + * Event subject. + * + * @var mixed usually object or callable + */ + protected $subject; + + /** + * Array of arguments. + * + * @var array + */ + protected $arguments; + + /** + * Encapsulate an event with $subject and $args. + * + * @param mixed $subject The subject of the event, usually an object. + * @param array $arguments Arguments to store in the event. + */ + public function __construct($subject = null, array $arguments = array()) + { + $this->subject = $subject; + $this->arguments = $arguments; + } + + /** + * Getter for subject property. + * + * @return mixed $subject The observer subject. + */ + public function getSubject() + { + return $this->subject; + } + + /** + * Get argument by key. + * + * @param string $key Key. + * + * @return mixed Contents of array key. + * + * @throws \InvalidArgumentException If key is not found. + */ + public function getArgument($key) + { + if ($this->hasArgument($key)) { + return $this->arguments[$key]; + } + + throw new \InvalidArgumentException(sprintf('Argument "%s" not found.', $key)); + } + + /** + * Add argument to event. + * + * @param string $key Argument name. + * @param mixed $value Value. + * + * @return GenericEvent + */ + public function setArgument($key, $value) + { + $this->arguments[$key] = $value; + + return $this; + } + + /** + * Getter for all arguments. + * + * @return array + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Set args property. + * + * @param array $args Arguments. + * + * @return GenericEvent + */ + public function setArguments(array $args = array()) + { + $this->arguments = $args; + + return $this; + } + + /** + * Has argument. + * + * @param string $key Key of arguments array. + * + * @return bool + */ + public function hasArgument($key) + { + return array_key_exists($key, $this->arguments); + } + + /** + * ArrayAccess for argument getter. + * + * @param string $key Array key. + * + * @return mixed + * + * @throws \InvalidArgumentException If key does not exist in $this->args. + */ + public function offsetGet($key) + { + return $this->getArgument($key); + } + + /** + * ArrayAccess for argument setter. + * + * @param string $key Array key to set. + * @param mixed $value Value. + */ + public function offsetSet($key, $value) + { + $this->setArgument($key, $value); + } + + /** + * ArrayAccess for unset argument. + * + * @param string $key Array key. + */ + public function offsetUnset($key) + { + if ($this->hasArgument($key)) { + unset($this->arguments[$key]); + } + } + + /** + * ArrayAccess has argument. + * + * @param string $key Array key. + * + * @return bool + */ + public function offsetExists($key) + { + return $this->hasArgument($key); + } + + /** + * IteratorAggregate for iterating over the object like an array. + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->arguments); + } +} diff --git a/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php b/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php new file mode 100644 index 00000000..7ef9ece7 --- /dev/null +++ b/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php @@ -0,0 +1,93 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * A read-only proxy for an event dispatcher. + * + * @author Bernhard Schussek <bschussek@gmail.com> + */ +class ImmutableEventDispatcher implements EventDispatcherInterface +{ + /** + * The proxied dispatcher. + * + * @var EventDispatcherInterface + */ + private $dispatcher; + + /** + * Creates an unmodifiable proxy for an event dispatcher. + * + * @param EventDispatcherInterface $dispatcher The proxied event dispatcher. + */ + public function __construct(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, Event $event = null) + { + return $this->dispatcher->dispatch($eventName, $event); + } + + /** + * {@inheritdoc} + */ + public function addListener($eventName, $listener, $priority = 0) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function removeListener($eventName, $listener) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + return $this->dispatcher->getListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + return $this->dispatcher->hasListeners($eventName); + } +} diff --git a/vendor/symfony/event-dispatcher/LICENSE b/vendor/symfony/event-dispatcher/LICENSE new file mode 100644 index 00000000..12a74531 --- /dev/null +++ b/vendor/symfony/event-dispatcher/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2016 Fabien Potencier + +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. diff --git a/vendor/symfony/event-dispatcher/README.md b/vendor/symfony/event-dispatcher/README.md new file mode 100644 index 00000000..185c3fec --- /dev/null +++ b/vendor/symfony/event-dispatcher/README.md @@ -0,0 +1,15 @@ +EventDispatcher Component +========================= + +The EventDispatcher component provides tools that allow your application +components to communicate with each other by dispatching events and listening to +them. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/event_dispatcher/index.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php new file mode 100644 index 00000000..2e4c3fd9 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php @@ -0,0 +1,382 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +abstract class AbstractEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + /* Some pseudo events */ + const preFoo = 'pre.foo'; + const postFoo = 'post.foo'; + const preBar = 'pre.bar'; + const postBar = 'post.bar'; + + /** + * @var EventDispatcher + */ + private $dispatcher; + + private $listener; + + protected function setUp() + { + $this->dispatcher = $this->createEventDispatcher(); + $this->listener = new TestEventListener(); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->listener = null; + } + + abstract protected function createEventDispatcher(); + + public function testInitialState() + { + $this->assertEquals(array(), $this->dispatcher->getListeners()); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testAddListener() + { + $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + $this->assertCount(1, $this->dispatcher->getListeners(self::preFoo)); + $this->assertCount(1, $this->dispatcher->getListeners(self::postFoo)); + $this->assertCount(2, $this->dispatcher->getListeners()); + } + + public function testGetListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener1->name = '1'; + $listener2->name = '2'; + $listener3->name = '3'; + + $this->dispatcher->addListener('pre.foo', array($listener1, 'preFoo'), -10); + $this->dispatcher->addListener('pre.foo', array($listener2, 'preFoo'), 10); + $this->dispatcher->addListener('pre.foo', array($listener3, 'preFoo')); + + $expected = array( + array($listener2, 'preFoo'), + array($listener3, 'preFoo'), + array($listener1, 'preFoo'), + ); + + $this->assertSame($expected, $this->dispatcher->getListeners('pre.foo')); + } + + public function testGetAllListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener4 = new TestEventListener(); + $listener5 = new TestEventListener(); + $listener6 = new TestEventListener(); + + $this->dispatcher->addListener('pre.foo', $listener1, -10); + $this->dispatcher->addListener('pre.foo', $listener2); + $this->dispatcher->addListener('pre.foo', $listener3, 10); + $this->dispatcher->addListener('post.foo', $listener4, -10); + $this->dispatcher->addListener('post.foo', $listener5); + $this->dispatcher->addListener('post.foo', $listener6, 10); + + $expected = array( + 'pre.foo' => array($listener3, $listener2, $listener1), + 'post.foo' => array($listener6, $listener5, $listener4), + ); + + $this->assertSame($expected, $this->dispatcher->getListeners()); + } + + public function testDispatch() + { + $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); + $this->dispatcher->dispatch(self::preFoo); + $this->assertTrue($this->listener->preFooInvoked); + $this->assertFalse($this->listener->postFooInvoked); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch('noevent')); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(self::preFoo)); + $event = new Event(); + $return = $this->dispatcher->dispatch(self::preFoo, $event); + $this->assertSame($event, $return); + } + + /** + * @group legacy + */ + public function testLegacyDispatch() + { + $event = new Event(); + $return = $this->dispatcher->dispatch(self::preFoo, $event); + $this->assertEquals('pre.foo', $event->getName()); + } + + public function testDispatchForClosure() + { + $invoked = 0; + $listener = function () use (&$invoked) { + ++$invoked; + }; + $this->dispatcher->addListener('pre.foo', $listener); + $this->dispatcher->addListener('post.foo', $listener); + $this->dispatcher->dispatch(self::preFoo); + $this->assertEquals(1, $invoked); + } + + public function testStopEventPropagation() + { + $otherListener = new TestEventListener(); + + // postFoo() stops the propagation, so only one listener should + // be executed + // Manually set priority to enforce $this->listener to be called first + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'), 10); + $this->dispatcher->addListener('post.foo', array($otherListener, 'preFoo')); + $this->dispatcher->dispatch(self::postFoo); + $this->assertTrue($this->listener->postFooInvoked); + $this->assertFalse($otherListener->postFooInvoked); + } + + public function testDispatchByPriority() + { + $invoked = array(); + $listener1 = function () use (&$invoked) { + $invoked[] = '1'; + }; + $listener2 = function () use (&$invoked) { + $invoked[] = '2'; + }; + $listener3 = function () use (&$invoked) { + $invoked[] = '3'; + }; + $this->dispatcher->addListener('pre.foo', $listener1, -10); + $this->dispatcher->addListener('pre.foo', $listener2); + $this->dispatcher->addListener('pre.foo', $listener3, 10); + $this->dispatcher->dispatch(self::preFoo); + $this->assertEquals(array('3', '2', '1'), $invoked); + } + + public function testRemoveListener() + { + $this->dispatcher->addListener('pre.bar', $this->listener); + $this->assertTrue($this->dispatcher->hasListeners(self::preBar)); + $this->dispatcher->removeListener('pre.bar', $this->listener); + $this->assertFalse($this->dispatcher->hasListeners(self::preBar)); + $this->dispatcher->removeListener('notExists', $this->listener); + } + + public function testAddSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testAddSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $listeners = $this->dispatcher->getListeners('pre.foo'); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Tests\TestEventSubscriberWithPriorities', $listeners[0][0]); + } + + public function testAddSubscriberWithMultipleListeners() + { + $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $listeners = $this->dispatcher->getListeners('pre.foo'); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertEquals('preFoo2', $listeners[0][1]); + } + + public function testRemoveSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testRemoveSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + } + + public function testRemoveSubscriberWithMultipleListeners() + { + $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $this->dispatcher->getListeners(self::preFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + } + + /** + * @group legacy + */ + public function testLegacyEventReceivesTheDispatcherInstance() + { + $dispatcher = null; + $this->dispatcher->addListener('test', function ($event) use (&$dispatcher) { + $dispatcher = $event->getDispatcher(); + }); + $this->dispatcher->dispatch('test'); + $this->assertSame($this->dispatcher, $dispatcher); + } + + public function testEventReceivesTheDispatcherInstanceAsArgument() + { + $listener = new TestWithDispatcher(); + $this->dispatcher->addListener('test', array($listener, 'foo')); + $this->assertNull($listener->name); + $this->assertNull($listener->dispatcher); + $this->dispatcher->dispatch('test'); + $this->assertEquals('test', $listener->name); + $this->assertSame($this->dispatcher, $listener->dispatcher); + } + + /** + * @see https://bugs.php.net/bug.php?id=62976 + * + * This bug affects: + * - The PHP 5.3 branch for versions < 5.3.18 + * - The PHP 5.4 branch for versions < 5.4.8 + * - The PHP 5.5 branch is not affected + */ + public function testWorkaroundForPhpBug62976() + { + $dispatcher = $this->createEventDispatcher(); + $dispatcher->addListener('bug.62976', new CallableClass()); + $dispatcher->removeListener('bug.62976', function () {}); + $this->assertTrue($dispatcher->hasListeners('bug.62976')); + } + + public function testHasListenersWhenAddedCallbackListenerIsRemoved() + { + $listener = function () {}; + $this->dispatcher->addListener('foo', $listener); + $this->dispatcher->removeListener('foo', $listener); + $this->assertFalse($this->dispatcher->hasListeners()); + } + + public function testGetListenersWhenAddedCallbackListenerIsRemoved() + { + $listener = function () {}; + $this->dispatcher->addListener('foo', $listener); + $this->dispatcher->removeListener('foo', $listener); + $this->assertSame(array(), $this->dispatcher->getListeners()); + } + + public function testHasListenersWithoutEventsReturnsFalseAfterHasListenersWithEventHasBeenCalled() + { + $this->assertFalse($this->dispatcher->hasListeners('foo')); + $this->assertFalse($this->dispatcher->hasListeners()); + } +} + +class CallableClass +{ + public function __invoke() + { + } +} + +class TestEventListener +{ + public $preFooInvoked = false; + public $postFooInvoked = false; + + /* Listener methods */ + + public function preFoo(Event $e) + { + $this->preFooInvoked = true; + } + + public function postFoo(Event $e) + { + $this->postFooInvoked = true; + + $e->stopPropagation(); + } +} + +class TestWithDispatcher +{ + public $name; + public $dispatcher; + + public function foo(Event $e, $name, $dispatcher) + { + $this->name = $name; + $this->dispatcher = $dispatcher; + } +} + +class TestEventSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('pre.foo' => 'preFoo', 'post.foo' => 'postFoo'); + } +} + +class TestEventSubscriberWithPriorities implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + 'pre.foo' => array('preFoo', 10), + 'post.foo' => array('postFoo'), + ); + } +} + +class TestEventSubscriberWithMultipleListeners implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('pre.foo' => array( + array('preFoo1'), + array('preFoo2', 10), + )); + } +} diff --git a/vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php new file mode 100644 index 00000000..0f3f5ba7 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php @@ -0,0 +1,273 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\Scope; +use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class ContainerAwareEventDispatcherTest extends AbstractEventDispatcherTest +{ + protected function createEventDispatcher() + { + $container = new Container(); + + return new ContainerAwareEventDispatcher($container); + } + + public function testAddAListenerService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->dispatch('onEvent', $event); + } + + public function testAddASubscriberService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\SubscriberService'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $service + ->expects($this->once()) + ->method('onEventWithPriority') + ->with($event) + ; + + $service + ->expects($this->once()) + ->method('onEventNested') + ->with($event) + ; + + $container = new Container(); + $container->set('service.subscriber', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addSubscriberService('service.subscriber', 'Symfony\Component\EventDispatcher\Tests\SubscriberService'); + + $dispatcher->dispatch('onEvent', $event); + $dispatcher->dispatch('onEventWithPriority', $event); + $dispatcher->dispatch('onEventNested', $event); + } + + public function testPreventDuplicateListenerService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 5); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 10); + + $dispatcher->dispatch('onEvent', $event); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testTriggerAListenerServiceOutOfScope() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $scope = new Scope('scope'); + $container = new Container(); + $container->addScope($scope); + $container->enterScope('scope'); + + $container->set('service.listener', $service, 'scope'); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $container->leaveScope('scope'); + $dispatcher->dispatch('onEvent'); + } + + public function testReEnteringAScope() + { + $event = new Event(); + + $service1 = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service1 + ->expects($this->exactly(2)) + ->method('onEvent') + ->with($event) + ; + + $scope = new Scope('scope'); + $container = new Container(); + $container->addScope($scope); + $container->enterScope('scope'); + + $container->set('service.listener', $service1, 'scope'); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + $dispatcher->dispatch('onEvent', $event); + + $service2 = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service2 + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container->enterScope('scope'); + $container->set('service.listener', $service2, 'scope'); + + $dispatcher->dispatch('onEvent', $event); + + $container->leaveScope('scope'); + + $dispatcher->dispatch('onEvent'); + } + + public function testHasListenersOnLazyLoad() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $event->setDispatcher($dispatcher); + $event->setName('onEvent'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $this->assertTrue($dispatcher->hasListeners()); + + if ($dispatcher->hasListeners('onEvent')) { + $dispatcher->dispatch('onEvent'); + } + } + + public function testGetListenersOnLazyLoad() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $listeners = $dispatcher->getListeners(); + + $this->assertTrue(isset($listeners['onEvent'])); + + $this->assertCount(1, $dispatcher->getListeners('onEvent')); + } + + public function testRemoveAfterDispatch() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->dispatch('onEvent', new Event()); + $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); + $this->assertFalse($dispatcher->hasListeners('onEvent')); + } + + public function testRemoveBeforeDispatch() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); + $this->assertFalse($dispatcher->hasListeners('onEvent')); + } +} + +class Service +{ + public function onEvent(Event $e) + { + } +} + +class SubscriberService implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + 'onEvent' => 'onEvent', + 'onEventWithPriority' => array('onEventWithPriority', 10), + 'onEventNested' => array(array('onEventNested')), + ); + } + + public function onEvent(Event $e) + { + } + + public function onEventWithPriority(Event $e) + { + } + + public function onEventNested(Event $e) + { + } +} diff --git a/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php new file mode 100644 index 00000000..4aa6226e --- /dev/null +++ b/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php @@ -0,0 +1,199 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests\Debug; + +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\Stopwatch\Stopwatch; + +class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + public function testAddRemoveListener() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $tdispatcher->addListener('foo', $listener = function () {; }); + $listeners = $dispatcher->getListeners('foo'); + $this->assertCount(1, $listeners); + $this->assertSame($listener, $listeners[0]); + + $tdispatcher->removeListener('foo', $listener); + $this->assertCount(0, $dispatcher->getListeners('foo')); + } + + public function testGetListeners() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $tdispatcher->addListener('foo', $listener = function () {; }); + $this->assertSame($dispatcher->getListeners('foo'), $tdispatcher->getListeners('foo')); + } + + public function testHasListeners() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $this->assertFalse($dispatcher->hasListeners('foo')); + $this->assertFalse($tdispatcher->hasListeners('foo')); + + $tdispatcher->addListener('foo', $listener = function () {; }); + $this->assertTrue($dispatcher->hasListeners('foo')); + $this->assertTrue($tdispatcher->hasListeners('foo')); + } + + public function testAddRemoveSubscriber() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $subscriber = new EventSubscriber(); + + $tdispatcher->addSubscriber($subscriber); + $listeners = $dispatcher->getListeners('foo'); + $this->assertCount(1, $listeners); + $this->assertSame(array($subscriber, 'call'), $listeners[0]); + + $tdispatcher->removeSubscriber($subscriber); + $this->assertCount(0, $dispatcher->getListeners('foo')); + } + + public function testGetCalledListeners() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + $tdispatcher->addListener('foo', $listener = function () {; }); + + $this->assertEquals(array(), $tdispatcher->getCalledListeners()); + $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure')), $tdispatcher->getNotCalledListeners()); + + $tdispatcher->dispatch('foo'); + + $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure')), $tdispatcher->getCalledListeners()); + $this->assertEquals(array(), $tdispatcher->getNotCalledListeners()); + } + + public function testGetCalledListenersNested() + { + $tdispatcher = null; + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $dispatcher->addListener('foo', function (Event $event, $eventName, $dispatcher) use (&$tdispatcher) { + $tdispatcher = $dispatcher; + $dispatcher->dispatch('bar'); + }); + $dispatcher->addListener('bar', function (Event $event) {}); + $dispatcher->dispatch('foo'); + $this->assertSame($dispatcher, $tdispatcher); + $this->assertCount(2, $dispatcher->getCalledListeners()); + } + + public function testLogger() + { + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger); + $tdispatcher->addListener('foo', $listener1 = function () {; }); + $tdispatcher->addListener('foo', $listener2 = function () {; }); + + $logger->expects($this->at(0))->method('debug')->with('Notified event "foo" to listener "closure".'); + $logger->expects($this->at(1))->method('debug')->with('Notified event "foo" to listener "closure".'); + + $tdispatcher->dispatch('foo'); + } + + public function testLoggerWithStoppedEvent() + { + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger); + $tdispatcher->addListener('foo', $listener1 = function (Event $event) { $event->stopPropagation(); }); + $tdispatcher->addListener('foo', $listener2 = function () {; }); + + $logger->expects($this->at(0))->method('debug')->with('Notified event "foo" to listener "closure".'); + $logger->expects($this->at(1))->method('debug')->with('Listener "closure" stopped propagation of the event "foo".'); + $logger->expects($this->at(2))->method('debug')->with('Listener "closure" was not called for event "foo".'); + + $tdispatcher->dispatch('foo'); + } + + public function testDispatchCallListeners() + { + $called = array(); + + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + $tdispatcher->addListener('foo', $listener1 = function () use (&$called) { $called[] = 'foo1'; }); + $tdispatcher->addListener('foo', $listener2 = function () use (&$called) { $called[] = 'foo2'; }); + + $tdispatcher->dispatch('foo'); + + $this->assertEquals(array('foo1', 'foo2'), $called); + } + + public function testDispatchNested() + { + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $loop = 1; + $dispatcher->addListener('foo', $listener1 = function () use ($dispatcher, &$loop) { + ++$loop; + if (2 == $loop) { + $dispatcher->dispatch('foo'); + } + }); + + $dispatcher->dispatch('foo'); + } + + public function testDispatchReusedEventNested() + { + $nestedCall = false; + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $dispatcher->addListener('foo', function (Event $e) use ($dispatcher) { + $dispatcher->dispatch('bar', $e); + }); + $dispatcher->addListener('bar', function (Event $e) use (&$nestedCall) { + $nestedCall = true; + }); + + $this->assertFalse($nestedCall); + $dispatcher->dispatch('foo'); + $this->assertTrue($nestedCall); + } + + public function testListenerCanRemoveItselfWhenExecuted() + { + $eventDispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $listener1 = function ($event, $eventName, EventDispatcherInterface $dispatcher) use (&$listener1) { + $dispatcher->removeListener('foo', $listener1); + }; + $eventDispatcher->addListener('foo', $listener1); + $eventDispatcher->addListener('foo', function () {}); + $eventDispatcher->dispatch('foo'); + + $this->assertCount(1, $eventDispatcher->getListeners('foo'), 'expected listener1 to be removed'); + } +} + +class EventSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('foo' => 'call'); + } +} diff --git a/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php b/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php new file mode 100644 index 00000000..0fdd6372 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php @@ -0,0 +1,200 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; + +class RegisterListenersPassTest extends \PHPUnit_Framework_TestCase +{ + /** + * Tests that event subscribers not implementing EventSubscriberInterface + * trigger an exception. + * + * @expectedException \InvalidArgumentException + */ + public function testEventSubscriberWithoutInterface() + { + // one service, not implementing any interface + $services = array( + 'my_event_subscriber' => array(0 => array()), + ); + + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $definition->expects($this->atLeastOnce()) + ->method('isPublic') + ->will($this->returnValue(true)); + $definition->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue('stdClass')); + + $builder = $this->getMock( + 'Symfony\Component\DependencyInjection\ContainerBuilder', + array('hasDefinition', 'findTaggedServiceIds', 'getDefinition') + ); + $builder->expects($this->any()) + ->method('hasDefinition') + ->will($this->returnValue(true)); + + // We don't test kernel.event_listener here + $builder->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->onConsecutiveCalls(array(), $services)); + + $builder->expects($this->atLeastOnce()) + ->method('getDefinition') + ->will($this->returnValue($definition)); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($builder); + } + + public function testValidEventSubscriber() + { + $services = array( + 'my_event_subscriber' => array(0 => array()), + ); + + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $definition->expects($this->atLeastOnce()) + ->method('isPublic') + ->will($this->returnValue(true)); + $definition->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue('Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService')); + + $builder = $this->getMock( + 'Symfony\Component\DependencyInjection\ContainerBuilder', + array('hasDefinition', 'findTaggedServiceIds', 'getDefinition', 'findDefinition') + ); + $builder->expects($this->any()) + ->method('hasDefinition') + ->will($this->returnValue(true)); + + // We don't test kernel.event_listener here + $builder->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->onConsecutiveCalls(array(), $services)); + + $builder->expects($this->atLeastOnce()) + ->method('getDefinition') + ->will($this->returnValue($definition)); + + $builder->expects($this->atLeastOnce()) + ->method('findDefinition') + ->will($this->returnValue($definition)); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($builder); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "foo" must be public as event listeners are lazy-loaded. + */ + public function testPrivateEventListener() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setPublic(false)->addTag('kernel.event_listener', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "foo" must be public as event subscribers are lazy-loaded. + */ + public function testPrivateEventSubscriber() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setPublic(false)->addTag('kernel.event_subscriber', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "foo" must not be abstract as event listeners are lazy-loaded. + */ + public function testAbstractEventListener() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_listener', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "foo" must not be abstract as event subscribers are lazy-loaded. + */ + public function testAbstractEventSubscriber() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_subscriber', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + } + + public function testEventSubscriberResolvableClassName() + { + $container = new ContainerBuilder(); + + $container->setParameter('subscriber.class', 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService'); + $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + + $definition = $container->getDefinition('event_dispatcher'); + $expected_calls = array( + array( + 'addSubscriberService', + array( + 'foo', + 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService', + ), + ), + ); + $this->assertSame($expected_calls, $definition->getMethodCalls()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage You have requested a non-existent parameter "subscriber.class" + */ + public function testEventSubscriberUnresolvableClassName() + { + $container = new ContainerBuilder(); + $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + } +} + +class SubscriberService implements \Symfony\Component\EventDispatcher\EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + } +} diff --git a/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php new file mode 100644 index 00000000..5faa5c8b --- /dev/null +++ b/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php @@ -0,0 +1,22 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\EventDispatcher; + +class EventDispatcherTest extends AbstractEventDispatcherTest +{ + protected function createEventDispatcher() + { + return new EventDispatcher(); + } +} diff --git a/vendor/symfony/event-dispatcher/Tests/EventTest.php b/vendor/symfony/event-dispatcher/Tests/EventTest.php new file mode 100644 index 00000000..9a822670 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Tests/EventTest.php @@ -0,0 +1,96 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcher; + +/** + * Test class for Event. + */ +class EventTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Symfony\Component\EventDispatcher\Event + */ + protected $event; + + /** + * @var \Symfony\Component\EventDispatcher\EventDispatcher + */ + protected $dispatcher; + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + $this->event = new Event(); + $this->dispatcher = new EventDispatcher(); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + $this->event = null; + $this->dispatcher = null; + } + + public function testIsPropagationStopped() + { + $this->assertFalse($this->event->isPropagationStopped()); + } + + public function testStopPropagationAndIsPropagationStopped() + { + $this->event->stopPropagation(); + $this->assertTrue($this->event->isPropagationStopped()); + } + + /** + * @group legacy + */ + public function testLegacySetDispatcher() + { + $this->event->setDispatcher($this->dispatcher); + $this->assertSame($this->dispatcher, $this->event->getDispatcher()); + } + + /** + * @group legacy + */ + public function testLegacyGetDispatcher() + { + $this->assertNull($this->event->getDispatcher()); + } + + /** + * @group legacy + */ + public function testLegacyGetName() + { + $this->assertNull($this->event->getName()); + } + + /** + * @group legacy + */ + public function testLegacySetName() + { + $this->event->setName('foo'); + $this->assertEquals('foo', $this->event->getName()); + } +} diff --git a/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php b/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php new file mode 100644 index 00000000..aebd82da --- /dev/null +++ b/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php @@ -0,0 +1,139 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\GenericEvent; + +/** + * Test class for Event. + */ +class GenericEventTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var GenericEvent + */ + private $event; + + private $subject; + + /** + * Prepares the environment before running a test. + */ + protected function setUp() + { + parent::setUp(); + + $this->subject = new \stdClass(); + $this->event = new GenericEvent($this->subject, array('name' => 'Event')); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->subject = null; + $this->event = null; + + parent::tearDown(); + } + + public function testConstruct() + { + $this->assertEquals($this->event, new GenericEvent($this->subject, array('name' => 'Event'))); + } + + /** + * Tests Event->getArgs(). + */ + public function testGetArguments() + { + // test getting all + $this->assertSame(array('name' => 'Event'), $this->event->getArguments()); + } + + public function testSetArguments() + { + $result = $this->event->setArguments(array('foo' => 'bar')); + $this->assertAttributeSame(array('foo' => 'bar'), 'arguments', $this->event); + $this->assertSame($this->event, $result); + } + + public function testSetArgument() + { + $result = $this->event->setArgument('foo2', 'bar2'); + $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event); + $this->assertEquals($this->event, $result); + } + + public function testGetArgument() + { + // test getting key + $this->assertEquals('Event', $this->event->getArgument('name')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testGetArgException() + { + $this->event->getArgument('nameNotExist'); + } + + public function testOffsetGet() + { + // test getting key + $this->assertEquals('Event', $this->event['name']); + + // test getting invalid arg + $this->setExpectedException('InvalidArgumentException'); + $this->assertFalse($this->event['nameNotExist']); + } + + public function testOffsetSet() + { + $this->event['foo2'] = 'bar2'; + $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event); + } + + public function testOffsetUnset() + { + unset($this->event['name']); + $this->assertAttributeSame(array(), 'arguments', $this->event); + } + + public function testOffsetIsset() + { + $this->assertTrue(isset($this->event['name'])); + $this->assertFalse(isset($this->event['nameNotExist'])); + } + + public function testHasArgument() + { + $this->assertTrue($this->event->hasArgument('name')); + $this->assertFalse($this->event->hasArgument('nameNotExist')); + } + + public function testGetSubject() + { + $this->assertSame($this->subject, $this->event->getSubject()); + } + + public function testHasIterator() + { + $data = array(); + foreach ($this->event as $key => $value) { + $data[$key] = $value; + } + $this->assertEquals(array('name' => 'Event'), $data); + } +} diff --git a/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php new file mode 100644 index 00000000..80a7e43b --- /dev/null +++ b/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php @@ -0,0 +1,105 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\ImmutableEventDispatcher; + +/** + * @author Bernhard Schussek <bschussek@gmail.com> + */ +class ImmutableEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $innerDispatcher; + + /** + * @var ImmutableEventDispatcher + */ + private $dispatcher; + + protected function setUp() + { + $this->innerDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->dispatcher = new ImmutableEventDispatcher($this->innerDispatcher); + } + + public function testDispatchDelegates() + { + $event = new Event(); + + $this->innerDispatcher->expects($this->once()) + ->method('dispatch') + ->with('event', $event) + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->dispatch('event', $event)); + } + + public function testGetListenersDelegates() + { + $this->innerDispatcher->expects($this->once()) + ->method('getListeners') + ->with('event') + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->getListeners('event')); + } + + public function testHasListenersDelegates() + { + $this->innerDispatcher->expects($this->once()) + ->method('hasListeners') + ->with('event') + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->hasListeners('event')); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testAddListenerDisallowed() + { + $this->dispatcher->addListener('event', function () { return 'foo'; }); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testAddSubscriberDisallowed() + { + $subscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface'); + + $this->dispatcher->addSubscriber($subscriber); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testRemoveListenerDisallowed() + { + $this->dispatcher->removeListener('event', function () { return 'foo'; }); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testRemoveSubscriberDisallowed() + { + $subscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface'); + + $this->dispatcher->removeSubscriber($subscriber); + } +} diff --git a/vendor/symfony/event-dispatcher/composer.json b/vendor/symfony/event-dispatcher/composer.json new file mode 100644 index 00000000..3a20c35f --- /dev/null +++ b/vendor/symfony/event-dispatcher/composer.json @@ -0,0 +1,44 @@ +{ + "name": "symfony/event-dispatcher", + "type": "library", + "description": "Symfony EventDispatcher Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "symfony/dependency-injection": "~2.6", + "symfony/expression-language": "~2.6", + "symfony/config": "~2.0,>=2.0.5", + "symfony/stopwatch": "~2.3", + "psr/log": "~1.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + } +} diff --git a/vendor/symfony/event-dispatcher/phpunit.xml.dist b/vendor/symfony/event-dispatcher/phpunit.xml.dist new file mode 100644 index 00000000..ae0586e0 --- /dev/null +++ b/vendor/symfony/event-dispatcher/phpunit.xml.dist @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd" + backupGlobals="false" + colors="true" + bootstrap="vendor/autoload.php" +> + <php> + <ini name="error_reporting" value="-1" /> + </php> + + <testsuites> + <testsuite name="Symfony EventDispatcher Component Test Suite"> + <directory>./Tests/</directory> + </testsuite> + </testsuites> + + <filter> + <whitelist> + <directory>./</directory> + <exclude> + <directory>./Resources</directory> + <directory>./Tests</directory> + <directory>./vendor</directory> + </exclude> + </whitelist> + </filter> +</phpunit> diff --git a/vendor/symfony/polyfill-mbstring/LICENSE b/vendor/symfony/polyfill-mbstring/LICENSE new file mode 100644 index 00000000..39fa189d --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2016 Fabien Potencier + +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. diff --git a/vendor/symfony/polyfill-mbstring/Mbstring.php b/vendor/symfony/polyfill-mbstring/Mbstring.php new file mode 100644 index 00000000..97e8c9b4 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Mbstring.php @@ -0,0 +1,664 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Mbstring; + +/** + * Partial mbstring implementation in PHP, iconv based, UTF-8 centric. + * + * Implemented: + * - mb_chr - Returns a specific character from its Unicode code point + * - mb_convert_encoding - Convert character encoding + * - mb_convert_variables - Convert character code in variable(s) + * - mb_decode_mimeheader - Decode string in MIME header field + * - mb_encode_mimeheader - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED + * - mb_convert_case - Perform case folding on a string + * - mb_get_info - Get internal settings of mbstring + * - mb_http_input - Detect HTTP input character encoding + * - mb_http_output - Set/Get HTTP output character encoding + * - mb_internal_encoding - Set/Get internal character encoding + * - mb_list_encodings - Returns an array of all supported encodings + * - mb_ord - Returns the Unicode code point of a character + * - mb_output_handler - Callback function converts character encoding in output buffer + * - mb_scrub - Replaces ill-formed byte sequences with substitute characters + * - mb_strlen - Get string length + * - mb_strpos - Find position of first occurrence of string in a string + * - mb_strrpos - Find position of last occurrence of a string in a string + * - mb_strtolower - Make a string lowercase + * - mb_strtoupper - Make a string uppercase + * - mb_substitute_character - Set/Get substitution character + * - mb_substr - Get part of string + * - mb_stripos - Finds position of first occurrence of a string within another, case insensitive + * - mb_stristr - Finds first occurrence of a string within another, case insensitive + * - mb_strrchr - Finds the last occurrence of a character in a string within another + * - mb_strrichr - Finds the last occurrence of a character in a string within another, case insensitive + * - mb_strripos - Finds position of last occurrence of a string within another, case insensitive + * - mb_strstr - Finds first occurrence of a string within anothers + * - mb_strwidth - Return width of string + * - mb_substr_count - Count the number of substring occurrences + * + * Not implemented: + * - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more) + * - mb_decode_numericentity - Decode HTML numeric string reference to character + * - mb_encode_numericentity - Encode character to HTML numeric string reference + * - mb_ereg_* - Regular expression with multibyte support + * - mb_parse_str - Parse GET/POST/COOKIE data and set global variable + * - mb_preferred_mime_name - Get MIME charset string + * - mb_regex_encoding - Returns current encoding for multibyte regex as string + * - mb_regex_set_options - Set/Get the default options for mbregex functions + * - mb_send_mail - Send encoded mail + * - mb_split - Split multibyte string using regular expression + * - mb_strcut - Get part of string + * - mb_strimwidth - Get truncated string with specified width + * + * @author Nicolas Grekas <p@tchwork.com> + * + * @internal + */ +final class Mbstring +{ + const MB_CASE_FOLD = PHP_INT_MAX; + + private static $encodingList = array('ASCII', 'UTF-8'); + private static $language = 'neutral'; + private static $internalEncoding = 'UTF-8'; + private static $caseFold = array( + array('µ','Å¿',"\xCD\x85",'Ï‚',"\xCF\x90","\xCF\x91","\xCF\x95","\xCF\x96","\xCF\xB0","\xCF\xB1","\xCF\xB5","\xE1\xBA\x9B","\xE1\xBE\xBE"), + array('μ','s','ι', 'σ','β', 'θ', 'φ', 'Ï€', 'κ', 'Ï', 'ε', "\xE1\xB9\xA1",'ι'), + ); + + public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null) + { + if (is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) { + $fromEncoding = self::mb_detect_encoding($s, $fromEncoding); + } else { + $fromEncoding = self::getEncoding($fromEncoding); + } + + $toEncoding = self::getEncoding($toEncoding); + + if ('BASE64' === $fromEncoding) { + $s = base64_decode($s); + $fromEncoding = $toEncoding; + } + + if ('BASE64' === $toEncoding) { + return base64_encode($s); + } + + if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) { + if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) { + $fromEncoding = 'Windows-1252'; + } + if ('UTF-8' !== $fromEncoding) { + $s = iconv($fromEncoding, 'UTF-8//IGNORE', $s); + } + + return preg_replace_callback('/[\x80-\xFF]+/', array(__CLASS__, 'html_encoding_callback'), $s); + } + + if ('HTML-ENTITIES' === $fromEncoding) { + $s = html_entity_decode($s, ENT_COMPAT, 'UTF-8'); + $fromEncoding = 'UTF-8'; + } + + return iconv($fromEncoding, $toEncoding.'//IGNORE', $s); + } + + public static function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) + { + $vars = array(&$a, &$b, &$c, &$d, &$e, &$f); + + $ok = true; + array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) { + if (false === $v = Mbstring::mb_convert_encoding($v, $toEncoding, $fromEncoding)) { + $ok = false; + } + }); + + return $ok ? $fromEncoding : false; + } + + public static function mb_decode_mimeheader($s) + { + return iconv_mime_decode($s, 2, self::$internalEncoding); + } + + public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null) + { + trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', E_USER_WARNING); + } + + public static function mb_convert_case($s, $mode, $encoding = null) + { + if ('' === $s .= '') { + return ''; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding) { + $encoding = null; + if (!preg_match('//u', $s)) { + $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); + } + } else { + $s = iconv($encoding, 'UTF-8//IGNORE', $s); + } + + if (MB_CASE_TITLE == $mode) { + $s = preg_replace_callback('/\b\p{Ll}/u', array(__CLASS__, 'title_case_upper'), $s); + $s = preg_replace_callback('/\B[\p{Lu}\p{Lt}]+/u', array(__CLASS__, 'title_case_lower'), $s); + } else { + if (MB_CASE_UPPER == $mode) { + static $upper = null; + if (null === $upper) { + $upper = self::getData('upperCase'); + } + $map = $upper; + } else { + if (self::MB_CASE_FOLD === $mode) { + $s = str_replace(self::$caseFold[0], self::$caseFold[1], $s); + } + + static $lower = null; + if (null === $lower) { + $lower = self::getData('lowerCase'); + } + $map = $lower; + } + + static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4); + + $i = 0; + $len = strlen($s); + + while ($i < $len) { + $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; + $uchr = substr($s, $i, $ulen); + $i += $ulen; + + if (isset($map[$uchr])) { + $uchr = $map[$uchr]; + $nlen = strlen($uchr); + + if ($nlen == $ulen) { + $nlen = $i; + do { + $s[--$nlen] = $uchr[--$ulen]; + } while ($ulen); + } else { + $s = substr_replace($s, $uchr, $i - $ulen, $ulen); + $len += $nlen - $ulen; + $i += $nlen - $ulen; + } + } + } + } + + if (null === $encoding) { + return $s; + } + + return iconv('UTF-8', $encoding.'//IGNORE', $s); + } + + public static function mb_internal_encoding($encoding = null) + { + if (null === $encoding) { + return self::$internalEncoding; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding || false !== @iconv($encoding, $encoding, ' ')) { + self::$internalEncoding = $encoding; + + return true; + } + + return false; + } + + public static function mb_language($lang = null) + { + if (null === $lang) { + return self::$language; + } + + switch ($lang = strtolower($lang)) { + case 'uni': + case 'neutral': + self::$language = $lang; + + return true; + } + + return false; + } + + public static function mb_list_encodings() + { + return array('UTF-8'); + } + + public static function mb_encoding_aliases($encoding) + { + switch (strtoupper($encoding)) { + case 'UTF8': + case 'UTF-8': + return array('utf8'); + } + + return false; + } + + public static function mb_check_encoding($var = null, $encoding = null) + { + if (null === $encoding) { + if (null === $var) { + return false; + } + $encoding = self::$internalEncoding; + } + + return self::mb_detect_encoding($var, array($encoding)) || false !== @iconv($encoding, $encoding, $var); + } + + public static function mb_detect_encoding($str, $encodingList = null, $strict = false) + { + if (null === $encodingList) { + $encodingList = self::$encodingList; + } else { + if (!is_array($encodingList)) { + $encodingList = array_map('trim', explode(',', $encodingList)); + } + $encodingList = array_map('strtoupper', $encodingList); + } + + foreach ($encodingList as $enc) { + switch ($enc) { + case 'ASCII': + if (!preg_match('/[\x80-\xFF]/', $str)) { + return $enc; + } + break; + + case 'UTF8': + case 'UTF-8': + if (preg_match('//u', $str)) { + return 'UTF-8'; + } + break; + + default: + if (0 === strncmp($enc, 'ISO-8859-', 9)) { + return $enc; + } + } + } + + return false; + } + + public static function mb_detect_order($encodingList = null) + { + if (null === $encodingList) { + return self::$encodingList; + } + + if (!is_array($encodingList)) { + $encodingList = array_map('trim', explode(',', $encodingList)); + } + $encodingList = array_map('strtoupper', $encodingList); + + foreach ($encodingList as $enc) { + switch ($enc) { + default: + if (strncmp($enc, 'ISO-8859-', 9)) { + return false; + } + case 'ASCII': + case 'UTF8': + case 'UTF-8': + } + } + + self::$encodingList = $encodingList; + + return true; + } + + public static function mb_strlen($s, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strlen($s); + } + + return @iconv_strlen($s, $encoding); + } + + public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strpos($haystack, $needle, $offset); + } + + if ('' === $needle .= '') { + trigger_error(__METHOD__.': Empty delimiter', E_USER_WARNING); + + return false; + } + + return iconv_strpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strrpos($haystack, $needle, $offset); + } + + if ($offset != (int) $offset) { + $offset = 0; + } elseif ($offset = (int) $offset) { + if ($offset < 0) { + $haystack = self::mb_substr($haystack, 0, $offset, $encoding); + $offset = 0; + } else { + $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding); + } + } + + $pos = iconv_strrpos($haystack, $needle, $encoding); + + return false !== $pos ? $offset + $pos : false; + } + + public static function mb_strtolower($s, $encoding = null) + { + return self::mb_convert_case($s, MB_CASE_LOWER, $encoding); + } + + public static function mb_strtoupper($s, $encoding = null) + { + return self::mb_convert_case($s, MB_CASE_UPPER, $encoding); + } + + public static function mb_substitute_character($c = null) + { + if (0 === strcasecmp($c, 'none')) { + return true; + } + + return null !== $c ? false : 'none'; + } + + public static function mb_substr($s, $start, $length = null, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return substr($s, $start, null === $length ? 2147483647 : $length); + } + + if ($start < 0) { + $start = iconv_strlen($s, $encoding) + $start; + if ($start < 0) { + $start = 0; + } + } + + if (null === $length) { + $length = 2147483647; + } elseif ($length < 0) { + $length = iconv_strlen($s, $encoding) + $length - $start; + if ($length < 0) { + return ''; + } + } + + return iconv_substr($s, $start, $length, $encoding).''; + } + + public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) + { + $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); + $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); + + return self::mb_strpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_stristr($haystack, $needle, $part = false, $encoding = null) + { + $pos = self::mb_stripos($haystack, $needle, 0, $encoding); + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strrchr($haystack, $needle, $part); + } + $needle = self::mb_substr($needle, 0, 1, $encoding); + $pos = iconv_strrpos($haystack, $needle, $encoding); + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null) + { + $needle = self::mb_substr($needle, 0, 1, $encoding); + $pos = self::mb_strripos($haystack, $needle, $encoding); + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) + { + $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); + $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); + + return self::mb_strrpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_strstr($haystack, $needle, $part = false, $encoding = null) + { + $pos = strpos($haystack, $needle); + if (false === $pos) { + return false; + } + if ($part) { + return substr($haystack, 0, $pos); + } + + return substr($haystack, $pos); + } + + public static function mb_get_info($type = 'all') + { + $info = array( + 'internal_encoding' => self::$internalEncoding, + 'http_output' => 'pass', + 'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)', + 'func_overload' => 0, + 'func_overload_list' => 'no overload', + 'mail_charset' => 'UTF-8', + 'mail_header_encoding' => 'BASE64', + 'mail_body_encoding' => 'BASE64', + 'illegal_chars' => 0, + 'encoding_translation' => 'Off', + 'language' => self::$language, + 'detect_order' => self::$encodingList, + 'substitute_character' => 'none', + 'strict_detection' => 'Off', + ); + + if ('all' === $type) { + return $info; + } + if (isset($info[$type])) { + return $info[$type]; + } + + return false; + } + + public static function mb_http_input($type = '') + { + return false; + } + + public static function mb_http_output($encoding = null) + { + return null !== $encoding ? 'pass' === $encoding : 'pass'; + } + + public static function mb_strwidth($s, $encoding = null) + { + $encoding = self::getEncoding($encoding); + + if ('UTF-8' !== $encoding) { + $s = iconv($encoding, 'UTF-8//IGNORE', $s); + } + + $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide); + + return ($wide << 1) + iconv_strlen($s, 'UTF-8'); + } + + public static function mb_substr_count($haystack, $needle, $encoding = null) + { + return substr_count($haystack, $needle); + } + + public static function mb_output_handler($contents, $status) + { + return $contents; + } + + public static function mb_chr($code, $encoding = null) + { + if (0x80 > $code %= 0x200000) { + $s = chr($code); + } elseif (0x800 > $code) { + $s = chr(0xC0 | $code >> 6).chr(0x80 | $code & 0x3F); + } elseif (0x10000 > $code) { + $s = chr(0xE0 | $code >> 12).chr(0x80 | $code >> 6 & 0x3F).chr(0x80 | $code & 0x3F); + } else { + $s = chr(0xF0 | $code >> 18).chr(0x80 | $code >> 12 & 0x3F).chr(0x80 | $code >> 6 & 0x3F).chr(0x80 | $code & 0x3F); + } + + if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { + $s = mb_convert_encoding($s, $encoding, 'UTF-8'); + } + + return $s; + } + + public static function mb_ord($s, $encoding = null) + { + if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { + $s = mb_convert_encoding($s, 'UTF-8', $encoding); + } + + $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0; + if (0xF0 <= $code) { + return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80; + } + if (0xE0 <= $code) { + return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80; + } + if (0xC0 <= $code) { + return (($code - 0xC0) << 6) + $s[2] - 0x80; + } + + return $code; + } + + private static function getSubpart($pos, $part, $haystack, $encoding) + { + if (false === $pos) { + return false; + } + if ($part) { + return self::mb_substr($haystack, 0, $pos, $encoding); + } + + return self::mb_substr($haystack, $pos, null, $encoding); + } + + private static function html_encoding_callback($m) + { + $i = 1; + $entities = ''; + $m = unpack('C*', htmlentities($m[0], ENT_COMPAT, 'UTF-8')); + + while (isset($m[$i])) { + if (0x80 > $m[$i]) { + $entities .= chr($m[$i++]); + continue; + } + if (0xF0 <= $m[$i]) { + $c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; + } elseif (0xE0 <= $m[$i]) { + $c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; + } else { + $c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80; + } + + $entities .= '&#'.$c.';'; + } + + return $entities; + } + + private static function title_case_lower($s) + { + return self::mb_convert_case($s[0], MB_CASE_LOWER, 'UTF-8'); + } + + private static function title_case_upper($s) + { + return self::mb_convert_case($s[0], MB_CASE_UPPER, 'UTF-8'); + } + + private static function getData($file) + { + if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) { + return require $file; + } + + return false; + } + + private static function getEncoding($encoding) + { + if (null === $encoding) { + return self::$internalEncoding; + } + + $encoding = strtoupper($encoding); + + if ('8BIT' === $encoding || 'BINARY' === $encoding) { + return 'CP850'; + } + if ('UTF8' === $encoding) { + return 'UTF-8'; + } + + return $encoding; + } +} diff --git a/vendor/symfony/polyfill-mbstring/README.md b/vendor/symfony/polyfill-mbstring/README.md new file mode 100644 index 00000000..342e8286 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/README.md @@ -0,0 +1,13 @@ +Symfony Polyfill / Mbstring +=========================== + +This component provides a partial, native PHP implementation for the +[Mbstring](http://php.net/mbstring) extension. + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php new file mode 100644 index 00000000..3ca16416 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php @@ -0,0 +1,1101 @@ +<?php + +static $data = array ( + 'A' => 'a', + 'B' => 'b', + 'C' => 'c', + 'D' => 'd', + 'E' => 'e', + 'F' => 'f', + 'G' => 'g', + 'H' => 'h', + 'I' => 'i', + 'J' => 'j', + 'K' => 'k', + 'L' => 'l', + 'M' => 'm', + 'N' => 'n', + 'O' => 'o', + 'P' => 'p', + 'Q' => 'q', + 'R' => 'r', + 'S' => 's', + 'T' => 't', + 'U' => 'u', + 'V' => 'v', + 'W' => 'w', + 'X' => 'x', + 'Y' => 'y', + 'Z' => 'z', + 'À' => 'à ', + 'Ã' => 'á', + 'Â' => 'â', + 'Ã' => 'ã', + 'Ä' => 'ä', + 'Ã…' => 'Ã¥', + 'Æ' => 'æ', + 'Ç' => 'ç', + 'È' => 'è', + 'É' => 'é', + 'Ê' => 'ê', + 'Ë' => 'ë', + 'ÃŒ' => 'ì', + 'Ã' => 'Ã', + 'ÃŽ' => 'î', + 'Ã' => 'ï', + 'Ã' => 'ð', + 'Ñ' => 'ñ', + 'Ã’' => 'ò', + 'Ó' => 'ó', + 'Ô' => 'ô', + 'Õ' => 'õ', + 'Ö' => 'ö', + 'Ø' => 'ø', + 'Ù' => 'ù', + 'Ú' => 'ú', + 'Û' => 'û', + 'Ü' => 'ü', + 'Ã' => 'ý', + 'Þ' => 'þ', + 'Ä€' => 'Ä', + 'Ä‚' => 'ă', + 'Ä„' => 'Ä…', + 'Ć' => 'ć', + 'Ĉ' => 'ĉ', + 'ÄŠ' => 'Ä‹', + 'ÄŒ' => 'Ä', + 'ÄŽ' => 'Ä', + 'Ä' => 'Ä‘', + 'Ä’' => 'Ä“', + 'Ä”' => 'Ä•', + 'Ä–' => 'Ä—', + 'Ę' => 'Ä™', + 'Äš' => 'Ä›', + 'Äœ' => 'Ä', + 'Äž' => 'ÄŸ', + 'Ä ' => 'Ä¡', + 'Ä¢' => 'Ä£', + 'Ĥ' => 'Ä¥', + 'Ħ' => 'ħ', + 'Ĩ' => 'Ä©', + 'Ī' => 'Ä«', + 'Ĭ' => 'Ä', + 'Ä®' => 'į', + 'İ' => 'i', + 'IJ' => 'ij', + 'Ä´' => 'ĵ', + 'Ķ' => 'Ä·', + 'Ĺ' => 'ĺ', + 'Ä»' => 'ļ', + 'Ľ' => 'ľ', + 'Ä¿' => 'Å€', + 'Å' => 'Å‚', + 'Ń' => 'Å„', + 'Å…' => 'ņ', + 'Ň' => 'ň', + 'ÅŠ' => 'Å‹', + 'ÅŒ' => 'Å', + 'ÅŽ' => 'Å', + 'Å' => 'Å‘', + 'Å’' => 'Å“', + 'Å”' => 'Å•', + 'Å–' => 'Å—', + 'Ř' => 'Å™', + 'Åš' => 'Å›', + 'Åœ' => 'Å', + 'Åž' => 'ÅŸ', + 'Å ' => 'Å¡', + 'Å¢' => 'Å£', + 'Ť' => 'Å¥', + 'Ŧ' => 'ŧ', + 'Ũ' => 'Å©', + 'Ū' => 'Å«', + 'Ŭ' => 'Å', + 'Å®' => 'ů', + 'Ű' => 'ű', + 'Ų' => 'ų', + 'Å´' => 'ŵ', + 'Ŷ' => 'Å·', + 'Ÿ' => 'ÿ', + 'Ź' => 'ź', + 'Å»' => 'ż', + 'Ž' => 'ž', + 'Æ' => 'É“', + 'Æ‚' => 'ƃ', + 'Æ„' => 'Æ…', + 'Ɔ' => 'É”', + 'Ƈ' => 'ƈ', + 'Ɖ' => 'É–', + 'ÆŠ' => 'É—', + 'Æ‹' => 'ÆŒ', + 'ÆŽ' => 'Ç', + 'Æ' => 'É™', + 'Æ' => 'É›', + 'Æ‘' => 'Æ’', + 'Æ“' => 'É ', + 'Æ”' => 'É£', + 'Æ–' => 'É©', + 'Æ—' => 'ɨ', + 'Ƙ' => 'Æ™', + 'Æœ' => 'ɯ', + 'Æ' => 'ɲ', + 'ÆŸ' => 'ɵ', + 'Æ ' => 'Æ¡', + 'Æ¢' => 'Æ£', + 'Ƥ' => 'Æ¥', + 'Ʀ' => 'Ê€', + 'Ƨ' => 'ƨ', + 'Æ©' => 'ʃ', + 'Ƭ' => 'Æ', + 'Æ®' => 'ʈ', + 'Ư' => 'ư', + 'Ʊ' => 'ÊŠ', + 'Ʋ' => 'Ê‹', + 'Ƴ' => 'Æ´', + 'Ƶ' => 'ƶ', + 'Æ·' => 'Ê’', + 'Ƹ' => 'ƹ', + 'Ƽ' => 'ƽ', + 'Ç„' => 'dž', + 'Ç…' => 'dž', + 'LJ' => 'lj', + 'Lj' => 'lj', + 'ÇŠ' => 'ÇŒ', + 'Ç‹' => 'ÇŒ', + 'Ç' => 'ÇŽ', + 'Ç' => 'Ç', + 'Ç‘' => 'Ç’', + 'Ç“' => 'Ç”', + 'Ç•' => 'Ç–', + 'Ç—' => 'ǘ', + 'Ç™' => 'Çš', + 'Ç›' => 'Çœ', + 'Çž' => 'ÇŸ', + 'Ç ' => 'Ç¡', + 'Ç¢' => 'Ç£', + 'Ǥ' => 'Ç¥', + 'Ǧ' => 'ǧ', + 'Ǩ' => 'Ç©', + 'Ǫ' => 'Ç«', + 'Ǭ' => 'Ç', + 'Ç®' => 'ǯ', + 'DZ' => 'dz', + 'Dz' => 'dz', + 'Ç´' => 'ǵ', + 'Ƕ' => 'Æ•', + 'Ç·' => 'Æ¿', + 'Ǹ' => 'ǹ', + 'Ǻ' => 'Ç»', + 'Ǽ' => 'ǽ', + 'Ǿ' => 'Ç¿', + 'È€' => 'È', + 'È‚' => 'ȃ', + 'È„' => 'È…', + 'Ȇ' => 'ȇ', + 'Ȉ' => 'ȉ', + 'ÈŠ' => 'È‹', + 'ÈŒ' => 'È', + 'ÈŽ' => 'È', + 'È' => 'È‘', + 'È’' => 'È“', + 'È”' => 'È•', + 'È–' => 'È—', + 'Ș' => 'È™', + 'Èš' => 'È›', + 'Èœ' => 'È', + 'Èž' => 'ÈŸ', + 'È ' => 'Æž', + 'È¢' => 'È£', + 'Ȥ' => 'È¥', + 'Ȧ' => 'ȧ', + 'Ȩ' => 'È©', + 'Ȫ' => 'È«', + 'Ȭ' => 'È', + 'È®' => 'ȯ', + 'Ȱ' => 'ȱ', + 'Ȳ' => 'ȳ', + 'Ⱥ' => 'â±¥', + 'È»' => 'ȼ', + 'Ƚ' => 'Æš', + 'Ⱦ' => 'ⱦ', + 'É' => 'É‚', + 'Ƀ' => 'Æ€', + 'É„' => 'ʉ', + 'É…' => 'ÊŒ', + 'Ɇ' => 'ɇ', + 'Ɉ' => 'ɉ', + 'ÉŠ' => 'É‹', + 'ÉŒ' => 'É', + 'ÉŽ' => 'É', + 'Ͱ' => 'ͱ', + 'Ͳ' => 'ͳ', + 'Ͷ' => 'Í·', + 'Í¿' => 'ϳ', + 'Ά' => 'ά', + 'Έ' => 'Î', + 'Ή' => 'ή', + 'Ί' => 'ί', + 'ÎŒ' => 'ÏŒ', + 'ÎŽ' => 'Ï', + 'Î' => 'ÏŽ', + 'Α' => 'α', + 'Î’' => 'β', + 'Γ' => 'γ', + 'Δ' => 'δ', + 'Ε' => 'ε', + 'Ζ' => 'ζ', + 'Η' => 'η', + 'Θ' => 'θ', + 'Ι' => 'ι', + 'Κ' => 'κ', + 'Λ' => 'λ', + 'Μ' => 'μ', + 'Î' => 'ν', + 'Ξ' => 'ξ', + 'Ο' => 'ο', + 'Î ' => 'Ï€', + 'Ρ' => 'Ï', + 'Σ' => 'σ', + 'Τ' => 'Ï„', + 'Î¥' => 'Ï…', + 'Φ' => 'φ', + 'Χ' => 'χ', + 'Ψ' => 'ψ', + 'Ω' => 'ω', + 'Ϊ' => 'ÏŠ', + 'Ϋ' => 'Ï‹', + 'Ï' => 'Ï—', + 'Ϙ' => 'Ï™', + 'Ïš' => 'Ï›', + 'Ïœ' => 'Ï', + 'Ïž' => 'ÏŸ', + 'Ï ' => 'Ï¡', + 'Ï¢' => 'Ï£', + 'Ϥ' => 'Ï¥', + 'Ϧ' => 'ϧ', + 'Ϩ' => 'Ï©', + 'Ϫ' => 'Ï«', + 'Ϭ' => 'Ï', + 'Ï®' => 'ϯ', + 'Ï´' => 'θ', + 'Ï·' => 'ϸ', + 'Ϲ' => 'ϲ', + 'Ϻ' => 'Ï»', + 'Ͻ' => 'Í»', + 'Ͼ' => 'ͼ', + 'Ï¿' => 'ͽ', + 'Ѐ' => 'Ñ', + 'Ð' => 'Ñ‘', + 'Ђ' => 'Ñ’', + 'Ѓ' => 'Ñ“', + 'Є' => 'Ñ”', + 'Ð…' => 'Ñ•', + 'І' => 'Ñ–', + 'Ї' => 'Ñ—', + 'Ј' => 'ј', + 'Љ' => 'Ñ™', + 'Њ' => 'Ñš', + 'Ћ' => 'Ñ›', + 'ÐŒ' => 'Ñœ', + 'Ð' => 'Ñ', + 'ÐŽ' => 'Ñž', + 'Ð' => 'ÑŸ', + 'Ð' => 'а', + 'Б' => 'б', + 'Ð’' => 'в', + 'Г' => 'г', + 'Д' => 'д', + 'Е' => 'е', + 'Ж' => 'ж', + 'З' => 'з', + 'И' => 'и', + 'Й' => 'й', + 'К' => 'к', + 'Л' => 'л', + 'М' => 'м', + 'Ð' => 'н', + 'О' => 'о', + 'П' => 'п', + 'Ð ' => 'Ñ€', + 'С' => 'Ñ', + 'Т' => 'Ñ‚', + 'У' => 'у', + 'Ф' => 'Ñ„', + 'Ð¥' => 'Ñ…', + 'Ц' => 'ц', + 'Ч' => 'ч', + 'Ш' => 'ш', + 'Щ' => 'щ', + 'Ъ' => 'ÑŠ', + 'Ы' => 'Ñ‹', + 'Ь' => 'ÑŒ', + 'Ð' => 'Ñ', + 'Ю' => 'ÑŽ', + 'Я' => 'Ñ', + 'Ñ ' => 'Ñ¡', + 'Ñ¢' => 'Ñ£', + 'Ѥ' => 'Ñ¥', + 'Ѧ' => 'ѧ', + 'Ѩ' => 'Ñ©', + 'Ѫ' => 'Ñ«', + 'Ѭ' => 'Ñ', + 'Ñ®' => 'ѯ', + 'Ѱ' => 'ѱ', + 'Ѳ' => 'ѳ', + 'Ñ´' => 'ѵ', + 'Ѷ' => 'Ñ·', + 'Ѹ' => 'ѹ', + 'Ѻ' => 'Ñ»', + 'Ѽ' => 'ѽ', + 'Ѿ' => 'Ñ¿', + 'Ò€' => 'Ò', + 'ÒŠ' => 'Ò‹', + 'ÒŒ' => 'Ò', + 'ÒŽ' => 'Ò', + 'Ò' => 'Ò‘', + 'Ò’' => 'Ò“', + 'Ò”' => 'Ò•', + 'Ò–' => 'Ò—', + 'Ò˜' => 'Ò™', + 'Òš' => 'Ò›', + 'Òœ' => 'Ò', + 'Òž' => 'ÒŸ', + 'Ò ' => 'Ò¡', + 'Ò¢' => 'Ò£', + 'Ò¤' => 'Ò¥', + 'Ò¦' => 'Ò§', + 'Ò¨' => 'Ò©', + 'Òª' => 'Ò«', + 'Ò¬' => 'Ò', + 'Ò®' => 'Ò¯', + 'Ò°' => 'Ò±', + 'Ò²' => 'Ò³', + 'Ò´' => 'Òµ', + 'Ò¶' => 'Ò·', + 'Ò¸' => 'Ò¹', + 'Òº' => 'Ò»', + 'Ò¼' => 'Ò½', + 'Ò¾' => 'Ò¿', + 'Ó€' => 'Ó', + 'Ó' => 'Ó‚', + 'Óƒ' => 'Ó„', + 'Ó…' => 'Ó†', + 'Ó‡' => 'Óˆ', + 'Ó‰' => 'ÓŠ', + 'Ó‹' => 'ÓŒ', + 'Ó' => 'ÓŽ', + 'Ó' => 'Ó‘', + 'Ó’' => 'Ó“', + 'Ó”' => 'Ó•', + 'Ó–' => 'Ó—', + 'Ó˜' => 'Ó™', + 'Óš' => 'Ó›', + 'Óœ' => 'Ó', + 'Óž' => 'ÓŸ', + 'Ó ' => 'Ó¡', + 'Ó¢' => 'Ó£', + 'Ó¤' => 'Ó¥', + 'Ó¦' => 'Ó§', + 'Ó¨' => 'Ó©', + 'Óª' => 'Ó«', + 'Ó¬' => 'Ó', + 'Ó®' => 'Ó¯', + 'Ó°' => 'Ó±', + 'Ó²' => 'Ó³', + 'Ó´' => 'Óµ', + 'Ó¶' => 'Ó·', + 'Ó¸' => 'Ó¹', + 'Óº' => 'Ó»', + 'Ó¼' => 'Ó½', + 'Ó¾' => 'Ó¿', + 'Ô€' => 'Ô', + 'Ô‚' => 'Ôƒ', + 'Ô„' => 'Ô…', + 'Ô†' => 'Ô‡', + 'Ôˆ' => 'Ô‰', + 'ÔŠ' => 'Ô‹', + 'ÔŒ' => 'Ô', + 'ÔŽ' => 'Ô', + 'Ô' => 'Ô‘', + 'Ô’' => 'Ô“', + 'Ô”' => 'Ô•', + 'Ô–' => 'Ô—', + 'Ô˜' => 'Ô™', + 'Ôš' => 'Ô›', + 'Ôœ' => 'Ô', + 'Ôž' => 'ÔŸ', + 'Ô ' => 'Ô¡', + 'Ô¢' => 'Ô£', + 'Ô¤' => 'Ô¥', + 'Ô¦' => 'Ô§', + 'Ô¨' => 'Ô©', + 'Ôª' => 'Ô«', + 'Ô¬' => 'Ô', + 'Ô®' => 'Ô¯', + 'Ô±' => 'Õ¡', + 'Ô²' => 'Õ¢', + 'Ô³' => 'Õ£', + 'Ô´' => 'Õ¤', + 'Ôµ' => 'Õ¥', + 'Ô¶' => 'Õ¦', + 'Ô·' => 'Õ§', + 'Ô¸' => 'Õ¨', + 'Ô¹' => 'Õ©', + 'Ôº' => 'Õª', + 'Ô»' => 'Õ«', + 'Ô¼' => 'Õ¬', + 'Ô½' => 'Õ', + 'Ô¾' => 'Õ®', + 'Ô¿' => 'Õ¯', + 'Õ€' => 'Õ°', + 'Õ' => 'Õ±', + 'Õ‚' => 'Õ²', + 'Õƒ' => 'Õ³', + 'Õ„' => 'Õ´', + 'Õ…' => 'Õµ', + 'Õ†' => 'Õ¶', + 'Õ‡' => 'Õ·', + 'Õˆ' => 'Õ¸', + 'Õ‰' => 'Õ¹', + 'ÕŠ' => 'Õº', + 'Õ‹' => 'Õ»', + 'ÕŒ' => 'Õ¼', + 'Õ' => 'Õ½', + 'ÕŽ' => 'Õ¾', + 'Õ' => 'Õ¿', + 'Õ' => 'Ö€', + 'Õ‘' => 'Ö', + 'Õ’' => 'Ö‚', + 'Õ“' => 'Öƒ', + 'Õ”' => 'Ö„', + 'Õ•' => 'Ö…', + 'Õ–' => 'Ö†', + 'á‚ ' => 'â´€', + 'á‚¡' => 'â´', + 'á‚¢' => 'â´‚', + 'á‚£' => 'â´ƒ', + 'Ⴄ' => 'â´„', + 'á‚¥' => 'â´…', + 'Ⴆ' => 'â´†', + 'á‚§' => 'â´‡', + 'Ⴈ' => 'â´ˆ', + 'á‚©' => 'â´‰', + 'Ⴊ' => 'â´Š', + 'á‚«' => 'â´‹', + 'Ⴌ' => 'â´Œ', + 'á‚' => 'â´', + 'á‚®' => 'â´Ž', + 'Ⴏ' => 'â´', + 'á‚°' => 'â´', + 'Ⴑ' => 'â´‘', + 'Ⴒ' => 'â´’', + 'Ⴓ' => 'â´“', + 'á‚´' => 'â´”', + 'Ⴕ' => 'â´•', + 'á‚¶' => 'â´–', + 'á‚·' => 'â´—', + 'Ⴘ' => 'â´˜', + 'Ⴙ' => 'â´™', + 'Ⴚ' => 'â´š', + 'á‚»' => 'â´›', + 'Ⴜ' => 'â´œ', + 'Ⴝ' => 'â´', + 'Ⴞ' => 'â´ž', + 'á‚¿' => 'â´Ÿ', + 'Ⴠ' => 'â´ ', + 'áƒ' => 'â´¡', + 'Ⴢ' => 'â´¢', + 'Ⴣ' => 'â´£', + 'Ⴤ' => 'â´¤', + 'Ⴥ' => 'â´¥', + 'Ⴧ' => 'â´§', + 'áƒ' => 'â´', + 'Ḁ' => 'á¸', + 'Ḃ' => 'ḃ', + 'Ḅ' => 'ḅ', + 'Ḇ' => 'ḇ', + 'Ḉ' => 'ḉ', + 'Ḋ' => 'ḋ', + 'Ḍ' => 'á¸', + 'Ḏ' => 'á¸', + 'á¸' => 'ḑ', + 'Ḓ' => 'ḓ', + 'Ḕ' => 'ḕ', + 'Ḗ' => 'ḗ', + 'Ḙ' => 'ḙ', + 'Ḛ' => 'ḛ', + 'Ḝ' => 'á¸', + 'Ḟ' => 'ḟ', + 'Ḡ' => 'ḡ', + 'Ḣ' => 'ḣ', + 'Ḥ' => 'ḥ', + 'Ḧ' => 'ḧ', + 'Ḩ' => 'ḩ', + 'Ḫ' => 'ḫ', + 'Ḭ' => 'á¸', + 'Ḯ' => 'ḯ', + 'Ḱ' => 'ḱ', + 'Ḳ' => 'ḳ', + 'Ḵ' => 'ḵ', + 'Ḷ' => 'ḷ', + 'Ḹ' => 'ḹ', + 'Ḻ' => 'ḻ', + 'Ḽ' => 'ḽ', + 'Ḿ' => 'ḿ', + 'á¹€' => 'á¹', + 'Ṃ' => 'ṃ', + 'Ṅ' => 'á¹…', + 'Ṇ' => 'ṇ', + 'Ṉ' => 'ṉ', + 'Ṋ' => 'ṋ', + 'Ṍ' => 'á¹', + 'Ṏ' => 'á¹', + 'á¹' => 'ṑ', + 'á¹’' => 'ṓ', + 'á¹”' => 'ṕ', + 'á¹–' => 'á¹—', + 'Ṙ' => 'á¹™', + 'Ṛ' => 'á¹›', + 'Ṝ' => 'á¹', + 'Ṟ' => 'ṟ', + 'á¹ ' => 'ṡ', + 'á¹¢' => 'á¹£', + 'Ṥ' => 'á¹¥', + 'Ṧ' => 'á¹§', + 'Ṩ' => 'ṩ', + 'Ṫ' => 'ṫ', + 'Ṭ' => 'á¹', + 'á¹®' => 'ṯ', + 'á¹°' => 'á¹±', + 'á¹²' => 'á¹³', + 'á¹´' => 'á¹µ', + 'á¹¶' => 'á¹·', + 'Ṹ' => 'á¹¹', + 'Ṻ' => 'á¹»', + 'á¹¼' => 'á¹½', + 'á¹¾' => 'ṿ', + 'Ẁ' => 'áº', + 'Ẃ' => 'ẃ', + 'Ẅ' => 'ẅ', + 'Ẇ' => 'ẇ', + 'Ẉ' => 'ẉ', + 'Ẋ' => 'ẋ', + 'Ẍ' => 'áº', + 'Ẏ' => 'áº', + 'áº' => 'ẑ', + 'Ẓ' => 'ẓ', + 'Ẕ' => 'ẕ', + 'ẞ' => 'ß', + 'Ạ' => 'ạ', + 'Ả' => 'ả', + 'Ấ' => 'ấ', + 'Ầ' => 'ầ', + 'Ẩ' => 'ẩ', + 'Ẫ' => 'ẫ', + 'Ậ' => 'áº', + 'Ắ' => 'ắ', + 'Ằ' => 'ằ', + 'Ẳ' => 'ẳ', + 'Ẵ' => 'ẵ', + 'Ặ' => 'ặ', + 'Ẹ' => 'ẹ', + 'Ẻ' => 'ẻ', + 'Ẽ' => 'ẽ', + 'Ế' => 'ế', + 'Ề' => 'á»', + 'Ể' => 'ể', + 'Ễ' => 'á»…', + 'Ệ' => 'ệ', + 'Ỉ' => 'ỉ', + 'Ị' => 'ị', + 'Ọ' => 'á»', + 'Ỏ' => 'á»', + 'á»' => 'ố', + 'á»’' => 'ồ', + 'á»”' => 'ổ', + 'á»–' => 'á»—', + 'Ộ' => 'á»™', + 'Ớ' => 'á»›', + 'Ờ' => 'á»', + 'Ở' => 'ở', + 'á» ' => 'ỡ', + 'Ợ' => 'ợ', + 'Ụ' => 'ụ', + 'Ủ' => 'á»§', + 'Ứ' => 'ứ', + 'Ừ' => 'ừ', + 'Ử' => 'á»', + 'á»®' => 'ữ', + 'á»°' => 'á»±', + 'Ỳ' => 'ỳ', + 'á»´' => 'ỵ', + 'á»¶' => 'á»·', + 'Ỹ' => 'ỹ', + 'Ỻ' => 'á»»', + 'Ỽ' => 'ỽ', + 'Ỿ' => 'ỿ', + 'Ἀ' => 'á¼€', + 'Ἁ' => 'á¼', + 'Ἂ' => 'ἂ', + 'Ἃ' => 'ἃ', + 'Ἄ' => 'ἄ', + 'á¼' => 'á¼…', + 'Ἆ' => 'ἆ', + 'á¼' => 'ἇ', + 'Ἐ' => 'á¼', + 'á¼™' => 'ἑ', + 'Ἒ' => 'á¼’', + 'á¼›' => 'ἓ', + 'Ἔ' => 'á¼”', + 'á¼' => 'ἕ', + 'Ἠ' => 'á¼ ', + 'Ἡ' => 'ἡ', + 'Ἢ' => 'á¼¢', + 'Ἣ' => 'á¼£', + 'Ἤ' => 'ἤ', + 'á¼' => 'á¼¥', + 'á¼®' => 'ἦ', + 'Ἧ' => 'á¼§', + 'Ἰ' => 'á¼°', + 'á¼¹' => 'á¼±', + 'Ἲ' => 'á¼²', + 'á¼»' => 'á¼³', + 'á¼¼' => 'á¼´', + 'á¼½' => 'á¼µ', + 'á¼¾' => 'á¼¶', + 'Ἷ' => 'á¼·', + 'Ὀ' => 'á½€', + 'Ὁ' => 'á½', + 'Ὂ' => 'ὂ', + 'Ὃ' => 'ὃ', + 'Ὄ' => 'ὄ', + 'á½' => 'á½…', + 'á½™' => 'ὑ', + 'á½›' => 'ὓ', + 'á½' => 'ὕ', + 'Ὗ' => 'á½—', + 'Ὠ' => 'á½ ', + 'Ὡ' => 'ὡ', + 'Ὢ' => 'á½¢', + 'Ὣ' => 'á½£', + 'Ὤ' => 'ὤ', + 'á½' => 'á½¥', + 'á½®' => 'ὦ', + 'Ὧ' => 'á½§', + 'ᾈ' => 'á¾€', + 'ᾉ' => 'á¾', + 'ᾊ' => 'ᾂ', + 'ᾋ' => 'ᾃ', + 'ᾌ' => 'ᾄ', + 'á¾' => 'á¾…', + 'ᾎ' => 'ᾆ', + 'á¾' => 'ᾇ', + 'ᾘ' => 'á¾', + 'á¾™' => 'ᾑ', + 'ᾚ' => 'á¾’', + 'á¾›' => 'ᾓ', + 'ᾜ' => 'á¾”', + 'á¾' => 'ᾕ', + 'ᾞ' => 'á¾–', + 'ᾟ' => 'á¾—', + 'ᾨ' => 'á¾ ', + 'ᾩ' => 'ᾡ', + 'ᾪ' => 'á¾¢', + 'ᾫ' => 'á¾£', + 'ᾬ' => 'ᾤ', + 'á¾' => 'á¾¥', + 'á¾®' => 'ᾦ', + 'ᾯ' => 'á¾§', + 'Ᾰ' => 'á¾°', + 'á¾¹' => 'á¾±', + 'Ὰ' => 'á½°', + 'á¾»' => 'á½±', + 'á¾¼' => 'á¾³', + 'Ὲ' => 'á½²', + 'Έ' => 'á½³', + 'Ὴ' => 'á½´', + 'á¿‹' => 'á½µ', + 'ῌ' => 'ῃ', + 'Ῐ' => 'á¿', + 'á¿™' => 'á¿‘', + 'Ὶ' => 'á½¶', + 'á¿›' => 'á½·', + 'Ῠ' => 'á¿ ', + 'á¿©' => 'á¿¡', + 'Ὺ' => 'ὺ', + 'á¿«' => 'á½»', + 'Ῥ' => 'á¿¥', + 'Ὸ' => 'ὸ', + 'Ό' => 'á½¹', + 'Ὼ' => 'á½¼', + 'á¿»' => 'á½½', + 'ῼ' => 'ῳ', + 'Ω' => 'ω', + 'K' => 'k', + 'â„«' => 'Ã¥', + 'Ⅎ' => 'â…Ž', + 'â… ' => 'â…°', + 'â…¡' => 'â…±', + 'â…¢' => 'â…²', + 'â…£' => 'â…³', + 'â…¤' => 'â…´', + 'â…¥' => 'â…µ', + 'â…¦' => 'â…¶', + 'â…§' => 'â…·', + 'â…¨' => 'â…¸', + 'â…©' => 'â…¹', + 'â…ª' => 'â…º', + 'â…«' => 'â…»', + 'â…¬' => 'â…¼', + 'â…' => 'â…½', + 'â…®' => 'â…¾', + 'â…¯' => 'â…¿', + 'Ↄ' => 'ↄ', + 'â’¶' => 'â“', + 'â’·' => 'â“‘', + 'â’¸' => 'â“’', + 'â’¹' => 'â““', + 'â’º' => 'â“”', + 'â’»' => 'â“•', + 'â’¼' => 'â“–', + 'â’½' => 'â“—', + 'â’¾' => 'ⓘ', + 'â’¿' => 'â“™', + 'â“€' => 'ⓚ', + 'â“' => 'â“›', + 'â“‚' => 'ⓜ', + 'Ⓝ' => 'â“', + 'â“„' => 'ⓞ', + 'â“…' => 'ⓟ', + 'Ⓠ' => 'â“ ', + 'Ⓡ' => 'â“¡', + 'Ⓢ' => 'â“¢', + 'Ⓣ' => 'â“£', + 'Ⓤ' => 'ⓤ', + 'â“‹' => 'â“¥', + 'Ⓦ' => 'ⓦ', + 'â“' => 'â“§', + 'Ⓨ' => 'ⓨ', + 'â“' => 'â“©', + 'â°€' => 'â°°', + 'â°' => 'â°±', + 'â°‚' => 'â°²', + 'â°ƒ' => 'â°³', + 'â°„' => 'â°´', + 'â°…' => 'â°µ', + 'â°†' => 'â°¶', + 'â°‡' => 'â°·', + 'â°ˆ' => 'â°¸', + 'â°‰' => 'â°¹', + 'â°Š' => 'â°º', + 'â°‹' => 'â°»', + 'â°Œ' => 'â°¼', + 'â°' => 'â°½', + 'â°Ž' => 'â°¾', + 'â°' => 'â°¿', + 'â°' => 'â±€', + 'â°‘' => 'â±', + 'â°’' => 'ⱂ', + 'â°“' => 'ⱃ', + 'â°”' => 'ⱄ', + 'â°•' => 'â±…', + 'â°–' => 'ⱆ', + 'â°—' => 'ⱇ', + 'â°˜' => 'ⱈ', + 'â°™' => 'ⱉ', + 'â°š' => 'ⱊ', + 'â°›' => 'ⱋ', + 'â°œ' => 'ⱌ', + 'â°' => 'â±', + 'â°ž' => 'ⱎ', + 'â°Ÿ' => 'â±', + 'â° ' => 'â±', + 'â°¡' => 'ⱑ', + 'â°¢' => 'â±’', + 'â°£' => 'ⱓ', + 'â°¤' => 'â±”', + 'â°¥' => 'ⱕ', + 'â°¦' => 'â±–', + 'â°§' => 'â±—', + 'â°¨' => 'ⱘ', + 'â°©' => 'â±™', + 'â°ª' => 'ⱚ', + 'â°«' => 'â±›', + 'â°¬' => 'ⱜ', + 'â°' => 'â±', + 'â°®' => 'ⱞ', + 'â± ' => 'ⱡ', + 'â±¢' => 'É«', + 'â±£' => 'áµ½', + 'Ɽ' => 'ɽ', + 'â±§' => 'ⱨ', + 'Ⱪ' => 'ⱪ', + 'Ⱬ' => 'ⱬ', + 'â±' => 'É‘', + 'â±®' => 'ɱ', + 'Ɐ' => 'É', + 'â±°' => 'É’', + 'â±²' => 'â±³', + 'â±µ' => 'â±¶', + 'â±¾' => 'È¿', + 'Ɀ' => 'É€', + 'â²€' => 'â²', + 'Ⲃ' => 'ⲃ', + 'Ⲅ' => 'â²…', + 'Ⲇ' => 'ⲇ', + 'Ⲉ' => 'ⲉ', + 'Ⲋ' => 'ⲋ', + 'Ⲍ' => 'â²', + 'Ⲏ' => 'â²', + 'â²' => 'ⲑ', + 'â²’' => 'ⲓ', + 'â²”' => 'ⲕ', + 'â²–' => 'â²—', + 'Ⲙ' => 'â²™', + 'Ⲛ' => 'â²›', + 'Ⲝ' => 'â²', + 'Ⲟ' => 'ⲟ', + 'â² ' => 'ⲡ', + 'â²¢' => 'â²£', + 'Ⲥ' => 'â²¥', + 'Ⲧ' => 'â²§', + 'Ⲩ' => 'ⲩ', + 'Ⲫ' => 'ⲫ', + 'Ⲭ' => 'â²', + 'â²®' => 'ⲯ', + 'â²°' => 'â²±', + 'â²²' => 'â²³', + 'â²´' => 'â²µ', + 'â²¶' => 'â²·', + 'Ⲹ' => 'â²¹', + 'Ⲻ' => 'â²»', + 'â²¼' => 'â²½', + 'â²¾' => 'ⲿ', + 'â³€' => 'â³', + 'Ⳃ' => 'ⳃ', + 'Ⳅ' => 'â³…', + 'Ⳇ' => 'ⳇ', + 'Ⳉ' => 'ⳉ', + 'Ⳋ' => 'ⳋ', + 'Ⳍ' => 'â³', + 'Ⳏ' => 'â³', + 'â³' => 'ⳑ', + 'â³’' => 'ⳓ', + 'â³”' => 'ⳕ', + 'â³–' => 'â³—', + 'Ⳙ' => 'â³™', + 'Ⳛ' => 'â³›', + 'Ⳝ' => 'â³', + 'Ⳟ' => 'ⳟ', + 'â³ ' => 'ⳡ', + 'â³¢' => 'â³£', + 'Ⳬ' => 'ⳬ', + 'â³' => 'â³®', + 'â³²' => 'â³³', + 'Ꙁ' => 'ê™', + 'Ꙃ' => 'ꙃ', + 'Ꙅ' => 'ê™…', + 'Ꙇ' => 'ꙇ', + 'Ꙉ' => 'ꙉ', + 'Ꙋ' => 'ꙋ', + 'Ꙍ' => 'ê™', + 'Ꙏ' => 'ê™', + 'ê™' => 'ꙑ', + 'ê™’' => 'ꙓ', + 'ê™”' => 'ꙕ', + 'ê™–' => 'ê™—', + 'Ꙙ' => 'ê™™', + 'Ꙛ' => 'ê™›', + 'Ꙝ' => 'ê™', + 'Ꙟ' => 'ꙟ', + 'ê™ ' => 'ꙡ', + 'Ꙣ' => 'ꙣ', + 'Ꙥ' => 'ꙥ', + 'Ꙧ' => 'ê™§', + 'Ꙩ' => 'ꙩ', + 'Ꙫ' => 'ꙫ', + 'Ꙭ' => 'ê™', + 'Ꚁ' => 'êš', + 'êš‚' => 'ꚃ', + 'êš„' => 'êš…', + 'Ꚇ' => 'ꚇ', + 'Ꚉ' => 'ꚉ', + 'Ꚋ' => 'êš‹', + 'Ꚍ' => 'êš', + 'Ꚏ' => 'êš', + 'êš' => 'êš‘', + 'êš’' => 'êš“', + 'êš”' => 'êš•', + 'êš–' => 'êš—', + 'Ꚙ' => 'êš™', + 'êšš' => 'êš›', + 'Ꜣ' => 'ꜣ', + 'Ꜥ' => 'ꜥ', + 'Ꜧ' => 'ꜧ', + 'Ꜩ' => 'ꜩ', + 'Ꜫ' => 'ꜫ', + 'Ꜭ' => 'êœ', + 'Ꜯ' => 'ꜯ', + 'Ꜳ' => 'ꜳ', + 'Ꜵ' => 'ꜵ', + 'Ꜷ' => 'ꜷ', + 'Ꜹ' => 'ꜹ', + 'Ꜻ' => 'ꜻ', + 'Ꜽ' => 'ꜽ', + 'Ꜿ' => 'ꜿ', + 'ê€' => 'ê', + 'ê‚' => 'êƒ', + 'ê„' => 'ê…', + 'ê†' => 'ê‡', + 'êˆ' => 'ê‰', + 'êŠ' => 'ê‹', + 'êŒ' => 'ê', + 'êŽ' => 'ê', + 'ê' => 'ê‘', + 'ê’' => 'ê“', + 'ê”' => 'ê•', + 'ê–' => 'ê—', + 'ê˜' => 'ê™', + 'êš' => 'ê›', + 'êœ' => 'ê', + 'êž' => 'êŸ', + 'ê ' => 'ê¡', + 'ê¢' => 'ê£', + 'ê¤' => 'ê¥', + 'ê¦' => 'ê§', + 'ê¨' => 'ê©', + 'êª' => 'ê«', + 'ê¬' => 'ê', + 'ê®' => 'ê¯', + 'ê¹' => 'êº', + 'ê»' => 'ê¼', + 'ê½' => 'áµ¹', + 'ê¾' => 'ê¿', + 'Ꞁ' => 'êž', + 'êž‚' => 'ꞃ', + 'êž„' => 'êž…', + 'Ꞇ' => 'ꞇ', + 'êž‹' => 'ꞌ', + 'êž' => 'É¥', + 'êž' => 'êž‘', + 'êž’' => 'êž“', + 'êž–' => 'êž—', + 'Ꞙ' => 'êž™', + 'êžš' => 'êž›', + 'êžœ' => 'êž', + 'êžž' => 'ꞟ', + 'êž ' => 'êž¡', + 'Ꞣ' => 'ꞣ', + 'Ꞥ' => 'ꞥ', + 'Ꞧ' => 'êž§', + 'Ꞩ' => 'êž©', + 'Ɦ' => 'ɦ', + 'êž«' => 'Éœ', + 'Ɡ' => 'É¡', + 'êž' => 'ɬ', + 'êž°' => 'Êž', + 'êž±' => 'ʇ', + 'A' => 'ï½', + 'ï¼¢' => 'b', + 'ï¼£' => 'c', + 'D' => 'd', + 'ï¼¥' => 'ï½…', + 'F' => 'f', + 'ï¼§' => 'g', + 'H' => 'h', + 'I' => 'i', + 'J' => 'j', + 'K' => 'k', + 'L' => 'l', + 'ï¼' => 'ï½', + 'ï¼®' => 'n', + 'O' => 'ï½', + 'ï¼°' => 'ï½', + 'ï¼±' => 'q', + 'ï¼²' => 'ï½’', + 'ï¼³' => 's', + 'ï¼´' => 'ï½”', + 'ï¼µ' => 'u', + 'ï¼¶' => 'ï½–', + 'ï¼·' => 'ï½—', + 'X' => 'x', + 'ï¼¹' => 'ï½™', + 'Z' => 'z', + 'ð€' => 'ð¨', + 'ð' => 'ð©', + 'ð‚' => 'ðª', + 'ðƒ' => 'ð«', + 'ð„' => 'ð¬', + 'ð…' => 'ð', + 'ð†' => 'ð®', + 'ð‡' => 'ð¯', + 'ðˆ' => 'ð°', + 'ð‰' => 'ð±', + 'ðŠ' => 'ð²', + 'ð‹' => 'ð³', + 'ðŒ' => 'ð´', + 'ð' => 'ðµ', + 'ðŽ' => 'ð¶', + 'ð' => 'ð·', + 'ð' => 'ð¸', + 'ð‘' => 'ð¹', + 'ð’' => 'ðº', + 'ð“' => 'ð»', + 'ð”' => 'ð¼', + 'ð•' => 'ð½', + 'ð–' => 'ð¾', + 'ð—' => 'ð¿', + 'ð˜' => 'ð‘€', + 'ð™' => 'ð‘', + 'ðš' => 'ð‘‚', + 'ð›' => 'ð‘ƒ', + 'ðœ' => 'ð‘„', + 'ð' => 'ð‘…', + 'ðž' => 'ð‘†', + 'ðŸ' => 'ð‘‡', + 'ð ' => 'ð‘ˆ', + 'ð¡' => 'ð‘‰', + 'ð¢' => 'ð‘Š', + 'ð£' => 'ð‘‹', + 'ð¤' => 'ð‘Œ', + 'ð¥' => 'ð‘', + 'ð¦' => 'ð‘Ž', + 'ð§' => 'ð‘', + 'ð‘¢ ' => 'ð‘£€', + '𑢡' => 'ð‘£', + 'ð‘¢¢' => '𑣂', + 'ð‘¢£' => '𑣃', + '𑢤' => '𑣄', + 'ð‘¢¥' => 'ð‘£…', + '𑢦' => '𑣆', + 'ð‘¢§' => '𑣇', + '𑢨' => '𑣈', + '𑢩' => '𑣉', + '𑢪' => '𑣊', + '𑢫' => '𑣋', + '𑢬' => '𑣌', + 'ð‘¢' => 'ð‘£', + 'ð‘¢®' => '𑣎', + '𑢯' => 'ð‘£', + 'ð‘¢°' => 'ð‘£', + 'ð‘¢±' => '𑣑', + 'ð‘¢²' => 'ð‘£’', + 'ð‘¢³' => '𑣓', + 'ð‘¢´' => 'ð‘£”', + 'ð‘¢µ' => '𑣕', + 'ð‘¢¶' => 'ð‘£–', + 'ð‘¢·' => 'ð‘£—', + '𑢸' => '𑣘', + 'ð‘¢¹' => 'ð‘£™', + '𑢺' => '𑣚', + 'ð‘¢»' => 'ð‘£›', + 'ð‘¢¼' => '𑣜', + 'ð‘¢½' => 'ð‘£', + 'ð‘¢¾' => '𑣞', + '𑢿' => '𑣟', +); + +$result =& $data; +unset($data); + +return $result; diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php new file mode 100644 index 00000000..ec942212 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php @@ -0,0 +1,1109 @@ +<?php + +static $data = array ( + 'a' => 'A', + 'b' => 'B', + 'c' => 'C', + 'd' => 'D', + 'e' => 'E', + 'f' => 'F', + 'g' => 'G', + 'h' => 'H', + 'i' => 'I', + 'j' => 'J', + 'k' => 'K', + 'l' => 'L', + 'm' => 'M', + 'n' => 'N', + 'o' => 'O', + 'p' => 'P', + 'q' => 'Q', + 'r' => 'R', + 's' => 'S', + 't' => 'T', + 'u' => 'U', + 'v' => 'V', + 'w' => 'W', + 'x' => 'X', + 'y' => 'Y', + 'z' => 'Z', + 'µ' => 'Μ', + 'à ' => 'À', + 'á' => 'Ã', + 'â' => 'Â', + 'ã' => 'Ã', + 'ä' => 'Ä', + 'Ã¥' => 'Ã…', + 'æ' => 'Æ', + 'ç' => 'Ç', + 'è' => 'È', + 'é' => 'É', + 'ê' => 'Ê', + 'ë' => 'Ë', + 'ì' => 'ÃŒ', + 'Ã' => 'Ã', + 'î' => 'ÃŽ', + 'ï' => 'Ã', + 'ð' => 'Ã', + 'ñ' => 'Ñ', + 'ò' => 'Ã’', + 'ó' => 'Ó', + 'ô' => 'Ô', + 'õ' => 'Õ', + 'ö' => 'Ö', + 'ø' => 'Ø', + 'ù' => 'Ù', + 'ú' => 'Ú', + 'û' => 'Û', + 'ü' => 'Ü', + 'ý' => 'Ã', + 'þ' => 'Þ', + 'ÿ' => 'Ÿ', + 'Ä' => 'Ä€', + 'ă' => 'Ä‚', + 'Ä…' => 'Ä„', + 'ć' => 'Ć', + 'ĉ' => 'Ĉ', + 'Ä‹' => 'ÄŠ', + 'Ä' => 'ÄŒ', + 'Ä' => 'ÄŽ', + 'Ä‘' => 'Ä', + 'Ä“' => 'Ä’', + 'Ä•' => 'Ä”', + 'Ä—' => 'Ä–', + 'Ä™' => 'Ę', + 'Ä›' => 'Äš', + 'Ä' => 'Äœ', + 'ÄŸ' => 'Äž', + 'Ä¡' => 'Ä ', + 'Ä£' => 'Ä¢', + 'Ä¥' => 'Ĥ', + 'ħ' => 'Ħ', + 'Ä©' => 'Ĩ', + 'Ä«' => 'Ī', + 'Ä' => 'Ĭ', + 'į' => 'Ä®', + 'ı' => 'I', + 'ij' => 'IJ', + 'ĵ' => 'Ä´', + 'Ä·' => 'Ķ', + 'ĺ' => 'Ĺ', + 'ļ' => 'Ä»', + 'ľ' => 'Ľ', + 'Å€' => 'Ä¿', + 'Å‚' => 'Å', + 'Å„' => 'Ń', + 'ņ' => 'Å…', + 'ň' => 'Ň', + 'Å‹' => 'ÅŠ', + 'Å' => 'ÅŒ', + 'Å' => 'ÅŽ', + 'Å‘' => 'Å', + 'Å“' => 'Å’', + 'Å•' => 'Å”', + 'Å—' => 'Å–', + 'Å™' => 'Ř', + 'Å›' => 'Åš', + 'Å' => 'Åœ', + 'ÅŸ' => 'Åž', + 'Å¡' => 'Å ', + 'Å£' => 'Å¢', + 'Å¥' => 'Ť', + 'ŧ' => 'Ŧ', + 'Å©' => 'Ũ', + 'Å«' => 'Ū', + 'Å' => 'Ŭ', + 'ů' => 'Å®', + 'ű' => 'Ű', + 'ų' => 'Ų', + 'ŵ' => 'Å´', + 'Å·' => 'Ŷ', + 'ź' => 'Ź', + 'ż' => 'Å»', + 'ž' => 'Ž', + 'Å¿' => 'S', + 'Æ€' => 'Ƀ', + 'ƃ' => 'Æ‚', + 'Æ…' => 'Æ„', + 'ƈ' => 'Ƈ', + 'ÆŒ' => 'Æ‹', + 'Æ’' => 'Æ‘', + 'Æ•' => 'Ƕ', + 'Æ™' => 'Ƙ', + 'Æš' => 'Ƚ', + 'Æž' => 'È ', + 'Æ¡' => 'Æ ', + 'Æ£' => 'Æ¢', + 'Æ¥' => 'Ƥ', + 'ƨ' => 'Ƨ', + 'Æ' => 'Ƭ', + 'ư' => 'Ư', + 'Æ´' => 'Ƴ', + 'ƶ' => 'Ƶ', + 'ƹ' => 'Ƹ', + 'ƽ' => 'Ƽ', + 'Æ¿' => 'Ç·', + 'Ç…' => 'Ç„', + 'dž' => 'Ç„', + 'Lj' => 'LJ', + 'lj' => 'LJ', + 'Ç‹' => 'ÇŠ', + 'ÇŒ' => 'ÇŠ', + 'ÇŽ' => 'Ç', + 'Ç' => 'Ç', + 'Ç’' => 'Ç‘', + 'Ç”' => 'Ç“', + 'Ç–' => 'Ç•', + 'ǘ' => 'Ç—', + 'Çš' => 'Ç™', + 'Çœ' => 'Ç›', + 'Ç' => 'ÆŽ', + 'ÇŸ' => 'Çž', + 'Ç¡' => 'Ç ', + 'Ç£' => 'Ç¢', + 'Ç¥' => 'Ǥ', + 'ǧ' => 'Ǧ', + 'Ç©' => 'Ǩ', + 'Ç«' => 'Ǫ', + 'Ç' => 'Ǭ', + 'ǯ' => 'Ç®', + 'Dz' => 'DZ', + 'dz' => 'DZ', + 'ǵ' => 'Ç´', + 'ǹ' => 'Ǹ', + 'Ç»' => 'Ǻ', + 'ǽ' => 'Ǽ', + 'Ç¿' => 'Ǿ', + 'È' => 'È€', + 'ȃ' => 'È‚', + 'È…' => 'È„', + 'ȇ' => 'Ȇ', + 'ȉ' => 'Ȉ', + 'È‹' => 'ÈŠ', + 'È' => 'ÈŒ', + 'È' => 'ÈŽ', + 'È‘' => 'È', + 'È“' => 'È’', + 'È•' => 'È”', + 'È—' => 'È–', + 'È™' => 'Ș', + 'È›' => 'Èš', + 'È' => 'Èœ', + 'ÈŸ' => 'Èž', + 'È£' => 'È¢', + 'È¥' => 'Ȥ', + 'ȧ' => 'Ȧ', + 'È©' => 'Ȩ', + 'È«' => 'Ȫ', + 'È' => 'Ȭ', + 'ȯ' => 'È®', + 'ȱ' => 'Ȱ', + 'ȳ' => 'Ȳ', + 'ȼ' => 'È»', + 'È¿' => 'â±¾', + 'É€' => 'Ɀ', + 'É‚' => 'É', + 'ɇ' => 'Ɇ', + 'ɉ' => 'Ɉ', + 'É‹' => 'ÉŠ', + 'É' => 'ÉŒ', + 'É' => 'ÉŽ', + 'É' => 'Ɐ', + 'É‘' => 'â±', + 'É’' => 'â±°', + 'É“' => 'Æ', + 'É”' => 'Ɔ', + 'É–' => 'Ɖ', + 'É—' => 'ÆŠ', + 'É™' => 'Æ', + 'É›' => 'Æ', + 'Éœ' => 'êž«', + 'É ' => 'Æ“', + 'É¡' => 'Ɡ', + 'É£' => 'Æ”', + 'É¥' => 'êž', + 'ɦ' => 'Ɦ', + 'ɨ' => 'Æ—', + 'É©' => 'Æ–', + 'É«' => 'â±¢', + 'ɬ' => 'êž', + 'ɯ' => 'Æœ', + 'ɱ' => 'â±®', + 'ɲ' => 'Æ', + 'ɵ' => 'ÆŸ', + 'ɽ' => 'Ɽ', + 'Ê€' => 'Ʀ', + 'ʃ' => 'Æ©', + 'ʇ' => 'êž±', + 'ʈ' => 'Æ®', + 'ʉ' => 'É„', + 'ÊŠ' => 'Ʊ', + 'Ê‹' => 'Ʋ', + 'ÊŒ' => 'É…', + 'Ê’' => 'Æ·', + 'Êž' => 'êž°', + 'Í…' => 'Ι', + 'ͱ' => 'Ͱ', + 'ͳ' => 'Ͳ', + 'Í·' => 'Ͷ', + 'Í»' => 'Ͻ', + 'ͼ' => 'Ͼ', + 'ͽ' => 'Ï¿', + 'ά' => 'Ά', + 'Î' => 'Έ', + 'ή' => 'Ή', + 'ί' => 'Ί', + 'α' => 'Α', + 'β' => 'Î’', + 'γ' => 'Γ', + 'δ' => 'Δ', + 'ε' => 'Ε', + 'ζ' => 'Ζ', + 'η' => 'Η', + 'θ' => 'Θ', + 'ι' => 'Ι', + 'κ' => 'Κ', + 'λ' => 'Λ', + 'μ' => 'Μ', + 'ν' => 'Î', + 'ξ' => 'Ξ', + 'ο' => 'Ο', + 'Ï€' => 'Î ', + 'Ï' => 'Ρ', + 'Ï‚' => 'Σ', + 'σ' => 'Σ', + 'Ï„' => 'Τ', + 'Ï…' => 'Î¥', + 'φ' => 'Φ', + 'χ' => 'Χ', + 'ψ' => 'Ψ', + 'ω' => 'Ω', + 'ÏŠ' => 'Ϊ', + 'Ï‹' => 'Ϋ', + 'ÏŒ' => 'ÎŒ', + 'Ï' => 'ÎŽ', + 'ÏŽ' => 'Î', + 'Ï' => 'Î’', + 'Ï‘' => 'Θ', + 'Ï•' => 'Φ', + 'Ï–' => 'Î ', + 'Ï—' => 'Ï', + 'Ï™' => 'Ϙ', + 'Ï›' => 'Ïš', + 'Ï' => 'Ïœ', + 'ÏŸ' => 'Ïž', + 'Ï¡' => 'Ï ', + 'Ï£' => 'Ï¢', + 'Ï¥' => 'Ϥ', + 'ϧ' => 'Ϧ', + 'Ï©' => 'Ϩ', + 'Ï«' => 'Ϫ', + 'Ï' => 'Ϭ', + 'ϯ' => 'Ï®', + 'ϰ' => 'Κ', + 'ϱ' => 'Ρ', + 'ϲ' => 'Ϲ', + 'ϳ' => 'Í¿', + 'ϵ' => 'Ε', + 'ϸ' => 'Ï·', + 'Ï»' => 'Ϻ', + 'а' => 'Ð', + 'б' => 'Б', + 'в' => 'Ð’', + 'г' => 'Г', + 'д' => 'Д', + 'е' => 'Е', + 'ж' => 'Ж', + 'з' => 'З', + 'и' => 'И', + 'й' => 'Й', + 'к' => 'К', + 'л' => 'Л', + 'м' => 'М', + 'н' => 'Ð', + 'о' => 'О', + 'п' => 'П', + 'Ñ€' => 'Ð ', + 'Ñ' => 'С', + 'Ñ‚' => 'Т', + 'у' => 'У', + 'Ñ„' => 'Ф', + 'Ñ…' => 'Ð¥', + 'ц' => 'Ц', + 'ч' => 'Ч', + 'ш' => 'Ш', + 'щ' => 'Щ', + 'ÑŠ' => 'Ъ', + 'Ñ‹' => 'Ы', + 'ÑŒ' => 'Ь', + 'Ñ' => 'Ð', + 'ÑŽ' => 'Ю', + 'Ñ' => 'Я', + 'Ñ' => 'Ѐ', + 'Ñ‘' => 'Ð', + 'Ñ’' => 'Ђ', + 'Ñ“' => 'Ѓ', + 'Ñ”' => 'Є', + 'Ñ•' => 'Ð…', + 'Ñ–' => 'І', + 'Ñ—' => 'Ї', + 'ј' => 'Ј', + 'Ñ™' => 'Љ', + 'Ñš' => 'Њ', + 'Ñ›' => 'Ћ', + 'Ñœ' => 'ÐŒ', + 'Ñ' => 'Ð', + 'Ñž' => 'ÐŽ', + 'ÑŸ' => 'Ð', + 'Ñ¡' => 'Ñ ', + 'Ñ£' => 'Ñ¢', + 'Ñ¥' => 'Ѥ', + 'ѧ' => 'Ѧ', + 'Ñ©' => 'Ѩ', + 'Ñ«' => 'Ѫ', + 'Ñ' => 'Ѭ', + 'ѯ' => 'Ñ®', + 'ѱ' => 'Ѱ', + 'ѳ' => 'Ѳ', + 'ѵ' => 'Ñ´', + 'Ñ·' => 'Ѷ', + 'ѹ' => 'Ѹ', + 'Ñ»' => 'Ѻ', + 'ѽ' => 'Ѽ', + 'Ñ¿' => 'Ѿ', + 'Ò' => 'Ò€', + 'Ò‹' => 'ÒŠ', + 'Ò' => 'ÒŒ', + 'Ò' => 'ÒŽ', + 'Ò‘' => 'Ò', + 'Ò“' => 'Ò’', + 'Ò•' => 'Ò”', + 'Ò—' => 'Ò–', + 'Ò™' => 'Ò˜', + 'Ò›' => 'Òš', + 'Ò' => 'Òœ', + 'ÒŸ' => 'Òž', + 'Ò¡' => 'Ò ', + 'Ò£' => 'Ò¢', + 'Ò¥' => 'Ò¤', + 'Ò§' => 'Ò¦', + 'Ò©' => 'Ò¨', + 'Ò«' => 'Òª', + 'Ò' => 'Ò¬', + 'Ò¯' => 'Ò®', + 'Ò±' => 'Ò°', + 'Ò³' => 'Ò²', + 'Òµ' => 'Ò´', + 'Ò·' => 'Ò¶', + 'Ò¹' => 'Ò¸', + 'Ò»' => 'Òº', + 'Ò½' => 'Ò¼', + 'Ò¿' => 'Ò¾', + 'Ó‚' => 'Ó', + 'Ó„' => 'Óƒ', + 'Ó†' => 'Ó…', + 'Óˆ' => 'Ó‡', + 'ÓŠ' => 'Ó‰', + 'ÓŒ' => 'Ó‹', + 'ÓŽ' => 'Ó', + 'Ó' => 'Ó€', + 'Ó‘' => 'Ó', + 'Ó“' => 'Ó’', + 'Ó•' => 'Ó”', + 'Ó—' => 'Ó–', + 'Ó™' => 'Ó˜', + 'Ó›' => 'Óš', + 'Ó' => 'Óœ', + 'ÓŸ' => 'Óž', + 'Ó¡' => 'Ó ', + 'Ó£' => 'Ó¢', + 'Ó¥' => 'Ó¤', + 'Ó§' => 'Ó¦', + 'Ó©' => 'Ó¨', + 'Ó«' => 'Óª', + 'Ó' => 'Ó¬', + 'Ó¯' => 'Ó®', + 'Ó±' => 'Ó°', + 'Ó³' => 'Ó²', + 'Óµ' => 'Ó´', + 'Ó·' => 'Ó¶', + 'Ó¹' => 'Ó¸', + 'Ó»' => 'Óº', + 'Ó½' => 'Ó¼', + 'Ó¿' => 'Ó¾', + 'Ô' => 'Ô€', + 'Ôƒ' => 'Ô‚', + 'Ô…' => 'Ô„', + 'Ô‡' => 'Ô†', + 'Ô‰' => 'Ôˆ', + 'Ô‹' => 'ÔŠ', + 'Ô' => 'ÔŒ', + 'Ô' => 'ÔŽ', + 'Ô‘' => 'Ô', + 'Ô“' => 'Ô’', + 'Ô•' => 'Ô”', + 'Ô—' => 'Ô–', + 'Ô™' => 'Ô˜', + 'Ô›' => 'Ôš', + 'Ô' => 'Ôœ', + 'ÔŸ' => 'Ôž', + 'Ô¡' => 'Ô ', + 'Ô£' => 'Ô¢', + 'Ô¥' => 'Ô¤', + 'Ô§' => 'Ô¦', + 'Ô©' => 'Ô¨', + 'Ô«' => 'Ôª', + 'Ô' => 'Ô¬', + 'Ô¯' => 'Ô®', + 'Õ¡' => 'Ô±', + 'Õ¢' => 'Ô²', + 'Õ£' => 'Ô³', + 'Õ¤' => 'Ô´', + 'Õ¥' => 'Ôµ', + 'Õ¦' => 'Ô¶', + 'Õ§' => 'Ô·', + 'Õ¨' => 'Ô¸', + 'Õ©' => 'Ô¹', + 'Õª' => 'Ôº', + 'Õ«' => 'Ô»', + 'Õ¬' => 'Ô¼', + 'Õ' => 'Ô½', + 'Õ®' => 'Ô¾', + 'Õ¯' => 'Ô¿', + 'Õ°' => 'Õ€', + 'Õ±' => 'Õ', + 'Õ²' => 'Õ‚', + 'Õ³' => 'Õƒ', + 'Õ´' => 'Õ„', + 'Õµ' => 'Õ…', + 'Õ¶' => 'Õ†', + 'Õ·' => 'Õ‡', + 'Õ¸' => 'Õˆ', + 'Õ¹' => 'Õ‰', + 'Õº' => 'ÕŠ', + 'Õ»' => 'Õ‹', + 'Õ¼' => 'ÕŒ', + 'Õ½' => 'Õ', + 'Õ¾' => 'ÕŽ', + 'Õ¿' => 'Õ', + 'Ö€' => 'Õ', + 'Ö' => 'Õ‘', + 'Ö‚' => 'Õ’', + 'Öƒ' => 'Õ“', + 'Ö„' => 'Õ”', + 'Ö…' => 'Õ•', + 'Ö†' => 'Õ–', + 'áµ¹' => 'ê½', + 'áµ½' => 'â±£', + 'á¸' => 'Ḁ', + 'ḃ' => 'Ḃ', + 'ḅ' => 'Ḅ', + 'ḇ' => 'Ḇ', + 'ḉ' => 'Ḉ', + 'ḋ' => 'Ḋ', + 'á¸' => 'Ḍ', + 'á¸' => 'Ḏ', + 'ḑ' => 'á¸', + 'ḓ' => 'Ḓ', + 'ḕ' => 'Ḕ', + 'ḗ' => 'Ḗ', + 'ḙ' => 'Ḙ', + 'ḛ' => 'Ḛ', + 'á¸' => 'Ḝ', + 'ḟ' => 'Ḟ', + 'ḡ' => 'Ḡ', + 'ḣ' => 'Ḣ', + 'ḥ' => 'Ḥ', + 'ḧ' => 'Ḧ', + 'ḩ' => 'Ḩ', + 'ḫ' => 'Ḫ', + 'á¸' => 'Ḭ', + 'ḯ' => 'Ḯ', + 'ḱ' => 'Ḱ', + 'ḳ' => 'Ḳ', + 'ḵ' => 'Ḵ', + 'ḷ' => 'Ḷ', + 'ḹ' => 'Ḹ', + 'ḻ' => 'Ḻ', + 'ḽ' => 'Ḽ', + 'ḿ' => 'Ḿ', + 'á¹' => 'á¹€', + 'ṃ' => 'Ṃ', + 'á¹…' => 'Ṅ', + 'ṇ' => 'Ṇ', + 'ṉ' => 'Ṉ', + 'ṋ' => 'Ṋ', + 'á¹' => 'Ṍ', + 'á¹' => 'Ṏ', + 'ṑ' => 'á¹', + 'ṓ' => 'á¹’', + 'ṕ' => 'á¹”', + 'á¹—' => 'á¹–', + 'á¹™' => 'Ṙ', + 'á¹›' => 'Ṛ', + 'á¹' => 'Ṝ', + 'ṟ' => 'Ṟ', + 'ṡ' => 'á¹ ', + 'á¹£' => 'á¹¢', + 'á¹¥' => 'Ṥ', + 'á¹§' => 'Ṧ', + 'ṩ' => 'Ṩ', + 'ṫ' => 'Ṫ', + 'á¹' => 'Ṭ', + 'ṯ' => 'á¹®', + 'á¹±' => 'á¹°', + 'á¹³' => 'á¹²', + 'á¹µ' => 'á¹´', + 'á¹·' => 'á¹¶', + 'á¹¹' => 'Ṹ', + 'á¹»' => 'Ṻ', + 'á¹½' => 'á¹¼', + 'ṿ' => 'á¹¾', + 'áº' => 'Ẁ', + 'ẃ' => 'Ẃ', + 'ẅ' => 'Ẅ', + 'ẇ' => 'Ẇ', + 'ẉ' => 'Ẉ', + 'ẋ' => 'Ẋ', + 'áº' => 'Ẍ', + 'áº' => 'Ẏ', + 'ẑ' => 'áº', + 'ẓ' => 'Ẓ', + 'ẕ' => 'Ẕ', + 'ẛ' => 'á¹ ', + 'ạ' => 'Ạ', + 'ả' => 'Ả', + 'ấ' => 'Ấ', + 'ầ' => 'Ầ', + 'ẩ' => 'Ẩ', + 'ẫ' => 'Ẫ', + 'áº' => 'Ậ', + 'ắ' => 'Ắ', + 'ằ' => 'Ằ', + 'ẳ' => 'Ẳ', + 'ẵ' => 'Ẵ', + 'ặ' => 'Ặ', + 'ẹ' => 'Ẹ', + 'ẻ' => 'Ẻ', + 'ẽ' => 'Ẽ', + 'ế' => 'Ế', + 'á»' => 'Ề', + 'ể' => 'Ể', + 'á»…' => 'Ễ', + 'ệ' => 'Ệ', + 'ỉ' => 'Ỉ', + 'ị' => 'Ị', + 'á»' => 'Ọ', + 'á»' => 'Ỏ', + 'ố' => 'á»', + 'ồ' => 'á»’', + 'ổ' => 'á»”', + 'á»—' => 'á»–', + 'á»™' => 'Ộ', + 'á»›' => 'Ớ', + 'á»' => 'Ờ', + 'ở' => 'Ở', + 'ỡ' => 'á» ', + 'ợ' => 'Ợ', + 'ụ' => 'Ụ', + 'á»§' => 'Ủ', + 'ứ' => 'Ứ', + 'ừ' => 'Ừ', + 'á»' => 'Ử', + 'ữ' => 'á»®', + 'á»±' => 'á»°', + 'ỳ' => 'Ỳ', + 'ỵ' => 'á»´', + 'á»·' => 'á»¶', + 'ỹ' => 'Ỹ', + 'á»»' => 'Ỻ', + 'ỽ' => 'Ỽ', + 'ỿ' => 'Ỿ', + 'á¼€' => 'Ἀ', + 'á¼' => 'Ἁ', + 'ἂ' => 'Ἂ', + 'ἃ' => 'Ἃ', + 'ἄ' => 'Ἄ', + 'á¼…' => 'á¼', + 'ἆ' => 'Ἆ', + 'ἇ' => 'á¼', + 'á¼' => 'Ἐ', + 'ἑ' => 'á¼™', + 'á¼’' => 'Ἒ', + 'ἓ' => 'á¼›', + 'á¼”' => 'Ἔ', + 'ἕ' => 'á¼', + 'á¼ ' => 'Ἠ', + 'ἡ' => 'Ἡ', + 'á¼¢' => 'Ἢ', + 'á¼£' => 'Ἣ', + 'ἤ' => 'Ἤ', + 'á¼¥' => 'á¼', + 'ἦ' => 'á¼®', + 'á¼§' => 'Ἧ', + 'á¼°' => 'Ἰ', + 'á¼±' => 'á¼¹', + 'á¼²' => 'Ἲ', + 'á¼³' => 'á¼»', + 'á¼´' => 'á¼¼', + 'á¼µ' => 'á¼½', + 'á¼¶' => 'á¼¾', + 'á¼·' => 'Ἷ', + 'á½€' => 'Ὀ', + 'á½' => 'Ὁ', + 'ὂ' => 'Ὂ', + 'ὃ' => 'Ὃ', + 'ὄ' => 'Ὄ', + 'á½…' => 'á½', + 'ὑ' => 'á½™', + 'ὓ' => 'á½›', + 'ὕ' => 'á½', + 'á½—' => 'Ὗ', + 'á½ ' => 'Ὠ', + 'ὡ' => 'Ὡ', + 'á½¢' => 'Ὢ', + 'á½£' => 'Ὣ', + 'ὤ' => 'Ὤ', + 'á½¥' => 'á½', + 'ὦ' => 'á½®', + 'á½§' => 'Ὧ', + 'á½°' => 'Ὰ', + 'á½±' => 'á¾»', + 'á½²' => 'Ὲ', + 'á½³' => 'Έ', + 'á½´' => 'Ὴ', + 'á½µ' => 'á¿‹', + 'á½¶' => 'Ὶ', + 'á½·' => 'á¿›', + 'ὸ' => 'Ὸ', + 'á½¹' => 'Ό', + 'ὺ' => 'Ὺ', + 'á½»' => 'á¿«', + 'á½¼' => 'Ὼ', + 'á½½' => 'á¿»', + 'á¾€' => 'ᾈ', + 'á¾' => 'ᾉ', + 'ᾂ' => 'ᾊ', + 'ᾃ' => 'ᾋ', + 'ᾄ' => 'ᾌ', + 'á¾…' => 'á¾', + 'ᾆ' => 'ᾎ', + 'ᾇ' => 'á¾', + 'á¾' => 'ᾘ', + 'ᾑ' => 'á¾™', + 'á¾’' => 'ᾚ', + 'ᾓ' => 'á¾›', + 'á¾”' => 'ᾜ', + 'ᾕ' => 'á¾', + 'á¾–' => 'ᾞ', + 'á¾—' => 'ᾟ', + 'á¾ ' => 'ᾨ', + 'ᾡ' => 'ᾩ', + 'á¾¢' => 'ᾪ', + 'á¾£' => 'ᾫ', + 'ᾤ' => 'ᾬ', + 'á¾¥' => 'á¾', + 'ᾦ' => 'á¾®', + 'á¾§' => 'ᾯ', + 'á¾°' => 'Ᾰ', + 'á¾±' => 'á¾¹', + 'á¾³' => 'á¾¼', + 'á¾¾' => 'Ι', + 'ῃ' => 'ῌ', + 'á¿' => 'Ῐ', + 'á¿‘' => 'á¿™', + 'á¿ ' => 'Ῠ', + 'á¿¡' => 'á¿©', + 'á¿¥' => 'Ῥ', + 'ῳ' => 'ῼ', + 'â…Ž' => 'Ⅎ', + 'â…°' => 'â… ', + 'â…±' => 'â…¡', + 'â…²' => 'â…¢', + 'â…³' => 'â…£', + 'â…´' => 'â…¤', + 'â…µ' => 'â…¥', + 'â…¶' => 'â…¦', + 'â…·' => 'â…§', + 'â…¸' => 'â…¨', + 'â…¹' => 'â…©', + 'â…º' => 'â…ª', + 'â…»' => 'â…«', + 'â…¼' => 'â…¬', + 'â…½' => 'â…', + 'â…¾' => 'â…®', + 'â…¿' => 'â…¯', + 'ↄ' => 'Ↄ', + 'â“' => 'â’¶', + 'â“‘' => 'â’·', + 'â“’' => 'â’¸', + 'â““' => 'â’¹', + 'â“”' => 'â’º', + 'â“•' => 'â’»', + 'â“–' => 'â’¼', + 'â“—' => 'â’½', + 'ⓘ' => 'â’¾', + 'â“™' => 'â’¿', + 'ⓚ' => 'â“€', + 'â“›' => 'â“', + 'ⓜ' => 'â“‚', + 'â“' => 'Ⓝ', + 'ⓞ' => 'â“„', + 'ⓟ' => 'â“…', + 'â“ ' => 'Ⓠ', + 'â“¡' => 'Ⓡ', + 'â“¢' => 'Ⓢ', + 'â“£' => 'Ⓣ', + 'ⓤ' => 'Ⓤ', + 'â“¥' => 'â“‹', + 'ⓦ' => 'Ⓦ', + 'â“§' => 'â“', + 'ⓨ' => 'Ⓨ', + 'â“©' => 'â“', + 'â°°' => 'â°€', + 'â°±' => 'â°', + 'â°²' => 'â°‚', + 'â°³' => 'â°ƒ', + 'â°´' => 'â°„', + 'â°µ' => 'â°…', + 'â°¶' => 'â°†', + 'â°·' => 'â°‡', + 'â°¸' => 'â°ˆ', + 'â°¹' => 'â°‰', + 'â°º' => 'â°Š', + 'â°»' => 'â°‹', + 'â°¼' => 'â°Œ', + 'â°½' => 'â°', + 'â°¾' => 'â°Ž', + 'â°¿' => 'â°', + 'â±€' => 'â°', + 'â±' => 'â°‘', + 'ⱂ' => 'â°’', + 'ⱃ' => 'â°“', + 'ⱄ' => 'â°”', + 'â±…' => 'â°•', + 'ⱆ' => 'â°–', + 'ⱇ' => 'â°—', + 'ⱈ' => 'â°˜', + 'ⱉ' => 'â°™', + 'ⱊ' => 'â°š', + 'ⱋ' => 'â°›', + 'ⱌ' => 'â°œ', + 'â±' => 'â°', + 'ⱎ' => 'â°ž', + 'â±' => 'â°Ÿ', + 'â±' => 'â° ', + 'ⱑ' => 'â°¡', + 'â±’' => 'â°¢', + 'ⱓ' => 'â°£', + 'â±”' => 'â°¤', + 'ⱕ' => 'â°¥', + 'â±–' => 'â°¦', + 'â±—' => 'â°§', + 'ⱘ' => 'â°¨', + 'â±™' => 'â°©', + 'ⱚ' => 'â°ª', + 'â±›' => 'â°«', + 'ⱜ' => 'â°¬', + 'â±' => 'â°', + 'ⱞ' => 'â°®', + 'ⱡ' => 'â± ', + 'â±¥' => 'Ⱥ', + 'ⱦ' => 'Ⱦ', + 'ⱨ' => 'â±§', + 'ⱪ' => 'Ⱪ', + 'ⱬ' => 'Ⱬ', + 'â±³' => 'â±²', + 'â±¶' => 'â±µ', + 'â²' => 'â²€', + 'ⲃ' => 'Ⲃ', + 'â²…' => 'Ⲅ', + 'ⲇ' => 'Ⲇ', + 'ⲉ' => 'Ⲉ', + 'ⲋ' => 'Ⲋ', + 'â²' => 'Ⲍ', + 'â²' => 'Ⲏ', + 'ⲑ' => 'â²', + 'ⲓ' => 'â²’', + 'ⲕ' => 'â²”', + 'â²—' => 'â²–', + 'â²™' => 'Ⲙ', + 'â²›' => 'Ⲛ', + 'â²' => 'Ⲝ', + 'ⲟ' => 'Ⲟ', + 'ⲡ' => 'â² ', + 'â²£' => 'â²¢', + 'â²¥' => 'Ⲥ', + 'â²§' => 'Ⲧ', + 'ⲩ' => 'Ⲩ', + 'ⲫ' => 'Ⲫ', + 'â²' => 'Ⲭ', + 'ⲯ' => 'â²®', + 'â²±' => 'â²°', + 'â²³' => 'â²²', + 'â²µ' => 'â²´', + 'â²·' => 'â²¶', + 'â²¹' => 'Ⲹ', + 'â²»' => 'Ⲻ', + 'â²½' => 'â²¼', + 'ⲿ' => 'â²¾', + 'â³' => 'â³€', + 'ⳃ' => 'Ⳃ', + 'â³…' => 'Ⳅ', + 'ⳇ' => 'Ⳇ', + 'ⳉ' => 'Ⳉ', + 'ⳋ' => 'Ⳋ', + 'â³' => 'Ⳍ', + 'â³' => 'Ⳏ', + 'ⳑ' => 'â³', + 'ⳓ' => 'â³’', + 'ⳕ' => 'â³”', + 'â³—' => 'â³–', + 'â³™' => 'Ⳙ', + 'â³›' => 'Ⳛ', + 'â³' => 'Ⳝ', + 'ⳟ' => 'Ⳟ', + 'ⳡ' => 'â³ ', + 'â³£' => 'â³¢', + 'ⳬ' => 'Ⳬ', + 'â³®' => 'â³', + 'â³³' => 'â³²', + 'â´€' => 'á‚ ', + 'â´' => 'á‚¡', + 'â´‚' => 'á‚¢', + 'â´ƒ' => 'á‚£', + 'â´„' => 'Ⴄ', + 'â´…' => 'á‚¥', + 'â´†' => 'Ⴆ', + 'â´‡' => 'á‚§', + 'â´ˆ' => 'Ⴈ', + 'â´‰' => 'á‚©', + 'â´Š' => 'Ⴊ', + 'â´‹' => 'á‚«', + 'â´Œ' => 'Ⴌ', + 'â´' => 'á‚', + 'â´Ž' => 'á‚®', + 'â´' => 'Ⴏ', + 'â´' => 'á‚°', + 'â´‘' => 'Ⴑ', + 'â´’' => 'Ⴒ', + 'â´“' => 'Ⴓ', + 'â´”' => 'á‚´', + 'â´•' => 'Ⴕ', + 'â´–' => 'á‚¶', + 'â´—' => 'á‚·', + 'â´˜' => 'Ⴘ', + 'â´™' => 'Ⴙ', + 'â´š' => 'Ⴚ', + 'â´›' => 'á‚»', + 'â´œ' => 'Ⴜ', + 'â´' => 'Ⴝ', + 'â´ž' => 'Ⴞ', + 'â´Ÿ' => 'á‚¿', + 'â´ ' => 'Ⴠ', + 'â´¡' => 'áƒ', + 'â´¢' => 'Ⴢ', + 'â´£' => 'Ⴣ', + 'â´¤' => 'Ⴤ', + 'â´¥' => 'Ⴥ', + 'â´§' => 'Ⴧ', + 'â´' => 'áƒ', + 'ê™' => 'Ꙁ', + 'ꙃ' => 'Ꙃ', + 'ê™…' => 'Ꙅ', + 'ꙇ' => 'Ꙇ', + 'ꙉ' => 'Ꙉ', + 'ꙋ' => 'Ꙋ', + 'ê™' => 'Ꙍ', + 'ê™' => 'Ꙏ', + 'ꙑ' => 'ê™', + 'ꙓ' => 'ê™’', + 'ꙕ' => 'ê™”', + 'ê™—' => 'ê™–', + 'ê™™' => 'Ꙙ', + 'ê™›' => 'Ꙛ', + 'ê™' => 'Ꙝ', + 'ꙟ' => 'Ꙟ', + 'ꙡ' => 'ê™ ', + 'ꙣ' => 'Ꙣ', + 'ꙥ' => 'Ꙥ', + 'ê™§' => 'Ꙧ', + 'ꙩ' => 'Ꙩ', + 'ꙫ' => 'Ꙫ', + 'ê™' => 'Ꙭ', + 'êš' => 'Ꚁ', + 'ꚃ' => 'êš‚', + 'êš…' => 'êš„', + 'ꚇ' => 'Ꚇ', + 'ꚉ' => 'Ꚉ', + 'êš‹' => 'Ꚋ', + 'êš' => 'Ꚍ', + 'êš' => 'Ꚏ', + 'êš‘' => 'êš', + 'êš“' => 'êš’', + 'êš•' => 'êš”', + 'êš—' => 'êš–', + 'êš™' => 'Ꚙ', + 'êš›' => 'êšš', + 'ꜣ' => 'Ꜣ', + 'ꜥ' => 'Ꜥ', + 'ꜧ' => 'Ꜧ', + 'ꜩ' => 'Ꜩ', + 'ꜫ' => 'Ꜫ', + 'êœ' => 'Ꜭ', + 'ꜯ' => 'Ꜯ', + 'ꜳ' => 'Ꜳ', + 'ꜵ' => 'Ꜵ', + 'ꜷ' => 'Ꜷ', + 'ꜹ' => 'Ꜹ', + 'ꜻ' => 'Ꜻ', + 'ꜽ' => 'Ꜽ', + 'ꜿ' => 'Ꜿ', + 'ê' => 'ê€', + 'êƒ' => 'ê‚', + 'ê…' => 'ê„', + 'ê‡' => 'ê†', + 'ê‰' => 'êˆ', + 'ê‹' => 'êŠ', + 'ê' => 'êŒ', + 'ê' => 'êŽ', + 'ê‘' => 'ê', + 'ê“' => 'ê’', + 'ê•' => 'ê”', + 'ê—' => 'ê–', + 'ê™' => 'ê˜', + 'ê›' => 'êš', + 'ê' => 'êœ', + 'êŸ' => 'êž', + 'ê¡' => 'ê ', + 'ê£' => 'ê¢', + 'ê¥' => 'ê¤', + 'ê§' => 'ê¦', + 'ê©' => 'ê¨', + 'ê«' => 'êª', + 'ê' => 'ê¬', + 'ê¯' => 'ê®', + 'êº' => 'ê¹', + 'ê¼' => 'ê»', + 'ê¿' => 'ê¾', + 'êž' => 'Ꞁ', + 'ꞃ' => 'êž‚', + 'êž…' => 'êž„', + 'ꞇ' => 'Ꞇ', + 'ꞌ' => 'êž‹', + 'êž‘' => 'êž', + 'êž“' => 'êž’', + 'êž—' => 'êž–', + 'êž™' => 'Ꞙ', + 'êž›' => 'êžš', + 'êž' => 'êžœ', + 'ꞟ' => 'êžž', + 'êž¡' => 'êž ', + 'ꞣ' => 'Ꞣ', + 'ꞥ' => 'Ꞥ', + 'êž§' => 'Ꞧ', + 'êž©' => 'Ꞩ', + 'ï½' => 'A', + 'b' => 'ï¼¢', + 'c' => 'ï¼£', + 'd' => 'D', + 'ï½…' => 'ï¼¥', + 'f' => 'F', + 'g' => 'ï¼§', + 'h' => 'H', + 'i' => 'I', + 'j' => 'J', + 'k' => 'K', + 'l' => 'L', + 'ï½' => 'ï¼', + 'n' => 'ï¼®', + 'ï½' => 'O', + 'ï½' => 'ï¼°', + 'q' => 'ï¼±', + 'ï½’' => 'ï¼²', + 's' => 'ï¼³', + 'ï½”' => 'ï¼´', + 'u' => 'ï¼µ', + 'ï½–' => 'ï¼¶', + 'ï½—' => 'ï¼·', + 'x' => 'X', + 'ï½™' => 'ï¼¹', + 'z' => 'Z', + 'ð¨' => 'ð€', + 'ð©' => 'ð', + 'ðª' => 'ð‚', + 'ð«' => 'ðƒ', + 'ð¬' => 'ð„', + 'ð' => 'ð…', + 'ð®' => 'ð†', + 'ð¯' => 'ð‡', + 'ð°' => 'ðˆ', + 'ð±' => 'ð‰', + 'ð²' => 'ðŠ', + 'ð³' => 'ð‹', + 'ð´' => 'ðŒ', + 'ðµ' => 'ð', + 'ð¶' => 'ðŽ', + 'ð·' => 'ð', + 'ð¸' => 'ð', + 'ð¹' => 'ð‘', + 'ðº' => 'ð’', + 'ð»' => 'ð“', + 'ð¼' => 'ð”', + 'ð½' => 'ð•', + 'ð¾' => 'ð–', + 'ð¿' => 'ð—', + 'ð‘€' => 'ð˜', + 'ð‘' => 'ð™', + 'ð‘‚' => 'ðš', + 'ð‘ƒ' => 'ð›', + 'ð‘„' => 'ðœ', + 'ð‘…' => 'ð', + 'ð‘†' => 'ðž', + 'ð‘‡' => 'ðŸ', + 'ð‘ˆ' => 'ð ', + 'ð‘‰' => 'ð¡', + 'ð‘Š' => 'ð¢', + 'ð‘‹' => 'ð£', + 'ð‘Œ' => 'ð¤', + 'ð‘' => 'ð¥', + 'ð‘Ž' => 'ð¦', + 'ð‘' => 'ð§', + 'ð‘£€' => 'ð‘¢ ', + 'ð‘£' => '𑢡', + '𑣂' => 'ð‘¢¢', + '𑣃' => 'ð‘¢£', + '𑣄' => '𑢤', + 'ð‘£…' => 'ð‘¢¥', + '𑣆' => '𑢦', + '𑣇' => 'ð‘¢§', + '𑣈' => '𑢨', + '𑣉' => '𑢩', + '𑣊' => '𑢪', + '𑣋' => '𑢫', + '𑣌' => '𑢬', + 'ð‘£' => 'ð‘¢', + '𑣎' => 'ð‘¢®', + 'ð‘£' => '𑢯', + 'ð‘£' => 'ð‘¢°', + '𑣑' => 'ð‘¢±', + 'ð‘£’' => 'ð‘¢²', + '𑣓' => 'ð‘¢³', + 'ð‘£”' => 'ð‘¢´', + '𑣕' => 'ð‘¢µ', + 'ð‘£–' => 'ð‘¢¶', + 'ð‘£—' => 'ð‘¢·', + '𑣘' => '𑢸', + 'ð‘£™' => 'ð‘¢¹', + '𑣚' => '𑢺', + 'ð‘£›' => 'ð‘¢»', + '𑣜' => 'ð‘¢¼', + 'ð‘£' => 'ð‘¢½', + '𑣞' => 'ð‘¢¾', + '𑣟' => '𑢿', +); + +$result =& $data; +unset($data); + +return $result; diff --git a/vendor/symfony/polyfill-mbstring/bootstrap.php b/vendor/symfony/polyfill-mbstring/bootstrap.php new file mode 100644 index 00000000..33722910 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/bootstrap.php @@ -0,0 +1,56 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Mbstring as p; + +if (!function_exists('mb_strlen')) { + define('MB_CASE_UPPER', 0); + define('MB_CASE_LOWER', 1); + define('MB_CASE_TITLE', 2); + + function mb_convert_encoding($s, $to, $from = null) { return p\Mbstring::mb_convert_encoding($s, $to, $from); } + function mb_decode_mimeheader($s) { return p\Mbstring::mb_decode_mimeheader($s); } + function mb_encode_mimeheader($s, $charset = null, $transferEnc = null, $lf = null, $indent = null) { return p\Mbstring::mb_encode_mimeheader($s, $charset, $transferEnc, $lf, $indent); } + function mb_convert_case($s, $mode, $enc = null) { return p\Mbstring::mb_convert_case($s, $mode, $enc); } + function mb_internal_encoding($enc = null) { return p\Mbstring::mb_internal_encoding($enc); } + function mb_language($lang = null) { return p\Mbstring::mb_language($lang); } + function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); } + function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); } + function mb_check_encoding($var = null, $encoding = null) { return p\Mbstring::mb_check_encoding($var, $encoding); } + function mb_detect_encoding($str, $encodingList = null, $strict = false) { return p\Mbstring::mb_detect_encoding($str, $encodingList, $strict); } + function mb_detect_order($encodingList = null) { return p\Mbstring::mb_detect_order($encodingList); } + function mb_parse_str($s, &$result = array()) { parse_str($s, $result); } + function mb_strlen($s, $enc = null) { return p\Mbstring::mb_strlen($s, $enc); } + function mb_strpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strpos($s, $needle, $offset, $enc); } + function mb_strtolower($s, $enc = null) { return p\Mbstring::mb_strtolower($s, $enc); } + function mb_strtoupper($s, $enc = null) { return p\Mbstring::mb_strtoupper($s, $enc); } + function mb_substitute_character($char = null) { return p\Mbstring::mb_substitute_character($char); } + function mb_substr($s, $start, $length = 2147483647, $enc = null) { return p\Mbstring::mb_substr($s, $start, $length, $enc); } + function mb_stripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_stripos($s, $needle, $offset, $enc); } + function mb_stristr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_stristr($s, $needle, $part, $enc); } + function mb_strrchr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrchr($s, $needle, $part, $enc); } + function mb_strrichr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrichr($s, $needle, $part, $enc); } + function mb_strripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strripos($s, $needle, $offset, $enc); } + function mb_strrpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strrpos($s, $needle, $offset, $enc); } + function mb_strstr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strstr($s, $needle, $part, $enc); } + function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); } + function mb_http_output($enc = null) { return p\Mbstring::mb_http_output($enc); } + function mb_strwidth($s, $enc = null) { return p\Mbstring::mb_strwidth($s, $enc); } + function mb_substr_count($haystack, $needle, $enc = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $enc); } + function mb_output_handler($contents, $status) { return p\Mbstring::mb_output_handler($contents, $status); } + function mb_http_input($type = '') { return p\Mbstring::mb_http_input($type); } + function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) { return p\Mbstring::mb_convert_variables($toEncoding, $fromEncoding, $a, $b, $c, $d, $e, $f); } +} +if (!function_exists('mb_chr')) { + function mb_ord($s, $enc = null) { return p\Mbstring::mb_ord($s, $enc); } + function mb_chr($code, $enc = null) { return p\Mbstring::mb_chr($code, $enc); } + function mb_scrub($s, $enc = null) { $enc = null === $enc ? mb_internal_encoding() : $enc; return mb_convert_encoding($s, $enc, $enc); } +} diff --git a/vendor/symfony/polyfill-mbstring/composer.json b/vendor/symfony/polyfill-mbstring/composer.json new file mode 100644 index 00000000..48fc3ddf --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/composer.json @@ -0,0 +1,34 @@ +{ + "name": "symfony/polyfill-mbstring", + "type": "library", + "description": "Symfony polyfill for the Mbstring extension", + "keywords": ["polyfill", "shim", "compatibility", "portable", "mbstring"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" }, + "files": [ "bootstrap.php" ] + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + } +} diff --git a/vendor/zendframework/zendxml/.gitignore b/vendor/zendframework/zendxml/.gitignore new file mode 100644 index 00000000..0a4f6e27 --- /dev/null +++ b/vendor/zendframework/zendxml/.gitignore @@ -0,0 +1,5 @@ +composer.lock +vendor +.buildpath +.project +.settings diff --git a/vendor/zendframework/zendxml/.travis.yml b/vendor/zendframework/zendxml/.travis.yml new file mode 100644 index 00000000..877b0650 --- /dev/null +++ b/vendor/zendframework/zendxml/.travis.yml @@ -0,0 +1,43 @@ +sudo: false + +language: php + +branches: + except: + - /^release-.*$/ + - /^ghgfk-.*$/ + +cache: + directories: + - $HOME/.composer/cache + +matrix: + allow_failures: + - php: hhvm +matrix: + fast_finish: true + include: + - php: 5.3 + - php: 5.4 + - php: 5.5 + env: + - EXECUTE_CS_CHECK=true + - php: 5.6 + - php: 7 + - php: hhvm + allow_failures: + - php: hhvm + +before_install: + - composer self-update + +install: + - travis_retry composer install --no-interaction --ignore-platform-reqs + +script: + - ./vendor/bin/phpunit -c ./tests + - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/phpcs --standard=PSR2 --ignore=tests/Bootstrap.php library tests ; fi + +notifications: + irc: "irc.freenode.org#zftalk.dev" + email: false diff --git a/vendor/zendframework/zendxml/CHANGELOG.md b/vendor/zendframework/zendxml/CHANGELOG.md new file mode 100644 index 00000000..bca2961b --- /dev/null +++ b/vendor/zendframework/zendxml/CHANGELOG.md @@ -0,0 +1,24 @@ +# Changelog + +All notable changes to this project will be documented in this file, in reverse chronological order by release. + +## 1.0.2 - 2016-02-04 + +### Added + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- [#11](https://github.com/zendframework/ZendXml/pull/11) updates the + dependencies to PHP `^5.3.3 || ^7.0` and PHPUnit `^3.7 || ^4.0`, ensuring + better compatibility with other components, and with PHP 7. The test matrix + was also expanded to add PHP 7 as a required platform. diff --git a/vendor/zendframework/zendxml/LICENSE.md b/vendor/zendframework/zendxml/LICENSE.md new file mode 100644 index 00000000..141d3a2d --- /dev/null +++ b/vendor/zendframework/zendxml/LICENSE.md @@ -0,0 +1,12 @@ +Copyright (c) 2014-2015, Zend Technologies USA, Inc. +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 Zend Technologies USA, 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. diff --git a/vendor/zendframework/zendxml/README.md b/vendor/zendframework/zendxml/README.md new file mode 100644 index 00000000..2c67008d --- /dev/null +++ b/vendor/zendframework/zendxml/README.md @@ -0,0 +1,50 @@ +ZendXml +======= + +An utility component for XML usage and best practices in PHP + +Installation +------------ + +You can install using: + +``` +curl -s https://getcomposer.org/installer | php +php composer.phar install +``` + +Notice that this library doesn't have any external dependencies, the usage of composer is for autoloading and standard purpose. + + +ZendXml\Security +---------------- + +This is a security component to prevent [XML eXternal Entity](https://www.owasp.org/index.php/XML_External_Entity_%28XXE%29_Processing) (XXE) and [XML Entity Expansion](http://projects.webappsec.org/w/page/13247002/XML%20Entity%20Expansion) (XEE) attacks on XML documents. + +The XXE attack is prevented disabling the load of external entities in the libxml library used by PHP, using the function [libxml_disable_entity_loader](http://www.php.net/manual/en/function.libxml-disable-entity-loader.php). + +The XEE attack is prevented looking inside the XML document for ENTITY usage. If the XML document uses ENTITY the library throw an Exception. + +We have two static methods to scan and load XML document from a string (scan) and from a file (scanFile). You can decide to get a SimpleXMLElement or DOMDocument as result, using the following use cases: + +```php +use ZendXml\Security as XmlSecurity; + +$xml = <<<XML +<?xml version="1.0"?> +<results> + <result>test</result> +</results> +XML; + +// SimpleXML use case +$simplexml = XmlSecurity::scan($xml); +printf ("SimpleXMLElement: %s\n", ($simplexml instanceof \SimpleXMLElement) ? 'yes' : 'no'); + +// DOMDocument use case +$dom = new \DOMDocument('1.0'); +$dom = XmlSecurity::scan($xml, $dom); +printf ("DOMDocument: %s\n", ($dom instanceof \DOMDocument) ? 'yes' : 'no'); +``` + + diff --git a/vendor/zendframework/zendxml/composer.json b/vendor/zendframework/zendxml/composer.json new file mode 100644 index 00000000..d9efb222 --- /dev/null +++ b/vendor/zendframework/zendxml/composer.json @@ -0,0 +1,40 @@ +{ + "name": "zendframework/zendxml", + "description": "Utility library for XML usage, best practices, and security in PHP", + "type": "library", + "license": "BSD-3-Clause", + "keywords": [ + "zf2", + "xml", + "security" + ], + "homepage": "http://packages.zendframework.com/", + "autoload": { + "psr-0": { + "ZendXml\\": "library/" + } + }, + "autoload-dev": { + "psr-4": { + "ZendTest\\Xml\\": "tests/ZendXmlTest/" + } + }, + "repositories": [ + { + "type": "composer", + "url": "http://packages.zendframework.com/" + } + ], + "require": { + "php": "^5.3.3 || ^7.0" + }, + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "require-dev": { + "phpunit/phpunit": "^3.7 || ^4.0", + "squizlabs/php_codesniffer": "^1.5" + } +} diff --git a/vendor/zendframework/zendxml/library/ZendXml/Exception/ExceptionInterface.php b/vendor/zendframework/zendxml/library/ZendXml/Exception/ExceptionInterface.php new file mode 100644 index 00000000..c55eb903 --- /dev/null +++ b/vendor/zendframework/zendxml/library/ZendXml/Exception/ExceptionInterface.php @@ -0,0 +1,14 @@ +<?php +/** + * Zend Framework (http://framework.zend.com/) + * + * @link http://github.com/zendframework/zf2 for the canonical source repository + * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ + +namespace ZendXml\Exception; + +interface ExceptionInterface +{ +} diff --git a/vendor/zendframework/zendxml/library/ZendXml/Exception/InvalidArgumentException.php b/vendor/zendframework/zendxml/library/ZendXml/Exception/InvalidArgumentException.php new file mode 100644 index 00000000..0fef6b29 --- /dev/null +++ b/vendor/zendframework/zendxml/library/ZendXml/Exception/InvalidArgumentException.php @@ -0,0 +1,17 @@ +<?php +/** + * Zend Framework (http://framework.zend.com/) + * + * @link http://github.com/zendframework/zf2 for the canonical source repository + * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ + +namespace ZendXml\Exception; + +/** + * Invalid argument exception + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/zendframework/zendxml/library/ZendXml/Exception/RuntimeException.php b/vendor/zendframework/zendxml/library/ZendXml/Exception/RuntimeException.php new file mode 100644 index 00000000..b730da4f --- /dev/null +++ b/vendor/zendframework/zendxml/library/ZendXml/Exception/RuntimeException.php @@ -0,0 +1,17 @@ +<?php +/** + * Zend Framework (http://framework.zend.com/) + * + * @link http://github.com/zendframework/zf2 for the canonical source repository + * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ + +namespace ZendXml\Exception; + +/** + * Runtime exception + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/zendframework/zendxml/library/ZendXml/Security.php b/vendor/zendframework/zendxml/library/ZendXml/Security.php new file mode 100644 index 00000000..892e2ab1 --- /dev/null +++ b/vendor/zendframework/zendxml/library/ZendXml/Security.php @@ -0,0 +1,374 @@ +<?php +/** + * Zend Framework (http://framework.zend.com/) + * + * @link http://github.com/zendframework/zf2 for the canonical source repository + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +namespace ZendXml; + +use DOMDocument; +use SimpleXMLElement; + +class Security +{ + const ENTITY_DETECT = 'Detected use of ENTITY in XML, disabled to prevent XXE/XEE attacks'; + + /** + * Heuristic scan to detect entity in XML + * + * @param string $xml + * @throws Exception\RuntimeException If entity expansion or external entity declaration was discovered. + */ + protected static function heuristicScan($xml) + { + foreach (self::getEntityComparison($xml) as $compare) { + if (strpos($xml, $compare) !== false) { + throw new Exception\RuntimeException(self::ENTITY_DETECT); + } + } + } + + /** + * Scan XML string for potential XXE and XEE attacks + * + * @param string $xml + * @param DomDocument $dom + * @throws Exception\RuntimeException + * @return SimpleXMLElement|DomDocument|boolean + */ + public static function scan($xml, DOMDocument $dom = null) + { + // If running with PHP-FPM we perform an heuristic scan + // We cannot use libxml_disable_entity_loader because of this bug + // @see https://bugs.php.net/bug.php?id=64938 + if (self::isPhpFpm()) { + self::heuristicScan($xml); + } + + if (null === $dom) { + $simpleXml = true; + $dom = new DOMDocument(); + } + + if (!self::isPhpFpm()) { + $loadEntities = libxml_disable_entity_loader(true); + $useInternalXmlErrors = libxml_use_internal_errors(true); + } + + // Load XML with network access disabled (LIBXML_NONET) + // error disabled with @ for PHP-FPM scenario + set_error_handler(function ($errno, $errstr) { + if (substr_count($errstr, 'DOMDocument::loadXML()') > 0) { + return true; + } + return false; + }, E_WARNING); + $result = $dom->loadXml($xml, LIBXML_NONET); + restore_error_handler(); + + if (!$result) { + // Entity load to previous setting + if (!self::isPhpFpm()) { + libxml_disable_entity_loader($loadEntities); + libxml_use_internal_errors($useInternalXmlErrors); + } + return false; + } + + // Scan for potential XEE attacks using ENTITY, if not PHP-FPM + if (!self::isPhpFpm()) { + foreach ($dom->childNodes as $child) { + if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) { + if ($child->entities->length > 0) { + throw new Exception\RuntimeException(self::ENTITY_DETECT); + } + } + } + } + + // Entity load to previous setting + if (!self::isPhpFpm()) { + libxml_disable_entity_loader($loadEntities); + libxml_use_internal_errors($useInternalXmlErrors); + } + + if (isset($simpleXml)) { + $result = simplexml_import_dom($dom); + if (!$result instanceof SimpleXMLElement) { + return false; + } + return $result; + } + return $dom; + } + + /** + * Scan XML file for potential XXE/XEE attacks + * + * @param string $file + * @param DOMDocument $dom + * @throws Exception\InvalidArgumentException + * @return SimpleXMLElement|DomDocument + */ + public static function scanFile($file, DOMDocument $dom = null) + { + if (!file_exists($file)) { + throw new Exception\InvalidArgumentException( + "The file $file specified doesn't exist" + ); + } + return self::scan(file_get_contents($file), $dom); + } + + /** + * Return true if PHP is running with PHP-FPM + * + * This method is mainly used to determine whether or not heuristic checks + * (vs libxml checks) should be made, due to threading issues in libxml; + * under php-fpm, threading becomes a concern. + * + * However, PHP versions 5.5.22+ and 5.6.6+ contain a patch to the + * libxml support in PHP that makes the libxml checks viable; in such + * versions, this method will return false to enforce those checks, which + * are more strict and accurate than the heuristic checks. + * + * @return boolean + */ + public static function isPhpFpm() + { + $isVulnerableVersion = ( + version_compare(PHP_VERSION, '5.5.22', 'lt') + || ( + version_compare(PHP_VERSION, '5.6', 'gte') + && version_compare(PHP_VERSION, '5.6.6', 'lt') + ) + ); + + if (substr(php_sapi_name(), 0, 3) === 'fpm' && $isVulnerableVersion) { + return true; + } + return false; + } + + /** + * Determine and return the string(s) to use for the <!ENTITY comparison. + * + * @param string $xml + * @return string[] + */ + protected static function getEntityComparison($xml) + { + $encodingMap = self::getAsciiEncodingMap(); + return array_map(function ($encoding) use ($encodingMap) { + $generator = isset($encodingMap[$encoding]) ? $encodingMap[$encoding] : $encodingMap['UTF-8']; + return $generator('<!ENTITY'); + }, self::detectXmlEncoding($xml, self::detectStringEncoding($xml))); + } + + /** + * Determine the string encoding. + * + * Determines string encoding from either a detected BOM or a + * heuristic. + * + * @param string $xml + * @return string File encoding + */ + protected static function detectStringEncoding($xml) + { + return self::detectBom($xml) ?: self::detectXmlStringEncoding($xml); + } + + /** + * Attempt to match a known BOM. + * + * Iterates through the return of getBomMap(), comparing the initial bytes + * of the provided string to the BOM of each; if a match is determined, + * it returns the encoding. + * + * @param string $string + * @return false|string Returns encoding on success. + */ + protected static function detectBom($string) + { + foreach (self::getBomMap() as $criteria) { + if (0 === strncmp($string, $criteria['bom'], $criteria['length'])) { + return $criteria['encoding']; + } + } + return false; + } + + /** + * Attempt to detect the string encoding of an XML string. + * + * @param string $xml + * @return string Encoding + */ + protected static function detectXmlStringEncoding($xml) + { + foreach (self::getAsciiEncodingMap() as $encoding => $generator) { + $prefix = $generator('<' . '?xml'); + if (0 === strncmp($xml, $prefix, strlen($prefix))) { + return $encoding; + } + } + + // Fallback + return 'UTF-8'; + } + + /** + * Attempt to detect the specified XML encoding. + * + * Using the file's encoding, determines if an "encoding" attribute is + * present and well-formed in the XML declaration; if so, it returns a + * list with both the ASCII representation of that declaration and the + * original file encoding. + * + * If not, a list containing only the provided file encoding is returned. + * + * @param string $xml + * @param string $fileEncoding + * @return string[] Potential XML encodings + */ + protected static function detectXmlEncoding($xml, $fileEncoding) + { + $encodingMap = self::getAsciiEncodingMap(); + $generator = $encodingMap[$fileEncoding]; + $encAttr = $generator('encoding="'); + $quote = $generator('"'); + $close = $generator('>'); + + $closePos = strpos($xml, $close); + if (false === $closePos) { + return array($fileEncoding); + } + + $encPos = strpos($xml, $encAttr); + if (false === $encPos + || $encPos > $closePos + ) { + return array($fileEncoding); + } + + $encPos += strlen($encAttr); + $quotePos = strpos($xml, $quote, $encPos); + if (false === $quotePos) { + return array($fileEncoding); + } + + $encoding = self::substr($xml, $encPos, $quotePos); + return array( + // Following line works because we're only supporting 8-bit safe encodings at this time. + str_replace('\0', '', $encoding), // detected encoding + $fileEncoding, // file encoding + ); + } + + /** + * Return a list of BOM maps. + * + * Returns a list of common encoding -> BOM maps, along with the character + * length to compare against. + * + * @link https://en.wikipedia.org/wiki/Byte_order_mark + * @return array + */ + protected static function getBomMap() + { + return array( + array( + 'encoding' => 'UTF-32BE', + 'bom' => pack('CCCC', 0x00, 0x00, 0xfe, 0xff), + 'length' => 4, + ), + array( + 'encoding' => 'UTF-32LE', + 'bom' => pack('CCCC', 0xff, 0xfe, 0x00, 0x00), + 'length' => 4, + ), + array( + 'encoding' => 'GB-18030', + 'bom' => pack('CCCC', 0x84, 0x31, 0x95, 0x33), + 'length' => 4, + ), + array( + 'encoding' => 'UTF-16BE', + 'bom' => pack('CC', 0xfe, 0xff), + 'length' => 2, + ), + array( + 'encoding' => 'UTF-16LE', + 'bom' => pack('CC', 0xff, 0xfe), + 'length' => 2, + ), + array( + 'encoding' => 'UTF-8', + 'bom' => pack('CCC', 0xef, 0xbb, 0xbf), + 'length' => 3, + ), + ); + } + + /** + * Return a map of encoding => generator pairs. + * + * Returns a map of encoding => generator pairs, where the generator is a + * callable that accepts a string and returns the appropriate byte order + * sequence of that string for the encoding. + * + * @return array + */ + protected static function getAsciiEncodingMap() + { + return array( + 'UTF-32BE' => function ($ascii) { + return preg_replace('/(.)/', "\0\0\0\\1", $ascii); + }, + 'UTF-32LE' => function ($ascii) { + return preg_replace('/(.)/', "\\1\0\0\0", $ascii); + }, + 'UTF-32odd1' => function ($ascii) { + return preg_replace('/(.)/', "\0\\1\0\0", $ascii); + }, + 'UTF-32odd2' => function ($ascii) { + return preg_replace('/(.)/', "\0\0\\1\0", $ascii); + }, + 'UTF-16BE' => function ($ascii) { + return preg_replace('/(.)/', "\0\\1", $ascii); + }, + 'UTF-16LE' => function ($ascii) { + return preg_replace('/(.)/', "\\1\0", $ascii); + }, + 'UTF-8' => function ($ascii) { + return $ascii; + }, + 'GB-18030' => function ($ascii) { + return $ascii; + }, + ); + } + + /** + * Binary-safe substr. + * + * substr() is not binary-safe; this method loops by character to ensure + * multi-byte characters are aggregated correctly. + * + * @param string $string + * @param int $start + * @param int $end + * @return string + */ + protected static function substr($string, $start, $end) + { + $substr = ''; + for ($i = $start; $i < $end; $i += 1) { + $substr .= $string[$i]; + } + return $substr; + } +} diff --git a/vendor/zendframework/zendxml/tests/Bootstrap.php b/vendor/zendframework/zendxml/tests/Bootstrap.php new file mode 100644 index 00000000..a9d0e6a5 --- /dev/null +++ b/vendor/zendframework/zendxml/tests/Bootstrap.php @@ -0,0 +1,92 @@ +<?php +/** + * Zend Framework (http://framework.zend.com/) + * + * @link http://github.com/zendframework/zf2 for the canonical source repository + * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @package Zend + */ + +/** + * Set error reporting to the level to which Zend Framework code must comply. + */ +error_reporting( E_ALL | E_STRICT ); + +if (class_exists('PHPUnit_Runner_Version', true)) { + $phpUnitVersion = PHPUnit_Runner_Version::id(); + if ('@package_version@' !== $phpUnitVersion && version_compare($phpUnitVersion, '3.7.0', '<')) { + echo 'This version of PHPUnit (' . + PHPUnit_Runner_Version::id() . + ') is not supported for ZendXml unit tests - use v 3.7.0 or higher.' + . PHP_EOL + ; + exit(1); + } + unset($phpUnitVersion); +} + +/** + * Setup autoloading + */ +// Try to use Composer autoloader +if (file_exists(__DIR__ . '/../vendor/autoload.php')) { + include_once __DIR__ . '/../vendor/autoload.php'; +} +// ... or use a simple SPL autoloader +else{ + + // update include path + set_include_path(implode(PATH_SEPARATOR, array( + __DIR__.'/../src', + __DIR__, + get_include_path() + ))); + + /** + * @link https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md#example-implementation + */ + spl_autoload_register(function ($className) { + $className = ltrim($className, '\\'); + $fileName = ''; + $namespace = ''; + if ($lastNsPos = strrpos($className, '\\')) { + $namespace = substr($className, 0, $lastNsPos); + $className = substr($className, $lastNsPos + 1); + $fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR; + } + $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php'; + require $fileName; + }); + +} + +/** + * Code coverage option + */ +if (defined('TESTS_GENERATE_REPORT') && TESTS_GENERATE_REPORT === true) { + $codeCoverageFilter = new PHP_CodeCoverage_Filter(); + + $lastArg = end($_SERVER['argv']); + if (is_dir($zfCoreTests . '/' . $lastArg)) { + $codeCoverageFilter->addDirectoryToWhitelist($zfCoreLibrary . '/' . $lastArg); + } elseif (is_file($zfCoreTests . '/' . $lastArg)) { + $codeCoverageFilter->addDirectoryToWhitelist(dirname($zfCoreLibrary . '/' . $lastArg)); + } else { + $codeCoverageFilter->addDirectoryToWhitelist($zfCoreLibrary); + } + + /* + * Omit from code coverage reports the contents of the tests directory + */ + $codeCoverageFilter->addDirectoryToBlacklist($zfCoreTests, ''); + $codeCoverageFilter->addDirectoryToBlacklist(PEAR_INSTALL_DIR, ''); + $codeCoverageFilter->addDirectoryToBlacklist(PHP_LIBDIR, ''); + + unset($codeCoverageFilter); +} + +/* + * Unset global variables that are no longer needed. + */ +unset($phpUnitVersion); diff --git a/vendor/zendframework/zendxml/tests/ZendXmlTest/MultibyteTest.php b/vendor/zendframework/zendxml/tests/ZendXmlTest/MultibyteTest.php new file mode 100644 index 00000000..165e8fa5 --- /dev/null +++ b/vendor/zendframework/zendxml/tests/ZendXmlTest/MultibyteTest.php @@ -0,0 +1,125 @@ +<?php +/** + * Zend Framework (http://framework.zend.com/) + * + * @link http://github.com/zendframework/zf2 for the canonical source repository + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +namespace ZendTest\Xml; + +use ZendXml\Security as XmlSecurity; +use ZendXml\Exception; +use DOMDocument; +use ReflectionMethod; +use SimpleXMLElement; + +/** + * @group ZF2015-06 + */ +class MultibyteTest extends \PHPUnit_Framework_TestCase +{ + public function multibyteEncodings() + { + return array( + 'UTF-16LE' => array('UTF-16LE', pack('CC', 0xff, 0xfe), 3), + 'UTF-16BE' => array('UTF-16BE', pack('CC', 0xfe, 0xff), 3), + 'UTF-32LE' => array('UTF-32LE', pack('CCCC', 0xff, 0xfe, 0x00, 0x00), 4), + 'UTF-32BE' => array('UTF-32BE', pack('CCCC', 0x00, 0x00, 0xfe, 0xff), 4), + ); + } + + public function getXmlWithXXE() + { + return <<<XML +<?xml version="1.0" encoding="{ENCODING}"?> +<!DOCTYPE methodCall [ + <!ENTITY pocdata SYSTEM "file:///etc/passwd"> +]> +<methodCall> + <methodName>retrieved: &pocdata;</methodName> +</methodCall> +XML; + } + + /** + * Invoke ZendXml\Security::heuristicScan with the provided XML. + * + * @param string $xml + * @return void + * @throws Exception\RuntimeException + */ + public function invokeHeuristicScan($xml) + { + $r = new ReflectionMethod('ZendXml\Security', 'heuristicScan'); + $r->setAccessible(true); + return $r->invoke(null, $xml); + } + + /** + * @dataProvider multibyteEncodings + * @group heuristicDetection + */ + public function testDetectsMultibyteXXEVectorsUnderFPMWithEncodedStringMissingBOM($encoding, $bom, $bomLength) + { + $xml = $this->getXmlWithXXE(); + $xml = str_replace('{ENCODING}', $encoding, $xml); + $xml = iconv('UTF-8', $encoding, $xml); + $this->assertNotSame(0, strncmp($xml, $bom, $bomLength)); + $this->setExpectedException('ZendXml\Exception\RuntimeException', 'ENTITY'); + $this->invokeHeuristicScan($xml); + } + + /** + * @dataProvider multibyteEncodings + */ + public function testDetectsMultibyteXXEVectorsUnderFPMWithEncodedStringUsingBOM($encoding, $bom) + { + $xml = $this->getXmlWithXXE(); + $xml = str_replace('{ENCODING}', $encoding, $xml); + $orig = iconv('UTF-8', $encoding, $xml); + $xml = $bom . $orig; + $this->setExpectedException('ZendXml\Exception\RuntimeException', 'ENTITY'); + $this->invokeHeuristicScan($xml); + } + + public function getXmlWithoutXXE() + { + return <<<XML +<?xml version="1.0" encoding="{ENCODING}"?> +<methodCall> + <methodName>retrieved: &pocdata;</methodName> +</methodCall> +XML; + } + + /** + * @dataProvider multibyteEncodings + */ + public function testDoesNotFlagValidMultibyteXmlAsInvalidUnderFPM($encoding) + { + $xml = $this->getXmlWithoutXXE(); + $xml = str_replace('{ENCODING}', $encoding, $xml); + $xml = iconv('UTF-8', $encoding, $xml); + try { + $result = $this->invokeHeuristicScan($xml); + $this->assertNull($result); + } catch (\Exception $e) { + $this->fail('Security scan raised exception when it should not have'); + } + } + + /** + * @dataProvider multibyteEncodings + * @group mixedEncoding + */ + public function testDetectsXXEWhenXMLDocumentEncodingDiffersFromFileEncoding($encoding, $bom) + { + $xml = $this->getXmlWithXXE(); + $xml = str_replace('{ENCODING}', 'UTF-8', $xml); + $xml = iconv('UTF-8', $encoding, $xml); + $xml = $bom . $xml; + $this->setExpectedException('ZendXml\Exception\RuntimeException', 'ENTITY'); + $this->invokeHeuristicScan($xml); + } +} diff --git a/vendor/zendframework/zendxml/tests/ZendXmlTest/SecurityTest.php b/vendor/zendframework/zendxml/tests/ZendXmlTest/SecurityTest.php new file mode 100644 index 00000000..fa3b30bf --- /dev/null +++ b/vendor/zendframework/zendxml/tests/ZendXmlTest/SecurityTest.php @@ -0,0 +1,135 @@ +<?php +/** + * Zend Framework (http://framework.zend.com/) + * + * @link http://github.com/zendframework/zf2 for the canonical source repository + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +namespace ZendTest\Xml; + +use ZendXml\Security as XmlSecurity; +use ZendXml\Exception; +use DOMDocument; +use SimpleXMLElement; + +class SecurityTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException ZendXml\Exception\RuntimeException + */ + public function testScanForXEE() + { + $xml = <<<XML +<?xml version="1.0"?> +<!DOCTYPE results [<!ENTITY harmless "completely harmless">]> +<results> + <result>This result is &harmless;</result> +</results> +XML; + + $this->setExpectedException('ZendXml\Exception\RuntimeException'); + $result = XmlSecurity::scan($xml); + } + + public function testScanForXXE() + { + $file = tempnam(sys_get_temp_dir(), 'ZendXml_Security'); + file_put_contents($file, 'This is a remote content!'); + $xml = <<<XML +<?xml version="1.0"?> +<!DOCTYPE root +[ +<!ENTITY foo SYSTEM "file://$file"> +]> +<results> + <result>&foo;</result> +</results> +XML; + + try { + $result = XmlSecurity::scan($xml); + } catch (Exception\RuntimeException $e) { + unlink($file); + return; + } + $this->fail('An expected exception has not been raised.'); + } + + public function testScanSimpleXmlResult() + { + $result = XmlSecurity::scan($this->getXml()); + $this->assertTrue($result instanceof SimpleXMLElement); + $this->assertEquals($result->result, 'test'); + } + + public function testScanDom() + { + $dom = new DOMDocument('1.0'); + $result = XmlSecurity::scan($this->getXml(), $dom); + $this->assertTrue($result instanceof DOMDocument); + $node = $result->getElementsByTagName('result')->item(0); + $this->assertEquals($node->nodeValue, 'test'); + } + + public function testScanInvalidXml() + { + $xml = <<<XML +<foo>test</bar> +XML; + + $result = XmlSecurity::scan($xml); + $this->assertFalse($result); + } + + public function testScanInvalidXmlDom() + { + $xml = <<<XML +<foo>test</bar> +XML; + + $dom = new DOMDocument('1.0'); + $result = XmlSecurity::scan($xml, $dom); + $this->assertFalse($result); + } + + public function testScanFile() + { + $file = tempnam(sys_get_temp_dir(), 'ZendXml_Security'); + file_put_contents($file, $this->getXml()); + + $result = XmlSecurity::scanFile($file); + $this->assertTrue($result instanceof SimpleXMLElement); + $this->assertEquals($result->result, 'test'); + unlink($file); + } + + public function testScanXmlWithDTD() + { + $xml = <<<XML +<?xml version="1.0"?> +<!DOCTYPE results [ +<!ELEMENT results (result+)> +<!ELEMENT result (#PCDATA)> +]> +<results> + <result>test</result> +</results> +XML; + + $dom = new DOMDocument('1.0'); + $result = XmlSecurity::scan($xml, $dom); + $this->assertTrue($result instanceof DOMDocument); + $this->assertTrue($result->validate()); + } + + protected function getXml() + { + return <<<XML +<?xml version="1.0"?> +<results> + <result>test</result> +</results> +XML; + } +} diff --git a/vendor/zendframework/zendxml/tests/phpunit.xml.dist b/vendor/zendframework/zendxml/tests/phpunit.xml.dist new file mode 100755 index 00000000..069784bd --- /dev/null +++ b/vendor/zendframework/zendxml/tests/phpunit.xml.dist @@ -0,0 +1,27 @@ +<phpunit bootstrap="./Bootstrap.php" colors="true"> + <testsuites> + <testsuite name="ZendXml Test Suite"> + <directory>./ZendXmlTest</directory> + <exclude>./ZendXmlTest/TestAsset</exclude> + </testsuite> + </testsuites> + + <groups> + <exclude> + </exclude> + </groups> + + <listeners> + </listeners> + + <filter> + <blacklist> + <directory suffix=".php">./ZendXmlTest</directory> + <directory>../vendor</directory> + </blacklist> + </filter> + + <php> + </php> + +</phpunit> diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index bfef20bc..00000000 --- a/yarn.lock +++ /dev/null @@ -1,2413 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -abbrev@1: - version "1.0.9" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" - -align-text@^0.1.1, align-text@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" - dependencies: - kind-of "^3.0.2" - longest "^1.0.1" - repeat-string "^1.5.2" - -ansi-escapes@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" - -ansi-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.0.0.tgz#c5061b6e0ef8a81775e50f5d66151bf6bf371107" - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - -aproba@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.0.4.tgz#2713680775e7614c8ba186c065d4e2e52d1072c0" - -archy@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" - -are-we-there-yet@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz#80e470e95a084794fe1899262c5667c6e88de1b3" - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.0 || ^1.1.13" - -arr-diff@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" - dependencies: - arr-flatten "^1.0.1" - -arr-flatten@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b" - -array-differ@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" - -array-find-index@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" - -array-index@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-index/-/array-index-1.0.0.tgz#ec56a749ee103e4e08c790b9c353df16055b97f9" - dependencies: - debug "^2.2.0" - es6-symbol "^3.0.2" - -array-uniq@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - -array-unique@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" - -asn1@~0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" - -assert-plus@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" - -assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - -async-foreach@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" - -async@~0.2.6: - version "0.2.10" - resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - -aws-sign2@~0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" - -aws4@^1.2.1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.5.0.tgz#0a29ffb79c31c9e712eeb087e8e7a64b4a56d755" - -balanced-match@^0.4.1: - version "0.4.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" - -bcrypt-pbkdf@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.0.tgz#3ca76b85241c7170bf7d9703e7b9aa74630040d4" - dependencies: - tweetnacl "^0.14.3" - -beeper@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809" - -block-stream@*: - version "0.0.9" - resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" - dependencies: - inherits "~2.0.0" - -boom@2.x.x: - version "2.10.1" - resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" - dependencies: - hoek "2.x.x" - -bower@^1.3.12, bower@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/bower/-/bower-1.8.0.tgz#55dbebef0ad9155382d9e9d3e497c1372345b44a" - -brace-expansion@^1.0.0: - version "1.1.6" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" - dependencies: - balanced-match "^0.4.1" - concat-map "0.0.1" - -braces@^1.8.2: - version "1.8.5" - resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" - dependencies: - expand-range "^1.8.1" - preserve "^0.2.0" - repeat-element "^1.1.2" - -buffer-shims@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" - -builtin-modules@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - -camelcase-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" - dependencies: - camelcase "^2.0.0" - map-obj "^1.0.0" - -camelcase@^1.0.2: - version "1.2.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" - -camelcase@^2.0.0, camelcase@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" - -camelcase@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" - -caseless@~0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" - -center-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" - dependencies: - align-text "^0.1.3" - lazy-cache "^1.0.3" - -chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -cli-cursor@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" - dependencies: - restore-cursor "^1.0.1" - -cli-width@^1.0.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-1.1.1.tgz#a4d293ef67ebb7b88d4a4d42c0ccf00c4d1e366d" - -cli@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cli/-/cli-1.0.1.tgz#22817534f24bfa4950c34d532d48ecbc621b8c14" - dependencies: - exit "0.1.2" - glob "^7.1.1" - -cliui@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" - dependencies: - center-align "^0.1.1" - right-align "^0.1.1" - wordwrap "0.0.2" - -cliui@^3.0.3, cliui@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi "^2.0.0" - -clone-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" - -clone-stats@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" - -clone-stats@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" - -clone@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/clone/-/clone-0.2.0.tgz#c6126a90ad4f72dbf5acdb243cc37724fe93fc1f" - -clone@^1.0.0, clone@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149" - -cloneable-readable@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.0.0.tgz#a6290d413f217a61232f95e458ff38418cfb0117" - dependencies: - inherits "^2.0.1" - process-nextick-args "^1.0.6" - through2 "^2.0.1" - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - -combined-stream@^1.0.5, combined-stream@~1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" - dependencies: - delayed-stream "~1.0.0" - -commander@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" - dependencies: - graceful-readlink ">= 1.0.0" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - -concat-with-sourcemaps@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.0.4.tgz#f55b3be2aeb47601b10a2d5259ccfb70fd2f1dd6" - dependencies: - source-map "^0.5.1" - -console-browserify@1.1.x: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" - dependencies: - date-now "^0.1.4" - -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - -core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - -cross-spawn@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" - dependencies: - lru-cache "^4.0.1" - which "^1.2.9" - -cryptiles@2.x.x: - version "2.0.5" - resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" - dependencies: - boom "2.x.x" - -currently-unhandled@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" - dependencies: - array-find-index "^1.0.1" - -d@^0.1.1, d@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/d/-/d-0.1.1.tgz#da184c535d18d8ee7ba2aa229b914009fae11309" - dependencies: - es5-ext "~0.10.2" - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - dependencies: - assert-plus "^1.0.0" - -date-now@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" - -dateformat@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.0.0.tgz#2743e3abb5c3fc2462e527dca445e04e9f4dee17" - -debug@^2.2.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b" - dependencies: - ms "0.7.2" - -decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - -defaults@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" - dependencies: - clone "^1.0.2" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - -deprecated@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/deprecated/-/deprecated-0.0.1.tgz#f9c9af5464afa1e7a971458a8bdef2aa94d5bb19" - -detect-file@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-0.1.0.tgz#4935dedfd9488648e006b0129566e9386711ea63" - dependencies: - fs-exists-sync "^0.1.0" - -dom-serializer@0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" - dependencies: - domelementtype "~1.1.1" - entities "~1.1.1" - -domelementtype@1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" - -domelementtype@~1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" - -domhandler@2.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738" - dependencies: - domelementtype "1" - -domutils@1.5: - version "1.5.1" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" - dependencies: - dom-serializer "0" - domelementtype "1" - -duplexer2@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" - dependencies: - readable-stream "~1.1.9" - -ecc-jsbn@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" - dependencies: - jsbn "~0.1.0" - -end-of-stream@~0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-0.1.5.tgz#8e177206c3c80837d85632e8b9359dfe8b2f6eaf" - dependencies: - once "~1.3.0" - -entities@1.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26" - -entities@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" - -error-ex@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.0.tgz#e67b43f3e82c96ea3a584ffee0b9fc3325d802d9" - dependencies: - is-arrayish "^0.2.1" - -es5-ext@^0.10.7, es5-ext@~0.10.11, es5-ext@~0.10.2: - version "0.10.12" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.12.tgz#aa84641d4db76b62abba5e45fd805ecbab140047" - dependencies: - es6-iterator "2" - es6-symbol "~3.1" - -es6-iterator@2: - version "2.0.0" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.0.tgz#bd968567d61635e33c0b80727613c9cb4b096bac" - dependencies: - d "^0.1.1" - es5-ext "^0.10.7" - es6-symbol "3" - -es6-symbol@3, es6-symbol@^3.0.2, es6-symbol@~3.1: - version "3.1.0" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.0.tgz#94481c655e7a7cad82eba832d97d5433496d7ffa" - dependencies: - d "~0.1.1" - es5-ext "~0.10.11" - -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - -exit-hook@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" - -exit@0.1.2, exit@0.1.x: - version "0.1.2" - resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" - -expand-brackets@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" - dependencies: - is-posix-bracket "^0.1.0" - -expand-range@^1.8.1: - version "1.8.2" - resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" - dependencies: - fill-range "^2.1.0" - -expand-tilde@^1.2.1, expand-tilde@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-1.2.2.tgz#0b81eba897e5a3d31d1c3d102f8f01441e559449" - dependencies: - os-homedir "^1.0.1" - -extend@^3.0.0, extend@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" - -extglob@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" - dependencies: - is-extglob "^1.0.0" - -extsprintf@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" - -fancy-log@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.0.tgz#45be17d02bb9917d60ccffd4995c999e6c8c9948" - dependencies: - chalk "^1.1.1" - time-stamp "^1.0.0" - -figures@^1.3.5: - version "1.7.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" - dependencies: - escape-string-regexp "^1.0.5" - object-assign "^4.1.0" - -filename-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" - -fill-range@^2.1.0: - version "2.2.3" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" - dependencies: - is-number "^2.1.0" - isobject "^2.0.0" - randomatic "^1.1.3" - repeat-element "^1.1.2" - repeat-string "^1.5.2" - -find-index@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4" - -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" - dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" - -findup-sync@^0.4.2: - version "0.4.3" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.4.3.tgz#40043929e7bc60adf0b7f4827c4c6e75a0deca12" - dependencies: - detect-file "^0.1.0" - is-glob "^2.0.1" - micromatch "^2.3.7" - resolve-dir "^0.1.0" - -findup-sync@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.3.0.tgz#37930aa5d816b777c03445e1966cc6790a4c0b16" - dependencies: - glob "~5.0.0" - -fined@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/fined/-/fined-1.0.2.tgz#5b28424b760d7598960b7ef8480dff8ad3660e97" - dependencies: - expand-tilde "^1.2.1" - lodash.assignwith "^4.0.7" - lodash.isempty "^4.2.1" - lodash.isplainobject "^4.0.4" - lodash.isstring "^4.0.1" - lodash.pick "^4.2.1" - parse-filepath "^1.0.1" - -first-chunk-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz#59bfb50cd905f60d7c394cd3d9acaab4e6ad934e" - -flagged-respawn@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-0.3.2.tgz#ff191eddcd7088a675b2610fffc976be9b8074b5" - -for-in@^0.1.5: - version "0.1.6" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.6.tgz#c9f96e89bfad18a545af5ec3ed352a1d9e5b4dc8" - -for-own@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.4.tgz#0149b41a39088c7515f51ebe1c1386d45f935072" - dependencies: - for-in "^0.1.5" - -foreachasync@3.x: - version "3.0.0" - resolved "https://registry.yarnpkg.com/foreachasync/-/foreachasync-3.0.0.tgz#5502987dc8714be3392097f32e0071c9dee07cf6" - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - -form-data@~2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.2.tgz#89c3534008b97eada4cbb157d58f6f5df025eae4" - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.5" - mime-types "^2.1.12" - -fs-exists-sync@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - -fstream@^1.0.0, fstream@^1.0.2: - version "1.0.10" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.10.tgz#604e8a92fe26ffd9f6fae30399d4984e1ab22822" - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" - -gauge@~2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.6.0.tgz#d35301ad18e96902b4751dcbbe40f4218b942a46" - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-color "^0.1.7" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - -gauge@~2.7.1: - version "2.7.2" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.2.tgz#15cecc31b02d05345a5d6b0e171cdb3ad2307774" - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - supports-color "^0.2.0" - wide-align "^1.1.0" - -gaze@^0.5.1: - version "0.5.2" - resolved "https://registry.yarnpkg.com/gaze/-/gaze-0.5.2.tgz#40b709537d24d1d45767db5a908689dfe69ac44f" - dependencies: - globule "~0.1.0" - -gaze@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.2.tgz#847224677adb8870d679257ed3388fdb61e40105" - dependencies: - globule "^1.0.0" - -generate-function@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" - -generate-object-property@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" - dependencies: - is-property "^1.0.0" - -get-caller-file@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" - -get-stdin@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" - -getpass@^0.1.1: - version "0.1.6" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6" - dependencies: - assert-plus "^1.0.0" - -glob-base@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" - dependencies: - glob-parent "^2.0.0" - is-glob "^2.0.0" - -glob-parent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" - dependencies: - is-glob "^2.0.0" - -glob-stream@^3.1.5: - version "3.1.18" - resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-3.1.18.tgz#9170a5f12b790306fdfe598f313f8f7954fd143b" - dependencies: - glob "^4.3.1" - glob2base "^0.0.12" - minimatch "^2.0.1" - ordered-read-streams "^0.1.0" - through2 "^0.6.1" - unique-stream "^1.0.0" - -glob-watcher@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-0.0.6.tgz#b95b4a8df74b39c83298b0c05c978b4d9a3b710b" - dependencies: - gaze "^0.5.1" - -glob2base@^0.0.12: - version "0.0.12" - resolved "http://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz#9d419b3e28f12e83a362164a277055922c9c0d56" - dependencies: - find-index "^0.1.1" - -glob@^4.3.1: - version "4.5.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-4.5.3.tgz#c6cb73d3226c1efef04de3c56d012f03377ee15f" - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "^2.0.1" - once "^1.3.0" - -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@~7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.2" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@~3.1.21: - version "3.1.21" - resolved "https://registry.yarnpkg.com/glob/-/glob-3.1.21.tgz#d29e0a055dea5138f4d07ed40e8982e83c2066cd" - dependencies: - graceful-fs "~1.2.0" - inherits "1" - minimatch "~0.2.11" - -glob@~5.0.0: - version "5.0.15" - resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" - -global-modules@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-0.2.3.tgz#ea5a3bed42c6d6ce995a4f8a1269b5dae223828d" - dependencies: - global-prefix "^0.1.4" - is-windows "^0.2.0" - -global-prefix@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-0.1.5.tgz#8d3bc6b8da3ca8112a160d8d496ff0462bfef78f" - dependencies: - homedir-polyfill "^1.0.0" - ini "^1.3.4" - is-windows "^0.2.0" - which "^1.2.12" - -globule@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/globule/-/globule-1.1.0.tgz#c49352e4dc183d85893ee825385eb994bb6df45f" - dependencies: - glob "~7.1.1" - lodash "~4.16.4" - minimatch "~3.0.2" - -globule@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/globule/-/globule-0.1.0.tgz#d9c8edde1da79d125a151b79533b978676346ae5" - dependencies: - glob "~3.1.21" - lodash "~1.0.1" - minimatch "~0.2.11" - -glogg@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.0.tgz#7fe0f199f57ac906cf512feead8f90ee4a284fc5" - dependencies: - sparkles "^1.0.0" - -graceful-fs@^3.0.0: - version "3.0.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-3.0.11.tgz#7613c778a1afea62f25c630a086d7f3acbbdd818" - dependencies: - natives "^1.1.0" - -graceful-fs@^4.1.2: - version "4.1.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" - -graceful-fs@~1.2.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-1.2.3.tgz#15a4806a57547cb2d2dbf27f42e89a8c3451b364" - -"graceful-readlink@>= 1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" - -gulp-bower@^0.0.13: - version "0.0.13" - resolved "https://registry.yarnpkg.com/gulp-bower/-/gulp-bower-0.0.13.tgz#7ca4e3c5a599d08fada2b1c054cce8cde4e74235" - dependencies: - bower "^1.3.12" - gulp-util "^3.0.1" - inquirer "^0.11.2" - through2 "0.6.2" - walk "2.3.3" - -gulp-cli@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/gulp-cli/-/gulp-cli-1.2.2.tgz#7392def6316c6e7939a4f296f3f540151ae3a275" - dependencies: - archy "^1.0.0" - chalk "^1.1.0" - fancy-log "^1.1.0" - gulplog "^1.0.0" - interpret "^1.0.0" - liftoff "^2.1.0" - lodash.isfunction "^3.0.8" - lodash.isplainobject "^4.0.4" - lodash.isstring "^4.0.1" - lodash.sortby "^4.5.0" - matchdep "^1.0.0" - mute-stdout "^1.0.0" - pretty-hrtime "^1.0.0" - semver-greatest-satisfied-range "^1.0.0" - tildify "^1.0.0" - v8flags "^2.0.9" - wreck "^6.3.0" - yargs "^3.28.0" - -gulp-concat@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/gulp-concat/-/gulp-concat-2.6.1.tgz#633d16c95d88504628ad02665663cee5a4793353" - dependencies: - concat-with-sourcemaps "^1.0.0" - through2 "^2.0.0" - vinyl "^2.0.0" - -gulp-sass@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/gulp-sass/-/gulp-sass-3.0.0.tgz#22adde0b2bcc40f9bb38435b49f48cd459e002d3" - dependencies: - gulp-util "^3.0" - lodash.clonedeep "^4.3.2" - node-sass "^4.0.0" - through2 "^2.0.0" - vinyl-sourcemaps-apply "^0.2.0" - -gulp-uglify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/gulp-uglify/-/gulp-uglify-2.0.0.tgz#cbe4aae4fe0b6bdd760335bc46f200fff699c4af" - dependencies: - gulplog "^1.0.0" - has-gulplog "^0.1.0" - lodash "^4.13.1" - make-error-cause "^1.1.1" - through2 "^2.0.0" - uglify-js "2.7.0" - uglify-save-license "^0.4.1" - vinyl-sourcemaps-apply "^0.2.0" - -gulp-util@^3.0, gulp-util@^3.0.0, gulp-util@^3.0.1: - version "3.0.8" - resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f" - dependencies: - array-differ "^1.0.0" - array-uniq "^1.0.2" - beeper "^1.0.0" - chalk "^1.0.0" - dateformat "^2.0.0" - fancy-log "^1.1.0" - gulplog "^1.0.0" - has-gulplog "^0.1.0" - lodash._reescape "^3.0.0" - lodash._reevaluate "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.template "^3.0.0" - minimist "^1.1.0" - multipipe "^0.1.2" - object-assign "^3.0.0" - replace-ext "0.0.1" - through2 "^2.0.0" - vinyl "^0.5.0" - -gulp@^3.9.1: - version "3.9.1" - resolved "https://registry.yarnpkg.com/gulp/-/gulp-3.9.1.tgz#571ce45928dd40af6514fc4011866016c13845b4" - dependencies: - archy "^1.0.0" - chalk "^1.0.0" - deprecated "^0.0.1" - gulp-util "^3.0.0" - interpret "^1.0.0" - liftoff "^2.1.0" - minimist "^1.1.0" - orchestrator "^0.3.0" - pretty-hrtime "^1.0.0" - semver "^4.1.0" - tildify "^1.0.0" - v8flags "^2.0.2" - vinyl-fs "^0.3.0" - -gulplog@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" - dependencies: - glogg "^1.0.0" - -har-validator@~2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" - dependencies: - chalk "^1.1.1" - commander "^2.9.0" - is-my-json-valid "^2.12.4" - pinkie-promise "^2.0.0" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - dependencies: - ansi-regex "^2.0.0" - -has-color@^0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/has-color/-/has-color-0.1.7.tgz#67144a5260c34fc3cca677d041daf52fe7b78b2f" - -has-gulplog@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" - dependencies: - sparkles "^1.0.0" - -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - -hawk@~3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" - dependencies: - boom "2.x.x" - cryptiles "2.x.x" - hoek "2.x.x" - sntp "1.x.x" - -hoek@2.x.x: - version "2.16.3" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" - -homedir-polyfill@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" - dependencies: - parse-passwd "^1.0.0" - -hosted-git-info@^2.1.4: - version "2.1.5" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.1.5.tgz#0ba81d90da2e25ab34a332e6ec77936e1598118b" - -htmlparser2@3.8.x: - version "3.8.3" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.8.3.tgz#996c28b191516a8be86501a7d79757e5c70c1068" - dependencies: - domelementtype "1" - domhandler "2.3" - domutils "1.5" - entities "1.0" - readable-stream "1.1" - -http-signature@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" - dependencies: - assert-plus "^0.2.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -in-publish@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51" - -indent-string@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" - dependencies: - repeating "^2.0.0" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b" - -inherits@2, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - -ini@^1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" - -inquirer@^0.11.2: - version "0.11.4" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.11.4.tgz#81e3374e8361beaff2d97016206d359d0b32fa4d" - dependencies: - ansi-escapes "^1.1.0" - ansi-regex "^2.0.0" - chalk "^1.0.0" - cli-cursor "^1.0.1" - cli-width "^1.0.1" - figures "^1.3.5" - lodash "^3.3.1" - readline2 "^1.0.1" - run-async "^0.1.0" - rx-lite "^3.1.2" - string-width "^1.0.1" - strip-ansi "^3.0.0" - through "^2.3.6" - -interpret@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c" - -invert-kv@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" - -is-absolute@^0.2.3: - version "0.2.6" - resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-0.2.6.tgz#20de69f3db942ef2d87b9c2da36f172235b1b5eb" - dependencies: - is-relative "^0.2.1" - is-windows "^0.2.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - -is-buffer@^1.0.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b" - -is-builtin-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" - dependencies: - builtin-modules "^1.0.0" - -is-dotfile@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d" - -is-equal-shallow@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" - dependencies: - is-primitive "^2.0.0" - -is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - -is-extglob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" - -is-finite@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - dependencies: - number-is-nan "^1.0.0" - -is-glob@^2.0.0, is-glob@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" - dependencies: - is-extglob "^1.0.0" - -is-my-json-valid@^2.12.4: - version "2.15.0" - resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.15.0.tgz#936edda3ca3c211fd98f3b2d3e08da43f7b2915b" - dependencies: - generate-function "^2.0.0" - generate-object-property "^1.1.0" - jsonpointer "^4.0.0" - xtend "^4.0.0" - -is-number@^2.0.2, is-number@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" - dependencies: - kind-of "^3.0.2" - -is-posix-bracket@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" - -is-primitive@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" - -is-property@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" - -is-relative@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-0.2.1.tgz#d27f4c7d516d175fb610db84bbeef23c3bc97aa5" - dependencies: - is-unc-path "^0.1.1" - -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - -is-unc-path@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-0.1.2.tgz#6ab053a72573c10250ff416a3814c35178af39b9" - dependencies: - unc-path-regex "^0.1.0" - -is-utf8@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - -is-windows@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - -isarray@1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - -isexe@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-1.1.2.tgz#36f3e22e60750920f5e7241a476a8c6a42275ad0" - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - dependencies: - isarray "1.0.0" - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - -jodid25519@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967" - dependencies: - jsbn "~0.1.0" - -jsbn@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.0.tgz#650987da0dd74f4ebf5a11377a2aa2d273e97dfd" - -jshint@^2.9.4: - version "2.9.4" - resolved "https://registry.yarnpkg.com/jshint/-/jshint-2.9.4.tgz#5e3ba97848d5290273db514aee47fe24cf592934" - dependencies: - cli "~1.0.0" - console-browserify "1.1.x" - exit "0.1.x" - htmlparser2 "3.8.x" - lodash "3.7.x" - minimatch "~3.0.2" - shelljs "0.3.x" - strip-json-comments "1.0.x" - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - -jsonpointer@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" - -jsprim@^1.2.2: - version "1.3.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.3.1.tgz#2a7256f70412a29ee3670aaca625994c4dcff252" - dependencies: - extsprintf "1.0.2" - json-schema "0.2.3" - verror "1.3.6" - -kind-of@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47" - dependencies: - is-buffer "^1.0.2" - -lazy-cache@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" - -lcid@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" - dependencies: - invert-kv "^1.0.0" - -liftoff@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-2.3.0.tgz#a98f2ff67183d8ba7cfaca10548bd7ff0550b385" - dependencies: - extend "^3.0.0" - findup-sync "^0.4.2" - fined "^1.0.1" - flagged-respawn "^0.3.2" - lodash.isplainobject "^4.0.4" - lodash.isstring "^4.0.1" - lodash.mapvalues "^4.4.0" - rechoir "^0.6.2" - resolve "^1.1.7" - -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - -lodash._basecopy@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" - -lodash._basetostring@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5" - -lodash._basevalues@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7" - -lodash._getnative@^3.0.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" - -lodash._isiterateecall@^3.0.0: - version "3.0.9" - resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" - -lodash._reescape@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reescape/-/lodash._reescape-3.0.0.tgz#2b1d6f5dfe07c8a355753e5f27fac7f1cde1616a" - -lodash._reevaluate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz#58bc74c40664953ae0b124d806996daca431e2ed" - -lodash._reinterpolate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" - -lodash._root@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" - -lodash.assign@^4.0.3, lodash.assign@^4.0.6, lodash.assign@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" - -lodash.assignwith@^4.0.7: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.assignwith/-/lodash.assignwith-4.2.0.tgz#127a97f02adc41751a954d24b0de17e100e038eb" - -lodash.clonedeep@^4.3.2: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - -lodash.escape@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" - dependencies: - lodash._root "^3.0.0" - -lodash.isarguments@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" - -lodash.isarray@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" - -lodash.isempty@^4.2.1: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" - -lodash.isfunction@^3.0.8: - version "3.0.8" - resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.8.tgz#4db709fc81bc4a8fd7127a458a5346c5cdce2c6b" - -lodash.isplainobject@^4.0.4: - version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - -lodash.isstring@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" - -lodash.keys@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" - dependencies: - lodash._getnative "^3.0.0" - lodash.isarguments "^3.0.0" - lodash.isarray "^3.0.0" - -lodash.mapvalues@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c" - -lodash.mergewith@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55" - -lodash.pick@^4.2.1: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" - -lodash.restparam@^3.0.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" - -lodash.sortby@^4.5.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" - -lodash.template@^3.0.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f" - dependencies: - lodash._basecopy "^3.0.0" - lodash._basetostring "^3.0.0" - lodash._basevalues "^3.0.0" - lodash._isiterateecall "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.escape "^3.0.0" - lodash.keys "^3.0.0" - lodash.restparam "^3.0.0" - lodash.templatesettings "^3.0.0" - -lodash.templatesettings@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz#fb307844753b66b9f1afa54e262c745307dba8e5" - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.escape "^3.0.0" - -lodash@3.7.x, lodash@^3.3.1: - version "3.7.0" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.7.0.tgz#3678bd8ab995057c07ade836ed2ef087da811d45" - -lodash@^4.0.0, lodash@^4.13.1: - version "4.17.4" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" - -lodash@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551" - -lodash@~4.16.4: - version "4.16.6" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.16.6.tgz#d22c9ac660288f3843e16ba7d2b5d06cca27d777" - -longest@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" - -loud-rejection@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" - dependencies: - currently-unhandled "^0.4.1" - signal-exit "^3.0.0" - -lru-cache@2: - version "2.7.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" - -lru-cache@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e" - dependencies: - pseudomap "^1.0.1" - yallist "^2.0.0" - -make-error-cause@^1.1.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/make-error-cause/-/make-error-cause-1.2.2.tgz#df0388fcd0b37816dff0a5fb8108939777dcbc9d" - dependencies: - make-error "^1.2.0" - -make-error@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.2.1.tgz#9a6dfb4844423b9f145806728d05c6e935670e75" - -map-cache@^0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - -map-obj@^1.0.0, map-obj@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" - -matchdep@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/matchdep/-/matchdep-1.0.1.tgz#a57a33804491fbae208aba8f68380437abc2dca5" - dependencies: - findup-sync "~0.3.0" - micromatch "^2.3.7" - resolve "~1.1.6" - stack-trace "0.0.9" - -meow@^3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" - dependencies: - camelcase-keys "^2.0.0" - decamelize "^1.1.2" - loud-rejection "^1.0.0" - map-obj "^1.0.1" - minimist "^1.1.3" - normalize-package-data "^2.3.4" - object-assign "^4.0.1" - read-pkg-up "^1.0.1" - redent "^1.0.0" - trim-newlines "^1.0.0" - -micromatch@^2.3.7: - version "2.3.11" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" - dependencies: - arr-diff "^2.0.0" - array-unique "^0.2.1" - braces "^1.8.2" - expand-brackets "^0.1.4" - extglob "^0.3.1" - filename-regex "^2.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.1" - kind-of "^3.0.2" - normalize-path "^2.0.1" - object.omit "^2.0.0" - parse-glob "^3.0.4" - regex-cache "^0.4.2" - -mime-db@~1.25.0: - version "1.25.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.25.0.tgz#c18dbd7c73a5dbf6f44a024dc0d165a1e7b1c392" - -mime-types@^2.1.12, mime-types@~2.1.7: - version "2.1.13" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.13.tgz#e07aaa9c6c6b9a7ca3012c69003ad25a39e92a88" - dependencies: - mime-db "~1.25.0" - -"minimatch@2 || 3", minimatch@^3.0.2, minimatch@~3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" - dependencies: - brace-expansion "^1.0.0" - -minimatch@^2.0.1: - version "2.0.10" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7" - dependencies: - brace-expansion "^1.0.0" - -minimatch@~0.2.11: - version "0.2.14" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.2.14.tgz#c74e780574f63c6f9a090e90efbe6ef53a6a756a" - dependencies: - lru-cache "2" - sigmund "~1.0.0" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - -minimist@^1.1.0, minimist@^1.1.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - -"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - dependencies: - minimist "0.0.8" - -ms@0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" - -multipipe@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" - dependencies: - duplexer2 "0.0.2" - -mute-stdout@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/mute-stdout/-/mute-stdout-1.0.0.tgz#5b32ea07eb43c9ded6130434cf926f46b2a7fd4d" - -mute-stream@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" - -nan@^2.3.2: - version "2.5.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.0.tgz#aa8f1e34531d807e9e27755b234b4a6ec0c152a8" - -natives@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/natives/-/natives-1.1.0.tgz#e9ff841418a6b2ec7a495e939984f78f163e6e31" - -node-gyp@^3.3.1: - version "3.4.0" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.4.0.tgz#dda558393b3ecbbe24c9e6b8703c71194c63fa36" - dependencies: - fstream "^1.0.0" - glob "^7.0.3" - graceful-fs "^4.1.2" - minimatch "^3.0.2" - mkdirp "^0.5.0" - nopt "2 || 3" - npmlog "0 || 1 || 2 || 3" - osenv "0" - path-array "^1.0.0" - request "2" - rimraf "2" - semver "2.x || 3.x || 4 || 5" - tar "^2.0.0" - which "1" - -node-sass@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.1.1.tgz#dc3e27d25bd827b6276ea243be357c7c7cd07111" - dependencies: - async-foreach "^0.1.3" - chalk "^1.1.1" - cross-spawn "^3.0.0" - gaze "^1.0.0" - get-stdin "^4.0.1" - glob "^7.0.3" - in-publish "^2.0.0" - lodash.assign "^4.2.0" - lodash.clonedeep "^4.3.2" - lodash.mergewith "^4.6.0" - meow "^3.7.0" - mkdirp "^0.5.1" - nan "^2.3.2" - node-gyp "^3.3.1" - npmlog "^4.0.0" - request "^2.61.0" - sass-graph "^2.1.1" - stdout-stream "^1.4.0" - -"nopt@2 || 3": - version "3.0.6" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" - dependencies: - abbrev "1" - -normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: - version "2.3.5" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.5.tgz#8d924f142960e1777e7ffe170543631cc7cb02df" - dependencies: - hosted-git-info "^2.1.4" - is-builtin-module "^1.0.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-path@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.0.1.tgz#47886ac1662760d4261b7d979d241709d3ce3f7a" - -"npmlog@0 || 1 || 2 || 3": - version "3.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-3.1.2.tgz#2d46fa874337af9498a2f12bb43d8d0be4a36873" - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.6.0" - set-blocking "~2.0.0" - -npmlog@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.0.2.tgz#d03950e0e78ce1527ba26d2a7592e9348ac3e75f" - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.1" - set-blocking "~2.0.0" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - -oauth-sign@~0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" - -object-assign@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" - -object-assign@^4.0.1, object-assign@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" - -object.omit@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" - dependencies: - for-own "^0.1.4" - is-extendable "^0.1.1" - -once@^1.3.0, once@~1.3.0: - version "1.3.3" - resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" - dependencies: - wrappy "1" - -onetime@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" - -orchestrator@^0.3.0: - version "0.3.8" - resolved "https://registry.yarnpkg.com/orchestrator/-/orchestrator-0.3.8.tgz#14e7e9e2764f7315fbac184e506c7aa6df94ad7e" - dependencies: - end-of-stream "~0.1.5" - sequencify "~0.0.7" - stream-consume "~0.1.0" - -ordered-read-streams@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz#fd565a9af8eb4473ba69b6ed8a34352cb552f126" - -os-homedir@^1.0.0, os-homedir@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - -os-locale@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" - dependencies: - lcid "^1.0.0" - -os-tmpdir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - -osenv@0: - version "0.1.4" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - -parse-filepath@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.1.tgz#159d6155d43904d16c10ef698911da1e91969b73" - dependencies: - is-absolute "^0.2.3" - map-cache "^0.2.0" - path-root "^0.1.1" - -parse-glob@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" - dependencies: - glob-base "^0.3.0" - is-dotfile "^1.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.0" - -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - dependencies: - error-ex "^1.2.0" - -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - -path-array@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-array/-/path-array-1.0.1.tgz#7e2f0f35f07a2015122b868b7eac0eb2c4fec271" - dependencies: - array-index "^1.0.0" - -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" - dependencies: - pinkie-promise "^2.0.0" - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - -path-root-regex@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" - -path-root@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" - dependencies: - path-root-regex "^0.1.0" - -path-type@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" - dependencies: - graceful-fs "^4.1.2" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - -preserve@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" - -pretty-hrtime@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" - -process-nextick-args@^1.0.6, process-nextick-args@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" - -pseudomap@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - -punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - -qs@~6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442" - -randomatic@^1.1.3: - version "1.1.6" - resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.6.tgz#110dcabff397e9dcff7c0789ccc0a49adf1ec5bb" - dependencies: - is-number "^2.0.2" - kind-of "^3.0.2" - -read-pkg-up@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" - dependencies: - find-up "^1.0.0" - read-pkg "^1.0.0" - -read-pkg@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" - dependencies: - load-json-file "^1.0.0" - normalize-package-data "^2.3.2" - path-type "^1.0.0" - -readable-stream@1.1, readable-stream@~1.1.9: - version "1.1.13" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -"readable-stream@>=1.0.28 <1.1.0-0": - version "1.0.34" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -"readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.1.5: - version "2.2.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.2.tgz#a9e6fec3c7dda85f8bb1b3ba7028604556fc825e" - dependencies: - buffer-shims "^1.0.0" - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - string_decoder "~0.10.x" - util-deprecate "~1.0.1" - -readline2@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - mute-stream "0.0.5" - -rechoir@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - dependencies: - resolve "^1.1.6" - -redent@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" - dependencies: - indent-string "^2.1.0" - strip-indent "^1.0.1" - -regex-cache@^0.4.2: - version "0.4.3" - resolved "http://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" - dependencies: - is-equal-shallow "^0.1.3" - is-primitive "^2.0.0" - -remove-trailing-separator@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.1.tgz#615ebb96af559552d4bf4057c8436d486ab63cc4" - -repeat-element@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" - -repeat-string@^1.5.2: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - dependencies: - is-finite "^1.0.0" - -replace-ext@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" - -replace-ext@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" - -request@2, request@^2.61.0: - version "2.79.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" - dependencies: - aws-sign2 "~0.6.0" - aws4 "^1.2.1" - caseless "~0.11.0" - combined-stream "~1.0.5" - extend "~3.0.0" - forever-agent "~0.6.1" - form-data "~2.1.1" - har-validator "~2.0.6" - hawk "~3.1.3" - http-signature "~1.1.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.7" - oauth-sign "~0.8.1" - qs "~6.3.0" - stringstream "~0.0.4" - tough-cookie "~2.3.0" - tunnel-agent "~0.4.1" - uuid "^3.0.0" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" - -resolve-dir@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-0.1.1.tgz#b219259a5602fac5c5c496ad894a6e8cc430261e" - dependencies: - expand-tilde "^1.2.2" - global-modules "^0.2.3" - -resolve@^1.1.6, resolve@^1.1.7: - version "1.2.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.2.0.tgz#9589c3f2f6149d1417a40becc1663db6ec6bc26c" - -resolve@~1.1.6: - version "1.1.7" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" - -restore-cursor@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" - dependencies: - exit-hook "^1.0.0" - onetime "^1.0.0" - -right-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" - dependencies: - align-text "^0.1.1" - -rimraf@2: - version "2.5.4" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" - dependencies: - glob "^7.0.5" - -run-async@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" - dependencies: - once "^1.3.0" - -rx-lite@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" - -sass-graph@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.1.2.tgz#965104be23e8103cb7e5f710df65935b317da57b" - dependencies: - glob "^7.0.0" - lodash "^4.0.0" - yargs "^4.7.1" - -semver-greatest-satisfied-range@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.0.0.tgz#4fb441e2a8d26c40b598327557318de272a558a0" - dependencies: - semver "^4.2.0" - semver-regex "^1.0.0" - -semver-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-1.0.0.tgz#92a4969065f9c70c694753d55248fc68f8f652c9" - -"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^4.1.0, semver@^4.2.0: - version "4.3.6" - resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" - -sequencify@~0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/sequencify/-/sequencify-0.0.7.tgz#90cff19d02e07027fd767f5ead3e7b95d1e7380c" - -set-blocking@^2.0.0, set-blocking@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - -shelljs@0.3.x: - version "0.3.0" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.3.0.tgz#3596e6307a781544f591f37da618360f31db57b1" - -sigmund@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" - -signal-exit@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - -sntp@1.x.x: - version "1.0.9" - resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" - dependencies: - hoek "2.x.x" - -source-map@^0.5.1, source-map@~0.5.1: - version "0.5.6" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" - -sparkles@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3" - -spdx-correct@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" - dependencies: - spdx-license-ids "^1.0.2" - -spdx-expression-parse@~1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" - -spdx-license-ids@^1.0.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" - -sshpk@^1.7.0: - version "1.10.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.10.1.tgz#30e1a5d329244974a1af61511339d595af6638b0" - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - dashdash "^1.12.0" - getpass "^0.1.1" - optionalDependencies: - bcrypt-pbkdf "^1.0.0" - ecc-jsbn "~0.1.1" - jodid25519 "^1.0.0" - jsbn "~0.1.0" - tweetnacl "~0.14.0" - -stack-trace@0.0.9: - version "0.0.9" - resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.9.tgz#a8f6eaeca90674c333e7c43953f275b451510695" - -stdout-stream@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.0.tgz#a2c7c8587e54d9427ea9edb3ac3f2cd522df378b" - dependencies: - readable-stream "^2.0.1" - -stream-consume@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/stream-consume/-/stream-consume-0.1.0.tgz#a41ead1a6d6081ceb79f65b061901b6d8f3d1d0f" - -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - -stringstream@~0.0.4: - version "0.0.5" - resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - dependencies: - ansi-regex "^2.0.0" - -strip-bom@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-1.0.0.tgz#85b8862f3844b5a6d5ec8467a93598173a36f794" - dependencies: - first-chunk-stream "^1.0.0" - is-utf8 "^0.2.0" - -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - dependencies: - is-utf8 "^0.2.0" - -strip-indent@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" - dependencies: - get-stdin "^4.0.1" - -strip-json-comments@1.0.x: - version "1.0.4" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" - -supports-color@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a" - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - -tar@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" - dependencies: - block-stream "*" - fstream "^1.0.2" - inherits "2" - -textarea-caret@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/textarea-caret/-/textarea-caret-3.0.2.tgz#f360c48699aa1abf718680a43a31a850665c2caf" - -through2@0.6.2, through2@^0.6.1: - version "0.6.2" - resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.2.tgz#53265824c555e7fcdc4111dcdc52c7de64636c75" - dependencies: - readable-stream ">=1.0.28 <1.1.0-0" - xtend ">=4.0.0 <4.1.0-0" - -through2@^2.0.0, through2@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" - dependencies: - readable-stream "^2.1.5" - xtend "~4.0.1" - -through@^2.3.6: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - -tildify@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/tildify/-/tildify-1.2.0.tgz#dcec03f55dca9b7aa3e5b04f21817eb56e63588a" - dependencies: - os-homedir "^1.0.0" - -time-stamp@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.0.1.tgz#9f4bd23559c9365966f3302dbba2b07c6b99b151" - -tough-cookie@~2.3.0: - version "2.3.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" - dependencies: - punycode "^1.4.1" - -trim-newlines@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" - -tunnel-agent@~0.4.1: - version "0.4.3" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - -uglify-js@2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.7.0.tgz#f021e38ba2ca740860f5bd5c695c2a817345f0ec" - dependencies: - async "~0.2.6" - source-map "~0.5.1" - uglify-to-browserify "~1.0.0" - yargs "~3.10.0" - -uglify-save-license@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/uglify-save-license/-/uglify-save-license-0.4.1.tgz#95726c17cc6fd171c3617e3bf4d8d82aa8c4cce1" - -uglify-to-browserify@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" - -unc-path-regex@^0.1.0: - version "0.1.2" - resolved "http://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" - -unique-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-1.0.0.tgz#d59a4a75427447d9aa6c91e70263f8d26a4b104b" - -user-home@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - -uuid@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" - -v8flags@^2.0.2, v8flags@^2.0.9: - version "2.0.11" - resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.0.11.tgz#bca8f30f0d6d60612cc2c00641e6962d42ae6881" - dependencies: - user-home "^1.1.1" - -validate-npm-package-license@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" - dependencies: - spdx-correct "~1.0.0" - spdx-expression-parse "~1.0.0" - -verror@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" - dependencies: - extsprintf "1.0.2" - -vinyl-fs@^0.3.0: - version "0.3.14" - resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-0.3.14.tgz#9a6851ce1cac1c1cea5fe86c0931d620c2cfa9e6" - dependencies: - defaults "^1.0.0" - glob-stream "^3.1.5" - glob-watcher "^0.0.6" - graceful-fs "^3.0.0" - mkdirp "^0.5.0" - strip-bom "^1.0.0" - through2 "^0.6.1" - vinyl "^0.4.0" - -vinyl-sourcemaps-apply@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz#ab6549d61d172c2b1b87be5c508d239c8ef87705" - dependencies: - source-map "^0.5.1" - -vinyl@^0.4.0: - version "0.4.6" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.4.6.tgz#2f356c87a550a255461f36bbeb2a5ba8bf784847" - dependencies: - clone "^0.2.0" - clone-stats "^0.0.1" - -vinyl@^0.5.0: - version "0.5.3" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.5.3.tgz#b0455b38fc5e0cf30d4325132e461970c2091cde" - dependencies: - clone "^1.0.0" - clone-stats "^0.0.1" - replace-ext "0.0.1" - -vinyl@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.0.1.tgz#1c3b4931e7ac4c1efee743f3b91a74c094407bb6" - dependencies: - clone "^1.0.0" - clone-buffer "^1.0.0" - clone-stats "^1.0.0" - cloneable-readable "^1.0.0" - is-stream "^1.1.0" - remove-trailing-separator "^1.0.1" - replace-ext "^1.0.0" - -walk@2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/walk/-/walk-2.3.3.tgz#b4c0e8c42464c16dbbe1d71666765eac07819e5f" - dependencies: - foreachasync "3.x" - -which-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" - -which@1, which@^1.2.12, which@^1.2.9: - version "1.2.12" - resolved "https://registry.yarnpkg.com/which/-/which-1.2.12.tgz#de67b5e450269f194909ef23ece4ebe416fa1192" - dependencies: - isexe "^1.1.1" - -wide-align@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.0.tgz#40edde802a71fea1f070da3e62dcda2e7add96ad" - dependencies: - string-width "^1.0.1" - -window-size@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" - -window-size@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" - -window-size@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" - -wordwrap@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" - -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - -wreck@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/wreck/-/wreck-6.3.0.tgz#a1369769f07bbb62d6a378336a7871fc773c740b" - dependencies: - boom "2.x.x" - hoek "2.x.x" - -"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" - -y18n@^3.2.0, y18n@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" - -yallist@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.0.0.tgz#306c543835f09ee1a4cb23b7bce9ab341c91cdd4" - -yargs-parser@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-2.4.1.tgz#85568de3cf150ff49fa51825f03a8c880ddcc5c4" - dependencies: - camelcase "^3.0.0" - lodash.assign "^4.0.6" - -yargs@^3.28.0: - version "3.32.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" - dependencies: - camelcase "^2.0.1" - cliui "^3.0.3" - decamelize "^1.1.1" - os-locale "^1.4.0" - string-width "^1.0.1" - window-size "^0.1.4" - y18n "^3.2.0" - -yargs@^4.7.1: - version "4.8.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-4.8.1.tgz#c0c42924ca4aaa6b0e6da1739dfb216439f9ddc0" - dependencies: - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - lodash.assign "^4.0.3" - os-locale "^1.4.0" - read-pkg-up "^1.0.1" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^1.0.1" - which-module "^1.0.0" - window-size "^0.2.0" - y18n "^3.2.1" - yargs-parser "^2.4.1" - -yargs@~3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" - dependencies: - camelcase "^1.0.2" - cliui "^2.1.0" - decamelize "^1.0.0" - window-size "0.1.0" |